記事の概要
AWS Copilotを使って作成したECS上のRailsアプリケーションにおいて、CI/CDパイプラインを構築します。
また、その際にハマったポイントを共有します。
↓の記事の続きになります。
AWS Copilotを使ってRailsコンテナの本番環境を爆速で構築する
AWS Copilot
AWSでコンテナ化されたアプリケーションの開発、リリースを容易に行うためのコマンドラインツールです。
コマンドを叩くとCloudFormationが動き、必要なリソースの作成やデプロイを行うことができる。CI/CDパイプラインもコマンド一つで作成できます。
※Fargate起動タイプのみサポートしています
Pipeline
Githubへのpushをトリガにしてコンテナデプロイを実行するための仕組みです。
CopilotによってPipelineを作成すると、CloudFormationによってCodeBuildのリソースが作成されます。
Pipeline作成方法
手順概要
- GitHubの個人アクセストークン準備
- master.keyのAmazon SSMへの登録
- Pipelineの定義
- Pipelineの作成
- ビルドプロジェクトのイメージ修正(Rubyのバージョン次第)
GitHubの個人アクセストークン準備
PipelineからGithubリポジトリを参照するために、GitHubの個人アクセストークンが必要なので事前に準備しておく必要があります。
アクセストークンはrepo
、admin:repo_hook
のスコープが必要です。
個人アクセストークンを使用する
master.keyのAmazon SSMへの登録
Railsのmaster.keyは普通はGit管理から外すためGithub上にありませんが、PipelineはGithubリポジトリを参照してDockerイメージを作るためmaster.keyを何かしらの方法でECSに渡す必要があります。
セキュアに管理するためにAmazon SSMのパラメータストアを利用します。
下記コマンドでmaster.keyの文字列をAmazon SSMのパラメータストアに登録します。
タグの値については適宜自身のものに変更してください。
$ aws ssm put-parameter --name RAILS_MASTER_KEY --value master.keyの文字列 --type SecureString\
--tags Key=copilot-environment,Value=production Key=copilot-application,Value=copilot-demo
続いて、Serviceのmanifest.ymlを変更します。
secretsを宣言することでECSからSSMのパラメータストアを参照できます。
「ECSで参照できる環境変数: SSMに登録したパラメータのキー」のように指定します。
variables:
RAILS_ENV: development
secrets:
RAILS_MASTER_KEY: RAILS_MASTER_KEY # ECSで参照できる環境変数: SSMに登録したパラメータのキー
Pipelineの定義
続いてPipelineを定義します。
EnvironmentやServiceを作成した時と同じように対話型で進めていきます。
このタイミングでPipelineが参照するGithubリポジトリや個人アクセストークンを指定します。
$ copilot pipeline init
Would you like to add an environment to your pipeline? [? for help] (y/N)
> y
Which environment would you like to add to your pipeline? [Use arrows to move, type to filter, ? for more help]
> production
Which GitHub repository would you like to use for your service? [Use arrows to move, type to filter, ? for more help]
> https://github.com/ssshun/rails_copilot
Please enter your GitHub Personal Access Token for your repository rails_copilot: [? for help]
> XXXXXXXXXXXXXX
完了すると以下の2ファイルが作成されます。基本はデフォルトのままで問題ないです。
- buildspec.yml
- pipeline.yml
Pipelineの作成
以下のコマンドでPipelineを作成
$ git add . && git commit -m "Adding Pipeline Buildspec" && git push
$ copilot pipeline update
しばらく経ってからCodeBuildを確認すると、ビルドプロジェクトとパイプラインが作成されているのがわかります。
ビルドプロジェクトのイメージ修正
アプリケーションのRubyのバージョン次第ではこの手順は不要です。
2020/08/08時点では、アプリケーションがRuby2.7を利用する場合、この作業が必要でした。
buildspec.ymlを修正し、ビルドプロジェクトのrubyランタイムのバージョンを2.7に変更します。
version: 0.2
phases:
install:
runtime-versions:
docker: 18
ruby: 2.7
続いて、AWSコンソールにて、ビルドプロジェクト > ビルド詳細 > 環境 からイメージを修正します。
CopilotでPipelineを作成した場合、イメージがaws/codebuild/amazonlinux2-aarch64-standard:1.0
になるのですが、Ruby2.7のランタイムがaws/codebuild/amazonlinux2-aarch64-standard:1.0
に対応していないためです。
Ruby2.7に対応しているaws/codebuild/amazonlinux2-x86_64-standard:3.0
に変更します。
buildspecを修正したので、Pipelineを更新します。
$ git add . && git commit -m "Modifying Pipeline Buildspec" && git push
$ copilot pipeline update
ハマったポイント
Pipeline名がCloudFormationの命名規則違反
プロジェクトディレクトリにアンダースコアを入れて、そのままPipelineを作成すると以下のようなエラーが発生します。
check if pipeline exists: describe stack pipeline-copilot-demo-ssshun-rails_copilot: ValidationError: 1 validation error detected: Value 'pipeline-copilot-demo-ssshun-rails_copilot' at 'stackName' failed to satisfy constraint: Member must satisfy regular expression pattern: [a-zA-Z][-a-zA-Z0-9]*|arn:[-a-zA-Z0-9:/._+]*
原因ですが、プロジェクトのディレクトリ名にアンダースコアが含まれると、デフォルトのPipeline名にもアンダースコアが入ります。
そのため、このままだとCloudFormationのスタック名の命名規則に違反してしまいます。
# The name of the pipeline
name: pipeline-copilot-demo-ssshun-rails_copilot # プロジェクトディレクトリがrails_copilotの場合のデフォルト
以下のようにアンダースコアが含まれないように名前を修正すれば解消されます。
# The name of the pipeline
name: pipeline-copilot-demo-ssshun-rails-copilot
ビルドプロジェクトのランタイムとイメージの不整合
CodeBuildにてビルドが成功しないのでログを確認したところ以下のエラーが発生していました。
アプリケーションのランタイム(Ruby2.7)とビルドプロジェクトのランタイム(Ruby2.6)の不整合が原因と思われます。
rbenv: version `ruby-2.7.1' is not installed (set by /codebuild/output/src020247698/src/.ruby-version)
ビルドプロジェクトのランタイムをRuby2.7に変更したらいけるかと思い、buildspecを修正しましたが、
version: 0.2
phases:
install:
runtime-versions:
docker: 18
ruby: 2.7
再度、別のエラーが発生しました。
Ruby2.7のランタイムがCodeBuildのビルドプロジェクトのイメージaws/codebuild/amazonlinux2-aarch64-standard:1.0
に対応していないためでした。
Phase complete: DOWNLOAD_SOURCE State: FAILED
Phase context status code: YAML_FILE_ERROR Message: Unknown runtime version named '2.7' of ruby. This
build image has the following versions: 2.6
上の手順で書いた通り、イメージをRuby2.7に対応しているaws/codebuild/amazonlinux2-x86_64-standard:3.0
に変更して解決しました。
ECSからSSMのパラメータストアが参照不可
master.key参照のためにAmazon SSMのパラメータストアを登録する際に、タグは要らないだろうと思って以下のように登録していました。
$ aws ssm put-parameter --name RAILS_MASTER_KEY --value master.keyの文字列 --type SecureString
その状態でPipelineでデプロイしたところ、ECSタスクで以下のエラーが発生しました。
パラメータストアのRAILS_MASTER_KEYの参照権限がないとのことでした。
Fetching secret data from SSM Parameter Store in ap-northeast-1:AccessDeniedException:
User: arn:aws:sts::XXXXXXXXXXXX:assumed-role/copilot-demo-production-api-ExecutionRole-DYGK7EGS469J/0e93e07304b641d58e5fc147122d2f4c is not authorized to perform: ssm:GetParameters on resource: arn:aws:ssm:ap-northeast-1:XXXXXXXXXXXX:parameter/RAILS_MASTER_KEY
status code: 400, request id: 8b6bac83-1d83-4abe-8d19-bbaa4a5e4a29
調べていくと、manifest.yml
でsecretsを設定するとECSのタスク実行ロールにSSM参照用のIAMポリシーが作成されることがわかりました。
ポリシーをよくよく見ると、パラメータストアのタグで条件指定して参照権限を付与していました。ここら辺のきめ細やかさはすごいですね。
{
"Version": "2012-10-17",
"Statement": [
{
"Condition": {
"StringEquals": {
"ssm:ResourceTag/copilot-environment": "production",
"ssm:ResourceTag/copilot-application": "copilot-demo"
}
},
"Action": [
"ssm:GetParameters"
],
"Resource": [
"arn:aws:ssm:ap-northeast-1:XXXXXXXXXXXX:parameter/*"
],
"Effect": "Allow"
},
{
"Condition": {
"StringEquals": {
"secretsmanager:ResourceTag/copilot-application": "copilot-demo",
"secretsmanager:ResourceTag/copilot-environment": "production"
}
},
"Action": [
"secretsmanager:GetSecretValue"
],
"Resource": [
"arn:aws:secretsmanager:ap-northeast-1:XXXXXXXXXXXX:secret:*"
],
"Effect": "Allow"
},
{
"Action": [
"kms:Decrypt"
],
"Resource": [
"arn:aws:kms:ap-northeast-1:XXXXXXXXXXXX:key/*"
],
"Effect": "Allow"
}
]
}
パラメータストア登録時には以下のようにしっかりとタグ指定しましょう。
$ aws ssm put-parameter --name RAILS_MASTER_KEY --value master.keyの文字列 --type SecureString\
--tags Key=copilot-environment,Value=production Key=copilot-application,Value=copilot-demo
まとめ
今回作成したコードは以下です。
https://github.com/ssshun/rails-copilot
AWS Copilotを使って作成したECS上のRailsアプリケーションにおいて、CI/CDパイプラインを構築しました。
AWSコンソールでの操作は最小限で簡単にデプロイフローを作成できました。