40分後に気づいた
CloudFormationのスタック更新を実行して、別の作業に移りました。
40分後、ふとAWSコンソールを開いたら、EC2インスタンスが10台ずつ、合計50台起動していました。
「え?」
最初は状況が理解できませんでした。なんでこんなにインスタンスが立ってるんだ?
次の瞬間、理解しました。
「あぁ、やっちゃった」
焦りというより、「まずいな」という冷静な認識が先に来ました。これまでいろんな経験をしてきたので、このくらいでパニックにはなりません。でも、まずい。さっさと止めて、直さなければ。
そして、じわじわと広がる後悔。
「なんで確認しなかったんだ」
気になったのは、やっぱりコストでした。スポットインスタンスとはいえ、50台が40分。これ、いくらになるんだろう。
何が起きたのか
今回の作業は、Jenkins Agentとして使うSpotFleetに、複数のインスタンスサイズパターン(t-nano、t-small、t-medium、t-large、c-xlarge)を追加するものでした。
Claude Codeと対話しながら、CloudFormationテンプレートを修正していました。差分は大きく、LaunchTemplateが増え、SpotFleetリソースが5つに分かれ、パラメータも追加されました。
CloudFormationテンプレートで5つのSpotFleetリソースを定義し、それぞれに以下のような設定を書いていました:
TargetCapacity: !Ref MaxTargetCapacity
MaxTargetCapacity パラメータは 10 に設定していました。つまり、各SpotFleetが10台ずつインスタンスを起動する設定になっていたのです。
本来、この構成では Jenkins EC2 Fleet Plugin がSpotFleetのTargetCapacityを動的に管理する設計でした。
- ジョブキューにジョブがあれば、Pluginが必要な分だけTargetCapacityを増やす
- ジョブがなければ、Pluginが0に戻す
つまり、CloudFormationテンプレート側で初期TargetCapacityを設定すべきではありませんでした。正しくは:
TargetCapacity: 0
TargetCapacityについては、知っていました。でも、見落としました。
スタック更新を実行した後、別の作業に移りました。CloudFormationは時間がかかります。その間、他の作業をするのは自然な流れでした。
40分後、確認しました。
50台起動していました。
小さなミスの積み重ね
今回のトラブルを振り返って、気づいたことがあります。
これは、単一のミスではありませんでした。
複数のチェックポイントをすり抜けた結果でした。
すり抜けたチェックポイント
1. 設計段階:デフォルト値を「安全側」に倒す
テンプレート設計時に、TargetCapacity: 0 というデフォルト値を設定していませんでした。「安全側」に倒すという設計思想が欠けていました。
ここでデフォルト値を0にしていれば、そもそも起動しませんでした。
2. レビュー段階:AI生成コードの重要パラメータを確認
差分が大きすぎて、細かくレビューする気力がありませんでした。「まあ大丈夫だろう」と思って流してしまいました。
ここでTargetCapacityの設定に気づいていれば、防げました。
3. 実行後段階:スタック更新完了の通知を設定
スタック更新完了の通知を設定していませんでした。EventBridgeやSNSを使えば、スタック更新完了を自動通知できます。
ここで通知があれば、確認を忘れませんでした。
4. 確認段階:通知後の即座確認を習慣化
スタック更新を実行した後、すぐに確認すればよかったのですが、CloudFormationは時間がかかるため、その間、他の作業に移りました。
ここで確認していれば、もっと早く気づけました。
どれか一つでもあれば、防げた
これらのチェックポイントのうち、どれか一つでも機能していれば、50台起動は防げました。
でも、すべてがすり抜けました。
小さなミスの積み重ねが、大きなトラブルになりました。
こういうトラブルは、いつもそうです。単一のミスで起きるわけではありません。複数のチェックポイントをすり抜けて、初めて起きるのです。
緊急対応:AI駆動開発の「凄さ」を知る
50台起動を確認した瞬間、頭の中でいくつかの選択肢が浮かびました。
- CloudFormationテンプレートを修正して、再度スタック更新する
- AWS CLIで直接SpotFleetのTargetCapacityを0にする
- コンソールから手動でインスタンスを終了する
「まず止める」
これを最優先にしました。
ここで、Claude Codeに対応を依頼しました。
「50台起動してしまった。まずSpotFleetのTargetCapacityを0にして、起動中のインスタンスを終了させたい」
Claude Codeは、丁寧に実行条件を整理してから対応してくれました。
まず、起動中のSpotFleetリクエストをすべて確認。次に、各SpotFleetのTargetCapacityを0に設定するコマンドを正確に生成してくれました:
aws ec2 modify-spot-fleet-request \
--spot-fleet-request-id sfr-xxx \
--target-capacity 0
5つのSpotFleetすべてに対して、正確なIDを指定して実行。
次に、起動中のインスタンスIDをすべて取得して、一括で終了させるコマンドを生成してくれました:
aws ec2 terminate-instances \
--instance-ids i-xxx i-yyy i-zzz ...
このプロセスが、驚くほど早かったです。
自分でやろうとしたら、SpotFleetのIDを確認して、コマンドを書いて、実行して、インスタンスIDを確認して、また書いて、実行して...という手順を踏む必要がありました。ミスも起きるかもしれません。
でも、Claude Codeは正確にコマンドを書いて、実行条件を整理してから実行してくれました。人よりも早い。
リカバリースピードは、ものすごく早かったです。
止まりました。
AI駆動開発の両面性
今回の経験で、AI駆動開発の「怖さ」と「凄さ」を同時に知りました。
怖さ:
- 差分が大きいと、レビューしきれない
- 「大丈夫だろう」と流してしまう
- 知っていることでも、見落とす
凄さ:
- リカバリーが早い
- コマンドを正確に書いてくれる
- 実行条件を整理してから実行してくれる
AI駆動開発は、失敗も引き起こすし、リカバリーも助けてくれます。
テンプレートを修正する
緊急対応が終わったら、次はCloudFormationテンプレートの修正です。
TargetCapacity: 0 # Jenkins EC2 Fleet Plugin に管理を委ねる
コメントも追加しました。「なぜ0なのか」を明記しておかないと、また同じミスをするかもしれません。
修正をコミットして、再度スタック更新を実行しました。今度は、画面を見ながら確認しました。インスタンスが起動しないことを確認するまで。
起動しませんでした。
ほっとしました。
コストはどうなったか
落ち着いてから、気になっていたコストを計算してみました。
稼働状況:
- 合計インスタンス数:50台
- 稼働時間:約50分(気づくまで40分 + 対応に10分)
- 価格体系:Spot料金
内訳(us-west-2、Spot料金):
- c8i.xlarge × 10台:$0.0680/時間 × 10 × 0.833時間 = $0.57
- t3.nano × 10台:$0.0011/時間 × 10 × 0.833時間 = $0.009
- t3.small × 10台:$0.0044/時間 × 10 × 0.833時間 = $0.037
- t3.medium × 10台:$0.0088/時間 × 10 × 0.833時間 = $0.073
- t3.large × 10台:$0.0176/時間 × 10 × 0.833時間 = $0.147
推定コスト:約 $1.41 USD
Spot料金のおかげで、予想していたよりずっと安く済みました。
「$1.41で済んだ」と思った瞬間、肩の力が抜けました。でも同時に、「これでいいのか?」という疑問も浮かびました。
$1.41で済んだから、学びが薄れるんじゃないか。
もし気づくのがもっと遅れていたら。もしオンデマンドインスタンスだったら。もし本番環境だったら。
考えたくないですが、考えなければいけません。
次に向けて:チェックポイントを増やす
今回のトラブルは、複数のチェックポイントをすり抜けた結果でした。
では、次はどうすればよいのでしょうか。
チェックポイントを増やせばよいのです。
具体的なチェックポイント
1. 設計段階:デフォルト値を「安全側」に倒す
TargetCapacity、MinSize、MaxSizeなどのパラメータは、デフォルト値を0または最小値にします。「何もしない」が安全な状態にするのです。
2. レビュー段階:重要パラメータを必ず確認する
差分が大きいときこそ、丁寧にレビューします。特に、容量やサイズに関するパラメータは必ず確認します。
3. 実行後段階:スタック更新完了の通知を設定する
EventBridgeとSNSを使って、スタック更新完了を自動通知します。Slackやメール、スマホの通知でもよいでしょう。
4. 確認段階:通知が来たら即座に確認する
CloudFormationは時間がかかるので、他の作業に移るのは仕方ありません。でも、通知が来たらすぐに確認する。それを習慣にします。
一つが失敗しても、他でカバーできる
チェックポイントを増やす意味は、一つが失敗しても、他でカバーできるようにすることです。
今回の教訓は:
- 小さなミスの積み重ねで、大きなトラブルになる
- チェックポイントを増やせば、どれか一つでも機能すれば防げる
- AI駆動開発でも、最終判断は自分がする
- デフォルト値は「安全側」に倒す
- AIはリカバリーの強力な味方になる
そして、もう一つ。
知っていることと、実践することは違う。
TargetCapacityについては知っていました。でも、見落としました。知識と実践の間には、常にギャップがあります。
そのギャップを埋めるのは、チェックポイントです。
この記事で触れた「チェックポイントを増やす」という考え方は、SRE(Site Reliability Engineering)の基本的な思想と通じます。Googleの経験に基づくこの書籍では、障害を前提としたシステム設計や、人間のミスを仕組みでカバーする方法が体系的に解説されています。
[📦 商品リンク: moshimo-book-sre-google]
クラウドインフラを扱う上で、サーバやネットワークの基礎知識は欠かせません。SpotFleetやCloudFormationを使いこなすためにも、こちらの書籍で基本を押さえておくことをお勧めします。
[📦 商品リンク: moshimo-book-server-infra]
AWS運用の基本から実践的なノウハウまで体系的に学べる一冊です。EC2やSpotFleetの適切な設定方法を理解し、今回のようなインシデントを未然に防ぐために役立ちます。
[📦 商品リンク: moshimo-book-aws-operations]
次はもっとうまくやれるはずだ(でも本当にそうだろうか)
$1.41で済んだことに、安堵している自分がいます。でも、それでいいのでしょうか。
いや、この経験を活かして、次に繋げていきたいと思います。
チェックポイントを増やしていきます。
- デフォルト値を「安全側」に倒すテンプレート設計を心がけます
- 重要パラメータのレビューを徹底します
- EventBridgeで通知を飛ばすように設定します
- 通知が来たら即座に確認する習慣を作ります
一つが失敗しても、他でカバーできる。
次は、もっとうまくやれるはずです。
でも、本当にそうでしょうか。
また忘れた頃に、同じことを繰り返すんじゃないか。
チェックポイントを増やすと決めても、実際に設定するのは面倒です。EventBridgeの設定を後回しにして、そのまま忘れるかもしれません。通知が来ても、「あとで確認しよう」と思って、また40分後になるかもしれません。
人間は忘れる生き物です。そして、「大丈夫だろう」と油断する生き物です。
今回の教訓を活かせるかどうかは、結局、未来の自分次第です。
未来の自分を、どこまで信じられるか。
その問いに、まだ答えは出ていません。