はじめに
AWS Robomakerを用いてROSプロジェクトのデプロイ管理は非常に便利ですが、その仕組みについて完全に理解していないといくつかハマりポイントが存在します。わたしのプロジェクトではCDKを用いて各デバイスごとにCFnのスタックを作成し、それらが依存するスタックでRobomakerのリソースを作成してデプロイまで行っています。今回その中でドキュメントを読んでもなかなか理解できずにハマってしまったポイントをまとめました。
GreengrassとRobomaker関連
Greengrass Groupに設定するRoleについて
Greengrass Groupに設定するRoleは、Lambda関数とコネクタがAWSサービスとやり取りするために必要な権限であり、今回Robomakerで必要な権限は、これとなります。
【2020/08/11】
ドキュメントには記載がありませんので、必要な方はAWSサポートにもご確認ください。
Greengrassのログ設定について
Greengrassに設定するLoggerは、GreengrassSystem
とLambda
の2種類あり、これらはTYPEであるとおり、FileSystem
かAWSCloudWatch
を指定します。
AWSのガイドなどでマネージメントコンソールからリソースを作成した場合、ログが自動的にFileSystemにはかれていたため、設定しないとログがでないと気が付きにくいですが、設定しないと下記のようなWARNが発生します。
[2020-06-11T17:47:37.219+09:00][DEBUG]-will keep the log files for the following lambdas {"readingPath": "/home/ggc_user/greengrass/ggc/var/log/user", "lambdas": "map[]"}
[2020-06-11T17:47:37.219+09:00][WARN]-failed to list the user log directory {"path": "/home/ggc_user/greengrass/ggc/var/log/user"}
[2020-06-11T18:04:55.47+09:00][DEBUG]-will keep the log files for the following lambdas {"readingPath": "/home/ggc_user/greengrass/ggc/var/log/user/ap-northeast-1/*************", "lambdas": "map[aws-robomaker-deployment-function-x86_DO_NOT_DELETE:true]"}
[2020-06-11T18:04:55.47+09:00][WARN]-failed to list the user log directory {"path": "/home/ggc_user/greengrass/ggc/var/log/user/ap-northeast-1/*************"}
また、これらのログ出力先をCloudWatch
にする場合は、前述のGreengrassのRoleに対して権限を与える必要があります。今回はCDKで実装しているため、最終的な実装は下記のようになりました。
this.robo_role = new iam.Role(scope, 'RoboRole', {
roleName: 'RoboRole',
assumedBy: new iam.CompositePrincipal(
new iam.ServicePrincipal("lambda.amazonaws.com"),
new iam.ServicePrincipal("greengrass.amazonaws.com")
)
});
const s3_bucket_policy = new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
"s3:List*",
"s3:Get*"
],
resources: [
"arn:aws:s3:::mybucket/ros_bundle/*"
]
});
const robomaker_deploy_policy = new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
"robomaker:UpdateRobotDeployment"
],
resources: [
"*"
]
});
const cloudwatch_log_policy = new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams"
],
resources: [
this.getServiceResourceArn("logs", "log-group:", "aws/greengrass/GreengrassSystem/*"),
this.getServiceResourceArn("logs", "log-group:", "aws/greengrass/Lambda/*")
]
});
const robo_policy = new iam.Policy(scope, "RoboPolicy");
robo_policy.addStatements(s3_bucket_policy);
robo_policy.addStatements(robomaker_deploy_policy);
robo_policy.addStatements(cloudwatch_log_policy);
this.robo_role.attachInlinePolicy(robo_policy);
※現地点でのAWSLambdaBasicExecutionRole
には、DescribeLogStreams
がなくエラーが出てしまったためポリシーステートメントを作成しています。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
}
]
}
Robomakerでデプロイ時のLambdaの実行権限をstackからは変更できない
Greengrass FunctionDefinitionVersionのDefaultConfig
であるRunAsにデフォルトであるggc_user
やggc_group
の代わりに実行させるための設定、今回はroot権限(UID/GID=0)を設定しましたが、Robomakerからデプロイを行うとこの設定は無効になります。
【2020/08/11】
ドキュメントには記載がありませんので、必要な方はAWSサポートにもご確認ください。
マネージメントコンソールから設定した場合、CFnのstackでRobomakerのデプロイを変えた場合やGreengrassの設定を変更すると再設定が必要となり、100+のような台数を管理する場合には非常に面倒です。そのため、今回は/etc/passwd
のggc_user
に対して下記の設定をしています。
ggc_user:x:0:0::/home/ggc_user:/bin/false
RobomakerのROSデプロイ前に実行するファイルのインストールモジュールについて
こちらの9項に下記の設定をするとありますが、
install(FILES deploymentScripts/post_launch_script.sh
DESTINATION ${CATKIN_GLOBAL_SHARE_DESTINATION}
)
この設定をすべきROSのモジュールは、launchファイルが存在するモジュールと同じでなければいけません。ドキュメントを読んだ限り同一モジュールという指定がなかったためlaunchモジュールと別であっても、ROSのPATHが通っているため検索を行うかと勘違いしていましたが、launchで指定したモジュール内のみを使用するため注意が必要です。
またここでCMakeのinstall時のパーミッションですが、こちらを確認すると、設定しない場合644
となりますが、Robomakerが実行前にchmod +x
を実行するため、特に設定する必要はないです。しかしシェルの内部でまた別のシェルを呼び出して実行する場合、そのシェルに対して権限を設定する必要があります。
RobomakerのPrelaunch fileの実行時のPATHについて
開発時にUbuntuなどを用いている場合のデフォルトPATHは下記のようになります。
$ cat /etc/environment
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games"
しかしRobomakerのLambdaから実行されるシェルにおいてPATHは、下記のようになります。
[2020-06-17T10:56:56.616+09:00][INFO]-PATH=/home/ggc_user/roboMakerDeploymentPackage/******************/opt/ros/melodic/bin:/opt/ros/melodic/bin:/home/ggc_user/roboMakerDeploymentPackage/******************/usr/sbin:/home/ggc_user/roboMakerDeploymentPackage/******************/usr/bin:/home/ggc_user/roboMakerDeploymentPackage/******************/usr/local/bin:/home/ggc_user/roboMakerDeploymentPackage/******************/sbin:/home/ggc_user/roboMakerDeploymentPackage/******************/bin:/usr/bin:/usr/local/bin:/bin:/home/ggc_user/greengrass/ggc/deployment/lambda/arn.aws.lambda.ap-northeast-1.***********.function.aws-robomaker-deployment-function-x86_DO_NOT_DELETE.3
通常の実行では問題になりませんが、例えばこのシェルでROSで使用するカーネルモジュールのロードを行いたいときにinsmod
やrmmod
を使用しますが、これらのコマンドは、/sbin
にあり
$ which insmod
/sbin/insmod
デフォルトで設定されているrobomakerのlambdaのPATHには、設定されていません。
そのため、必要なPATHはシェルの内部で自分で設定を追記する必要があります。
export PATH="${PATH}:/sbin"
このような原因でローカルでの実行では問題ないが、なぜかRobomaker経由だと動かないということがありました。
CFnの1度の更新でRobotに紐づくFleetの変更ができない
これは非常に気が付きにくいですが、Fleetの変更時の更新方法は、Replacement
のため、CFnはまず新しいフリートにおいてRobotリソースを作成しようとしますが、ここに設定しているGreengrassGroupId
は、同時に複数のRobotに紐づくことを許可していないため、下記のエラーが発生します。
2/5 | 4:57:17 PM | UPDATE_FAILED | AWS::RoboMaker::Robot |
Greengrass GroupId is already in use by: ******** (Service: AWSRoboMaker; Status Code: 400; Error Code: ResourceAlreadyExistsException;)
そのため、Fleet変更には必ずRobotの削除という前ステップが必要となります。またこのときに削除されたRobotでデプロイされていたROSアプリケーションがAWSリソースが削除されたときにどのような挙動になるかは、公開されていません。
【2020/08/11】
ドキュメントには記載がありませんので、必要な方はAWSサポートにもご確認ください。
IAM PolicyとIoT PolicyではCFnでの更新方法が異なる
IAM PolicyのPolicyDocumentの更新方法は、No interruption
ですが、IoT PolicyのPolicyDocumentの更新方法は、Replacement
です。つまりIoT Policyの権限が変わるような場合、PolicyNameも同じタイミングで変更しなければ、こちらのエラーが発生してしまいます。同じPolicyでもそれぞれ挙動が異なるため、ドキュメントは正確に読む必要があります。
Greengrassのコアが認識されない
こちらのマネージメントコンソールからデプロイしている場合に気が付きにくいですが、証明書とエンドポイントだけ設定すれば自動で認識されるわけではありません。こちらの4項にあるようにコアの検索を定義する必要があります。CDKで行う場合には、GreengrassのFunctionにこちらの7項で説明がある設定をします。
CDKで行う場合には下記のようになります。
new CfnFunctionDefinitionVersion(scope, 'GGFuncDefVer', {
functionDefinitionId: func.ref,
functions: [
{
functionArn: "arn:aws:lambda:::function:GGIPDetector:1",
functionConfiguration: {
pinned: true,
memorySize: 32768,
timeout: 3
},
id: 'GGLambda'
}
],
})
その他について
Stack TagsとResource Tagsについて
CDKのstacksには、
stack.tags – Returns a TagManager that you can use to add or remove stack-level tags. This tag manager tags all resources within the stack, and also tags the stack itself when it's created through AWS CloudFormation.
とありますが、これはスタックタグの上にリソースタグが上書きされるのか、リソースタグの上にスタックタグが上書きされるのか明示的に書かれておらずソースを読んで確認する必要があります。
今回使用しているバージョンはv1.32.2なので、下記のリポジトリより
stack tagsとresource tagsの設定している部分を探す。
そこではstack tagsに対してresource tagsがオーバライドされていることが確認できます。
そもそもCDKのタグの実装はec2のeipであれば、
/**
* `AWS::EC2::EIP.Tags`
* @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-eip.html#cfn-ec2-eip-tags
*/
readonly tags?: cdk.CfnTag[];
greengrassのgroupの場合は、下記の注意書きのようにanyであり
この Json プロパティタイプは、キーと値のペアのマップとして処理されます。次の形式を使用します。これは、AWS CloudFormation テンプレートのほとんどの Tags 実装とは異なります。
/**
* `AWS::Greengrass::Group.Tags`
* @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-greengrass-group.html#cfn-greengrass-group-tags
*/
readonly tags?: any;
robomakerのfleetについては、特に特記事項がありませんがanyです。
/**
* `AWS::RoboMaker::Fleet.Tags`
* @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-robomaker-fleet.html#cfn-robomaker-fleet-tags
*/
readonly tags?: any;
これがCFnではどうやって扱われているかというと
通常のJSONフォーマットは下記であり
"Tags" : [
{
"Key" : "keyname1",
"Value" : "value1"
},
{
"Key" : "keyname2",
"Value" : "value2"
}
]
greengrass系のTagsは、下記のようになります。
"Tags": {
"KeyName0": "value1",
"KeyName1": "value2",
"KeyName2": "value3"
}
CDKでまとめてデプロイするときに依存関係が自動的に解決されない
下記のissuesで議論中です。依存関係のあるスタックの更新については、手動で実行をする必要があります。
まとめ
- CDKはいま絶賛開発中であり、ドキュメントや事例も少なくソースコードやissueを読まなければわからないことも多いです
- AWSのマネージメントコンソールからCDKを用いたCFnへの移行は結構なコストが必要であり、今回はAWSサポートに問い合わせをしながら2ヶ月近く実装に時間がかかりました
- AWSリソースのプロビジョニングを自動でできるようになり、Greengrass Coreのプロビジョニングをrc.localに設定したスクリプトによって実行することによって、完全に自動化されたロボットのプロビジョニングができるようになりました
CDKやGreengrass、Robomakerなどはやってみた系の記事が多いですが、いざ実際に本番環境で動かすことを想定して準備するとわからないことがたくさんあり大変でした。これでインフラまわりが完全に自動化されたことでロボットのスケールなども簡単に管理できるようになりました。