Эта статья является частью серии статей о azure-iot-edge-integration-test-шаблон. После Части 2 я собираюсь сосредоточиться на МанифестГенератор в этой статье.

В этом образце шаблона приложение .NET ManifestGenerator работающий на агенте Azure Pipelines, создает Манифест развертывания Azure IoT Edge и развертывает его в Azure IoT Hub. Модули IoT Edge, подключенные к Центру Интернета вещей, будут извлекать образы контейнеров, указанные в манифесте.

Описание изображения

Основная цель ManifestGenerator приложение должно генерировать manifest.json. Он включает информацию обо всех модулях IoT Edge, включая стандартные и пользовательские модули системы.

  • Модуль по умолчанию: edgeAgent а также edgeHub являются модулями по умолчанию, которые управляют модулями и их связью.
  • Пользовательский модуль: вы можете указать, какие модули вы хотите развернуть в среде IoT Edge.
  • Маршрут связи IoT Edge: Здесь необходимо указать имя маршрута. Очень важно, чтобы эти имена были синхронизированы с маршрутами, определенными в приложениях каждого модуля. Например, маршрут, указанный в манифесте, выглядит как FROM /messages/modules/IothubConnector/outputs/reportRequest INTO BrokeredEndpoint("/modules/WeatherObserver/inputs/reportRequest"). IothubConnector модуль должен указать reportRequest имя маршрута в методе отправки сообщения и WeatherObserver указать reportRequest имя маршрута в его методе обратного вызова.
  • URL-адрес изображения, переменные среды, привязка каталога монтирования: вы должны указать URL-адрес изображения, переменные среды, привязать каталог монтирования и другие конфигурации, как показано ниже.

Вы можете увидеть образец здесь — manifest.example.json

"FileGenerator": {
  "version": "1.0",
  "type": "docker",
  "status": "running",
  "restartPolicy": "always",
  "settings": {
    "image": "crsample1.azurecr.io/file-generator:20220818.2",
    "createOptions": "{\"HostConfig\":{\"Binds\":[\"/edge/upload/reports:/genroot\"]}}"
  },
  "env": {
    "OUTPUT_DIRECTORY_PATH": {
      "value": "/genroot"
    },
    "ROS_TOPIC_NAME": {
      "value": "ros2_topic_download"
    }
  }
},
Войти в полноэкранный режим

Выйти из полноэкранного режима

Описание изображения

В этом разделе я собираюсь описать, как установить конфигурации для manifest.json.


Извлечь учетные данные

manifest.json требуются некоторые учетные данные, включая строку подключения Центра Интернета вещей, ключ реестра контейнеров, ключ учетной записи хранения, ключ локального хранилища BLOB-объектов. Первые три можно извлечь с помощью команды Azure CLI, поскольку агент Azure Pipelines имеет право доступа к Azure Service Connection. Для ключа локального хранилища BLOB-объектов это может быть любая строка base64 длиной 16 байт. Агент Azure Pipeline генерирует его случайным образом и устанавливает в качестве переменных среды.


Переменные среды

С Azure Pipelines Задача интерфейса командной строки .NET Coreвы можете передать необходимые переменные как переменные среды .NET в ManifestGenerator приложение при его запуске. Эти переменные включают учетные данные, созданные агентом Azure Pipelines на предыдущем шаге.

- task: DotNetCoreCLI@2
  displayName: Generate/deploy IoT Edge manifest
  inputs:
    command: run
    projects: $(Build.SourcesDirectory)/src/apps/ManifestGenerator/ManifestGenerator/ManifestGenerator.csproj
    arguments: --configuration Release
  env:
    STORAGE_ACCOUNT_NAME: $(STORAGE_ACCOUNT_NAME)
    STORAGE_ACCOUNT_KEY: $(storageAccountKey)
    ACR_NAME: $(ACR_NAME)
    ACR_PASS: $(AcrKey)
    IOTHUB_CONNECTOR_IMAGE: $(ACR_NAME).azurecr.io/${{ parameters.EdgeImages.module1.repository }}:${{ parameters.EdgeImages.module1.tag }}
    WEATHER_OBSERVER_IMAGE: $(ACR_NAME).azurecr.io/${{ parameters.EdgeImages.module2.repository }}:${{ parameters.EdgeImages.module2.tag }}
    FILE_GENERATOR_IMAGE: $(ACR_NAME).azurecr.io/${{ parameters.EdgeImages.module3.repository }}:${{ parameters.EdgeImages.module3.tag }}
    FILE_UPLOADER_IMAGE: $(ACR_NAME).azurecr.io/${{ parameters.EdgeImages.module4.repository }}:${{ parameters.EdgeImages.module4.tag }}
    FILE_UPDATER_IMAGE: $(ACR_NAME).azurecr.io/${{ parameters.EdgeImages.module5.repository }}:${{ parameters.EdgeImages.module5.tag }}
    IOTHUB_DEVICE_ID: $(IOTHUB_DEVICE_ID)
    IOTHUB_CONNECTION_STRING: $(iotHubCs)
    LOCAL_STORAGE_KEY: $(LocalStorageKey)
    ORGANIZATION_NAME: $(TEST_ORGANIZATION_NAME)
Войти в полноэкранный режим

Выйти из полноэкранного режима


appsettings.json

ManifestGenerator приложение сохраняет appsettings.json который определяет расчетные значения. Вам не нужно изменять его, если вы не измените дизайн, например пути подключения привязки или срок действия токена SAS.

{
  "RouteTelemetry": "telemetry",
  "RouteReportRequest": "reportRequest",
  "RouteReportResponse": "reportResponse",
  "RouteUpdateRequest": "updateRequest",
  "RouteUpdateResponse": "updateResponse",
  "Ros2Topic": "ros2_topic_download",
  "FileGeneratorContainerBind": "/edge/upload/reports:/genroot",
  "FileUploaderContainerBind": "/edge/upload:/uploadroot",
  "FileUpdaterContainerBind": "/edge/download:/downloadroot",
  "LocalBlobStorageBind": "/edge/localblob:/blobroot",
  "CloudBlobContainerName": "weather",
  "LocalBlobContainerName": "weather",
  "LocalBlobAccountName": "stlocal",
  "LocalBlobEndpoint": "
  "FileGeneratorWorkdir": "/genroot",
  "FileUploaderWorkdir": "/uploadroot",
  "FileUpdaterWorkdir": "/downloadroot",
  "SasExpirationMonths": 6
}
Войти в полноэкранный режим

Выйти из полноэкранного режима

В этом примере шаблона используется IoTEdgeObjectModel NuGet-пакет. Этот пакет поможет вам сократить количество строк кода, для которого вы пишете manifest.json. Вам не нужно указывать конфигурацию системного модуля или другие конфигурации по умолчанию. По моему опыту в последнем проекте, я написал manifest.json все сам с нуля с экземплярами словаря С#. Используя IoTEdgeObjectModel package, вы можете сократить примерно 50% ваших кодов.

Три основных класса этого пакета: EdgeAgentDesiredProperties, EdgeHubDesiredProperties, ModuleSpecificationDesiredProperties.

Вы указываете edgeAgent свойства, как показано ниже, с EdgeModuleSpecification учебный класс.

EdgeAgentDesiredProperties edgeAgentDesiredProperties = new ()
{
    SystemModuleVersion = "1.3",
    RegistryCredentials = new List<RegistryCredential>()
    {
        new RegistryCredential(acrName, $"{acrName}.azurecr.io", acrName, acrPass),
    },
    EdgeModuleSpecifications = new List<EdgeModuleSpecification>()
    {
        new EdgeModuleSpecification(name:"IothubConnector", image:iothubConnectorImage, environmentVariables:iothubConnectorEnv),
        new EdgeModuleSpecification(name:"WeatherObserver", image:weatherObserverImage),
        new EdgeModuleSpecification(name:"FileGenerator", image:fileGeneratorImage, createOptions:fileGeneratorCreateOptions, environmentVariables:fileGeneratorEnv),
        new EdgeModuleSpecification(name:"FileUploader", image:fileUploaderImage, createOptions:fileUploaderCreateOptions, environmentVariables:fileUploaderEnv),
        new EdgeModuleSpecification(name:"FileUpdater", image:fileUpdaterImage, createOptions:fileUpdaterCreateOptions, environmentVariables:fileUpdaterEnv),
        new EdgeModuleSpecification(name:"LocalBlobStorage", image:localBlobStorageImage, createOptions:localBlobStorageCreateOptions, environmentVariables:localBlobStorageEnv),
    },
};
Войти в полноэкранный режим

Выйти из полноэкранного режима

EdgeHubDesiredProperties в основном указывает связь по маршруту Azure IoT Edge.

EdgeHubDesiredProperties edgeHubConfig = new ()
{
    Routes = new List<Route>()
    {
        new Route("route_telemetry", route_telemetry),
        new Route("route_c2w", route_c2w),
        new Route("route_w2c", route_w2c),
        new Route("route_w2u", route_w2u),
        new Route("route_u2w", route_u2w),
    },
};
Войти в полноэкранный режим

Выйти из полноэкранного режима

ModuleSpecificationDesiredProperties задает пользовательские модули и требуемые свойства их двойников модулей.

ModuleSpecificationDesiredProperties localBlobStorage = new ()
{
    Name = "LocalBlobStorage",
    DesiredProperties = new Dictionary<string, object>
    {
        ["deviceAutoDeleteProperties"] = new Dictionary<string, object>
        {
            ["deleteOn"] = true,
            ["deleteAfterMinutes"] = 5,
            ["retainWhileUploading"] = true,
        },
        ["deviceToCloudUploadProperties"] = new Dictionary<string, object>
        {
            ["uploadOn"] = true,
            ["uploadOrder"] = "NewestFirst",
            ["deleteAfterUpload"] = true,
            ["cloudStorageConnectionString"] = cloudStorageSasConnectionString,
            ["storageContainersForUpload"] = new Dictionary<string, object>
            {
                [localBlobContainerName] = new Dictionary<string, object>
                {
                    ["target"] = iotHubDeviceId,
                }
            },
        },
    },
};
Войти в полноэкранный режим

Выйти из полноэкранного режима

  • ManifestGenerator имеет класс обслуживания SasService.cs который создает Маркер SAS (подпись общего доступа). Таким образом, у вас может быть одно хранилище BLOB-объектов Azure для нескольких пограничных устройств из разных объектов. Этот токен SAS заставляет пограничные устройства следовать границе безопасности, поэтому пограничные устройства не могут получить доступ к данным разных объектов.

Описание изображения

  • Важно преобразовать сгенерированный токен SAS в строку подключения. Эта строка подключения задается как переменная среды LocalBlobStorage. За FileUpdaterвы можете использовать AzureSasCredential преобразовать токен SAS в читаемый для BlobClient.
string[] sasContents = weatherFileInfo.BlobSasUrl.Split('?');
AzureSasCredential azureSasCredential = new (sasContents[1]);
Uri blobUri = new (sasContents[0]);
BlobClient blobClient = new (blobUri, azureSasCredential, null);
await blobClient.DownloadToAsync(zipFilePath).ConfigureAwait(false);
Войти в полноэкранный режим

Выйти из полноэкранного режима

Однако, LocalBlobClient по умолчанию требуется строка подключения большого двоичного объекта, а не токен SAS. Поэтому вам нужно преобразовать токен SAS в строку подключения большого двоичного объекта в Программа ManfiestGenerator.cs

string sasUri = directoryClient.GenerateSasUri(sasBuilder).ToString();
string[] sasContents = sasUri.Split('?');
string sasConnectionString = $"BlobEndpoint=https://{this.dataLakeServiceClient.AccountName}.blob.core.windows.net/{blobContainerName}/{sasDirectory};SharedAccessSignature={sasContents[1]}";
Войти в полноэкранный режим

Выйти из полноэкранного режима