AWS IoT Greengrassをコンテナで動かす
概要
これをやって見ます。
今回の構成
今回は以下のEdge deviceにGreengrass(GG)をDockerコンテナとしてデプロイする。GGのバージョンは2.5.3
┌──────────────────┐
│ GGv2 Components │ ┌──────────────────┐
├──────────────────┤ │ │
│ GGv2 Container │◄──────────────────►│ Greengrass │
├──────────────────┤ │ │
│ Docker │ └──────────────────┘
├──────────────────┤ AWS Cloud
│ Linux VM │
└──────────────────┘
Edge device
この方法は、どうやらGGのコンテナが起動するときに、/root/.aws
をマウントし、そこにあるファイルからパラメータを読み取るようだ。以降、そのためのファイルを作っていく。
クレデンシャルファイルを作る
動かすにはIAMユーザーのクレデンシャルが必要で以下の2が推奨されているが(ちょっと使い方を調べないといけなさそうなので)今回はひとまず1でやってみる。つまり、今使っているIAMユーザーのクレデンシャルを設定する。
- Long-term credentials for an IAM user
- Temporary credentials for an IAM role
以下のようなファイルをgreengrass-v2-credentials/credentials
として置いておく
[default]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
2.の方法だとaws_session_token
も必要とのこと。あとでこのやり方も調べたい
環境ファイルを作る
.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
DEPLOY_DEV_TOOLS=true
GGのイメージをpull
docker pull amazon/aws-iot-greengrass:latest
コンテナ起動
-itd
としてデーモンとして起動しています。
docker run --init -itd --name aws-iot-greengrass \
-v /home/ssm-user/gg-core/gg-config/greengrass-v2-credentials:/root/.aws/:ro \
--env-file .env \
-p 8883 \
amazon/aws-iot-greengrass:latest
出力
Installing Greengrass for the first time...
Running Greengrass with the following options: -Droot=/greengrass/v2 -Dlog.store=FILE -Dlog.level= -jar /opt/greengrassv2/lib/Greengrass.jar --provision true --deploy-dev-tools true --aws-region ap-northeast-1 --start false --thing-name MyGreengrassCore --thing-group-name MyGreengrassCoreGroup --tes-role-name GreengrassV2TokenExchangeRole --tes-role-alias-name GreengrassCoreTokenExchangeRoleAlias --component-default-user ggc_user:ggc_group
Provisioning AWS IoT resources for the device with IoT Thing Name: [MyGreengrassCore]...
Found IoT policy "GreengrassV2IoTThingPolicy", reusing it
Creating keys and certificate...
Attaching policy to certificate...
Creating IoT Thing "MyGreengrassCore"...
Attaching certificate to IoT thing...
Successfully provisioned AWS IoT resources for the device with IoT Thing Name: [MyGreengrassCore]!
Adding IoT Thing [MyGreengrassCore] into Thing Group: [MyGreengrassCoreGroup]...
Successfully added Thing into Thing Group: [MyGreengrassCoreGroup]
Setting up resources for aws.greengrass.TokenExchangeService ...
TES role alias "GreengrassCoreTokenExchangeRoleAlias" does not exist, creating new alias...
IoT role policy "GreengrassTESCertificatePolicyGreengrassCoreTokenExchangeRoleAlias" for TES Role alias not exist, creating policy...
Attaching TES role policy to IoT thing...
No managed IAM policy found, looking for user defined policy...
IAM policy named "GreengrassV2TokenExchangeRoleAccess" already exists. Please attach it to the IAM role if not already
Configuring Nucleus with provisioned resource details...
Downloading Root CA from "https://www.amazontrust.com/repository/AmazonRootCA1.pem"
Created device configuration
Successfully configured Nucleus with provisioned resource details!
Creating a deployment for Greengrass first party components to the thing group
Configured Nucleus to deploy aws.greengrass.Cli component
Creating user ggc_user
ggc_user created
Creating group ggc_group
ggc_group created
Added ggc_user to ggc_group
Nucleus start set to false, exiting...
Making loader script executable...
Starting Greengrass...
+ set +m
++ dirname /greengrass/v2/alts/current/distro/bin/loader
+ PWD=/greengrass/v2/alts/current/distro/bin
+ sigterm_received=0
++ cd /greengrass/v2/alts/current/distro/bin/../../../..
++ pwd
+ GG_ROOT=/greengrass/v2
+ echo 'Greengrass root: /greengrass/v2'
Greengrass root: /greengrass/v2
+ LAUNCH_DIR=/greengrass/v2/alts/current
+ CONFIG_FILE=
+ is_directory_link /greengrass/v2/alts/new
+ '[' -L /greengrass/v2/alts/new ']'
+ is_directory_link /greengrass/v2/alts/broken
+ '[' -L /greengrass/v2/alts/broken ']'
+ is_directory_link /greengrass/v2/alts/old
+ '[' -L /greengrass/v2/alts/old ']'
+ j=1
+ '[' 1 -le 3 ']'
+ '[' 0 -eq 0 ']'
+ launch_kernel
+ is_directory_link /greengrass/v2/alts/current
+ '[' -L /greengrass/v2/alts/current ']'
+ '[' -d /greengrass/v2/alts/current ']'
+ '[' -f /greengrass/v2/alts/current/launch.params ']'
++ cat /greengrass/v2/alts/current/launch.params
+ JVM_OPTIONS='-Dlog.level= -Dlog.store=FILE'
+ JVM_OPTIONS='-Dlog.level= -Dlog.store=FILE -Droot=/greengrass/v2'
+ OPTIONS='--setup-system-service false'
+ '[' '!' -z ']'
+ echo 'JVM options: -Dlog.level=' -Dlog.store=FILE -Droot=/greengrass/v2
JVM options: -Dlog.level= -Dlog.store=FILE -Droot=/greengrass/v2
+ echo 'Nucleus options: --setup-system-service' false
Nucleus options: --setup-system-service false
+ child_pid=
+ trap 'echo Received SIGTERM; sigterm_received=1; kill -TERM ${child_pid}; wait ${child_pid}; echo Killed child PID' TERM
+ child_pid=104
+ wait 104
+ 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.
お、動いたっぽいですね。コンテナに接続します。
$ docker exec -it aws-iot-greengrass /bin/bash
起動のログはコンテナの中のここです。
bash-4.2# more /greengrass/v2/logs/greengrass.log
bash-4.2# /greengrass/v2/bin/greengrass-cli component list
Feb 08, 2022 12:23:47 PM software.amazon.awssdk.eventstreamrpc.EventStreamRPCConnection$1 onConnectionSetup
INFO: Socket connection /greengrass/v2/ipc.socket:8033 to server result [AWS_ERROR_SUCCESS]
Feb 08, 2022 12:23:47 PM software.amazon.awssdk.eventstreamrpc.EventStreamRPCConnection$1 onProtocolMessage
INFO: Connection established with event stream RPC server
Components currently running in Greengrass:
Component Name: DeploymentService
Version: 0.0.0
State: RUNNING
Configuration: null
Component Name: UpdateSystemPolicyService
Version: 0.0.0
State: RUNNING
Configuration: null
Component Name: TelemetryAgent
Version: 0.0.0
State: RUNNING
Configuration: null
Component Name: aws.greengrass.Nucleus
Version: 2.5.3
State: FINISHED
Configuration: {"awsRegion":"ap-northeast-1","componentStoreMaxSizeBytes":"10000000000","deploymentPollingFrequencySeconds":"15","envStage":"prod","fleetStatus":{"periodicStatusPublishIntervalSeconds":86400.0},"greengrassDataPlanePort":"8443","httpClient":{},"iotCredEndpoint":"c1jaik620mmr8w.credentials.iot.ap-northeast-1.amazonaws.com","iotDataEndpoint":"a3su8icnk38ik7-ats.iot.ap-northeast-1.amazonaws.com","iotRoleAlias":"GreengrassCoreTokenExchangeRoleAlias","jvmOptions":"-Dlog.level= -Dlog.store=FILE","logging":{},"mqtt":{"spooler":{}},"networkProxy":{"proxy":{}},"platformOverride":{},"runWithDefault":{"posixUser":"ggc_user:ggc_group"},"telemetry":{}}
Component Name: FleetStatusService
Version: null
State: RUNNING
Configuration: null
Component Name: aws.greengrass.Cli
Version: 2.5.3
State: RUNNING
Configuration: {"AuthorizedPosixGroups":null,"AuthorizedWindowsGroups":null}
awsコマンドが入っていればこれも使えます
bash-4.2# aws greengrassv2 list-components
{
"components": []
}
おそらくこのコマンドではWebコンソールからデプロイしたコンポーネントのみが表示される(なので今は何も表示されない)
Core deviceの状態確認。Core device落ちてるはずなのになぜかHealthy
とでる。ようわからん。
aws greengrassv2 get-core-device \
--core-device-thing-name MyGreengrassCore
{
"coreDeviceThingName": "MyGreengrassCore",
"coreVersion": "2.5.3",
"platform": "linux",
"architecture": "amd64",
"status": "HEALTHY",
"lastStatusUpdateTimestamp": "2022-01-30T20:38:27.706000+09:00",
"tags": {}
}
Greengrassコンポーネントを作って実行する
CLIの場合
CLIは実は以下の蓋通りの方法がある。
- Create a component (GDK CLI)
- Create a component (shell commands)
2の方はここに自動でコンポーネントを作るシェルスクリプトを置いてく
このスクリプトでやっている内容は以下のとおり
コンポーネントはartifacts
と呼ばれるコード(ここではPythonコード)とrecipes
というメタデータでできている。以下のようにファイルを配置する。
/root
`-- greengrassv2
|-- artifacts
| `-- com.example.HelloWorld
| `-- 1.0.0
| `-- hello_world.py
`-- recipes
`-- com.example.HelloWorld-1.0.0.json
{
"RecipeFormatVersion": "2020-01-25",
"ComponentName": "com.example.HelloWorld",
"ComponentVersion": "1.0.0",
"ComponentDescription": "My first AWS IoT Greengrass component.",
"ComponentPublisher": "Amazon",
"ComponentConfiguration": {
"DefaultConfiguration": {
"Message": "world"
}
},
"Manifests": [
{
"Platform": {
"os": "linux"
},
"Lifecycle": {
"Run": "python3 -u {artifacts:path}/hello_world.py \"{configuration:/Message}\""
}
},
{
"Platform": {
"os": "windows"
},
"Lifecycle": {
"Run": "py -3 -u {artifacts:path}/hello_world.py \"{configuration:/Message}\""
}
}
]
}
import sys
message = "Hello, %s!" % sys.argv[1]
# Print the message to stdout, which Greengrass saves in a log file.
print(message)
コンポーネントをデプロイする
sudo /greengrass/v2/bin/greengrass-cli deployment create \
--recipeDir ~/greengrassv2/recipes \
--artifactDir ~/greengrassv2/artifacts \
--merge "com.example.HelloWorld=1.0.0"
/greengrass/v2/logs/com.example.HelloWorld.log
にメッセージが見えるはず。
全てローカル(コンテナ内)での作業なのでGGコアノードの台数が増えたら繰り返しがつらい。テストでしか使えないと思う。
Webコンソールの場合
AWS IoT
> Greengrass
> Components
> Create
componentで直接レシピを書く方法。やってることはほぼCLIの場合と同じ。artifacts
をS3においておくので複数のGreengrassコアノードにデプロイしやすい。
Lambda Functionをデプロイ
多分これが一番楽な方法。
Lambdaがサポートする言語をGGが全てサポートしている訳ではないので注意が必要。GGではPythonは3.8まで。
Lambda
Lambda側でコンポーネントにするLambda Functionを作る。明示的にバージョンをつけておく。$LATEST
は使えないので、Lambda > Functions > Function_NameにてActions > Publish new versionするなど。
Greengrass
- AWS IoT > Greengrass > Componentsにて Create component > Import Lambda functionから、作っておいたLambda Functionを選んでデプロイ
- Create componentのオプションのLinux process configuration - optionalのところでIsolation modeをNo containerにしないとデプロイに失敗する。今回はGG自体がコンテナだからその上にコンテナはデプロイできないのかもしれない。
ちなみにIsolation modeでGreengrass containerを選んでしまった場合のエラーはこんなん。
2022-02-12T11:18:41.701Z [ERROR] (pool-2-thread-63) just-print-message: FATAL: Failed to reset thread's mount namespace due to an unexpected error. To maintain consistency, GGC will crash and need to be manually restarted. {"errorString": "operation not permitted"}. {serviceInstance=0, serviceName=just-print-message, currentState=STARTING}
- Lambda function configuration - optionalのEvent sourcesを
-
Topic:
test/test
-
Type:
AWS IoT Core MQTT
にしておくと、IoT CoreのTestツールからのMQTTメッセージ送信をトリガーにFunctionを実行することができるのでおすすめ。
とりあえず動かすところまでやりました。
Discussion