Docker での Greengrass コンポーネント開発: ステップバイステップガイド

Docker での Greengrass コンポーネント開発: ステップバイステップガイド

岩佐 孝浩
岩佐 孝浩
12 min read
Greengrass Greengrass Development Kit IoT

はじめに

この記事では、AWS IoT Greengrass Core の Docker イメージ を使用して、ローカル環境での Greengrass コンポーネント開発方法を説明します。

概要

この投稿では、MQTT を通じて毎秒メッセージを AWS IoT Core に送信する Greengrass コンポーネントを構築します。作成したコンポーネントは、Greengrass CLI を使用してローカルの Docker コンテナにデプロイします。

この投稿の終わりには、プロジェクトディレクトリは次のようになります。

/
|-- components/
|   `-- mqtt_publisher/
|        |-- .gitignore
|        |-- gdk-config.json
|        |-- main.py
|        |-- recipe.yaml
|        `-- requirements.txt
`-- docker/
  |-- greengrass-v2-credentials/
  |   `-- credentials
  |-- .env
  `-- docker-compose.yml

Greengrass カスタムコンポーネントの開発

Greengrass Development Kit (GDK) のインストール

Greengrass Development Kit (GDK) をインストールします。

pip install -U git+https://github.com/aws-greengrass/aws-greengrass-gdk-cli.git@v1.1.0

開発の開始

gdk component init を実行して Greengrass コンポーネントを初期化します。

まず、components ディレクトリを作成し、コンポーネントを初期化します。

mkdir ./components
gdk component init \
  --language python \
  --template HelloWorld \
  --name components/mqtt_publisher

このコマンドにより、次のような基本的なコンポーネント構造が生成されます。

./
|-- components/
|   |-- mqtt_publisher/
|       |-- src/
|       |   |-- greeter.py
|       |-- tests/
|       |   |-- test_greeter.py
|       |-- .gitignore
|       |-- gdk-config.json
|       |-- main.py
|       |-- README.md
|       |-- recipe.yaml

コンポーネントメタデータの設定

gdk-config.json ファイルを更新してコンポーネントのメタデータを設定します。コンポーネントを gdk component publish を使用して S3 バケットにパブリッシュしない場合、publish.bucket フィールドを設定する必要はありません。

以下は、更新された gdk-config.json の例です。

{
  "component": {
    "com.example.MqttPublisher": {
      "author": "wasabee.io",
      "version": "0.0.1",
      "build": {
        "build_system": "zip"
      },
      "publish": {
        "bucket": "<PLACEHOLDER_BUCKET>",
        "region": "ap-northeast-1"
      }
    }
  },
  "gdk_version": "1.0.0"
}

詳しい情報は、GDK CLI 構成ファイルに関する公式ドキュメント をご参照ください。

この設定で、カスタム Greengrass コンポーネントの実装を開始する準備が整いました。

Python スクリプト

コンポーネントの main.py スクリプトを作成します。このスクリプトは、/mqtt-publisher トピックに毎秒 MQTT メッセージをパブリッシュします。

import json
import random
from datetime import datetime
from time import sleep

import boto3

client = boto3.client('iot-data')


def main():
    payload = {
        "value": random.randint(1, 10000),
        "datetime": datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
    }
    while True:
        client.publish(
            topic='/mqtt-publisher',
            payload=json.dumps(payload).encode(),
            qos=1,
            contentType='application/json',
        )
        print(f'Message was sent successfully: {payload}')
        sleep(1)


if __name__ == "__main__":
    main()

依存関係

requirements.txt ファイルを作成して、コンポーネントに必要な依存関係を指定します。

boto3==1.26.65

これらの依存関係は、recipe.yaml に定義されたコンポーネントのインストールプロセス中にインストールされます。

コンポーネントレシピ

コンポーネントのレシピを定義するために、recipe.yaml ファイルを作成します。レシピは、メタデータ、依存関係、およびライフサイクルフックを指定します。コンポーネントレシピ仕様の詳細については、公式ドキュメント をご参照ください。

例:

---
RecipeFormatVersion: "2020-01-25"
ComponentName: "{COMPONENT_NAME}"
ComponentVersion: "{COMPONENT_VERSION}"
ComponentDescription: "This is an mqtt publisher written in Python."
ComponentPublisher: "{COMPONENT_AUTHOR}"
ComponentDependencies:
  aws.greengrass.TokenExchangeService:
    VersionRequirement: '^2.0.0'
Manifests:
  - Platform:
      os: all
    Artifacts:
      - URI: "s3://BUCKET_NAME/COMPONENT_NAME/COMPONENT_VERSION/mqtt_publisher.zip"
        Unarchive: ZIP
    Lifecycle:
      Install: "pip3 install --user -r {artifacts:decompressedPath}/mqtt_publisher/requirements.txt"
      Run: "python3 -u {artifacts:decompressedPath}/mqtt_publisher/main.py"

コンポーネント依存関係

このスクリプトは boto3 ライブラリに依存して AWS IoT Core とやり取りします。ComponentDependencies セクションに aws.greengrass.TokenExchangeService コンポーネントを依存関係として指定します。

  • 目的: AWS サービスとやり取りするための資格情報を提供。
  • 主な機能: TokenExchangeService は、AWS 資格情報を提供するローカルサーバーを実行します。

詳しくは Greengrass ドキュメント をご参照ください。

AWS IoT Greengrass provides a public component, the token exchange service component, that you can define as a dependency in your custom component to interact with AWS services. The token exchange service provides your component with an environment variable, AWS_CONTAINER_CREDENTIALS_FULL_URI, that defines the URI to a local server that provides AWS credentials.

ライフサイクルフック

Lifecycle セクションでは、コンポーネントのインストールおよび実行中に実行するコマンドを指定します。

  1. Install: requirements.txt にリストされた Python ライブラリをインストールします。
  2. Run: コンポーネントが起動するときに main.py スクリプトを実行します。

レシピのプレースホルダー

レシピ内のプレースホルダー(例:{COMPONENT_NAME})は、ビルドプロセス中に gdk-config.json の値に置き換えられます。これらのプレースホルダーには以下が含まれます。

  • {COMPONENT_NAME}
  • {COMPONENT_VERSION}
  • {COMPONENT_AUTHOR}
  • アーティファクト URI コンポーネント(例:BUCKET_NAMECOMPONENT_NAMECOMPONENT_VERSION

コンポーネントのビルド

Greengrass Development Kit を使用してコンポーネントをビルドします。

cd components/mqtt_publisher
gdk component build

ビルド後、アーティファクトは greengrass-build ディレクトリに配置されます。ローカルの Docker コンテナへのデプロイでは gdk component publish を実行する必要はありません。

これで、Docker 上で動作する Greengrass Core にコンポーネントをデプロイする準備が整いました。

Docker 上の Greengrass Core

このセクションでは、Docker コンテナ内で Greengrass Core をセットアップし、資格情報を構成し、コンポーネントをデプロイする方法を説明します。

以下の作業は、<PROJECT_ROOT>/docker ディレクトリで実行します。

セキュリティ資格情報

Greengrass Core では、リソースの自動プロビジョニングのために AWS セキュリティ資格情報が必要です。永続的な資格情報も使用可能ですが、セキュリティ強化のため sts get-session-token による 一時的な資格情報 の使用を推奨します。

以下の AWS リソースがプロビジョニングされます。

  • AWS IoT
    • Greengrass Core デバイス
    • IoT Thing
    • IoT Thing グループ
    • 証明書
    • ポリシー(2 つ)
    • トークン交換ロールエイリアス
  • AWS IAM
    • トークン交換ロール
    • トークン交換ロールポリシー

一時的な資格情報を生成します。

aws sts get-session-token

資格情報をファイルに保存します。

mkdir ./greengrass-v2-credentials
nano ./greengrass-v2-credentials/credentials

credentials の例:

[default]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
aws_session_token = AQoEXAMPLEH4aoAH0gNCAPy...truncated...zrkuWJOgQs8IZZaIv2BXIa2R4Olgk

環境ファイル

Greengrass Core インストーラの環境変数を構成するために .env ファイルを作成します。詳細については、公式ドキュメント をご参照ください。

.env ファイルの例:

GGC_ROOT_PATH=/greengrass/v2
AWS_REGION=ap-northeast-1
PROVISION=true
THING_NAME=MyGreengrassCore
THING_GROUP_NAME=MyGreengrassCoreGroup
TES_ROLE_NAME=GreengrassV2TokenExchangeRole
TES_ROLE_ALIAS_NAME=GreengrassCoreTokenExchangeRoleAlias
COMPONENT_DEFAULT_USER=ggc_user:ggc_group

Docker で Greengrass Core を実行する

Docker で Greengrass Core を実行するために、docker-compose.yml ファイルを作成します。詳細については、ドキュメント をご参照ください。

docker-compose.yml の例:

version: '3.7'

services:
  greengrass:
    init: true
    container_name: aws-iot-greengrass
    image: amazon/aws-iot-greengrass:latest
    volumes:
      - ./greengrass-v2-credentials:/root/.aws/:ro
      - ../components:/root/components
    env_file: .env
    ports:
      - '8883:8883'

コンテナを実行します:

docker-compose up -d
docker-compose logs -f greengrass

次のようなログが表示され、Nucleus が正常に起動したことを確認できます:

aws-iot-greengrass | Launching Nucleus...
aws-iot-greengrass | Launched Nucleus successfully.

AWS 提供のコンポーネントをデプロイする

Greengrass CLI

ローカルデプロイメント用に Greengrass CLI コンポーネント (aws.greengrass.Cli) をインストールします。インストール後は /greengrass/v2/bin に配置されます。

docker-compose exec greengrass bash
cd /greengrass/v2
ls bin

We recommend that you use this component in only development environments, not production environments. This component provides access to information and operations that you typically won’t need in a production environment. Follow the principle of least privilege by deploying this component to only core devices where you need it.

トークン交換サービス

カスタムコンポーネントが AWS とやり取りできるようにするために、aws.greengrass.TokenExchangeService コンポーネントをデプロイします。このサービスは、一時的な資格情報をローカルサーバーを通じて提供します。

Greengrass core devices use X.509 certificates to connect to AWS IoT Core using TLS mutual authentication protocols. These certificates let devices interact with AWS IoT without AWS credentials, which typically comprise an access key ID and a secret access key.

AWS IoT Greengrass コンソールからのデプロイ

Greengrass Nucleus を含む AWS 提供のコンポーネントを AWS IoT Greengrass コンソールを通じてデプロイします。

デプロイが完了すると、/greengrass/v2/logs/greengrass.log に次のような成功ログが表示されます。

[INFO] (Thread-4) com.aws.greengrass.deployment.IotJobsHelper: Job status update was accepted. {Status=SUCCEEDED, ThingName=MyGreengrassCore, JobId=}
[INFO] (pool-2-thread-11) com.aws.greengrass.status.FleetStatusService: fss-status-update-published. Status update published to FSS. {trigger=THING_GROUP_DEPLOYMENT, serviceName=FleetStatusService,
[INFO] (pool-2-thread-11) com.aws.greengrass.deployment.DeploymentDirectoryManager: Persist link to last deployment. {link=/greengrass/v2/deployments/previous-success}
[INFO] (Thread-4) com.aws.greengrass.deployment.IotJobsHelper: Received empty jobs in notification . {ThingName=MyGreengrassCore}

トークン交換ロールの更新

GreengrassV2TokenExchangeRole IAM ポリシーを更新し、MQTT パブリッシングの権限を付与します:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:Connect",
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Publish",
      "Resource": "arn:aws:iot:*:<AWS_ACCOUNT_ID>:topic//mqtt-publisher*"
    }
  ]
}

ポリシーをアタッチします:

aws iam put-role-policy \
  --role-name GreengrassV2TokenExchangeRole \
  --policy-name IoTPolicy \
  --policy-document file://policy.json

カスタムコンポーネントのローカルデプロイ

Docker コンテナ内で Greengrass CLI を使用して、カスタムコンポーネントをデプロイします:greengrass-cli deployment create

cd /greengrass/v2
bin/greengrass-cli deployment create \
  --recipeDir /root/components/mqtt_publisher/greengrass-build/recipes \
  --artifactDir /root/components/mqtt_publisher/greengrass-build/artifacts \
  --merge "com.example.MqttPublisher=0.0.1"

デプロイメントステータスを確認するには、greengrass-cli deployment status を使用します:

bin/greengrass-cli deployment status -i <DEPLOYMENT_ID>

以下のようなレスポンスが表示されます:

INFO: Connection established with event stream RPC server
<DEPLOYMENT_ID>: SUCCEEDED

ログを監視してコンポーネントが実行中であることを確認します:

cd /greengrass/v2/logs
tail -f com.example.MqttPublisher.log

期待されるログ出力:

[INFO] (Copier) com.example.MqttPublisher: stdout. Message was sent successfully: {'value': 31, 'datetime': '2023-02-27 12:31:35'}. {scriptName=services.com.example.MqttPublisher.lifecycle.Run, serviceName=com.example.MqttPublisher, currentState=RUNNING}

AWS IoT テストクライアントでのテスト

AWS IoT コンソールの MQTT テストクライアントを使用して、メッセージが /mqtt-publisher トピックにパブリッシュされていることを確認します。

  1. MQTT テストクライアント に移動します。
  2. Topic filter フィールドに /# または /mqtt-publisher を入力します。
  3. Subscribe ボタンをクリックします。

カスタムコンポーネントからパブリッシュされたメッセージが表示されます。

まとめ

AWS IoT Greengrass Docker イメージを活用することで、コンポーネントのローカル開発とテストをより効率的かつ柔軟に行えます。この投稿が IoT アプリケーション開発プロセスの効率化に寄与できれば幸いです。

Happy Coding! 🚀

岩佐 孝浩

岩佐 孝浩

Software Developer at KAKEHASHI Inc.
AWS を活用したクラウドネイティブ・アプリケーションの要件定義・設計・開発に従事。 株式会社カケハシで、処方箋データ収集の新たな基盤の構築に携わっています。 Japan AWS Top Engineers 2020-2023