[追記:この構成だとひとつ古いDockerイメージしか使われないな]
AWS CodePipeline を使って Rails アプリケーションを Fargate を利用した ECS に Blue/Green デプロイします。
デプロイの流れ
- Rails アプリのソースコードを変更
- GitHub に Push または Pull Request をマージ
- CodePipeline のパイプラインが起動
- CodeBuild でアプリケーションをビルド
- CodeBuild で Docker イメージをビルド
- CodeBuild から ECR に Docker イメージを
latest
タグを付けて登録 - CodeDeploy で ECR から Docker イメージを取得して ECS にデプロイ
Rails アプリの Docker イメージは app
と web
の2つ使用します。
CodePipeline 構成
利用するサービスは以下の通りです。
- AWS Elastic Container Service (ECS)
- Fargate
- AWS CodeBuild
- AWS CodeDeploy
- ECS(Fargate) Blue/Green Deploy
- AWS Elastic Container Registry (ECR)
- GitHub
CodeBuild と CodeDeploy は事前に作成しておいてください。
全体像
パイプラインの完成形は下図のようになります。
Source ステージでは GitHub と ECR を並列で指定します。Docker イメージ複数使うときは全て追加しておきます。
次は GitHub Source です。
GitHub と連携してリポジトリとブランチを指定します。図のブランチは動作確認用に ecs
としていますが本番利用時は master
にしています。
GitHub Source の 出力アーティファクト はこの先のステージで使用しないのでデフォルト値にしています。
次は ECR Source です。利用する ECR のリポジトリとタグを指定してください。今回は常に latest
をデプロイします。
アクション名と出力アーティファクトはこの先のステージと設定ファイルで使用するので web
を指定します。
次は Build ステージです。
入力アーティファクト には GitHub Source の出力である SourceArtifact
を選択します。
プロジェクト名には事前に作成した CodeBuild のプロジェクトを選択してください。
ビルド結果は BuildArtifact
として S3 にアップロードします。アップロードされるファイルは taskdef.json
appspec.yaml
になります。
最後は Deploy ステージです。
アクションプロバイダーに ECS のBlue/Green を選択してください。専用の設定項目が表示されます。
入力アーティファクトには ECR アクションと Build ステージのアーティファクトを選択しています。今回は SourceArtifact
は使用しません。
CodeDeploy に関する設定は事前に作成したものを選択してください。
Amazon ECS タスク定義 は BuildArtifact
の taskdef.json
を指定します。AppSpec ファイルも同様に BuildArtifact
のファイルを指定してください。
この2つのファイルは buildspec.yaml
の files
セクションで指定されているので、ビルド後に S3 にアップロードされて CodePipeline が利用できるようになります。
AWS の公式ドキュメントでは SourceArtifact
を指定していますが、以下のエントリにあるように SourceArtifact
が 3MB 以上あるとエラーになるので BuildArtifact
を参照しています。
CodeDeployでtask definitionが読み込めない時の対処法 - Qiita
最後に taskdef.json
で Docker イメージが参照できるように入力アーティファクトを持つイメージの詳細に ECR Source のイメージを設定します。
設定ファイル
CodePipelie/CodeBuild/CodeDeploy には以下の3ファイルが必要です。すべて GitHub のリポジトリに追加しています。
taskdef.json
appspec.yaml
buildspec.yaml
Blue/Green デプロイを利用するので imagedefinitions.json
は不要です。ECR Source を利用するので imageDetail.json
も不要です。
各種設定値は環境に合わせて読み替えてください
taskdef.json
.family
の値は ECS のタスク定義で設定した名前にします。
.containerDefinitions[0].name
は Build ステージで入力した「入力アーティファクトを持つイメージの詳細」に合わせます。
.containerDefinitions[0].image
が「タスク定義のプレースホルダー文字」になります。<>
も含めて設定します。
{
"family": {{ ここは ECS task の family に合わせる }},
"taskRoleArn": "arn:aws:iam::000000000000:role/ecsTaskRole",
"executionRoleArn": "arn:aws:iam::000000000000:role/ecsTaskExecutionRole",
"networkMode": "awsvpc",
"containerDefinitions": [
{
"name": "app",
"image": "<app>",
"essential": true,
},
{
"name": "web",
"image": "<web>",
"portMappings": [
{
"containerPort": 80,
"hostPort": 80,
"protocol": "tcp"
}
],
"essential": false,
}
],
"requiresCompatibilities": [
"FARGATE"
],
"cpu": "256",
"memory": "512"
}
appspec.yaml
Resources[0].TargetServices.Properties.TaskDefinition
は <>
も含めて以下の通り設定します。
Subnets
や SecurityGroups
は環境に合わせて変更してください。
version: 0.0
Resources:
- TargetService:
Type: AWS::ECS::Service
Properties:
TaskDefinition: <TASK_DEFINITION>
LoadBalancerInfo:
ContainerName: web
ContainerPort: 80
NetworkConfiguration:
AwsvpcConfiguration:
Subnets:
- subnet-00000000
SecurityGroups:
- sg-00000000
AssignPublicIp: ENABLED
buildspec.yaml
files
セクションに appspec.yaml
と taskdef.json
が必要です。
version: 0.2
phases:
install:
runtime-versions:
docker: 18
pre_build:
commands:
- echo Logging in to Amazon ECR...
- $(aws ecr get-login --no-include-email --region ap-northeast-1)
build:
commands:
- echo Build started on `date`
- echo Compiling assets...
- echo Building the Docker image...
- docker-compose -f docker-compose.production.yml build --parallel
post_build:
commands:
- echo Build completed on `date`
- echo Pushing the Docker image...
- docker-compose -f docker-compose.yml push
artifacts:
files:
- appspec.yaml
- taskdef.json