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

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

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

Takahiro Iwasa
11 min read

Greengrass Core Docker イメージ を使うと、物理的なエッジデバイスを用意せずに、ローカル環境で AWS IoT Greengrass コンポーネントを開発できます。詳細は公式ドキュメントを参照してください。

この記事では、AWS IoT Core へ 1 秒ごとに MQTT メッセージをパブリッシュする 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 トピックへ 1 秒ごとに 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 と通信します。そのため、ComponentDependenciesaws.greengrass.TokenExchangeService コンポーネントを追加します。Token Exchange Service はローカルエンドポイントを公開し、カスタムコンポーネントへ 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.jsonversionNEXT_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

環境変数ファイル

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 と Greengrass Core Docker イメージを使うと、物理的なエッジデバイスを用意せずに MQTT パブリッシュ用コンポーネントを構築し、AWS IoT 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.