はじめに
本日のテーマはWebアプリケーションのEC2 → ECS Fargate移行の際に、
ECSデプロイツールであるecspressoを利用した話です。
移行対象のアプリケーションについて
- JavaのWebアプリケーション
- テスト・デプロイ作業は手動な部分が多い
元々のアプリケーションは複数機能が同一EC2上に同居しており、今回その一部アプリケーションをリプレースすると共に、コンテナ化する形を取りました。
プロジェクトの全体像
- コンテナ移行(EC2→ECS Fargate)
- CICDパイプライン用意(開発→テスト環境)
- リリースシェル用意(テスト→本番環境) 👈今回のテーマ
開発環境と本番環境のAWSアカウントが分かれており、
既存Webサーバーと同様に夜間リリースの運用にしたいという事情がありました。
他にも、テスト環境と本番環境の設定ファイルを書き換えたいといった事情もあり、今回はCICDパイプラインと別に
- テスト環境でテスト済のコンテナイメージを基に、本番環境にデプロイする為のリリースシェル
を用意しました。
リリースシェルについて
CICDパイプラインによって、テスト済のDockerイメージがテスト環境側のECRにある状態とはなりますが、ここで厄介なのがDockerイメージを「どのように本番ECRに持っていくか?」でした。
クロスアカウントレプリケーションを使えば、テストECRのイメージを本番ECRにレプリケーションする事は可能ですが、以下の問題がありました。
- 本番アカウントのECRに、リリースされていないDockerイメージも混在してしまう
- 結果、ECRリポジトリ内の見通しが悪くなる
- テスト環境と本番環境用のWebアプリケーションの設定ファイルの書き換えが必要
- 本番ECRにレプリケーションされたイメージを更に加工する必要がある
問題を解決するうえで、リリースシェルは以下の方式を取ることにしました。
リリースシェル概要
- テスト済のDockerイメージをテストECRからプル
- コンテナの再ビルド(設定ファイルを書き換え)
- イメージを本番ECRにプッシュ
- 本番ECSにデプロイ
上記の方法を取ることで、本番ECRに「リリースするDockerイメージ」のみ保持するようにしました。
また、本番ECRにプッシュしたイメージのタグは「リリース日」+「ビルド前のイメージタグ」とすることで、イメージの追跡が行える状態にしました。
リリースシェルの一部は以下の記事に記載してあります。
ecspresso採用の経緯
ここまで説明したリリースシェルですが、他にも以下の検討点がありました。
- ECSの構成のコード管理(タスク定義、サービス定義)をどうするか?
- 複数のテスト環境への簡易デプロイ手段の確立
特にECSのコード化については、デプロイの度にタスク定義・サービス定義が変わり、必要タスク数の増減もある為CloudFormationで管理しにくいという事情もあり、コード化+デプロイも行えるという点が便利だと感じた為ecspressoを採用しました。
ecspressoについて
- カヤックさんが公開しているオープンソースのECSデプロイツール
- ECSサービス、タスクに関わる最小限のリソースをコード管理できる
- 既存の ECSサービス、タスクの情報を元に構成ファイルを生成する機能
- ECSサービスのタスクを入れ替える機能
※作者の方がアドベントカレンダーを公開しており、こちらを参考にしました。
元々VPC周りは既に構築されている物を利用するという事情もあり、
ecspressのECSサービス、タスクに関わる最小限のリソースをコード管理できる点が良かったのと、
Javaのメモリリーク対策を考えた際にECSサービスのタスクを入れ替える(refresh)機能が非常に役に立ちました。
ecspressoのコマンド紹介
ecspressoによる実際のデプロイを説明する上で、リリースシェルに組み込んだコマンド等を先に紹介します。
# 既存のECR構成を取り込み(オプションは省略)
ecspresso init
# 以下3ファイルが生成
config.yaml リージョン、クラスター、サービス名を定義
ecs-service-def.json ECSサービス定義
ecs-task-def.json ECSタスク定義
# 上記設定ファイルを基にECSへデプロイ
ecspresso deploy --config config.yaml
リリースシェルの詳細
リリースシェルの具体的な内容について、実際のリリース手順と共にご紹介します。
リリース手順1
- リリース前にcronに「リリース日時」「ECRイメージURI」をセット ※リリース実行用のEC2上
* * * * * /bin/sh release.sh イメージURI
リリース手順2
ここからはrelease.sh
内で実行される処理です。
# リリース対象のイメージURI(再ビルド後)を定義
RELEASE_IMAGE_URI=${ACCOUNT_ID_RELEASE_IMAGE}.dkr.ecr.
${REGION_RELEASE_IMAGE}.amazonaws.com/${REPO_NAME_RELEASE_IMAGE}
:IMAGE_TAG_FOR_RELEASE_IMAGE
- cronで引数として渡した「ECRイメージURI」
- シェル内で定義している本番アカウントの「アカウントID」「リージョン名」
を基に、動的にリリース対象のイメージURIを定義しています。
※ここから、設定ファイル書き換えの為の再ビルド→本番ECRに対してのプッシュをシェル内で実施していますが、こちらについては別の記事で説明している為割愛します。
リリース手順3
こちらもrelease.sh
内で実行される処理です。
# ecspresso initにより事前に準備した設定ファイルのディレクトリに移動
cd /home/ec2-user/deploy/config
# タスク定義、サービスをデプロイ
ecspresso deploy --config=config.yaml
事前にecspresso init
で定義した設定ファイル(config.yaml
、ecs-service-def.json
、ecs-task-def.json
)を参照し、ecspresso deploy
を実行します。
「どのようにリリース対象のECRイメージを指定するか?」ですが、
ここではecspressoのテンプレート記法を利用しています。
"containerDefinitions": [
{
"cpu": 0,
"environment": [],
"essential": true,
"image": "{{ must_env `RELEASE_IMAGE_URI` }}",
ecs-task-def.json
内のimageURIを定義する箇所でテンプレート記法を利用しています。
※RELEASE_IMAGE_URI
は、リリース手順2で定義した「再ビルド後のイメージURI」です。
これにて、事前定義した設定ファイルを基にしたECSへのデプロイは完了です!
その他にも
定期実行しているメモリリーク対策のシェルにも、ecspressoを利用しています。
ecspresso refresh
により、タスク定義を新しく登録せずにタスクの再デプロイが可能です。
ecspresso init --config=config.yaml --region=リージョン --cluster=クラスター名 --service=サービス名
ecspresso refresh --config config.yaml
参考文献
ecspresso advent calendar