ECS Express Mode でデプロイにかかる時間を可能な限り短縮する
ECS Express Mode はデフォルトでカナリアデプロイである
ECS で簡単にコンテナアプリケーションをデプロイ可能な機能として Express Mode が登場しました。
本機能を利用すればタスク定義や ECS サービスなどの細かい設定は考慮不要となり、最低限コンテナイメージの URI さえあれば誰でもアプリケーションをデプロイできます。
また、あくまで通常の ALB と ECS を簡単に構築するという建付けなので、細かく設定したくなった場合は素の ALB や ECS としての運用に切り替え可能なことも大きな特徴です。
そんな ECS Express Mode ですが、デプロイ戦略は ECS ビルトインのカナリアデプロイ固定となり、後から変更できないという制限があります。

基本的には ECS Express Mode を構成しているリソースの設定は自由に変更可能ですが、デプロイ戦略は例外となります。
deploymentConfiguration: Canary by default - Express Mode service uses Canary deployments
Note that deployment strategy can not be updated on Express Mode services.
https://docs.aws.amazon.com/AmazonECS/latest/developerguide/express-service-work.html#express-service-service-defaults
恐らく ECS Express Mode が ECS サービスを更新する際にカナリアデプロイ前提の実装となっていると思われます。
変に変更してしまうと ECS Express Mode 経由の更新ができなくなってしまうのでしょう。
しかも、デフォルトでは Canary パーセント 5%、カナリアベイクタイム 3 分、デプロイベイクタイム 3 分のカナリアデプロイになり、単にコンテナイメージを差し替えるだけでもデプロイに 10 分近くかかります。
これはデプロイ自体が 2 段階で行われる上に、デプロイベイクタイムと呼ばれるデプロイ作業後に一定期間既存のタスクを残す時間が存在することが原因です。

また、デプロイ中にアプリケーションをテストするなら 5% 側を引き当てる必要があり、少しやり辛いです。
この辺りの挙動は少し取っ付きづらい良いに思えており、はじめて Express Mode を触った際はデフォルトのデプロイ戦略をローリングアップデートにして欲しいなと思いました。
ただ、良く考えてみると Canary パーセントとベイクタイムは変更可能なので、ローリングアップデートに近いカナリアデプロイにすることは可能です。
今回は ECS Express Mode で可能な限りシンプルなデプロイにするための方法を探ってみます。
通常の ECS のデフォルト設定だとデプロイにどの程度時間がかかるのか
通常の ECS サービスをマネジメントコンソールから作成した際のデフォルト設定は下図のようになります。

コンテナイメージが 400MB の ECS Fargate でデプロイを行ってみた結果、3 分程度で完了しました。

デプロイ時間には古い ECS タスクを削除する時間も含んでいるため、デプロイを開始してから新しい ECS タスクがターゲットグループに正常に認識されるまでだと、90 秒程度で完了しました。
イメージサイズやコンピューティング設定次第で変動しますが、おおよそのデプロイ時間としてはこのくらいだと思います。
※ より正確に言えば、minimumHealthyPercent = 0% で良いならデプロイ自体を 1 分以内で完了させることも可能です。
ただし、多くのケースでは新しいタスクのヘルスチェックが通ってから既存タスクを削除した方が望ましいと考えるため、今回は minimumHealthyPercent = 100% 前提とします。
ECS Express Mode のデフォルト設定でデプロイしてみる
Express Mode ではデフォルトの設定が下記のようになります。

この状態で ECS Express 側の設定からイメージ URI を変更してデプロイを行います。

この際、全体で 10 分 30 秒程度かかりました。

ECS ビルトインのカナリアデプロイではリスナールールの重み付けを変えながらトラフィックをコントロールします。

今回 5% 分を新しいトラフィックに流すためにリスナールール設定が変更されるまでも 3 分程度かかりました。
単純にデプロイ開始から初回デプロイ完了までの時間でもローリングアップデートより長くなってますね。
更にそこから 2 回目のデプロイ完了までベイクタイム合わせて 3 分 40 秒程度かかるので、完全に新しいバージョンに切り替わるまで 6 分 40 秒程度かかりりました。
その後、デプロイベイクタイムの 3 分程度は待機した後、既存タスクの撤去が行われてデプロイ完了となります。
今回のデプロイ時にかかった時間をまとめると下図のようになります。

また、カナリアデプロイなので、初回デプロイ後は 5 % のリクエストだけ新バージョンにトラフィックが流れます。
初回デプロイが終わった後に下記スクリプトを実行して、この挙動を確かめてみました。
※ 更新前のアプリケーションは Hello World v1、更新後のアプリケーションは Hello World v2 と返却するようにしつつ、100 回リクエストを送ってどちらが返ってくるかを判別させてます。
const https = require("https");
const url = process.argv[2];
if (!url) {
console.error("使用方法: node check.js <URL>");
console.error("例: node check.js https://example.com");
process.exit(1);
}
const requestCount = 100;
let v1count = 0;
let v2count = 0;
let done = 0;
for (let i = 0; i < requestCount; i++) {
https
.get(url, (res) => {
let data = "";
res.on("data", (chunk) => (data += chunk));
res.on("end", () => {
const hasV1 = data.includes("v1");
const hasV2 = data.includes("v2");
if (hasV1 && !hasV2) v1count++;
if (hasV2 && !hasV1) v2count++;
done++;
if (done === requestCount) {
const total = v1count + v2count;
console.log(`Result:`);
console.log(
` v1: ${v1count} (${((v1count / total) * 100).toFixed(1)}%)`
);
console.log(
` v2: ${v2count} (${((v2count / total) * 100).toFixed(1)}%)`
);
}
});
})
.on("error", (err) => {
console.error("Request error:", err);
});
}
デフォルト状態ではたしかに Canary パーセントが 5% のカナリアデプロイになってそうです。
% node check.js https://sa-c8b082120bfb4724a4a894a4fe848a48.ecs.ap-northeast-1.on.aws/
Result:
v1: 96 (96.0%)
v2: 4 (4.0%)
可能な限り簡単なデプロイ方式に変えてみる
ECS サービスの設定を変更して可能な限り簡単なデプロイ方式に変えてみます。
リソース一覧から ECS サービス詳細ページに遷移します。

「サービスを更新」をクリックします。

Canary パーセント 100% 、カナリアベイクタイム 0 分、デプロイベイクタイム 0 分 に設定して「更新」をクリックします。

この状態で再度デプロイを行うと、Service deployment rolled back because PRODUCTION_TRAFFIC_SHIFT lifecycle hook(s) failed. The weight for each target group must be between 0 and 999, inclusive というエラーでロールバックしてしまいました。
なるほど、Canary パーセント 100% は許容されないようです。

なら設定できないようにしてくれと思いつつ、Canary パーセント 5% 、カナリアベイクタイム 0 分、デプロイベイクタイム 0 分 に設定し直します。
カナリアベイクタイムが 0 分なら 2 回のデプロイは連続で行われるはずなので、時間差は 1 分にも満たないでしょう。

この状態でデプロイを行うと、無事 5 分程度で完了しました!

デプロイを開始してから新しい ECS タスクに全てのトラフィックがルーティングされるまでだと、4 分程度で完了しています(Canary ベイクタイムが実質無くなったため)。

最後に
ECS Express Mode で可能な限りデプロイフローを簡略化してみました。
Canary パーセントを 100% にできなかった分、思ったより上手くいきませんでしたがそれなりにデプロイ時間が短縮されています。
ECS Express Mode は通常の ALB と ECS とほぼ同じですが、デプロイ周りについては制限があるので注意しましょう。
今後のアップデートで ECS Express Mode 側の設定として柔軟にデプロイ戦略を選択できるようになると良いですね。







