AWS CDKでS3バケットに資材がPUTされたことをトリガーにECSの1つのコンテナを入れ替えるだけのシンプルなCI/CDパイプラインを作ってみた
ECSのコンテナを入れ替えるだけのシンプルなCI/CDパイプラインを作りたい
こんにちは、のんピ(@non____97)です。
皆さんはECSのコンテナを入れ替えるだけのシンプルなCI/CDパイプラインを作りたいなと思ったことはありますか? 私はあります。
コンテンツを更新したあとに環境に反映させるまでには以下ステップを踏むことになります。
- コンテナイメージをビルド
- コンテナイメージビルドに必要なアセットがあるのであれば、それを手元に持ってくる作業も
- ECRにコンテナイメージをPush
- タスク定義を更新
- ECSサービスの更新
中々手間です。CI/CDパイプラインを作りたいところです。
ということで、シンプルなCI/CDパイプラインをAWS CDKを作ってみます。
やってみた
検証環境
検証環境は以下のとおりです。

1つのECSタスクには複数のコンテナが同居しています。今回はこの内webコンテナだけを入れ替えます。
CI/CDパイプラインで行っていることは以下のとおりです。
要するにHTMLファイルなどのコンテンツをアップロードしたことをトリガーに、DockerfileやNginxのコンフィグファイルなどを固めたzipファイルをダウンロードしてきてコンテナイメージをビルド、デプロイする形です。
今回はアプリケーションの開発環境の都合上、Gitを使用していない or できないことを想定しています。そのため、S3バケットにコンテンツがアップロードされたことをトリガーに動作するようにしています。
また、ECSサービス側ではECSネイティブのBlue/Green Deploymentを使用するようにしています。
こちらのCI/CDパイプラインおよびシェルスクリプトのコードは以下GitHubリポジトリに保存しています。
Webアプリケーション自体の環境の説明やコードは以下記事、GitHubリポジトリをご覧ください。
動作確認
動作確認をします。
現在、アクセスをすると以下のようにテキストを返してくれます。
> curl http://EcsNat-AlbCo-MVM2FDwH12Al-988247753.us-east-1.elb.amazonaws.com/
index.txt v1.0
これをindex.txt v1.1に変更します。
index.htmlのファイルを変更します。
その後、指定したディレクトリをzipで固めて、CodePipelineのSourceステージで使用しているS3バケットに配置するシェルスクリプトを実行します。引数にはビルドしたイメージに付与するタグも指定します。
> bash ./scripts/upload-contents.sh 1.0.1 ./assets/html "" CicdPipelineStack
[1/3] html ディレクトリを zip に固めています: ./assets/html
ステージングディレクトリを作成...
staging_dir=/var/folders/4s/dby8gc1n2dj9n433fh6dsksr0000gn/T/tmp.jgtKZwncTU
html をコピー: ./assets/html -> /var/folders/4s/dby8gc1n2dj9n433fh6dsksr0000gn/T/tmp.jgtKZwncTU/html
zip 化: /var/folders/4s/dby8gc1n2dj9n433fh6dsksr0000gn/T/contents-XXXXXX.zip.sK55E8pazl
zip 作成: /var/folders/4s/dby8gc1n2dj9n433fh6dsksr0000gn/T/contents-XXXXXX.zip.sK55E8pazl
[2/3] ContentsBucket を解決しています (引数 or Stack 'CicdPipelineStack' の CfnOutput)
ContentsBucket: cicdpipelinestack-contentsbucket571b0902-ecg8fnfbdlxv
[3/3] S3 にアップロードしています (version=1.0.1)
アップロード完了: contents.zip (version=1.0.1) -> s3://cicdpipelinestack-contentsbucket571b0902-ecg8fnfbdlxv/contents.zip
アップロード完了したようですね。
CodePipelineを確認すると、もうDeployステージまで進んでいますね。

ECSタスクの状態を確認すると、新旧2種類計4つのECSタスクが起動していました。

私はライフサイクルフックの設定をしていたので、指定していたSlackチャンネルに通知が来ました。

この状態で本番リスナーとテストリスナーにアクセスします。
> curl http://EcsNat-AlbCo-MVM2FDwH12Al-988247753.us-east-1.elb.amazonaws.com/
index.txt v1.0
> curl http://EcsNat-AlbCo-MVM2FDwH12Al-988247753.us-east-1.elb.amazonaws.com:10080/
index.txt v1.1
テストリスナーにアクセスした場合、テキストが変わっていることが分かりますね。
再ルーティングボタンを押下して、本番トラフィックの再ルーティングを行います。
すると、本番リスナーでもレスポンスの内容が変わりました。
> curl http://EcsNat-AlbCo-MVM2FDwH12Al-988247753.us-east-1.elb.amazonaws.com/
index.txt v1.1
CodePipelineも正常に実行完了しています。

ロールバックした場合の挙動も確認しましょう。
index.htmlの内容をindex.txt v1.2に変更して、再度upload-contents.shでS3バケットにPUTします。
しばらくすると、指定したSlackチャンネルに再ルーティングするかロールバックするかの確認通知が来ました。
動作確認をします。
> curl http://EcsNat-AlbCo-MVM2FDwH12Al-988247753.us-east-1.elb.amazonaws.com/
index.txt v1.1
> curl http://EcsNat-AlbCo-MVM2FDwH12Al-988247753.us-east-1.elb.amazonaws.com:10080/
index.txt v1.2
テストリスナーのみレスポンスの内容が変わりましたね。
今回はロールバック時の挙動の確認なので、ロールバックボタンを押下します。

ロールバックを行うと、CodePipelineのDeployアクションが失敗しました。

ロールバック後に再度アクセスをすると、以下のようにテストリスナーアクセス時のコンテンツが元に戻りました。
> curl http://EcsNat-AlbCo-MVM2FDwH12Al-988247753.us-east-1.elb.amazonaws.com/
index.txt v1.1
> curl http://EcsNat-AlbCo-MVM2FDwH12Al-988247753.us-east-1.elb.amazonaws.com:10080/
index.txt v1.1
単純な処理でもCI/CDパイプラインを作成しよう
AWS CDKでS3バケットに資材がPUTされたことをトリガーにECSの1つのコンテナを入れ替えるだけのシンプルなCI/CDパイプラインを作ってみました。
単純な処理であっても何回も繰り返し行うのであれば、トータルではかなり時間を費やしてしまうことになります。自動化できるものは自動化してしまいましょう。
この記事が誰かの助けになれば幸いです。
以上、クラウド事業本部 コンサルティング部の のんピ(@non____97)でした!







