了解如何在 IoT Edge 中部署模块和建立路由

适用于:IoT Edge 1.6 勾选 IoT Edge 1.6

重要

IoT Edge 1.6 LTS 是支持的版本。 IoT Edge 1.5 LTS 支持将于 2026 年 11 月 10 日结束;IoT Edge 1.4 LTS 于 2024 年 11 月 12 日结束了支持期。 如果使用的是早期版本,请参阅 Update IoT Edge。

每个 IoT Edge 设备至少运行两个模块:$edgeAgent 和 $edgeHub,它们都是 IoT Edge 运行时的一部分。 IoT Edge 设备可以针对不同的进程运行多个模块。 使用部署清单告知设备要安装哪些模块以及如何将其设置为协同工作。

部署清单是一个 JSON 文档,用于描述以下内容:

  • IoT Edge 代理模块孪生,其中包含三个组件:
    • 设备上运行的每个模块的容器映像。
    • 用于访问包含模块镜像的私有容器注册表的凭据。
    • 有关如何创建和管理每个模块的说明。
  • IoT Edge hub 孪生模块,包括消息在模块之间和到 IoT 中心的流动方式。
  • 任何额外的模块孪生的所需属性(可选)。

所有 IoT Edge 设备都需要部署清单。 新安装的IoT Edge运行时会显示错误代码,直到设置有效的配置文件。

在 Azure IoT Edge 教程中,使用 Azure IoT Edge 门户中的向导生成部署清单。 还可以使用 REST 或 IoT 中心服务 SDK 以编程方式应用部署清单。 有关详细信息,请参阅了解 IoT Edge 部署。

创建部署清单。

部署清单是使用所需属性设置的模块孪生的列表。 它告知 IoT Edge 设备或设备组要安装哪些模块以及如何设置它们。 部署清单包含每个模块孪生的所需属性。 IoT Edge 设备报告每个模块 的报告属性 。

每个部署清单都需要两个模块: $edgeAgent 和 $edgeHub。 这些模块属于管理 IoT Edge 设备及其上运行的模块的 IoT Edge 运行时。 有关这些模块的详细信息,请参阅了解 IoT Edge 运行时及其体系结构。

除了两个运行时模块外,还可以添加最多 50 个在 IoT Edge 设备上运行的模块。

只有包含 IoT Edge 运行时($edgeAgent 和 $edgeHub)的部署清单才是有效的。

部署清单使用此格式:

{
  "modulesContent": {
    "$edgeAgent": { // required
      "properties.desired": {
        // desired properties of the IoT Edge agent
        // includes the image URIs of all deployed modules
        // includes container registry credentials
      }
    },
    "$edgeHub": { //required
      "properties.desired": {
        // desired properties of the IoT Edge hub
        // includes the routing information between modules and to IoT Hub
      }
    },
    "module1": {  // optional
      "properties.desired": {
        // desired properties of module1
      }
    },
    "module2": {  // optional
      "properties.desired": {
        // desired properties of module2
      }
    }
  }
}

配置模块

定义 IoT Edge 运行时如何在部署中安装模块。 IoT Edge 代理是管理 IoT Edge 设备的安装、更新和状态报告的运行时组件。 因此, $edgeAgent 模块孪生具有所有模块的配置和管理信息。 此信息包括 IoT Edge 代理本身的配置参数。

这些 $edgeAgent 属性使用此格式:

{
  "modulesContent": {
    "$edgeAgent": {
      "properties.desired": {
        "schemaVersion": "1.1",
        "runtime": {
          "settings":{
            "registryCredentials":{
              // let the IoT Edge agent use container images that aren't public
            }
          }
        },
        "systemModules": {
          "edgeAgent": {
            // configuration and management details
          },
          "edgeHub": {
            // configuration and management details
          }
        },
        "modules": {
          "module1": {
            // configuration and management details
          },
          "module2": {
            // configuration and management details
          }
        }
      }
    },
    "$edgeHub": { ... },
    "module1": { ... },
    "module2": { ... }
  }
}

IoT Edge 代理架构版本 1.1 随 IoT Edge 版本 1.0.10 一起发布,可让你设置模块启动顺序。 对运行版本 1.0.1 或更高版本的任何 IoT Edge 部署使用架构版本 1.1。

模块配置和管理

定义在 IoT Edge 设备上运行的模块,以及如何在 IoT Edge 代理所需属性列表中设置和管理这些模块。

有关可以或必须包含的所需属性的完整列表,请参阅 IoT Edge 代理和 IoT Edge 中心的属性。

例如:

{
  "modulesContent": {
    "$edgeAgent": {
      "properties.desired": {
        "schemaVersion": "1.1",
        "runtime": { ... },
        "systemModules": {
          "edgeAgent": { ... },
          "edgeHub": { ... }
        },
        "modules": {
          "module1": {
            "version": "1.0",
            "type": "docker",
            "status": "running",
            "restartPolicy": "always",
            "startupOrder": 2,
            "settings": {
              "image": "myacr.azurecr.cn/module1:latest",
              "createOptions": "{}"
            }
          },
          "module2": { ... }
        }
      }
    },
    "$edgeHub": { ... },
    "module1": { ... },
    "module2": { ... }
  }
}

每个模块都有一个 设置 属性,其中包含模块 映像、容器注册表中容器映像的地址,以及用于在启动时设置映像的任何 createOptions 。 若要了解详细信息,请参阅如何配置 IoT Edge 模块的容器创建选项。

EdgeHub 模块和自定义模块还具有三个属性,这些属性告诉 IoT Edge 代理如何管理它们:

  • 状态:模块在首次部署时是运行还是停止。 必需。

  • RestartPolicy:规定 IoT Edge 代理在模块停止时重启模块的时间和条件。 如果模块停止且没有任何错误,则不会自动重启。 有关详细信息,请参阅 Docker 文档 - 自动启动容器。 必需。

  • StartupOrder:在 IoT Edge 版本 1.0.10 中引入。 IoT Edge 代理在首次部署时用于启动模块的顺序。 顺序使用整数,其中启动值为 0 的模块先启动,然后跟着更高的数字。 $edgeAgent模块没有启动值,因为它始终先启动。 可选。

    IoT Edge 代理按启动值的顺序启动模块,但不会等待每个模块在启动下一个模块之前完成启动。

    启动顺序在某些模块依赖于其他模块时很有帮助。 例如,你可能希望 edgeHub 模块首先启动,以便在其他模块启动时路由消息。 或者,在启动将数据发送到它的模块之前,可能需要启动存储模块。 但始终将模块设计为处理其他模块的故障。 容器可以随时停止并重新启动,次数不限。

    注意

    更改模块的属性将重启该模块。 例如,如果更改以下项的属性,则会发生重启:

    • 模块图像
    • Docker 创建选项
    • 环境变量
    • 重启策略
    • 映像拉取策略
    • 版本
    • 启动顺序

    如果未更改任何模块属性,则不会触发模块重启。

声明路由

IoT Edge 中心管理模块、IoT 中心和下游设备之间的通信。 $edgeHub模块的实例(模块孪生)具有一个用于定义消息如何在部署中传递的所需属性routes。 可以在同一部署中设置多个路由。

使用以下语法在 $edgeHub 所需属性中声明路由:

{
  "modulesContent": {
    "$edgeAgent": { ... },
    "$edgeHub": {
      "properties.desired": {
        "schemaVersion": "1.1",
        "routes": {
          "route1": "FROM <source> WHERE <condition> INTO <sink>",
          "route2": {
            "route": "FROM <source> WHERE <condition> INTO <sink>",
            "priority": 0,
            "timeToLiveSecs": 86400
          }
        },
        "storeAndForwardConfiguration": {
          "timeToLiveSecs": 10
        }
      }
    },
    "module1": { ... },
    "module2": { ... }
  }
}

使用 IoT Edge 版本 1.0.10 发布的 IoT Edge Hub 架构版本 1,允许设置路由优先级和生存时间。 对运行版本 1.0.1 或更高版本的任何 IoT Edge 部署使用架构版本 1.1。

每个路由都需要传入消息的 源 和传出消息的 接收器 。 条件是可选的,可用于筛选消息。

将 优先级 分配给路由,以便首先处理重要消息。 当上游连接薄弱或有限时,此功能有助于确定关键数据优先于标准遥测消息。

源

源指定消息来自何处。 IoT Edge 可以路由来自模块或下游设备的消息。

通过使用 IoT SDK,模块可以使用类为其消息 ModuleClient 设置特定的输出队列。 不需要输出队列,但它们有助于管理多个路由。 下游设备使用 IoT SDK 中的 DeviceClient 类将消息发送到IoT Edge网关设备,就像将消息发送到IoT 中心一样。 有关详细信息,请参阅了解和使用 Azure IoT 中心 SDK。

源属性可以使用以下任一值:

源 描述
/* 来自任何模块或下游设备的所有设备到云消息或孪生更改通知。
/twinChangeNotifications 来自任何模块或下游设备的任何孪生更改(报告属性)。
/messages/* 模块通过部分或无输出或下游设备发送的任何设备到云消息。
/messages/modules/* 模块通过部分或无输出发送的任何设备到云消息。
/messages/modules/<moduleId>/* 通过某个或没有输出的特定模块发送的任何设备到云的消息。
/messages/modules/<moduleId>/outputs/* 特定模块通过某个输出发送的任何设备到云的消息。
/messages/modules/<moduleId>/outputs/<output> 特定模块通过特定输出发送的任何设备到云消息。

条件

条件在路由声明中是可选的。 若要将所有消息从源传递到接收器,请省略 WHERE 子句。 或者,使用 IoT 中心 查询语言筛选满足条件的消息或消息类型。 IoT Edge 路由不支持基于孪生标记或属性筛选消息。

在 IoT Edge 中的模块之间移动的消息使用与设备和 Azure IoT 中心之间的消息相同的格式。 所有消息都使用 JSON 格式,并具有 systemProperties、 appProperties 和 正文 参数。

使用以下语法围绕这三个参数中的任何一个生成查询:

  • 系统属性:$<propertyName> 或 {$<propertyName>}
  • 应用程序属性:<propertyName>
  • 正文属性:$body.<propertyName>

有关如何为消息属性创建查询的示例,请参阅 设备到云的消息路由查询表达式。

例如,你可能想要筛选从下游设备到达网关设备的消息。 从模块发送的消息包含名为 connectionModuleId 的系统属性。 若要将消息从下游设备直接路由到 IoT 中心并排除模块消息,请使用以下路由:

FROM /messages/* WHERE NOT IS_DEFINED($connectionModuleId) INTO $upstream

汇聚点

消息汇聚点定义消息的发送目标。 只有模块和 IoT 中心可以接收消息。 无法将消息路由到其他设备。 接收器属性不支持通配符。

接收器属性可以使用以下任何值:

汇聚点 描述
$upstream 将消息发送到 IoT 中心
BrokeredEndpoint("/modules/<moduleId>/inputs/<input>") 将消息发送到特定模块的特定输入

IoT Edge 提供至少一次保证。 如果路由无法将消息传送到其接收器,IoT Edge 中心会将消息存储在本地。 例如,如果 IoT Edge 中心无法连接到 IoT 中心或目标模块未连接。

IoT Edge 中心会一直存储消息,直到达到在 storeAndForwardConfiguration.timeToLiveSecs的 属性中设置的时间。

优先级和存活时间

将路由声明为定义路由的字符串,或者声明为具有路由字符串、优先级整数和生存时间整数的对象。

选项 1

"route1": "FROM <source> WHERE <condition> INTO <sink>",

选项 2 (IoT Edge 版本 1.0.10 中引入了 IoT Edge 中心架构版本 1.1)

"route2": {
  "route": "FROM <source> WHERE <condition> INTO <sink>",
  "priority": 0,
  "timeToLiveSecs": 86400
}

优先级 值范围为 0 到 9,其中 0 是最高优先级。 系统按终结点对消息进行排队。 系统处理特定终结点的所有优先级 0 消息,然后再处理同一终结点的任何优先级 1 消息。 如果同一终结点的多个路由具有相同的优先级,则系统会按照消息到达的顺序处理消息。 如果未设置优先级,路由将使用最低优先级。

timeToLiveSecs 属性使用 IoT Edge 中心的 storeAndForwardConfiguration 中的值,除非直接设置该值。 该值可以是任何正整数。

有关如何管理优先级队列的详细信息,请参阅 路由优先级和生存时间。

定义或更新所需属性

部署清单为部署到 IoT Edge 设备的每个模块设置所需的属性。 部署清单中的所需属性会覆盖模块孪生中当前存在的任何所需属性。

如果未在部署清单中设置模块孪生的所需属性,IoT 中心不会更改模块孪生。 而是以编程方式设置所需的属性。

允许更改设备孪生的相同机制也允许更改模块孪生。 有关详细信息,请参阅模块孪生开发人员指南。

部署清单示例

以下示例显示了有效的部署清单文档的外观。

{
  "modulesContent": {
    "$edgeAgent": {
      "properties.desired": {
        "schemaVersion": "1.1",
        "runtime": {
          "type": "docker",
          "settings": {
            "minDockerVersion": "v1.25",
            "loggingOptions": "",
            "registryCredentials": {
              "ContosoRegistry": {
                "username": "myacr",
                "password": "<password>",
                "address": "myacr.azurecr.cn"
              }
            }
          }
        },
        "systemModules": {
          "edgeAgent": {
            "type": "docker",
            "settings": {
              "image": "mcr.microsoft.com/azureiotedge-agent:1.6",
              "createOptions": "{}"
            }
          },
          "edgeHub": {
            "type": "docker",
            "status": "running",
            "restartPolicy": "always",
            "startupOrder": 0,
            "settings": {
              "image": "mcr.microsoft.com/azureiotedge-hub:1.6",
              "createOptions": "{\"HostConfig\":{\"PortBindings\":{\"443/tcp\":[{\"HostPort\":\"443\"}],\"5671/tcp\":[{\"HostPort\":\"5671\"}],\"8883/tcp\":[{\"HostPort\":\"8883\"}]}}}"
            }
          }
        },
        "modules": {
          "SimulatedTemperatureSensor": {
            "version": "1.6",
            "type": "docker",
            "status": "running",
            "restartPolicy": "always",
            "startupOrder": 2,
            "settings": {
              "image": "mcr.microsoft.com/azureiotedge-simulated-temperature-sensor:1.6",
              "createOptions": "{}"
            }
          },
          "filtermodule": {
            "version": "1.0",
            "type": "docker",
            "status": "running",
            "restartPolicy": "always",
            "startupOrder": 1,
            "env": {
              "tempLimit": {"value": "100"}
            },
            "settings": {
              "image": "myacr.azurecr.cn/filtermodule:latest",
              "createOptions": "{}"
            }
          }
        }
      }
    },
    "$edgeHub": {
      "properties.desired": {
        "schemaVersion": "1.1",
        "routes": {
          "sensorToFilter": {
            "route": "FROM /messages/modules/SimulatedTemperatureSensor/outputs/temperatureOutput INTO BrokeredEndpoint(\"/modules/filtermodule/inputs/input1\")",
            "priority": 0,
            "timeToLiveSecs": 1800
          },
          "filterToIoTHub": {
            "route": "FROM /messages/modules/filtermodule/outputs/output1 INTO $upstream",
            "priority": 1,
            "timeToLiveSecs": 1800
          }
        },
        "storeAndForwardConfiguration": {
          "timeToLiveSecs": 100
        }
      }
    }
  }
}

后续步骤