Amazon Web Services ブログ

AWS IoT Greengrass で Docker コンテナのライフサイクルを管理する

はじめに

この記事では、AWS IoT Greengrass カスタムコンポーネントを使用して Docker コンテナのライフサイクルを管理する方法について説明します。Docker コンテナのライフサイクルには、作成、実行、一時停止/再開、停止、および終了の5つのフェーズがあります。カスタムコンポーネントは、Docker SDK for Python を介して Docker Engine とやりとりし、ユーザーが開始したコマンドやアプリケーション送信コマンドなどのユースケースに基づいてプロセスを管理します。

AWS IoT Greengrass は、デバイス上の IoT アプリケーションを構築、デプロイ、管理するのに役立つオープンソースのモノのインターネット (IoT) のエッジランタイムおよびクラウドサービスです。AWS IoT Greengrass を使用して、エッジデバイスを AWS サービスまたはサードパーティのサービスに接続できるコンポーネントと呼ばれるビルド済みのソフトウェアモジュールを使用してエッジアプリケーションを構築できます。

AWS IoT Greengrass コンポーネントは、アプリケーション、ランタイムインストーラ、ライブラリ、またはデバイスで実行する任意のコードを表すことができます。以下の場所に保存されているイメージから Docker コンテナを実行するように AWS IoT Greengrass コンポーネントを設定できます。

  • Amazon Elastic Container Registry (Amazon ECR) のパブリックおよびプライベートイメージリポジトリ
  • Public Docker Hub repository
  • Public Docker Trusted Registry
  • Amazon S3 バケット

Greengrass コンポーネントには、設定可能な独自のライフサイクルがありますが、これらのライフサイクルはコンテナ化されたプロセスをサポートしていません。AWS IoT Greengrass で実行されている Docker コンテナを起動、停止、一時停止、および再開するには、Greengrass コアデバイスで実行されている Greengrass コンポーネントを使用して、Dockerを一時停止及び一時停止解除するなどのコマンドを使用できます。「ライフサイクルコンポーネント」と呼ばれるカスタムライフサイクルコンポーネントは、AWS IoT Core MQTT トピックをサブスクライブし、Docker Engine とやりとりする Python スクリプトで構成されています。

ソリューションの概要

以下は、ワークフローとアーキテクチャの実装方法の1例です。これらのビルディングブロックを活用して、特定のユースケースに合わせてさらに拡張できます。

  1. ユーザーが Docker コンテナコンポーネントとライフサイクルコンポーネントを Greengrass コアデバイスにデプロイします。
  2. アプリケーションは AWS IoT Core トピックに MQTT メッセージを発行します。MQTT メッセージは、コンテナ名と実行したいアクションを指定します。この例では、envという名前のコンテナに start コマンドを送信します。
  3. カスタムライフサイクルコンポーネントがトピックにサブスクライブされます。
  4. ライフサイクルコンポーネントはメッセージを受け取り、Python 用の Docker SDK を介して Docker エンジンと対話し、指定されたコンテナ名で目的のコマンドを実行します。
  5. ライフサイクルコンポーネントから受け取ったコマンドに基づいて、Docker Engine は指定されたコンテナを一時停止、一時停止解除、起動、または停止します。

ソリューション構成図

実施手順

前提条件

DockerコンテナをGreengrass コアデバイスにデプロイする

AWS IoT Greengrass で Docker コンテナを実行する方法の指示に従うか、オプションで Docker エンジン自体でコンテナを作成して実行することもできます。コンテナには必ず名前を付けてください。この例では、envの名前を使用します。

実行中の Docker コンテナがあり、対象の名前があることを確認してください。

docker container ls

カスタムライフサイクルコンポーネントを作成する

Greengrass コンポーネントを作成するには、コードを含む Python スクリプトと、コンポーネントが Greengrass Core デバイスにデプロイされたときにデプロイの詳細を指定する Greengrass レシピを作成する必要があります。

  1. 空のフォルダとcustomlifecycle.pyという名前のスクリプトファイルを作成します。
    mkdir -p ~/artifacts && touch ~/artifacts/customlifecycle.py
    
  2. お気に入りの統合開発環境(IDE)で、customlifecycle.pyを開き、次のコードを貼り付けます。必ずファイルを保存してください。注:以下のコードスニペットは MIT-0 ライセンスの下にあり、Github で入手できます。
    #Imports
    import time
    import json
    import traceback
    import docker
    import subprocess
    import awsiot.greengrasscoreipc
    import awsiot.greengrasscoreipc.client as client
    from awsiot.greengrasscoreipc.model import (
        IoTCoreMessage,
        QOS,
        SubscribeToIoTCoreRequest
    )
    
    TIMEOUT = 10
    ipc_client = awsiot.greengrasscoreipc.connect()
    topic = "docker"
    qos = QOS.AT_MOST_ONCE
    
    #IPC Stream Handler
    class StreamHandler(client.SubscribeToIoTCoreStreamHandler):
        def __init__(self):
            super().__init__()
    
        def on_stream_event(self, event: IoTCoreMessage) -> None:
            message = json.loads(event.message.payload.decode())
            
            try:
                client = docker.from_env()
                name = message["name"]
                command = message["command"]
            
                if command == "start":
                    container = client.containers.get(name)
                    container.start()
                    print("Starting container: " + name)
            
                elif command == "pause":
                    container = client.containers.get(name)
                    result = json.loads(container.pause())
                    print(result)
                    print("Pausing container: " + name)
                    
                elif command == "unpause":
                    container = client.containers.get(name)
                    print(container.unpause())
                    print("Unpausing container: " + name)
                    
                elif command == "stop":
                    container = client.containers.get(name)
                    container.stop()
                    print("Stopping container: " + name)
                    
                else:
                    print("Error")
                
            except:
                with tempfile.TemporaryFile() as tmp:
                tmp.write("Docker Error")
                    
        def on_stream_error(self, error: Exception) -> bool:
            message_string = "Error!"
    
            return True
    
        def on_stream_closed(self) -> None:
            pass
            
    #Initiate Subscription
    request = SubscribeToIoTCoreRequest()
    request.topic_name = topic
    request.qos = qos
    handler = StreamHandler()
    operation = ipc_client.new_subscribe_to_iot_core(handler)
    future = operation.activate(request)
    future_response = operation.get_response()
    future_response.result(TIMEOUT)
    
    while True:
        time.sleep(1)
    
    operation.close()
  3. バケットを作成し、次のコマンドを使用してバケット名を取得します。
    EPOCH_TIME=$(date +"%s") && S3_BUCKET=lifecycle-component-$EPOCH_TIME && aws s3 mb s3://$S3_BUCKET
  4. 次のコマンドを実行して、レシピを入れるフォルダとファイルを作成します。
    mkdir -p ~/recipes && touch ~/recipes/customlifecycle-1.0.0.json
  5. 作成したレシピファイルcustomlifecycle-1.0.0.jsonを開き、次の内容を貼り付けます。 [YOUR BUCKET NAME] をステップ3で取得したバケット名に置き換えます。
    {
        "RecipeFormatVersion": "2020-01-25",
        "ComponentName": "Docker-lifecycle-component",
        "ComponentVersion": "1.0.0",
        "ComponentType": "aws.greengrass.generic",
        "ComponentDescription": "A component that interacts with Docker daemon.",
        "ComponentPublisher": "Amazon",
        "ComponentConfiguration": {
          "DefaultConfiguration": {
            "accessControl": {
              "aws.greengrass.ipc.mqttproxy": {
                "docker_lifecycle:mqttproxy:1": {
                  "policyDescription": "Allows access to subscribe to all topics.",
                  "operations": [
                    "aws.greengrass#SubscribeToIoTCore"
                  ],
                  "resources": [
                    "*"
                  ]
                }
              }
            }
          }
        },
        "Manifests": [
          {
            "Lifecycle": {
              "Install": "pip3 install awsiotsdk",
              "Run": "python3 -u {artifacts:path}/customlifecycle.py"
            },
            "Artifacts": [
              {
                "Uri": "s3://[YOUR BUCKET NAME]/customlifecycle.py"
              }
            ]
          }
        ]
      }
  6. コンポーネントのアーティファクトを Amazon Simple Storage Service にアップロードします。
    aws s3 cp --recursive ~/artifacts/ s3://$S3_BUCKET/ 
  7. 次に、下記のコマンドを実行して Greengrass コンポーネントを公開します。
    cd ~/recipes && aws greengrassv2 create-component-version --inline-recipe fileb://customlifecycle-1.0.0.json
  8. AWS IoT コンソール > [Greengrass デバイス] > [コンポーネント] > [マイコンポーネント] から AWS Greengrass コンポーネントに追加されたことを確認します。

カスタムライフサイクルコンポーネントをデプロイする

次に、AWS CLI を使用して、カスタムライフサイクルコンポーネントを Greengrass コアデバイスにデプロイします。デプロイメントは、モノまたはモノのグループに適用できます。この場合、Greengrass コアのエンティティに直接デプロイを適用します。

  1. 以下のコマンドを使用して、デプロイメントマニフェストのフォルダとファイルを作成します。
    mkdir -p ~/deployments && touch ~/deployments/gg_deployment.json
  2. IDEで、以下をコピーしてgg_deployment.jsonファイルに貼り付けます。[targetARN] をモノの ARN で更新します。モノの ARN は、AWS IoT Core コンソールから取得できます。必ずファイルを保存してください。
    { 
       "targetArn": "[targetArn]", 
       "deploymentName": "Deployment for Custom Docker Lifecycle", 
       "components": { 
          "Docker-lifecycle-component": { 
             "componentVersion": "1.0.0" 
          }
        }
     }
  3. 以下のコマンドを使用しデプロイします。
    cd ~/deployments && aws greengrassv2 create-deployment --cli-input-json file://gg_deployment.json
  4. コンポーネントが Greengrass コアデバイスで実行されていることを確認します。インスタンス化されるまでに数分かかる場合があります。
    sudo /greengrass/v2/bin/greengrass-cli component list

カスタムライフサイクルコンポーネントのテスト

  1. AWS IoT Core コンソールに移動し、[MQTT テストクライアント]を選択します。
  2. [トピックに公開する]を選択します。
  3. トピック名に、 docker を入力します。
  4. メッセージペイロードで、以下のメッセージをコピーします。コマンド構文は、コンテナの名前と現在の状態によって異なります。
    {
      "command":"start",
      "name":"env"
    }
  5. コンテナの状態が変更されたことを確認してください。
    docker container ls

結論

このブログ記事では、AWS IoT Greengrass を使用して Docker コンテナのライフサイクルを制御する方法を説明しました。これは、AWS IoT Core MQTT トピックをサブスクライブし、メッセージの内容を使用して、Docker SDK for Python を使用した Docker デーモンに対してコマンドを実行するカスタムコンポーネントを使用して実現されました。

Greengrass コンポーネントの構築など、AWS IoT Greengrass をより深く掘り下げるには、AWS IoT Greengrass V2 ワークショップをご覧ください。

この記事は Kai-Matthias Dickman によって投稿された Managing Docker container lifecycle with AWS IoT Greengrass をソリューションアーキテクトの服部が翻訳しました。