こんにちは。CX事業本部Delivery部MADグループのきんじょーです。
担当している案件で、Elastic Beanstalkでデプロイしているアプリケーションの環境に、自動的に更新が走り失敗を繰り返しているという事象が発生しました。 同じ問題を抱える人のため、調査結果と対応方法についてブログにまとめます。
先に結論
- Elastic Beanstalkのサービスロールには、通常更新とマネージドプラットフォーム更新用の2種類があります。
- 通常更新と、マネージドプラットフォーム更新用のサービスロールには同じロールを設定しましょう。
- 現在、マネージドプラットフォーム更新に適切なポリシーは
AWSElasticBeanstalkManagedUpdatesCustomerRolePolicy
です。 - どちらのサービスロールも明示的に指定しない場合、何も気にする必要はありません。
マネージドプラットフォーム更新とは?
AWSのドキュメントによると以下のように記載されています。
AWS Elastic Beanstalk は定期的にプラットフォームの更新をリリースし、修正やソフトウェア更新、新機能を提供しています。マネージドプラットフォーム更新機能により、予定済みのメンテナンス期間中に、環境を自動的に最新バージョンのプラットフォームに更新できます。更新プロセス中も、アプリケーションは能力を低減させることなく稼動状態に保たれます。マネージド更新は、単一インスタンス環境とロードバランシング環境の両方で利用できます。
設定をオンにすると、プラットフォームのマイナーバージョンの更新やパッチ適用のため、自動的にAWS側でデプロイが開始されます。
実行されたマネージド更新はElastic Beanstalkの管理コンソールから確認できます。
失敗の原因はIAMの権限不足
以下はデプロイ時のElastic Beanstalkのイベントログです。
デプロイが開始されAWSEBEC2LaunchTemplate
というリソースの作成でエラーが発生しています。
CloudFormation側のログを確認すると、起動テンプレートの作成が許可されていない旨のエラーが出力されています。
IAMが怪しいと分かったので、マネージドプラットフォーム更新時に使用されるロールのポリシーを詳しく見ていきます。
Elastic Beanstalkで使用するロール
Elastic Beanstalkは3種類のIAMロールを設定可能です。
- サービスロール
- 通常のサービスロール
- Elastic Beanstalkのサービス自体が、デプロイ時にAWSのリソースを操作する際のロール
- マネージドプラットフォーム更新用サービスロール
- マネージドプラットフォーム更新で、環境を更新する時にのみ使用されるロール
- 通常のサービスロール
- IAMインスタンスプロファイル
- アプリケーションがデプロイされるEC2インスタンスに紐付けられるロール
マネージドプラットフォーム更新に使用されるサービスロール
サービスロールの指定は任意の設定項目です。
設定しなければaws-elasticbeanstalk-service-role
というロールが自動的に作成され、通常時とマネージド更新時の両方で使用されます。
マネージドプラットフォーム更新用サービスロールは、ebextentionsで明示的に指定可能です。
参考: aws:elasticbeanstalk:managedactions
設定可能な値は、サービスロールと同じか、AWSServiceRoleForElasticBeanstalkManagedUpdates
という固定の値で、固定値の場合同じ名前のロールが自動作成されます。
以下はebextensionsで設定する際の例です。
.ebextentionsp/managed-update-service-role.config
option_settings:
aws:elasticbeanstalk:managedactions:
ServiceRoleForManagedUpdates: AWSServiceRoleForElasticBeanstalkManagedUpdates
そして、
⚠️上記のようにebextensionsで AWSServiceRoleForElasticBeanstalkManagedUpdates
を指定しないでください!!⚠️
理由は以下の通りです。
自動作成されるAWSServiceRoleForElasticBeanstalkManagedUpdates
の権限が不足している
このロールには、AWSElasticBeanstalkManagedUpdatesServiceRolePolicy
というAWS管理ポリシーがアタッチされています。
AWSElasticBeanstalkServicerRolePolicy
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPassRoleToElasticBeanstalkAndDownstreamServices",
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "*",
"Condition": {
"StringLikeIfExists": {
"iam:PassedToService": [
"elasticbeanstalk.amazonaws.com",
"ec2.amazonaws.com",
"autoscaling.amazonaws.com",
"elasticloadbalancing.amazonaws.com",
"ecs.amazonaws.com",
"cloudformation.amazonaws.com"
]
}
}
},
{
"Sid": "SingleInstanceAPIs",
"Effect": "Allow",
"Action": [
"ec2:releaseAddress",
"ec2:allocateAddress",
"ec2:DisassociateAddress",
"ec2:AssociateAddress"
],
"Resource": "*"
},
{
"Sid": "ECS",
"Effect": "Allow",
"Action": [
"ecs:RegisterTaskDefinition",
"ecs:DeRegisterTaskDefinition",
"ecs:List*",
"ecs:Describe*"
],
"Resource": "*"
},
{
"Sid": "ElasticBeanstalkAPIs",
"Effect": "Allow",
"Action": [
"elasticbeanstalk:*"
],
"Resource": "*"
},
{
"Sid": "ReadOnlyAPIs",
"Effect": "Allow",
"Action": [
"cloudformation:Describe*",
"cloudformation:List*",
"ec2:Describe*",
"autoscaling:Describe*",
"elasticloadbalancing:Describe*",
"logs:DescribeLogGroups",
"sns:GetTopicAttributes",
"sns:ListSubscriptionsByTopic"
],
"Resource": "*"
},
{
"Sid": "ASG",
"Effect": "Allow",
"Action": [
"autoscaling:AttachInstances",
"autoscaling:CreateAutoScalingGroup",
"autoscaling:CreateLaunchConfiguration",
"autoscaling:DeleteAutoScalingGroup",
"autoscaling:DeleteLaunchConfiguration",
"autoscaling:DeleteScheduledAction",
"autoscaling:DetachInstances",
"autoscaling:PutNotificationConfiguration",
"autoscaling:PutScalingPolicy",
"autoscaling:PutScheduledUpdateGroupAction",
"autoscaling:ResumeProcesses",
"autoscaling:SuspendProcesses",
"autoscaling:TerminateInstanceInAutoScalingGroup",
"autoscaling:UpdateAutoScalingGroup"
],
"Resource": [
"arn:aws:autoscaling:*:*:launchConfiguration:*:launchConfigurationName/awseb-e-*",
"arn:aws:autoscaling:*:*:autoScalingGroup:*:autoScalingGroupName/awseb-e-*",
"arn:aws:autoscaling:*:*:launchConfiguration:*:launchConfigurationName/eb-*",
"arn:aws:autoscaling:*:*:autoScalingGroup:*:autoScalingGroupName/eb-*"
]
},
{
"Sid": "CFN",
"Effect": "Allow",
"Action": [
"cloudformation:CreateStack",
"cloudformation:CancelUpdateStack",
"cloudformation:DeleteStack",
"cloudformation:GetTemplate",
"cloudformation:UpdateStack"
],
"Resource": [
"arn:aws:cloudformation:*:*:stack/awseb-e-*",
"arn:aws:cloudformation:*:*:stack/eb-*"
]
},
{
"Sid": "EC2",
"Effect": "Allow",
"Action": [
"ec2:TerminateInstances"
],
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringLike": {
"ec2:ResourceTag/aws:cloudformation:stack-id": [
"arn:aws:cloudformation:*:*:stack/awseb-e-*",
"arn:aws:cloudformation:*:*:stack/eb-*"
]
}
}
},
{
"Sid": "S3Obj",
"Effect": "Allow",
"Action": [
"s3:DeleteObject",
"s3:GetObject",
"s3:GetObjectAcl",
"s3:GetObjectVersion",
"s3:GetObjectVersionAcl",
"s3:PutObject",
"s3:PutObjectAcl",
"s3:PutObjectVersionAcl"
],
"Resource": "arn:aws:s3:::elasticbeanstalk-*/*"
},
{
"Sid": "S3Bucket",
"Effect": "Allow",
"Action": [
"s3:GetBucketLocation",
"s3:GetBucketPolicy",
"s3:ListBucket",
"s3:PutBucketPolicy"
],
"Resource": "arn:aws:s3:::elasticbeanstalk-*"
},
{
"Sid": "CWL",
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:DeleteLogGroup",
"logs:PutRetentionPolicy"
],
"Resource": "arn:aws:logs:*:*:log-group:/aws/elasticbeanstalk/*"
},
{
"Sid": "ELB",
"Effect": "Allow",
"Action": [
"elasticloadbalancing:RegisterTargets",
"elasticloadbalancing:DeRegisterTargets",
"elasticloadbalancing:DeregisterInstancesFromLoadBalancer",
"elasticloadbalancing:RegisterInstancesWithLoadBalancer"
],
"Resource": [
"arn:aws:elasticloadbalancing:*:*:targetgroup/awseb-*",
"arn:aws:elasticloadbalancing:*:*:loadbalancer/awseb-e-*",
"arn:aws:elasticloadbalancing:*:*:targetgroup/eb-*",
"arn:aws:elasticloadbalancing:*:*:loadbalancer/eb-*"
]
},
{
"Sid": "SNS",
"Effect": "Allow",
"Action": [
"sns:CreateTopic"
],
"Resource": "arn:aws:sns:*:*:ElasticBeanstalkNotifications-Environment-*"
}
]
}
上記のように、このポリシーでは起動テンプレートの作成(ec2:CreateLaunchTemplate
)の許可が含まれていません。
一方、起動設定(autoscaling:CreateLaunchConfiguration
)は許可されているため、Elastic Beanstalkの環境が起動設定でAuto Scaling Groupを組んでいた頃から更新されていないものかと想像しています。
このマネージドプラットフォーム更新用のロールを自動作成していたため、権限が足りず起動テンプレートの作成に失敗していた。というのが本件の原因でした。
対応方法
以下のどちらかで対応可能です。
1.サービスロールを独自に作成し、通常更新と、マネージドプラットフォーム更新用のサービスロールに同じロールを設定する
この場合、サービスロールにはAWSElasticBeanstalkManagedUpdatesCustomerRolePolicy
という、AWS管理ポリシーをアタッチしてください。
AWS管理のロールではなく、サービスロールを独自に作成したい方はこちらの方法が良いでしょう。
2.サービスロール設定で、通常・マネージドプラットフォーム更新のどちらも設定をしない
何も指定しない場合、aws-elasticbeanstalk-service-role
というロールが自動作成され、このロールには適切なポリシーがアタッチされています。
AWSが用意するロールで問題ない場合、こちらの方が手間少ないです。
まとめ
- Elastic Beanstalkのサービスロールには、通常更新とマネージドプラットフォーム更新用の2種類があります。
- 通常更新と、マネージドプラットフォーム更新用のサービスロールには同じロールを設定しましょう。
- 別で設定するとロールを設定すると不十分な権限のポリシー
AWSElasticBeanstalkServicerRolePolicy
をアタッチしたサービスロールが自動作成され「起動テンプレート」を作成する際にエラーを起こします。
- 別で設定するとロールを設定すると不十分な権限のポリシー
- 現在、マネージドプラットフォーム更新に適切なポリシーは
AWSElasticBeanstalkManagedUpdatesCustomerRolePolicy
です。- サービスロールには、上記ポリシーを付与されていることを確認しましょう。
- どちらのサービスロールも明示的に指定しない場合、何も気にする必要はありません。
- 自動的に上記ポリシーが付与されたロールが自動作成され、通常とマネージドプラットフォーム更新の両方で使用されます。
最後に
Elastic Beanstalkは、インフラとアプリケーションのデプロイや運用監視を楽にしてくれます。一方、PaaSとして提供されているので、今回のように内部的に作成されるポリシーの内容など手を入れることが出来ないため、トレードオフとして割り切る必要があります。 が、それにしても今回はアタッチするIAMポリシーが古いまま更新がされていないように見えるので、もう少し調べて確認してみたいと思います。
この記事が少しでも誰かの役に立つと幸いです。
以上。CX事業本部Delivery部MADグループのきんじょーでした。