教學課程:將連線的登錄部署至巢狀 IoT Edge 階層

在本教學課程中,您會使用 Azure CLI 命令為 Azure IoT Edge 裝置建立兩層式的階層,並將連線的登錄部署為各層的模組。 在此案例中,最上層的裝置會與雲端登錄通訊。 較低層的裝置會與最上層中連線的登錄父代通訊。

如需搭配使用連線的登錄與 IoT Edge 的概觀,請參閱搭配使用連線的登錄與 Azure IoT Edge

必要條件

  • Azure IoT 中樞。 如需部署步驟,請參閱使用 Azure 入口網站建立 IoT 中樞

  • Azure 中的兩個連線登錄資源。 如需部署步驟,請參閱使用 Azure CLIAzure 入口網站的快速入門。

    • 就最上層而言,連線的登錄可以處於 ReadWrite 或 ReadOnly 模式。 本文假設處於 ReadWrite 模式,且連線的登錄名稱儲存在環境變數 $CONNECTED_REGISTRY_RW 中。
    • 就較低層而言,連線的登錄必須處於 ReadOnly 模式。 本文假設連線的登錄名稱儲存在環境變數 $CONNECTED_REGISTRY_RO 中。

將映像匯入至雲端登錄

使用 az acr import 命令,將下列容器映像匯入至您的雲端登錄。 如果您已匯入這些映像,請略過此步驟。

連線的登錄映像

若要支援巢狀 IoT Edge 案例,連線登錄執行階段的容器映像必須可在您的私人 Azure 容器登錄中使用。 使用 az acr import 命令,將連線的登錄映像匯入您的私人登錄中。

# Use the REGISTRY_NAME variable in the following Azure CLI commands to identify the registry
REGISTRY_NAME=<container-registry-name>

az acr import \
  --name $REGISTRY_NAME \
  --source mcr.microsoft.com/acr/connected-registry:0.8.0

IoT Edge 和 API Proxy 映像

若要在巢狀 IoT Edge 上支援連線的登錄,您必須為 IoT Edge 和 API Proxy 部署模組。 將這些映像匯入您的私人登錄中。

IoT Edge API Proxy 模組可讓 IoT Edge 裝置使用 HTTPS 通訊協定在相同的連接埠 (例如 443) 上公開多個服務。

az acr import \
  --name $REGISTRY_NAME \
  --source mcr.microsoft.com/azureiotedge-agent:1.2.4

az acr import \
  --name $REGISTRY_NAME \
  --source mcr.microsoft.com/azureiotedge-hub:1.2.4

az acr import \
  --name $REGISTRY_NAME \
  --source mcr.microsoft.com/azureiotedge-api-proxy:1.1.2

az acr import \
  --name $REGISTRY_NAME \
  --source mcr.microsoft.com/azureiotedge-diagnostics:1.2.4

hello-world 映像

若要測試連線的登錄,請匯入 hello-world 映像。 此存放庫將會同步處理至連線的登錄,並由連線的登錄用戶端提取。

az acr import \
  --name $REGISTRY_NAME \
  --source mcr.microsoft.com/hello-world:1.1.2

擷取連線的登錄設定

若要將每個連線的登錄部署至階層中的 IoT Edge 裝置,您必須從 Azure 中的連線登錄資源擷取組態設定。 如有需要,請對每個連線的登錄執行 az acr connected-registry get-settings 命令,以擷取設定。

根據預設,設定資訊不包含同步權杖密碼,而在部署連線的登錄時也需要此密碼。 選擇性地傳遞 --generate-password 1--generate-password 2 參數以產生其中一個密碼。 將產生的密碼儲存到安全的位置。 該密碼無法再次擷取。

警告

若重新產生密碼,將會輪換同步權杖認證。 如果您使用先前的密碼來設定裝置,則需要更新設定。

# Use the REGISTRY_NAME variable in the following Azure CLI commands to identify the registry
REGISTRY_NAME=<container-registry-name>

# Run the command for each registry resource in the hierarchy

az acr connected-registry get-settings \
  --registry $REGISTRY_NAME \
  --name $CONNECTED_REGISTRY_RW \
  --parent-protocol https

az acr connected-registry get-settings \
  --registry $REGISTRY_NAME \
  --name $CONNECTED_REGISTRY_RO \
  --parent-protocol https

命令輸出包含登錄連接字串和相關設定。 下列範例輸出顯示名為 myconnectedregistry、且父登錄為 contosoregistry 之連線登錄的連接字串:

{
  "ACR_REGISTRY_CONNECTION_STRING": "ConnectedRegistryName=myconnectedregistry;SyncTokenName=myconnectedregistry-sync-token;SyncTokenPassword=xxxxxxxxxxxxxxxx;ParentGatewayEndpoint=contosoregistry.eastus.data.azurecr.io;ParentEndpointProtocol=https"
}

設定部署資訊清單

部署資訊清單是一份 JSON 文件,說明要部署至 IoT Edge 裝置的模組。 如需詳細資訊,請參閱了解如何使用、設定以及重複使用 IoT Edge 模組

若要使用 Azure CLI 在每個 IoT Edge 裝置上部署連線的登錄模組,請在本機將下列部署資訊清單儲存為 JSON 檔案。 使用先前幾節中的資訊,更新每個資訊清單中的相關 JSON 值。 若要執行命令以將設定套用至裝置,請使用下一節中的檔案路徑。

最上層的部署資訊清單

對於最上層的裝置,請使用下列內容建立部署資訊清單檔 deploymentTopLayer.json。 此資訊清單類似於快速入門:將連線的登錄部署至 IoT Edge 裝置中使用的資訊清單。

注意

如果您已使用快速入門將連線的登錄部署至最上層 IoT Edge 裝置,您可以在巢狀階層的最上層使用該登錄。 請修改本教學課程中的部署步驟,在階層中加以設定 (未顯示)。

連線的登錄模組設定

  • 使用先前幾節中的權杖認證和連接字串,更新 env 節點中的相關 JSON 值。

  • 在節點 env 中,下列環境變數是選擇性的:

    變數 描述
    ACR_REGISTRY_LOGIN_SERVER 指定唯一的主機名稱或 FQDN。 如果使用,連線的登錄只會接受對此登入伺服器值提出的要求。

    若未提供任何值,則可以使用任何登入伺服器值來存取連線的登錄。
    ACR_REGISTRY_CERTIFICATE_VOLUME 如果連線的登錄可透過 HTTPS 來存取,請指向 HTTPS 憑證存放所在的磁碟區。

    若未設定,預設位置為 /var/acr/certs
    ACR_REGISTRY_DATA_VOLUME 覆寫連線的登錄將儲存映像的預設位置 /var/acr/data

    此位置必須符合容器的磁碟區繫結。

    重要

    如果連線的登錄接聽 80 和 443 以外的連接埠,則 ACR_REGISTRY_LOGIN_SERVER 值 (如果指定) 必須包含該連接埠。 範例:192.168.0.100:8080

  • 若未使用 API Proxy 模組,則應為連線的登錄設定 HostPort 繫結。 範例:

     "createOptions": "{\"HostConfig\":{\"Binds\":[\"/home/azureuser/connected-registry:/var/acr/data\"],\"PortBindings\":{\"8080/tcp\":[{\"HostPort\":\"8080\"}]}}}"
    

API Proxy 模組設定

  • API Proxy 會在設定為 NGINX_DEFAULT_PORT 的連接埠 8000 上接聽。 如需 API Proxy 設定的詳細資訊,請參閱 IoT Edge GitHub 存放庫
{
    "modulesContent": {
        "$edgeAgent": {
            "properties.desired": {
                "modules": {
                    "connected-registry": {
                        "settings": {
                            "image": "<REPLACE_WITH_CLOUD_REGISTRY_NAME>.azurecr.io/acr/connected-registry:0.8.0",
                            "createOptions": "{\"HostConfig\":{\"Binds\":[\"/home/azureuser/connected-registry:/var/acr/data\"]}}"
                        },
                        "type": "docker",
                        "env": {
                            "ACR_REGISTRY_CONNECTION_STRING": {
                                "value": "ConnectedRegistryName=<REPLACE_WITH_CONNECTED_REGISTRY_NAME>;SyncTokenName=<REPLACE_WITH_SYNC_TOKEN_NAME>;SyncTokenPassword=REPLACE_WITH_SYNC_TOKEN_PASSWORD;ParentGatewayEndpoint=<REPLACE_WITH_CLOUD_REGISTRY_NAME>.<REPLACE_WITH_CLOUD_REGISTRY_REGION>.data.azurecr.io;ParentEndpointProtocol=https"
                            }
                        },
                        "status": "running",
                        "restartPolicy": "always",
                        "version": "1.0"
                    },
                    "IoTEdgeAPIProxy": {
                        "settings": {
                            "image": "<REPLACE_WITH_CLOUD_REGISTRY_NAME>.azurecr.io/azureiotedge-api-proxy:1.1.2",
                            "createOptions": "{\"HostConfig\":{\"PortBindings\":{\"8000/tcp\":[{\"HostPort\":\"8000\"}]}}}"
                        },
                        "type": "docker",
                        "env": {
                            "NGINX_DEFAULT_PORT": {
                                "value": "8000"
                            },
                            "CONNECTED_ACR_ROUTE_ADDRESS": {
                                "value": "connected-registry:8080"
                            },
                            "BLOB_UPLOAD_ROUTE_ADDRESS": {
                                "value": "AzureBlobStorageonIoTEdge:11002"
                            }
                        },
                        "status": "running",
                        "restartPolicy": "always",
                        "version": "1.0"
                    }
                },
                "runtime": {
                    "settings": {
                        "minDockerVersion": "v1.25",
                        "registryCredentials": {
                            "cloudregistry": {
                                "address": "<REPLACE_WITH_CLOUD_REGISTRY_NAME>.azurecr.io",
                                "password": "<REPLACE_WITH_SYNC_TOKEN_PASSWORD>",
                                "username": "<REPLACE_WITH_SYNC_TOKEN_NAME>"
                            }
                        }
                    },
                    "type": "docker"
                },
                "schemaVersion": "1.1",
                "systemModules": {
                    "edgeAgent": {
                        "settings": {
                            "image": "<REPLACE_WITH_CLOUD_REGISTRY_NAME>.azurecr.io/azureiotedge-agent:1.2.4",
                            "createOptions": ""
                        },
                        "type": "docker",
                        "env": {
                            "SendRuntimeQualityTelemetry": {
                                "value": "false"
                            }
                        }
                    },
                    "edgeHub": {
                        "settings": {
                            "image": "<REPLACE_WITH_CLOUD_REGISTRY_NAME>.azurecr.io/azureiotedge-hub:1.2.4",
                            "createOptions": "{\"HostConfig\":{\"PortBindings\":{\"443/tcp\":[{\"HostPort\":\"443\"}],\"5671/tcp\":[{\"HostPort\":\"5671\"}],\"8883/tcp\":[{\"HostPort\":\"8883\"}]}}}"
                        },
                        "type": "docker",
                        "status": "running",
                        "restartPolicy": "always"
                    }
                }
            }
        },
        "$edgeHub": {
            "properties.desired": {
                "routes": {
                    "route": "FROM /messages/* INTO $upstream"
                },
                "schemaVersion": "1.1",
                "storeAndForwardConfiguration": {
                    "timeToLiveSecs": 7200
                }
            }
        }
    }
}

較低層的部署資訊清單

對於較低層的裝置,請使用下列內容建立部署資訊清單檔 deploymentLowerLayer.json

整體而言,較低層部署檔案與最上層部署檔案相似。 差異包括:

  • 它會從最上層連線的登錄提取必要的映像,而不是從雲端登錄提取。

    當您設定最上層連線的登錄時,請確定該登錄會在本機同步所有必要的映像,包括 azureiotedge-agentazureiotedge-hubazureiotedge-api-proxyacr/connected-registry。 較低層的 IoT 裝置必須從最上層連線的登錄提取這些映像。

  • 它會使用在較低層設定的同步權杖,對最上層連線的登錄進行驗證。

  • 它會使用最上層連線登錄的 IP 位址或 FQDN 來設定父閘道端點,而不是使用雲端登錄的 FQDN。

重要

在下列部署資訊清單中,$upstream 會作為裝載父連線登錄之裝置的 IP 位址或 FQDN。 不過,在環境變數中不支援 $upstream。 連線的登錄必須讀取環境變數 ACR_PARENT_GATEWAY_ENDPOINT,才能取得父閘道端點。 連線的登錄不使用 $upstream,而是支援從另一個環境變數動態解析 IP 位址或 FQDN。

在巢狀 IoT Edge 上,較低層上有一個環境變數 $IOTEDGE_PARENTHOSTNAME 等同於父裝置的 IP 位址或 FQDN。 請手動將環境變數替換為連接字串中的 ParentGatewayEndpoint 值,以避免將父 IP 位址或 FQDN 硬式編碼。 由於此範例中的父裝置正在連接埠 8000 上執行 nginx,請傳入 $IOTEDGE_PARENTHOSTNAME:8000。 您也需要在 ParentEndpointProtocol 中選取適當的通訊協定。

{
    "modulesContent": {
        "$edgeAgent": {
            "properties.desired": {
                "modules": {
                    "connected-registry": {
                        "settings": {
                            "image": "$upstream:8000/acr/connected-registry:0.8.0",
                            "createOptions": "{\"HostConfig\":{\"Binds\":[\"/home/azureuser/connected-registry:/var/acr/data\"]}}"
                        },
                        "type": "docker",
                        "env": {
                            "ACR_REGISTRY_CONNECTION_STRING": {
                                "value": "ConnectedRegistryName=<REPLACE_WITH_CONNECTED_REGISTRY_NAME>;SyncTokenName=<REPLACE_WITH_SYNC_TOKEN_NAME>;SyncTokenPassword=<REPLACE_WITH_SYNC_TOKEN_PASSWORD>;ParentGatewayEndpoint=$IOTEDGE_PARENTHOSTNAME:8000;ParentEndpointProtocol=https"
                            }
                        },
                        "status": "running",
                        "restartPolicy": "always",
                        "version": "1.0"
                    },
                    "IoTEdgeApiProxy": {
                        "settings": {
                            "image": "$upstream:8000/azureiotedge-api-proxy:1.1.2",
                            "createOptions": "{\"HostConfig\": {\"PortBindings\": {\"8000/tcp\": [{\"HostPort\": \"8000\"}]}}}"
                        },
                        "type": "docker",
                        "version": "1.0",
                        "env": {
                            "NGINX_DEFAULT_PORT": {
                                "value": "8000"
                            },
                            "CONNECTED_ACR_ROUTE_ADDRESS": {
                                "value": "connected-registry:8080"
                            },
                            "NGINX_CONFIG_ENV_VAR_LIST": {
                                    "value": "NGINX_DEFAULT_PORT,BLOB_UPLOAD_ROUTE_ADDRESS,CONNECTED_ACR_ROUTE_ADDRESS,IOTEDGE_PARENTHOSTNAME,DOCKER_REQUEST_ROUTE_ADDRESS"
                            },
                            "BLOB_UPLOAD_ROUTE_ADDRESS": {
                                "value": "AzureBlobStorageonIoTEdge:11002"
                            }
                        },
                        "status": "running",
                        "restartPolicy": "always",
                        "startupOrder": 3
                    }
                },
                "runtime": {
                    "settings": {
                        "minDockerVersion": "v1.25",
                        "registryCredentials": {
                            "connectedregistry": {
                                "address": "$upstream:8000",
                                "password": "<REPLACE_WITH_SYNC_TOKEN_PASSWORD>",
                                "username": "<REPLACE_WITH_SYNC_TOKEN_NAME>"
                            }
                        }
                    },
                    "type": "docker"
                },
                "schemaVersion": "1.1",
                "systemModules": {
                    "edgeAgent": {
                        "settings": {
                            "image": "$upstream:8000/azureiotedge-agent:1.2.4",
                            "createOptions": ""
                        },
                        "type": "docker",
                        "env": {
                            "SendRuntimeQualityTelemetry": {
                                "value": "false"
                            }
                        }
                    },
                    "edgeHub": {
                        "settings": {
                            "image": "$upstream:8000/azureiotedge-hub:1.2.4",
                            "createOptions": "{\"HostConfig\":{\"PortBindings\":{\"443/tcp\":[{\"HostPort\":\"443\"}],\"5671/tcp\":[{\"HostPort\":\"5671\"}],\"8883/tcp\":[{\"HostPort\":\"8883\"}]}}}"
                        },
                        "type": "docker",
                        "status": "running",
                        "restartPolicy": "always"
                    }
                }
            }
        },
        "$edgeHub": {
            "properties.desired": {
                "routes": {
                    "route": "FROM /messages/* INTO $upstream"
                },
                "schemaVersion": "1.1",
                "storeAndForwardConfiguration": {
                    "timeToLiveSecs": 7200
                }
            }
        }
    }
}

設定及部署連線的登錄模組

下列步驟取自教學課程:建立 IoT Edge 裝置的階層,專門用來在 IoT Edge 階層中部署連線的登錄模組。 如需個別步驟的詳細資料,請參閱該教學課程。

建立最上層和較低層的裝置

使用現有的 ARM 範本,建立最上層和較低層的虛擬機器。 此範本也會安裝 IoT Edge 代理程式。 如果您想要改為從自己的裝置部署,請參閱教學課程:安裝或解除安裝 Azure IoT Edge for Linux,以了解如何手動設定裝置。

重要

若後續要存取部署於最上層裝置上的模組,請確實開啟下列連接埠輸入:8000、443、5671、8883。 如需設定步驟,請參閱如何使用 Azure 入口網站開啟對虛擬機器的連接埠

建立和設定階層

依照 Azure CLI 或 Azure Cloud Shell 中的相關步驟,使用 iotedge-config 工具來建立和設定階層:

  1. 下載設定工具。

     mkdir nested_iot_edge_tutorial
     cd ~/nested_iot_edge_tutorial
     wget -O iotedge_config.tar "https://github.com/Azure-Samples/iotedge_config_cli/releases/download/latest/iotedge_config_cli.tar.gz"
     tar -xvf iotedge_config.tar
    

    此步驟會在您的教學課程目錄中建立 iotedge_config_cli_release 資料夾。 用來建立裝置階層的範本檔案是位於 ~/nested_iot_edge_tutorial/iotedge_config_cli_release/templates/tutorial 中的 iotedge_config.yaml 檔案。 在相同的目錄中,最上層和較低層有兩個部署資訊清單:deploymentTopLayer.jsondeploymentLowerLayer.json 檔案。

  2. 使用您的資訊編輯 iotedge_config.yaml。 編輯 iothub_hostnameiot_name、最上層和較低層的部署資訊清單檔名,以及您建立用來從每一層提取上游映像的用戶端權杖認證。 以下是範例組態檔:

    config_version: "1.0"
    
    iothub:
        iothub_hostname: <REPLACE_WITH_HUB_NAME>.azure-devices.net
        iothub_name: <REPLACE_WITH_HUB_NAME>
        ## Authentication method used by IoT Edge devices: symmetric_key or x509_certificate
        authentication_method: symmetric_key 
    
        ## Root certificate used to generate device CA certificates. Optional. If not provided a self-signed CA will be generated
        # certificates:
        #   root_ca_cert_path: ""
        #   root_ca_cert_key_path: ""
    
        ## IoT Edge configuration template to use
    configuration:
        template_config_path: "./templates/tutorial/device_config.toml"
        default_edge_agent: "$upstream:8000/azureiotedge-agent:1.2.4"
    
        ## Hierarchy of IoT Edge devices to create
    edgedevices:
        device_id: top-layer
        edge_agent: "<REPLACE_WITH_REGISTRY_NAME>.azurecr.io/azureiotedge-agent:1.2.4" ## Optional. If not provided, default_edge_agent will be used
        deployment: "./templates/tutorial/deploymentTopLayer.json" ## Optional. If provided, the given deployment file will be applied to the newly created device
            # hostname: "FQDN or IP" ## Optional. If provided, install.sh will not prompt user for this value nor the parent_hostname value
        container_auth: ## The token used to pull the image from cloud registry
            serveraddress: "<REPLACE_WITH_REGISTRY_NAME>.azurecr.io"
            username: "<REPLACE_WITH_SYNC_TOKEN_NAME_FOR_TOP_LAYER>"
            password: "<REPLACE_WITH_SYNC_TOKEN_PASSWORD_FOR_TOP_LAYER>"
        child:
            - device_id: lower-layer
              deployment: "./templates/tutorial/deploymentLowerLayer.json" ## Optional. If provided, the given deployment file will be applied to the newly created device
               # hostname: "FQDN or IP" ## Optional. If provided, install.sh will not prompt user for this value nor the parent_hostname value
              container_auth: ## The token used to pull the image from parent connected registry
                serveraddress: "$upstream:8000"
                username: "<REPLACE_WITH_SYNC_TOKEN_NAME_FOR_LOWER_LAYER>"
                password: "<REPLACE_WITH_SYNC_TOKEN_PASSWORD_FOR_LOWER_LAYER>"
    
  3. 準備最上層和較低層部署檔案:deploymentTopLayer.jsondeploymentLowerLayer.json。 將您先前在本文中建立的部署資訊清單檔複製到下列資料夾:~/nestedIotEdgeTutorial/iotedge_config_cli_release/templates/tutorial

  4. 瀏覽至您的 iotedge_config_cli_release 目錄,然後執行工具以建立 IoT Edge 裝置的階層。

    cd ~/nestedIotEdgeTutorial/iotedge_config_cli_release
    ./iotedge_config --config ~/nestedIotEdgeTutorial/iotedge_config_cli_release/templates/tutorial/iotedge_config.yaml --output ~/nestedIotEdgeTutorial/iotedge_config_cli_release/outputs -f
    

    搭配 --output 參數,此工具會在您選擇的目錄中建立裝置憑證、憑證套件組合和記錄檔。 搭配 -f 參數,此工具會在 IoT 中樞自動尋找現有的 IoT Edge 裝置,並加以移除,以避免發生錯誤,並讓您的中樞保持簡潔。

    此工具可能執行數分鐘。

  5. 使用 scp,將在先前的步驟中產生的 top-layer.ziplower-layer.zip 檔案複製到對應的最上層和較低層虛擬機器:

    scp <PATH_TO_CONFIGURATION_BUNDLE>   <USER>@<VM_IP_OR_FQDN>:~
    
  6. 連線至最上層裝置,以安裝設定套件組合。

    1. 將設定套件組合解壓縮。 您必須先安裝 zip。

      sudo apt install zip
      unzip ~/<PATH_TO_CONFIGURATION_BUNDLE>/<CONFIGURATION_BUNDLE>.zip #unzip top-layer.zip
      
    2. 執行 sudo ./install.sh。 輸入 IP 位址或主機名稱。 建議使用 IP 位址。

    3. 執行 sudo iotedge list 以確認所有模組都在執行中。

  7. 連線至較低層裝置,以安裝設定套件組合。

    1. 將設定套件組合解壓縮。 您必須先安裝 zip。

      sudo apt install zip
      unzip ~/<PATH_TO_CONFIGURATION_BUNDLE>/<CONFIGURATION_BUNDLE>.zip #unzip lower-layer.zip
      
    2. 執行 sudo ./install.sh。 輸入裝置和父 IP 位址或主機名稱。 建議使用 IP 位址。

    3. 執行 sudo iotedge list 以確認所有模組都在執行中。

若未指定裝置設定的部署檔案,或發生部署問題 (例如,最上層或較低層裝置上的部署資訊清單無效),請手動部署模組。 請參閱下列各節。

手動部署連線的登錄模組

使用下列命令,在 IoT Edge 裝置上手動部署連線的登錄模組:

az iot edge set-modules \
  --device-id <device-id> \
  --hub-name <hub-name> \
  --content <deployment-manifest-filename>

如需詳細資訊,請參閱使用 Azure CLI 部署 Azure IoT Edge 模組

成功部署之後,連線的登錄會顯示 Online 的狀態。

若要檢查連線登錄的狀態,請使用下列 az acr connected-registry show 命令:

az acr connected-registry show \
  --registry $REGISTRY_NAME \
  --name $CONNECTED_REGISTRY_RO \
  --output table

您可能需要等候數分鐘,讓連線的登錄部署完成。

成功部署之後,連線的登錄會顯示 Online 的狀態。

若要對部署進行疑難排解,請在受影響的裝置上執行 iotedge check。 如需詳細資訊,請參閱疑難排解

下一步

在本快速入門中,您已了解如何將連線的登錄部署至巢狀 IoT Edge 裝置。 請繼續參考下一個指南,了解如何從新部署的連線登錄提取映像。