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

适用于:IoT Edge 1.5 复选标记IoT Edge 1.5

重要

IoT Edge 1.5 LTS 是受支持的版本。 IoT Edge 1.4 LTS 的生命周期结束日期为 2024 年 11 月 12 日。 如果你使用的是早期版本,请参阅更新 IoT Edge

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

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

  • IoT Edge 代理模块孪生,其中包含三个组件:
    • 设备上运行的每个模块的容器映像
    • 用于访问具有模块映像的私有容器注册表的凭据
    • 有关如何创建和管理每个模块的说明
  • IoT Edge 中心模块孪生:描述消息如何在模块之间流动,并传送到 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.io/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模块孪生具有一个名为 路由 的所需属性,用于定义消息在部署中移动的方式。 可以在同一部署中设置多个路由。

使用以下语法在 $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 格式,并具有 systemPropertiesappProperties正文 参数。

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

  • 系统属性:$<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 中心会一直存储消息,直到达到在 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.io"
              }
            }
          }
        },
        "systemModules": {
          "edgeAgent": {
            "type": "docker",
            "settings": {
              "image": "mcr.microsoft.com/azureiotedge-agent:1.5",
              "createOptions": "{}"
            }
          },
          "edgeHub": {
            "type": "docker",
            "status": "running",
            "restartPolicy": "always",
            "startupOrder": 0,
            "settings": {
              "image": "mcr.microsoft.com/azureiotedge-hub:1.5",
              "createOptions": "{\"HostConfig\":{\"PortBindings\":{\"443/tcp\":[{\"HostPort\":\"443\"}],\"5671/tcp\":[{\"HostPort\":\"5671\"}],\"8883/tcp\":[{\"HostPort\":\"8883\"}]}}}"
            }
          }
        },
        "modules": {
          "SimulatedTemperatureSensor": {
            "version": "1.5",
            "type": "docker",
            "status": "running",
            "restartPolicy": "always",
            "startupOrder": 2,
            "settings": {
              "image": "mcr.microsoft.com/azureiotedge-simulated-temperature-sensor:1.5",
              "createOptions": "{}"
            }
          },
          "filtermodule": {
            "version": "1.0",
            "type": "docker",
            "status": "running",
            "restartPolicy": "always",
            "startupOrder": 1,
            "env": {
              "tempLimit": {"value": "100"}
            },
            "settings": {
              "image": "myacr.azurecr.io/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
        }
      }
    }
  }
}

后续步骤