Docker で Greengrass コンポーネント開発

Docker で Greengrass コンポーネント開発

Takahiro Iwasa
Takahiro Iwasa
11 min read
Greengrass Greengrass Development Kit IoT

この投稿では、 AWS IoT Greengrass の Docker イメージを使用して、ローカル環境で Greengrass コンポーネントを開発する方法について説明します。 詳細については、公式ドキュメントをご参照ください。

概要

この投稿では、毎秒 AWS IoT Core に対して MQTT 経由でメッセージを送信する 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 カスタムコンポーネント開発

AWS IoT Core を介して MQTT でメッセージをパブリッシュする Greengrass コンポーネントを開発します。

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

Greengrass Development Kit (GDK) をインストールしてください。

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

開発

gdk component init を実行して開発を開始してください。

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

このコマンドは以下のファイルを生成します。 この投稿では src および tests ディレクトリは使用しません。

./
|-- 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 を使用してコンポーネントをパブリッシュしない場合は、 publish.bucket に S3 バケットを設定する必要はありません。

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

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

Python スクリプト

以下のコードで main.py を作成してください。 このスクリプトは毎秒 MQTT メッセージをトピック /mqtt-publisher にパブリッシュします。

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 で指定されているコンポーネントのインストール時にインストールされます。

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"

コンポーネント依存関係

ComponentDependenciesaws.greengrass.TokenExchangeService を指定してください。 これは、 Python スクリプトが boto3 を使用しているため、インストールが必要です。

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.

ライフサイクル

requirements.txt に記載された Python ライブラリをインストールするために、ライフサイクルフックを使用できます。

プレースホルダー

次のプレースホルダは、 gdk component buildgdk-config.json で設定された値に置き換えられます。

  • {COMPONENT_NAME}
  • {COMPONENT_VERSION}
  • {COMPONENT_AUTHOR}
  • Artifacts.URI
    • BUCKET_NAME
    • COMPONENT_NAME
    • COMPONENT_VERSION

コンポーネントビルド

gdk component build を使用して、コンポーネントをビルドしてください。

$ cd components/mqtt_publisher
$ gdk component build

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

Greengrass Core in Docker

<PROJECT_ROOT>/docker ディレクトリで作業してください。

セキュリティクレデンシャル

Docker コンテナ内で Greengrass Core を自動プロビジョニングモードでセットアップする際、 Greengrass Core インストーラーは必要な AWS リソースをプロビジョニングするために AWS セキュリティクレデンシャルを使用する必要があります。 AWS の永続クレデンシャルを使用できますが、 sts get-session-token を使用して一時クレデンシャルの利用を強くお勧めします。

次の AWS リソースが自動で作成されます。

  • AWS IoT
    • Greengrass Core Device
    • IoT Thing
    • IoT Thing Group
    • Certificate
    • Policies (x2)
    • Token Exchange Role Alias
  • AWS IAM
    • Token Exchange Role
    • Token Exchange Role Policy

次のコマンドで一時クレデンシャルを取得してください。

$ aws sts get-session-token

Greengrass Core インストーラーが使用するクレデンシャルファイルを作成してください。

$ mkdir ./greengrass-v2-credentials
$ nano ./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 を作成してください。 詳細については、公式ドキュメントをご参照ください。

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-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
...
aws-iot-greengrass | Launching Nucleus...
aws-iot-greengrass | Launched Nucleus successfully.

AWS 提供コンポーネントデプロイ

AWS 提供コンポーネントを Docker コンテナにデプロイしてください。

Greengrass CLI

コンポーネントをローカルにデプロイするには、 Greengrass CLI コンポーネント (aws.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.

インストール後、 /greengrass/v2/bin に配置されます。

$ docker-compose exec greengrass bash

$ cd /greengrass/v2
$ ls bin
greengrass-cli  greengrass-cli.cmd

Token Exchange Service

Greengrass カスタムコンポーネントは、 Token Exchange Service コンポーネント (aws.greengrass.TokenExchangeService) を使用して AWS と通信します。 これは、 AWS IoT 認証プロバイダーに接続するローカルサーバーとして ECS コンテナインスタンスを実行し、 Token Exchange Role Alias を使用して AWS IoT に接続します。 カスタムコンポーネントが AWS SDK クライアントを作成するとき、クライアントは一時クレデンシャルを取得するためにローカルサーバーを使用します。

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 コンソールからデプロイ

AWS IoT Greengrass コンソールから AWS 提供コンポーネントをデプロイできます。

デプロイ後、 /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 Role

Python スクリプトが AWS IoT Core にメッセージをパブリッシュするため、 GreengrassV2TokenExchangeRole に以下の IAM ポリシーをアタッチしてください。 保存する前に <AWS_ACCOUNT_ID> をご自身の AWS アカウント ID で置き換えてください。

{
    "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 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"
...
Local deployment submitted! Deployment Id: <DEPLOYMENT_ID>

greengrass-cli deployment status を実行してデプロイメントのステータスを確認してください。

$ bin/greengrass-cli deployment status -i <DEPLOYMENT_ID>
...
INFO: Connection established with event stream RPC server
<DEPLOYMENT_ID>: SUCCEEDED

/greengrass/v2/logs/com.example.MqttPublisher.log に次のレコードが記録されているはずです。

$ 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 テストクライアントでテスト

MQTT テストクライアントを使用して、 Topic filter フィールドに /# または /mqtt-publisher を入力し、 Subscribe ボタンをクリックしてください。 カスタムコンポーネントから AWS IoT Core に送信されたメッセージが表示されるはずです。

まとめ

AWS IoT Greengrass の Docker イメージを利用することで、Greengrass コンポーネントを手軽に開発できるようになります。

この投稿が、お役に立てば幸いです。

Takahiro Iwasa

Takahiro Iwasa

Software Developer at KAKEHASHI Inc.
Involved in the requirements definition, design, and development of cloud-native applications using AWS. Now, building a new prescription data collection platform at KAKEHASHI Inc. Japan AWS Top Engineers 2020-2023.