Greengrass V2 を Raspberry Pi の Docker コンテナ上で 動かしてみた
Greengrass V2 は、Docker を使わなくても arm デバイスでも動かすことができますが、Docker コンテナ上で動かす場合、パブリックに公開されているコンテナイメージは実は amd64 向けのものしか提供されていません。
(以前の Greengrass であれば、arm, amd 両方のイメージが提供されていました。)
そこで、今回は arm64 向けにイメージをビルドして、Raspberry Pi の Docker コンテナ上で Greengrass V2 を動かしてみました。
検証環境
今回利用したデバイス及び環境は以下になります。
- Raspberry Pi 4(4GB)
- OS 環境は以下
$ lsb_release -a No LSB modules are available. Distributor ID: Debian Description: Debian GNU/Linux 11 (bullseye) Release: 11 Codename: bullseye
$ uname -m aarch64
それでは、これより構築作業を紹介していきます。
Docker のインストール
まず最初に、下記のコマンドを実行して Raspberry Pi に Docker をインストールします。
$ curl -fsSL https://get.docker.com -o get-docker.sh $ sudo sh get-docker.sh
下記バージョンのDocker がインストールできました。
$ docker -v Docker version 20.10.21, build baeda1f
ついでに、作業に使っているユーザーでも docker
コマンドが利用できるようにしておきます。
$ sudo usermod -aG docker pi
このまま作業すると docker コマンドが使えないことがあるので、Raspberry Pi にログオンし直しておきます。
AWS 認証情報の設定
Greengrass Core をインストールするにあたり、AWS の認証情報が必要となるので、そのための IAM を設定します。認証情報の準備としては、以下の2通りがありますが、インストール時のみに必要な情報であり、一時的な認証情報の方がセキュアなので後者を使うことにします。
- IAM User による永続的(長期的)な認証情報。
- IAM Role による一時的な認証情報
まず最初に IAM Policy を下記の内容で作成します。ポリシー名は greengrass-v2-provision-minimal-iam-policy
としました。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "iot:AddThingToThingGroup", "iot:AttachPolicy", "iot:AttachThingPrincipal", "iot:CreateKeysAndCertificate", "iot:CreatePolicy", "iot:CreateRoleAlias", "iot:CreateThing", "iot:CreateThingGroup", "iot:DescribeEndpoint", "iot:DescribeRoleAlias", "iot:DescribeThingGroup", "iot:GetPolicy", "iam:GetRole", "iam:CreateRole", "iam:PassRole", "iam:CreatePolicy", "iam:AttachRolePolicy", "iam:GetPolicy", "sts:GetCallerIdentity" ], "Resource": "*" }, { "Sid": "DeployDevTools", "Effect": "Allow", "Action": [ "greengrass:CreateDeployment", "iot:CancelJob", "iot:CreateJob", "iot:DeleteThingShadow", "iot:DescribeJob", "iot:DescribeThing", "iot:DescribeThingGroup", "iot:GetThingShadow", "iot:UpdateJob", "iot:UpdateThingShadow" ], "Resource": "*" } ] }
次に IAM Role を作成します。
適当な名前で IAM Role を作成し、上記ポリシーをアタッチします。今回は下記の通りとしました。
- 作成した IAM Role:
greengrass-v2-provision-role
- アタッチするIAM Policy:
greengrass-v2-provision-minimal-iam-policy
- 先程作成したポリシーです。
信頼関係は以下のとおりです。Principal
は利用環境に応じて変更して下さい。下記はスイッチロールで該当の AWS 環境を使っている想定になります。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::[AWS ACCOUNT ID]:role/[Switched IAM Role Name]" }, "Action": "sts:AssumeRole" } ] }
これで一時的な認証情報を取得する準備が整いました。一時的な認証情報は後で作成する credentials
ファイルに記載してコンテナ起動時に渡すので、このファイルを先に作成しておきます。
作業ディレクトリに greengrass-v2-credentials
というディレクトリを作成します。このディレクトリ以下に credentials
ファイルを作成します。
$ mkdir ./greengrass-v2-credentials $ touch ./greengrass-v2-credentials/credentials
次に一時的な認証情報を作成します。作成するための環境に特に指定はありませんが、 AWS CloudShell を使うのが便利です。
CloudShell を利用する際は、先程作成した IAM Role の信頼関係に記載されている Principal
の資格で作業することに注意して下さい。
実際に実行するコマンドは下記になります。本記事の通りに AWS リソースを作成している場合は AWS ACCOUNT ID
の箇所だけ修正して、そのまま貼り付けて実行します。
OUTPUT=`aws sts assume-role \ --role-arn arn:aws:iam::[AWS ACCOUNT ID]:role/greengrass-v2-provision-role \ --role-session-name "RoleSession01" ` echo "############ Copy and Paste the following credential to Greengrass Core Divice! ############" ; \ echo "[default]" ; \ echo "aws_access_key_id =`echo $OUTPUT | jq -r .Credentials.AccessKeyId`" ; \ echo "aws_secret_access_key = `echo $OUTPUT | jq -r .Credentials.SecretAccessKey`" ; \ echo "aws_session_token =`echo $OUTPUT | jq -r .Credentials.SessionToken`" ; \ echo ""
以下は、CloudShell を利用している様子です。
実行すると下記のような結果が表示されるので、[default]
以下の 4 行をコピーして、デバイス上の credentials
ファイルに貼り付けます。これで credentials
ファイルが完成しました。
なお、有効期限は作成から 1時間なので、1時間以内に Greengrass Core のインストールを実施して下さい。1時間経過したら再度同じコマンドを実行して認証情報を作り直します。
(有効期限を伸ばすこともできますが、むやみに伸ばすのはリスクになるので、とりあえずデフォルトで良いかと思います。)
############ Copy and Paste the following credential to Greengrass Core Divice! ############ [default] aws_access_key_id =ASXXXXXXXXXXXX aws_secret_access_key = XXXXXXXXXXXXXXXXXXXX aws_session_token = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
環境変数ファイルの作成
次に、コンテナ内の Greengrass Core インストーラに渡す情報を記載した「環境ファイル」を作成します。.env
というファイル名で作業ディレクトリ上に作成します。
ファイルの内容は次のとおりです。
GGC_ROOT_PATH=/greengrass/v2 AWS_REGION=ap-northeast-1 PROVISION=true THING_NAME=OnDockerTestRasPi01 THING_GROUP_NAME=On-Docker-RaspiOS-Group TES_ROLE_NAME=OnDockerTestGreengrassV2TokenExchangeRole TES_ROLE_ALIAS_NAME=OnDockerTestGreengrassCoreTokenExchangeRoleAlias COMPONENT_DEFAULT_USER=ggc_user:ggc_group DEPLOY_DEV_TOOLS=true
必要に応じて以下の項目を修正して下さい。
設定項目 | 設定内容 |
---|---|
GGC_ROOT_PATH |
Greengrass Core をインストールするディレクトリ。 |
AWS_REGION |
Greengrass を利用する AWS リージョン |
THING_NAME |
Greengrass Core が稼働する IoT thing の名前 |
THING_GROUP_NAME |
Greengrass Core が稼働する IoT thing の thing グループ |
TES_ROLE_NAME |
Greengrass デバイスが AWS リソースを利用する際に利用する一時的な認証情報のための IAM Role。 存在しなければ指定した名前で作成される |
TES_ROLE_ALIAS_NAME |
上記トークンの交換ロールエイリアス。 存在しなければ指定した名前で作成される |
DEPLOY_DEV_TOOLS |
Greengrass CLI コンポーネントのデプロイ有無 |
arm 対応の Greengrass コンテナイメージのビルド
次に、arm アーキテクチャ向けのイメージをビルドします。
以下の GitHub リリースページより Greengrass コンテナのパッケージファイルを Raspberry Pi 上にダウンロードして解凍します。
$ wget https://github.com/aws-greengrass/aws-greengrass-docker/archive/refs/tags/v2.5.3.tar.gz -O aws-greengrass-docker-2.5.3.tar.gz $ tar xzf aws-greengrass-docker-2.5.3.tar.gz
解凍すると次のようなファイル群が展開されます。
$ tree . ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── docker-compose.yml ├── Dockerfile ├── greengrass-entrypoint.sh ├── greengrass.zip.sha256 ├── LICENSE ├── modify-sudoers.sh └── README.md
Dockerfile
の中身は次のようになっています。利用する Greengrass Core のバージョンを指定したい場合は、7行目を編集して下さい。
今回は、本記事の執筆時点で最新の 2.5.3
を指定しています。
ちなみに、Greengrass V2 の イメージは arm に対応していませんが、AmazonLinux 2 のコンテナイメージは、arm64 に対応しています。
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 FROM amazonlinux:2 # Replace the args to lock to a specific version ARG GREENGRASS_RELEASE_VERSION=2.5.3 ARG GREENGRASS_ZIP_FILE=greengrass-${GREENGRASS_RELEASE_VERSION}.zip ARG GREENGRASS_RELEASE_URI=https://d2s8p88vqu9w66.cloudfront.net/releases/${GREENGRASS_ZIP_FILE} ARG GREENGRASS_ZIP_SHA256=greengrass.zip.sha256 # Author LABEL maintainer="AWS IoT Greengrass" # Greengrass Version LABEL greengrass-version=${GREENGRASS_RELEASE_VERSION} # Set up Greengrass v2 execution parameters # TINI_KILL_PROCESS_GROUP allows forwarding SIGTERM to all PIDs in the PID group so Greengrass can exit gracefully ENV TINI_KILL_PROCESS_GROUP=1 \ GGC_ROOT_PATH=/greengrass/v2 \ PROVISION=false \ AWS_REGION=us-east-1 \ THING_NAME=default_thing_name \ THING_GROUP_NAME=default_thing_group_name \ TES_ROLE_NAME=default_tes_role_name \ TES_ROLE_ALIAS_NAME=default_tes_role_alias_name \ COMPONENT_DEFAULT_USER=default_component_user \ DEPLOY_DEV_TOOLS=false \ INIT_CONFIG=default_init_config \ TRUSTED_PLUGIN=default_trusted_plugin_path \ THING_POLICY_NAME=default_thing_policy_name RUN env # Entrypoint script to install and run Greengrass COPY "greengrass-entrypoint.sh" / COPY "${GREENGRASS_ZIP_SHA256}" / # Install Greengrass v2 dependencies RUN yum update -y && yum install -y python37 tar unzip wget sudo procps which && \ amazon-linux-extras enable python3.8 && yum install -y python3.8 java-11-amazon-corretto-headless && \ wget $GREENGRASS_RELEASE_URI && sha256sum -c ${GREENGRASS_ZIP_SHA256} && \ rm -rf /var/cache/yum && \ chmod +x /greengrass-entrypoint.sh && \ mkdir -p /opt/greengrassv2 $GGC_ROOT_PATH && unzip $GREENGRASS_ZIP_FILE -d /opt/greengrassv2 && rm $GREENGRASS_ZIP_FILE && rm $GREENGRASS_ZIP_SHA256 # modify /etc/sudoers COPY "modify-sudoers.sh" / RUN chmod +x /modify-sudoers.sh RUN ./modify-sudoers.sh ENTRYPOINT ["/greengrass-entrypoint.sh"]
Dockerfile の内容を確認できたらイメージをビルドします。名前は適当なものでいいかと思います。今回は arm64/aws-iot-greengrass
としています。
タグは利用環境に応じて検討の余地があると思いますが、ひとまず Greengrass Core のバージョンを付けています。
docker build -t "arm64/aws-iot-greengrass:2.5.3" ./
ビルドには少し時間がかかります。正常にビルドできたら確認してみましょう。
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE arm64/aws-iot-greengrass 2.5.3 cba833b00343 About a minute ago 784MB
ついでにアーキテクチャも確認してみます。arm64
としてビルドされていますね。(当然といえば当然ですが...)
$ docker inspect arm64/aws-iot-greengrass:2.5.3 |jq '{ Architecture: .[].Architecture }' { "Architecture": "arm64" }
コンテナの起動
ここまでの作業で全ての準備ができたので、コンテナを起動したいと思います。
credentials
ファイルと .env
ファイルのパスは作業環境に応じたパスを指定して下さい。
$ docker run --rm --init -it --name aws-iot-greengrass \ -v ~/greengrass-v2-credentials:/root/.aws/:ro \ --env-file .env \ -p 8883 \ arm64/aws-iot-greengrass:2.5.3
次のようなログが最後に出れば起動成功です。
Installing Greengrass for the first time... (中略) + java -Dlog.store=FILE -Dlog.level= -Dlog.store=FILE -Droot=/greengrass/v2 -jar /greengrass/v2/alts/current/distro/lib/Greengrass.jar --setup-system-service false Launching Nucleus... Launched Nucleus successfully.
起動に成功していれば、コンソール上でもコアデバイスとして登録されていることが確認できます。
今回は同時に Greengrass CLI もデプロイしているので、そのデプロイメントもコンソールで確認できます。
カスタムコンポーネントをデプロイしてみる
Greengrass Core が正常に起動していれば、コンポーネントのデプロイ方法は特に変わる点はありません。
試しに下記で紹介したサンプルのコンポーネントをデプロイしてみます。
このコンポーネントは既に Greengrass に登録済みのもので、S3 バケットにアーティファクトが格納されています。これらを S3 からダウンロードしてデプロイするので、Greengrass Core デバイス(Raspberry Pi)に対して、S3 バケットへのアクセス権限を追加しておきます。
今回は簡単に確認するため、権限のゆるい AmazonS3ReadOnlyAccess
をアタッチしています。
それではコンソールからコンポーネントをデプロイしてみましょう。
メニューから「コンポーネント」画面を開いて、デプロイするコンポーネントをクリックします。
該当のコンポーネントのページで「Deploy」をクリックします。
デプロイ対象を選択します。既に Greengrass CLI をデプロイしたものがあるので、今回は同じものを選択します。
ターゲットの設定は「Next」をクリックして次に進みます。
コンポーネントの選択画面でも、今回は変更する必要がないのでそのまま「Next」をクリックします。
次の画面もそのまま「Next」で先に進みます。
次の画面は、デプロイ時のキャンセル設定などを指定する画面ですが、今回は特に変更せず先に進みます。
最後にレビュー画面が出るので、内容に問題なければ「Deploy」をクリックしてデプロイを開始します。
正常に Raspberry Pi にデプロイできたら、コンテナに接続して動作を確認してみます。
docker exec -it aws-iot-greengrass /bin/bash
デプロイしたコンポーネントのログに動作結果が出力されていることが確認できました。
bash-4.2# tail -F /greengrass/v2/logs/com.example.MyTest.log 2022-11-30T15:19:01.247Z [INFO] (Copier) com.example.MyTest: stdout. Hello, GDK! Current time: 2022-11-30 15:19:01.246271.. {scriptName=services.com.example.MyTest.lifecycle.Run, serviceName=com.example.MyTest, currentState=RUNNING} 2022-11-30T15:19:06.251Z [INFO] (Copier) com.example.MyTest: stdout. Hello, GDK! Current time: 2022-11-30 15:19:06.250896.. {scriptName=services.com.example.MyTest.lifecycle.Run, serviceName=com.example.MyTest, currentState=RUNNING} 2022-11-30T15:19:11.261Z [INFO] (Copier) com.example.MyTest: stdout. Hello, GDK! Current time: 2022-11-30 15:19:11.259950.. {scriptName=services.com.example.MyTest.lifecycle.Run, serviceName=com.example.MyTest, currentState=RUNNING}
このコンポーネントは コンテナ上の /tmp/Greengrass_GDKTest.log
にも5秒おきにメッセージを出力するものですが、こちらも正常な動作を確認できました。
bash-4.2# tail -F /tmp/Greengrass_GDKTest.log Hello, GDK! Current time: 2022-11-30 15:19:06.250896. Hello, GDK! Current time: 2022-11-30 15:19:11.259950. Hello, GDK! Current time: 2022-11-30 15:19:16.265841.
以上により、Docker コンテナによる Greengrass V2 の一連の動作を確認することができました。長かった…
全体の感想
全体を通して特にハマるような点はなかったですが、実際のデバイス運用を考えるといくつか課題がありそうに思いました。
例えば、デプロイ済みのコンポーネントに対して「機能を追加したい」といったような場合、コンポーネントのバージョンを更新することになりますが、途中でコンテナが停止するようなことがあると、次の起動時に初期化された状態で起動することになるので、その辺りも考慮した設計・運用を考える必要があります。
これらについては、改めて検証していきたいと思います。
最後に
今回は、とりあえずコンテナで Greengrass V2 を動かすことが確認できました。
準備工数がそれなりにかかったり、異常時に備えたローカルへのデータ保存(マウント)なども考慮する必要があり、利用するにはややハードルが高いと感じました。
今回は以上になります。