低帯域幅接続での効率的な Docker イメージのデプロイ

Azure Container Registry
Azure Functions
Azure IoT Edge
Azure IoT Hub
Azure Pipelines

この記事では、断続的または低帯域幅のインターネット接続を介して、コンテナー化されたモノのインターネット (IoT) エッジ モジュールをデプロイするためのソリューションについて説明します。

エッジ処理は、待機時間の短い接続を提供し、帯域幅を節約するため (モバイル シナリオなど) の主要なモノのインターネット (IoT) のパターンです。 通常、IoT システムでは、ソフトウェア コンテナー イメージをデプロイすることによって、エッジ デバイスをプロビジョニングします。 断続的で低帯域幅のインターネット接続でコンテナーのデプロイが中断されると、モバイル シナリオで障害が発生することがあります。 限られた帯域幅、断続的な接続、または低帯域幅の IoT シナリオでは、信頼性と回復性のあるデプロイ機能が必要となります。

この例では、大手物流会社が世界各地での製品出荷の追跡を改善したいと考えていました。 同社は、断続的で低帯域幅のインターネット接続の地域を含む多くのロケールに、陸運、空輸、海運のさまざまな輸送方法で商品を出荷しています。 商品の種類に応じて、製品の出荷物にはさまざまな IoT 保険がかけられ、異なる機能を持つ安全性デバイスまたは追跡デバイスが取り付けられています。 デバイスには、GPS トラッカー、温度センサー、データ キャプチャ ツールが含まれます。

最近開発した IoT Edge プラットフォームを介してデバイスを更新するときに問題が発生しました。 主な問題点は次のとおりです。

  • 更新されたソフトウェアをデバイスにデプロイするときの帯域幅の使用率が高くなります。
  • デバイス間で標準化された自動デプロイがありません。
  • テクノロジの選択に関する柔軟性が限られています。

これらの問題に対処するために、開発チームは次のようなソリューションを作成しました。

  • 各デバイスへのデプロイのサイズを最小限に抑えて、帯域幅を減らします。
  • IoT Edge プラットフォームから異種のリモート IoT デバイスへの標準化された Docker コンテナーのデプロイを実装します。
  • 信頼性の高いデプロイの監視を可能にします。
  • さまざまな Azure DevOps とクラウド サービスを利用し、顧客が優先するレガシ ツールを使用します。

このソリューションでは、接続性が限られた環境でのデバイス プロビジョニング プロセスの信頼性と回復性が大幅に向上しました。 この記事では、ソリューションの詳細とソリューションのオプションの評価プロセスについて説明します。

顧客要件

この顧客には次の要件がありました。

  • このソリューションでは、断続的で低帯域幅のクラウド接続をサポートする必要があります。
  • デプロイされたアプリケーションは引き続きローカルで実行する必要があります。
  • ローカル スタッフは、オフラインで、またはクラウドのラウンド トリップ遅延なしで機能を使用する必要があります。
  • 接続されている場合、ソリューションではクラウド接続を効率的に使用する必要があります。
  • ソリューションでは、製品間で一貫して定義されたビジネス ルールに従って、データ送信の優先順位を決定する必要があります。

また、次のような詳細な要件もありました。

  • イメージ ファイルは、低帯域幅の断続的な接続の衛星接続を介して転送されます。
  • 転送されるデータの量を最小限に抑える必要があります。
  • デバイスへのファイルの転送では、顧客が優先するサードパーティのアプリケーションを使用します。
  • デバイスのワークロードでは、IoT Edge の Docker イメージが使用されます。
  • イメージのサイズは、数十 MB から数 GB の範囲です。
  • IoT Edge モジュールは .NET Core 2.2 で記述されます。

考えられるユース ケース

このソリューションは、低帯域幅で断続的な接続を介して、ソフトウェア コンテナーでソリューションを配信する IoT シナリオに適しています。 たとえば、次のようになります。

  • 石油、ガス、鉱業のリモート監視
  • 無線での自動車の更新
  • 強力な接続が保証されない場所

Architecture

高帯域幅のシナリオの場合、Azure IoT Edge では、インターネットからアクセス可能な Docker レジストリ (Docker ハブまたは Azure Container Registry のようなプライベート ハブ) からイメージを直接プルします。 この機能は、docker pull <image_name> コマンドを実行するのと同じです。

衛星インターネット接続などの潜在的に断続的なネットワーク アクセスでは、Docker pull の方法は信頼性に欠けます。 Docker でイメージをプルしている間にインターネット接続が切断された場合、それまでのデータはキャッシュされません。 インターネット接続が再開されたら、Docker でイメージのプルを最初から再度開始する必要があります。

このソリューションでは、代替のデプロイ メカニズム (Docker イメージ ファイルのバイナリ部分置換) を使用して、帯域幅を減らし、断続的な接続を補正します。

Azure DevOps と Azure の高レベルのソリューション アーキテクチャを示す図。

データフロー

  1. 開発者が、ソース コード リポジトリ内のエッジ モジュールのソース コードを編集します。
  2. Container Registry に各モジュールの Docker イメージが格納されます。
  3. マニフェスト リポジトリには、すべてのワークストリームの配置マニフェストが含まれています。
  4. 各モジュールには、汎用 Docker ビルドを使用してモジュールを自動的に作成および登録する Azure Pipelines のビルド パイプラインがあります。
  5. デバイスへのイメージ パイプラインでは、マニフェスト ファイルで定義されているように、ターゲット デバイスに Docker イメージがデプロイされます。
  6. デバイスへのマニフェスト パイプラインでは、更新するデバイスの適切な Azure IoT Hub に配置マニフェストがプッシュされます。
  7. サード パーティ製の高速ファイル転送ソリューションによって、Azure Storage アカウントからデバイスにファイルが転送されます。
  8. Image Reconstruction IoT Edge モジュールによって、受信したパッチがデバイスに適用されます。
  9. IoT Hub では、Image Reconstruction モジュールからステータス メッセージを受け取り、デバイスの配置マニフェストを設定します。 パイプライン フローの残りの部分では、この配置マニフェストが使用されます。
  10. Azure Functions では、IoT Hub のメッセージ ストリームを監視して、SQL データベースを更新し、成功または失敗をユーザーに通知します。
  11. Azure SQL Database では、デプロイ中とデプロイ後に、ターゲット デバイスと Azure ベースのサービスでの事象を追跡します。

コンポーネント

  • Azure IoT Edge では、コンテナー化されたワークロードをデバイス上で実行し、低遅延の接続が提供され、帯域幅が節約されます。
  • Azure IoT Hub はクラウドでホストされるマネージド サービスであり、IoT アプリケーションと制御する IoT デバイス間で、中央のメッセージ ハブとしての役割を担います。
  • Azure Container Registry は、プライベート Docker コンテナー イメージと関連する成果物を格納および管理するためのクラウドベースのプライベート レジストリ サービスです。
  • Azure Pipelines では、継続的インテグレーション (CI) と継続的デリバリー (CD) を組み合わせて、コードのテストとビルドを自動的に実行し、任意のターゲットにそれを送ります。
  • Azure Functions は、インフラストラクチャをプロビジョニングまたは管理する必要なく、イベントトリガー型のコードを実行できるサーバーレス コンピューティング プラットフォームです。
  • Azure Storage では、すべての種類のビジネス データ、オブジェクト、ファイルのための非常にスケーラブルでパフォーマンスがよく、安全でコスト効率の高いストレージが提供されます。
  • Azure SQL Database は、クラウド向けに構築されたフル マネージドでマルチモデルのリレーショナル データベース サービスです。
  • Docker は、コンテナー化されたアプリケーションを開発、出荷、実行するためのオープン プラットフォームです。

代替

開発チームでは、完全な Docker イメージ差分転送ソリューションを決定する前に、いくつかのオプションを評価しました。 以下のセクションでは、代替の評価と結果について説明します。

チームは、次の評価基準で各オプションを検討しました。

  • ソリューションで要件が満たされているかどうか。
  • デバイスに実装する必要があるロジックの量 (少量、中量、または大量)。
  • Azure に実装する必要があるロジックの量 (少量、中量、または大量)。
  • コンテナー イメージを転送するための帯域効率 (イメージの合計サイズに対する転送データの割合)。

帯域幅の効率には、次のようなシナリオが含まれています。

  • デバイスにイメージが存在しない。
  • 同じ基本イメージを持つイメージがデバイスに存在する。
  • 以前のアプリケーション バージョンのイメージがデバイスに存在する。
  • 以前の基本イメージ上に構築されたアプリケーションのイメージがデバイスに存在する。

チームでは、帯域幅の効率を評価するために次のシナリオを使用しました。

シナリオ 説明
デバイス上に既に基本レイヤーがあるイメージを転送する デバイスに既に存在する別のイメージで基本イメージが共有されている場合に、新しいイメージを転送します。 このシナリオは、同じ OS およびフレームワーク内に別のアプリケーションが既に存在するときに、新しいアプリケーションを初めてデプロイすることを表します。
アプリケーション レイヤーを更新する 既存のアプリケーションのイメージのコードのみを変更します。 このシナリオは、ユーザーが新しい機能をコミットするときの一般的な変更を表します。
基本イメージを更新する アプリケーションが構築されている基本イメージのバージョンを変更します。

Docker レイヤーを転送するオプション

Docker コンテナー イメージは、読み取り専用のファイル システムの相違点の UnionFS マウントであり、コンテナーの実行中に行われた変更のための別の書き込み可能なレイヤーがあります。 このファイル システムは "レイヤー" と呼ばれ、基本的にはフォルダーとファイルです。 レイヤーは、コンテナーのルート ファイル システムのベースを形成するために積み重ねられます。 レイヤーは読み取り専用であるため、さまざまなイメージで共通のレイヤーがある場合は、同じレイヤーを共有できます。

Docker レイヤーを転送するオプションでは、イメージ間のレイヤーが再使用され、新しいレイヤーのみがデバイスに転送されます。 このオプションは、同じ基本レイヤーを共有するイメージ (通常は OS) の場合や、既存のイメージのバージョンを更新する場合に最も役に立ちます。

この方法の短所は次のとおりです。

  • オーケストレーターでは、どのデバイスにどのレイヤーが存在するかという情報を管理する必要があります。
  • 基本レイヤーの変更により、後続のすべてのレイヤーのハッシュが変更されます。
  • 比較するためには、一貫性のあるレイヤー ハッシュが必要となります。
  • Docker の保存と Docker の読み込みに依存関係がある可能性があります。

Docker クライアントを変更するオプション

このオプションでは、中断後にレイヤーのダウンロードが再開されるように、Docker クライアントを変更またはラップすることに重点が置かれています。 既定では、中断から約 30 分以内にインターネット接続が回復した場合、Docker pull によってダウンロードが再開されます。 それ以外の場合は、クライアントが終了し、それまでにダウンロードされたデータがすべて失われます。

この方法は実行可能ですが、次のような厄介な問題がありました。

  • 帯域幅の効率を最大化するには、イメージをプルする Docker デーモンにデバイス上のすべてのイメージを登録する必要があります。
  • この機能をサポートするには、オープンソースの Docker プロジェクトを変更する必要があり、オープンソースの保守管理者に拒否されるリスクがありました。
  • 顧客が好む高速ファイル転送ソリューションではなく、HTTP でデータが転送されるため、カスタムの再試行ロジックを開発する必要がありました。
  • 基本イメージが変更された場合、すべてのレイヤーを再送信する必要があります。

エッジ デバイスでビルドするオプション

この方法では、イメージのビルド環境を各デバイスに移動します。 次のデータがデバイスに送信されます。

  • ビルドするアプリケーションのソース コード
  • コードが依存しているすべての NuGet パッケージのコピー
  • .NET Core ビルド環境とランタイム用の Docker 基本イメージ
  • エンド イメージに関するメタデータ

その後、デバイス上のビルド エージェントによってイメージがビルドされ、デバイス Docker マネージャーに登録されます。

このソリューションは、次の理由で拒否されました。

  • 大きい Docker イメージをデバイスに移動する方法が依然として必要です。 .NET アプリケーションをビルドするためのイメージは、アプリ イメージ自体よりも大きくなります。
  • この方法は、チームがソース コードを持っているアプリケーションでのみ機能するため、サードパーティのイメージは使用できません。
  • このオプションでは、NuGet パッケージをパッケージ化し、デバイスへの移動を追跡する必要があります。
  • デバイスでイメージのビルドが失敗した場合、チームはビルド環境と作成されたイメージをリモートでデバッグする必要があります。 リモート デバッグでは、潜在的に限られているインターネット接続で通信量が多くなります。

完全なイメージ差分を転送するオプション

選択された方法では、Docker イメージを 1 つのバイナリ ファイルとして扱います。 Docker save コマンドでは、イメージが .tar ファイルとしてエクスポートされます。 このソリューションでは、既存の Docker イメージと新しい Docker イメージをエクスポートし、適用されると既存のイメージが新しいイメージに変換されるバイナリ差分を計算します。

このソリューションでは、デバイス上の既存の Docker イメージを追跡し、バイナリ差分パッチをビルドして、既存のイメージを新しいイメージに変換します。 システムでは、低帯域幅のインターネット接続を介して差分パッチのみが転送されます。 このソリューションでは、バイナリ パッチをビルドするためのカスタム ロジックがいくつか必要でしたが、デバイスに送信されるデータは最も少量でした。

評価結果

次の表は、評価基準に対して測定された上記の各ソリューションを示しています。

要件を満たす デバイスのロジック Azure のロジック トランスポート 最初の画像 デバイス上のベース アプリ レイヤーの更新 基本レイヤーの更新
Docker レイヤーを転送する はい Medium FileCatalyst 100% 10.5 % 22.4 % 100%
Docker クライアントを変更する はい Medium HTTP 100% 10.5 % 22.4 % 100%
エッジ デバイスでビルドする いいえ Medium FileCatalyst N/A 該当なし 該当なし 該当なし
完全なイメージ差分を転送する はい FileCatalyst 100% 3.2 % 0.01% 16.1 %

考慮事項

これらの考慮事項は、ワークロードの品質を向上させるための一連の基本原則である Azure Well-Architected Framework の要素を組み込んでいます。 詳細については、「Microsoft Azure Well-Architected Framework」を参照してください。

パフォーマンス効率

このソリューションにより、IoT デバイスの更新によって消費される帯域幅が大幅に削減されます。 次の表は、転送効率の違いの内訳を示しています。

Image Reconstructor がソースの場合:

イメージ名 イメージ サイズ パッチ サイズ データの削減
データの視覚化 228 MB 79.6 MB 65.1%
シミュレートされた WCD 188 MB 1.5 MB 99.2%
プロキシ 258 MB 29.9 MB 88.4%

以前のバージョンがソースの場合:

イメージ名 イメージ サイズ パッチ サイズ データの削減
データの視覚化 228 MB 0.01 MB 99.9%
シミュレートされた WCD 188 MB 0.5 MB 99.7%
プロキシ 258 MB 0.04 MB 99.9%

オペレーショナル エクセレンス

以下のセクションでは、このソリューションについて詳細に説明します。

ソース コード リポジトリ

開発者が、ソース コード リポジトリ内のエッジ モジュールのソース コードを編集します。 このリポジトリは、次のように、各モジュールのコードを含むフォルダーで構成されます。


\- repository root
    - modulea
    - modulea.csproj
    - module.json
    - Program.cs
    - Dockerfile

\- moduleb
    - moduleb.csproj
    - module.json
    - Program.cs
    - Dockerfile

ソース コード リポジトリの推奨される数は次のとおりです。

  • すべてのワークストリームのすべてのモジュール用に 1 つのリポジトリ。
  • ワークストリームごとに 1 つのソース コード リポジトリ。

Container Registry インスタンス

Container Registry に各モジュールの Docker イメージが格納されます。 Container Registry では次の 2 つの構成を利用できます。

  • すべてのイメージが格納される 1 つの Container Registry インスタンス。
  • 開発、テスト、デバッグのイメージを格納するインスタンスと、運用対応としてマークされたイメージのみを含むインスタンスの 2 つの Container Registry インスタンス。

マニフェスト リポジトリ

マニフェスト リポジトリには、すべてのワークストリームの配置マニフェストが含まれています。 テンプレートは、ワークストリームに応じたフォルダーにあります。 この例では、2 つのワークストリームは共有インフラストラクチャとコンテナ化されたアプリケーションです。


\- repository root
     - Workstream1
         - deployment.template.json
     - Workstream2
         - deployment.template.json

Docker イメージのビルド パイプライン

各モジュールには、Azure Pipelines のビルド パイプラインがあります。 このパイプラインでは、汎用 Docker ビルドを使用してモジュールを作成および登録します。 パイプラインには次の役割があります。

  • ソース コードのセキュリティ スキャン。
  • Docker イメージをビルドする基本イメージのセキュリティ スキャン。
  • モジュールの単体テストの実行。
  • Docker イメージにソースをビルド。 イメージ タグには BUILD_BUILDID が含まれており、イメージを作成したソース コードに常にリンクできるようになっています。
  • Container Registry インスタンスにイメージをプッシュします。
  • 差分ファイルを作成します。
  • イメージの署名ファイルを作成し、Azure ストレージ アカウントに保存します。

すべてのパイプライン インスタンスは、1 つの YAML パイプライン定義に基づいています。 パイプラインでは、環境変数を使用してモジュールに作用させることができます。 フィルターでは、特定のフォルダーに変更がコミットされた場合にのみ、各パイプラインがトリガーされます。 このフィルターにより、1 つのモジュールだけが更新されたときに、すべてのモジュールがビルドされることを回避できます。

デバイスへのイメージ パイプライン

デバイスへのイメージ パイプラインでは、マニフェスト ファイルで定義されているように、ターゲット デバイスに Docker イメージがデプロイされます。 このパイプラインをトリガーすると、デプロイが手動で開始されます。

パイプライン定義では、コンテナーでこれらのデプロイを実行することを指定します。 パイプラインでは、コンテナーのベースとなるイメージの変数入力がサポートされます。 1 つの変数で、すべてのパイプラインのデプロイを制御できます。

このイメージには、ビルドするパッチを決定し、パッチをビルドし、ファイル転送ツールの Azure 側に配布するためのコードが含まれています。

イメージ配布ツールでは、次の情報が必要となります。

  • デプロイするイメージ (リポジトリ内のマニフェストによって提供されます)。
  • デプロイするデバイス (パイプラインをトリガーするユーザーによって提供されます)。
  • ターゲット デバイス上に既に存在するイメージ (Azure SQL データベースによって提供されます)。

パイプラインの出力は次のとおりです。

  • ファイル転送ツールの Azure 側に送られるパッチ バンドル。これはデバイスに配布されます。
  • 各デバイスへの転送が開始されたイメージを示す SQL データベースのエントリ。
  • 新しいデプロイ セットの SQL データベースのエントリ。 これらのエントリには、デプロイを指示したユーザーの名前と電子メール アドレスが含まれています。

このパイプラインでは次の手順が実行されます。

  1. 配置マニフェストに基づいて、必要なイメージを決定します。
  2. デバイスにどのイメージがすでに存在するかを SQL に問い合わせます。 すべてのイメージが既に存在する場合、パイプラインは正常に終了します。
  3. 作成するパッチ バンドルを決定します。 アルゴリズムによって、最小のパッチ バンドルが生成される開始イメージが決定されます。
    • 入力: デプロイする新しいイメージを含む .tar ファイルと、デバイス上の既存のイメージの署名ファイル。
    • 出力: 作成する最小のパッチを決定する既存のイメージのランク。
  4. デバイスごとに必要なパッチ バンドルを作成します。 同様のパッチを一度ビルドし、必要とするすべてのデバイスにそれをコピーします。
  5. デプロイのためにファイル転送ツールのストレージ アカウントにパッチを配布します。
  6. SQL を更新して、対象となる各デバイスに新しいイメージを in transit とマークします。
  7. イメージをデプロイする人の名前と連絡先の電子メールと共に、デプロイ セットの情報を SQL に追加します。

元のファイルから変更ファイル、結果データへのワークフローを示す図。

デバイスへのマニフェスト パイプライン

デバイスへのマニフェスト パイプラインでは、更新するデバイスの適切な IoT Hub 接続に配置マニフェストがプッシュされます。 ユーザーがパイプラインを手動でトリガーし、対象となる IoT Hub インスタンスの環境変数を指定します。

パイプライン:

  • デプロイに必要なイメージを決定します。
  • 必要なイメージがターゲット デバイス上にすべてあることを確認するために SQL をクエリします。 そうではない場合、パイプラインは failed 状態で終了します。
  • 新しい配置マニフェストを適切な IoT Hub にプッシュします。

高速ファイル転送ソリューション

顧客は、Azure と IoT デバイス間の接続を提供するために、FileCatalyst と呼ばれるサード パーティ製の高速ファイル転送ソリューションを引き続き使用したいと考えていました。 このソリューションは、"最終的に一貫性のある" ファイル転送ツールです。つまり、転送に時間がかかる場合がありますが、最終的にはファイルの情報を失うことなく完了します。

このソリューションでは、接続の Azure 側では Azure Storage アカウントを使用し、イメージを受信する各デバイスでは顧客の既存のファイル転送ホスト VM を使用しました。 パッチ バンドルは、IoT Hub が実行される Linux VM に転送されます。

Image Reconstruction モジュール

Image Reconstruction IoT Edge モジュールによって、受信したパッチがデバイスに適用されます。 すべてのデバイスでは、Docker オープンソース レジストリを使用して、独自のローカル コンテナー レジストリをホストします。 Image Reconstruction プロセスは、ファイル転送 VM と同じホスト VM で実行されます。

モジュール:

  1. コンテナーにマウントされたフォルダー内のパッチ バンドルを受け取ります。
  2. パッチの内容を解凍して、構成ファイルを読み取ります。
  3. ローカル コンテナー レジストリから基本イメージをハッシュによってプルします。
  4. 基本イメージを .tar ファイルとして保存します。
  5. 基本イメージにパッチを適用します。
  6. 新しいイメージを含む .tar ファイルを Docker に読み込みます。
  7. 新しいイメージと、フレンドリ名とタグを含む構成ファイルをローカル コンテナー レジストリにプッシュします。
  8. 成功メッセージを IoT Hub に送信します。

プロセスが任意の時点で失敗した場合、このモジュールでは、IoT Hub にエラー メッセージを送信し、デプロイをトリガーしたユーザーに通知できるようにします。

IoT Hub

一部のデプロイ プロセスでは、IoT Hub が使用されます。 IoT Hub では、Image Reconstruction モジュールからステータス メッセージを受け取ることに加えて、デバイスの配置マニフェストを設定します。 パイプライン フローの残りの部分では、このマニフェストが使用されます。

オペレーション センターと IoT デバイスのイメージ再構築へのパッチのワークフローを示す図。

Azure Functions

Azure Functions では、IoT Hub からのメッセージ ストリームを監視し、クラウドでアクションを実行します。

成功メッセージの場合:

  • 関数では、デバイス上のイメージの SQL エントリの状態を in transit から succeeded に更新します。
  • このイメージがデプロイ セットに到着する最後のイメージの場合:
    • 関数では、デプロイの成功をユーザーに通知します。
    • 関数では、デバイスへのマニフェスト パイプラインを更新して、新しいイメージの使用を開始します。

エラー メッセージの場合:

  • 関数では、デバイス上のイメージの SQL エントリの状態を in transit から failed に更新します。
  • 関数では、イメージ転送エラーをユーザーに通知します。

Operations Center と IoT デバイス イメージ再構築メッセージ ワークフロー

SQL Database

SQL データベースでは、デプロイ中とデプロイ後に、ターゲット デバイスと Azure ベースのデプロイ サービスでの事象を追跡します。 Azure Functions と Azure Pipelines では、データベースとやり取りするために作成されるプライベート NuGet パッケージが使用されます。

SQL Database には次のデータが格納されます。

  • 各デバイスにどのイメージがあるか。
  • どのイメージが各デバイスに転送されているか。
  • デプロイされているどのイメージがセットに属しているか。
  • デプロイを指示したユーザー。

この例の目的は、将来のデータ ダッシュボードに必要なデータがシステムによって生成されるようにすることでした。 IoT Hub に問い合わせると、デバイスへのマニフェスト パイプラインに関する次のデータを取得できます。

  • デプロイの状態。
  • 特定のデバイス上のイメージ。
  • イメージを持つデバイス。
  • 成功した転送と失敗した転送に関する時系列データ。
  • ユーザーに基づくデプロイのクエリ。

共同作成者

この記事は、Microsoft によって保守されています。 当初の寄稿者は以下のとおりです。

プリンシパル作成者:

  • Kanio Dimitrov | プリンシパル ソフトウェア エンジニアリング リーダー

次の手順