Docker環境でGreengrassコンポーネントをビルド・デプロイする

Docker環境でGreengrassコンポーネントをビルド・デプロイする

Greengrass Core Dockerイメージを使って、AWS IoT Greengrassコンポーネントをローカルで開発します。

Takahiro Iwasa
12 min read

Greengrass CoreのDockerイメージを使えば、物理ハードウェア上ではなく、ローカル環境でAWS IoT Greengrassコンポーネントを開発できます。背景については公式ドキュメントを参照してください。

ここで構築するのは、MQTT経由で毎秒AWS IoT Coreにメッセージを送信するGreengrassコンポーネントです。Greengrass CLIを使い、ローカルのDockerコンテナにデプロイします。

この例の最後には、プロジェクトディレクトリは以下のような構成になります。

Terminal window
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)をインストールします。

Important

pip install gdkを実行すると、Greengrass Development Kitとは無関係のライブラリがインストールされます。

Terminal window
pip install -U git+https://github.com/aws-greengrass/[email protected]

開発の開始

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

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

このコマンドは、以下のファイルとディレクトリからなる基本的なコンポーネント構造を生成します。

Terminal window
components/
├── mqtt_publisher/
├── src/
├── greeter.py
├── tests/
├── test_greeter.py
├── .gitignore
├── gdk-config.json
├── main.py
├── README.md
├── recipe.yaml
ℹ️ Note

srcディレクトリとtestsディレクトリは、この例では使用しません。

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

gdk-config.jsonファイルをコンポーネントのメタデータで更新します。gdk component publishを使ってコンポーネントをS3バケットに公開しない場合、publish.bucketフィールド(10行目)は設定する必要はありません。

更新後のgdk-config.jsonの例です。

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

詳細については、GDK CLI設定ファイルに関する公式ドキュメントを参照してください。

🔥 Caution

versionの値としてNEXT_PATCHを使用しないでください。greengrass-cli deployment createを使ってコンポーネントをデプロイする際にエラーが発生します。

Pythonスクリプトの作成

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

main.py
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ファイルを作成します。これらの依存関係は、recipe.yamlで定義されたコンポーネントのインストール処理中にインストールされます。

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コンポーネントを依存関係として指定します。TokenExchangeServiceはローカルサーバーを実行し、カスタムコンポーネントにAWSの認証情報を提供します。

詳細については公式ドキュメントを参照してください。

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セクションでは、コンポーネントのインストール時および実行時に実行するコマンドを指定します。

  • Install: requirements.txtに記載されたPythonライブラリをインストールします。
  • Run: コンポーネント起動時にmain.pyスクリプトを実行します。

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

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

  • {COMPONENT_NAME}
  • {COMPONENT_VERSION}
  • {COMPONENT_AUTHOR}
  • Artifacts URI(BUCKET_NAMECOMPONENT_NAMECOMPONENT_VERSION

コンポーネントのビルド

Greengrass Development Kitを使ってコンポーネントをビルドするには、gdk component buildを使用します。

Terminal window
cd components/mqtt_publisher
gdk component build

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

🔥 Caution

gdk-config.jsonversion値としてNEXT_PATCHを使用すると、bin/greengrass-cli deployment create実行時にデプロイが失敗します。

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つ)
    • Token Exchange Roleエイリアス
  • AWS IAM
    • Token Exchangeロール
    • Token Exchangeロールポリシー

一時的な認証情報を生成します。

Terminal window
aws sts get-session-token

認証情報をファイルに保存します。

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

credentialsの内容の例です。

docker/greengrass-v2-credentials/credentials
[default]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
aws_session_token = AQoEXAMPLEH4aoAH0gNCAPy...truncated...zrkuWJOgQs8IZZaIv2BXIa2R4Olgk

.envファイル

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

.envファイルの例です。

docker/.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

Greengrass Coreの実行

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

docker-compose.ymlの例です。

docker/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'

コンテナを実行します。

Terminal window
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内に配置されます。

Terminal window
docker-compose exec greengrass bash
cd /greengrass/v2
ls bin
Important

Greengrass CLIを本番環境で使用しないでください。

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.

Token Exchange Service

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

https://docs.aws.amazon.com/greengrass/v2/developerguide/interact-with-aws-services.html

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}

Token Exchangeロールの更新

MQTTパブリッシュの権限を付与するため、GreengrassV2TokenExchangeRoleのIAMポリシーを更新します。

{
"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*"
}
]
}

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

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

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

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

Terminal window
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を使ってデプロイのステータスを確認します。

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

以下のレスポンスが表示されるはずです。

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

コンポーネントが正常に動作していることを確認するため、ログを監視します。

Terminal window
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 Test Clientでのテスト

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

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

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

まとめ

GDKを使ってMQTTパブリッシュを行うGreengrassコンポーネントを構築し、Docker化されたGreengrass Coreにデプロイしたところ、物理ハードウェアを一切使わずにメッセージがAWS IoT Coreに届くことがわかりました。Docker上でGreengrass Coreを実行すれば、本来は物理ハードウェアが必要な作業を、ローカルで繰り返し開発できるようになります。GDKによるビルド、ローカルでのgreengrass-cli deployment create、ログの監視は、実際のコアデバイスに対する場合と同じように機能するため、コンポーネント開発時のフィードバックループが大幅に短縮されます。Greengrass CLIコンポーネント自体は、本番環境に持ち込まないよう特に注意すべき唯一の要素です。というのも、その目的自体が、本番環境のフリートでは持たせるべきではないローカルデプロイやデバッグ操作へのアクセスを提供することにあるからです。

About the author

Takahiro Iwasa

Takahiro Iwasa

Software Developer

This blog shares technical notes from hands-on projects—architecture, implementation, and AWS service integrations.