背景
AWS CDKを使用してみたかったため、Claude君に色々提案してもらいながら
Fargateによるマイクロサービスをデプロイするという作業を進めていました。
問題の発生
症状
-
cdk deploy
が82/86(ECS Service作成段階)で30分以上停止してしまった - CloudFormationスタックがCREATE_IN_PROGRESSのまま停滞
- 試しに別ターミナルを開き、ECRにDockerイメージをプッシュすると、停止していたデプロイが突然再開・完了
原因
「ECRイメージが存在しない状態でdesiredCount
を1にしていたため」
this.service = new ecs.FargateService(this, "Service", {
...
desiredCount: 1, // 1つのタスクを起動する
...
});
cdk deploy
時に、空のECRリポジトリが作成されますが、この時点ではまだイメージのプッシュは完了していません。
その後のフェーズで、ECSサービスが存在しないイメージを参照しようとし、待機状態となっていました。
そりゃ無い物を必死に探しても無いわけです。そこに無ければ無いですね。
タスク起動にはECRからのイメージ取得が必要ですが、
イメージがまだ存在していない状態だと、参照できず起動失敗となります。
よって、CloudFormationが無期限に待機してしまう状態に陥ります...。
本来求められる順序としては、
1. ECRリポジトリ作成(空)(初回`cdk deploy`)
2. Dockerイメージプッシュ
3. ECS Service作成(`cdk deploy`)
が正しいですが、今回は一回のcdk deploy
の中で
1. ECRリポジトリ作成(空)
2. ECS Service作成 ⇒ イメージなしのため失敗 ⇒ 待機
3. (手動)別ターミナルでDockerイメージをプッシュ
4. イメージが参照できるようになったため解決
という流れになっていたため、正しくデプロイが出来ませんでした。
CloudFormationの待機状態について
これに関しては公式re:Postに記載がありました。
Amazon ECS で作成したサービスは、AWS CloudFormation テンプレートで指定された状態から外れると安定化できません。サービスが目的のタスク定義を使用して必要な数のタスクを起動したことを確認するために、AWS CloudFormation は継続的に DescribeServices API を呼び出します。これらの呼び出しにより、目的の状態が達成されるまでサービスの状態を確認します。呼び出し処理には最大 3 時間かかる場合があります。
今回のケースに当てはめると、
1. イメージが存在しない → タスクが起動できない
2. 「目的の状態」が達成されない → CloudFormationが待機継続
3. 手動でイメージプッシュ → タスク起動成功 → 待機解除
という感じになります。
解決方法
1. 初回デプロイ時(ECRリポジトリ作成時)にはdesiredCount
を0にする。
this.service = new ecs.FargateService(this, "Service", {
...
- desiredCount: 1, // 1つのタスクを起動する
+ desiredCount: 0, // タスク起動なし
...
});
2. デプロイを実施する。
cdk deploy
3. イメージをプッシュしておく
docker push ...
4. desiredCount
を1にする
this.service = new ecs.FargateService(this, "Service", {
...
- desiredCount: 0, // タスク起動なし
+ desiredCount: 1, // 1つのタスクを起動する
...
});
5. 再度デプロイ
cdk deploy
最後に
生成AIも発達し、人がコーディングに使う時間も大きく減りつつある世の中ですが、
一気にサービスを作ったからと言って一度に全部をデプロイしようとせず、段階的なアプローチを取ることが重要だということを学びました。
また、AWS、CDKは触り始めて日が浅いですが、デプロイ時の処理順序やCloudFormationの安定化戦略など、少しレイヤーが深い部分を知見として得ることができたので、今回の問題に直面してラッキーだったと思います。
もし、補足や誤りのある部分がありましたら、遠慮なくご指摘ください!
参考