Docker環境でGreengrassコンポーネントをビルド・デプロイする
Greengrass Core Dockerイメージを使って、AWS IoT Greengrassコンポーネントをローカルで開発します。
Greengrass CoreのDockerイメージを使えば、物理ハードウェア上ではなく、ローカル環境でAWS IoT Greengrassコンポーネントを開発できます。背景については公式ドキュメントを参照してください。
ここで構築するのは、MQTT経由で毎秒AWS IoT Coreにメッセージを送信するGreengrassコンポーネントです。Greengrass CLIを使い、ローカルのDockerコンテナにデプロイします。

この例の最後には、プロジェクトディレクトリは以下のような構成になります。
components/├── mqtt_publisher/│ ├── .gitignore│ ├── gdk-config.json│ ├── main.py│ ├── recipe.yaml│ ├── requirements.txtdocker/├── greengrass-v2-credentials/│ ├── credentials├── .env├── docker-compose.ymlGreengrassカスタムコンポーネントの開発
Greengrass Development Kit(GDK)のインストール
Greengrass Development Kit(GDK)をインストールします。
pip install gdkを実行すると、Greengrass Development Kitとは無関係のライブラリがインストールされます。
開発の開始
gdk component initを実行して、Greengrassコンポーネントを初期化します。
mkdir ./componentsgdk 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
srcディレクトリとtestsディレクトリは、この例では使用しません。
コンポーネントメタデータの設定
gdk-config.jsonファイルをコンポーネントのメタデータで更新します。gdk component publishを使ってコンポーネントをS3バケットに公開しない場合、publish.bucketフィールド(10行目)は設定する必要はありません。
更新後の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設定ファイルに関する公式ドキュメントを参照してください。
versionの値としてNEXT_PATCHを使用しないでください。greengrass-cli deployment createを使ってコンポーネントをデプロイする際にエラーが発生します。
Pythonスクリプトの作成
コンポーネント用のmain.pyスクリプトを作成します。このスクリプトは、/mqtt-publisherトピックに毎秒MQTTメッセージをパブリッシュします。
import jsonimport randomfrom datetime import datetimefrom 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で定義されたコンポーネントのインストール処理中にインストールされます。
boto3==1.26.65コンポーネントレシピ
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_NAME、COMPONENT_NAME、COMPONENT_VERSION)
コンポーネントのビルド
Greengrass Development Kitを使ってコンポーネントをビルドするには、gdk component buildを使用します。
cd components/mqtt_publishergdk component buildビルド後、成果物はgreengrass-buildディレクトリに配置されます。ローカルのDockerコンテナにデプロイする場合、gdk component publishを実行する必要はありません。
gdk-config.jsonのversion値として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ロールポリシー
一時的な認証情報を生成します。
aws sts get-session-token認証情報をファイルに保存します。
mkdir ./greengrass-v2-credentialsnano ./greengrass-v2-credentials/credentialscredentialsの内容の例です。
[default]aws_access_key_id = AKIAIOSFODNN7EXAMPLEaws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEYaws_session_token = AQoEXAMPLEH4aoAH0gNCAPy...truncated...zrkuWJOgQs8IZZaIv2BXIa2R4Olgk.envファイル
Greengrass Coreインストーラー用の環境変数を設定するため、.envファイルを作成します。詳細については公式ドキュメントを参照してください。
.envファイルの例です。
GGC_ROOT_PATH=/greengrass/v2AWS_REGION=ap-northeast-1PROVISION=trueTHING_NAME=MyGreengrassCoreTHING_GROUP_NAME=MyGreengrassCoreGroupTES_ROLE_NAME=GreengrassV2TokenExchangeRoleTES_ROLE_ALIAS_NAME=GreengrassCoreTokenExchangeRoleAliasCOMPONENT_DEFAULT_USER=ggc_user:ggc_groupGreengrass 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 -ddocker-compose logs -f greengrassNucleusが正常に起動したことを示すログが表示されるはずです。
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 bashcd /greengrass/v2ls binGreengrass 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*" } ]}ポリシーをアタッチします。
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/v2bin/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/logstail -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トピックにメッセージがパブリッシュされていることを確認します。
- MQTT test clientに移動します。
Topic filterフィールドに/#または/mqtt-publisherを入力します。Subscribeボタンをクリックします。
カスタムコンポーネントからパブリッシュされたメッセージが表示されるはずです。

まとめ
GDKを使ってMQTTパブリッシュを行うGreengrassコンポーネントを構築し、Docker化されたGreengrass Coreにデプロイしたところ、物理ハードウェアを一切使わずにメッセージがAWS IoT Coreに届くことがわかりました。Docker上でGreengrass Coreを実行すれば、本来は物理ハードウェアが必要な作業を、ローカルで繰り返し開発できるようになります。GDKによるビルド、ローカルでのgreengrass-cli deployment create、ログの監視は、実際のコアデバイスに対する場合と同じように機能するため、コンポーネント開発時のフィードバックループが大幅に短縮されます。Greengrass CLIコンポーネント自体は、本番環境に持ち込まないよう特に注意すべき唯一の要素です。というのも、その目的自体が、本番環境のフリートでは持たせるべきではないローカルデプロイやデバッグ操作へのアクセスを提供することにあるからです。
Related posts
SiteWise Edge GatewayでOPC UAデータをKinesisにストリーミングする
SiteWise Edge Gatewayとカスタムのgreengrassコンポーネントを使って、OPC UAテレメトリをKinesis Data Streamsへ橋渡しします。
AWS IoT Core における Kinesis Firehose のレコードセパレーター設定
IoT Core のトピックルールにある Firehose アクションでレコードセパレーターを設定し、S3 に改行区切りでレコードを格納する方法。
AWS IoT トピックペイロードによる Kinesis シャードの分離
IoT トピックのペイロードを顧客 ID ごとに異なる Kinesis シャードへルーティングし、後続処理でも顧客ごとの順序を保つ方法。
Cognito User PoolsとOIDCでSlackサインインを実装する
Cognito user poolをOIDC経由でSlackと連携させ、"Sign in with Slack"をAmplifyでNext.jsアプリに組み込みます。
Lambda Web AdapterでFastAPIをAWS Lambdaにデプロイする
Lambda Web Adapterを使うと、FastAPIで書いたAPIバックエンドをコンテナのまま単一のLambda関数にデプロイできます。
