0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CloudFormation SpotFleet で TargetCapacity を誤設定して50台起動させた話と対処法

Posted at

はじめに

Jenkins Agent用のSpotFleetをCloudFormationで管理している環境で、スタック更新時に誤って50台のEC2インスタンスが起動してしまいました。

本記事では、なぜこの問題が発生したのか、どう対処したのか、そして再発防止のために何をすべきかを記録します。

環境

  • AWS Region: us-west-2 (Oregon)
  • CloudFormation
  • Jenkins EC2 Fleet Plugin
  • SpotFleet (c8i.xlarge, t3.nano/small/medium/large)

問題の発生

発生状況

CloudFormationでSpotFleetの設定変更(複数インスタンスサイズパターンの追加)を実施し、スタック更新を実行しました。

約40分後にAWSコンソールを確認したところ、50台のEC2インスタンス(10台 × 5つのSpotFleet)が起動していました。

想定していた動作

本来、Jenkins EC2 Fleet Pluginが動的にTargetCapacityを管理する設計でした:

  • ジョブキューにジョブがあれば、必要な分だけTargetCapacityを増やす
  • ジョブがなければ、0に戻す

つまり、スタック作成・更新直後は何も起動しないはずでした。

原因

誤った設定

CloudFormationテンプレートで、以下のように設定していました:

Parameters:
  MaxTargetCapacity:
    Type: Number
    Default: 10

Resources:
  JenkinsAgentSpotFleet:
    Type: AWS::EC2::SpotFleet
    Properties:
      SpotFleetRequestConfigData:
        TargetCapacity: !Ref MaxTargetCapacity  # ← 問題の設定
        IamFleetRole: !GetAtt SpotFleetRole.Arn
        AllocationStrategy: lowestPrice
        # ... その他の設定

TargetCapacity: !Ref MaxTargetCapacity により、スタック作成・更新時に各SpotFleetが10台ずつ起動する設定になっていました。

正しい設定

Jenkins EC2 Fleet PluginがTargetCapacityを管理する場合、CloudFormation側の初期値は0にすべきでした:

Resources:
  JenkinsAgentSpotFleet:
    Type: AWS::EC2::SpotFleet
    Properties:
      SpotFleetRequestConfigData:
        TargetCapacity: 0  # Jenkins EC2 Fleet Plugin に管理を委ねる
        IamFleetRole: !GetAtt SpotFleetRole.Arn
        AllocationStrategy: lowestPrice
        # ... その他の設定

対処法

1. SpotFleetのTargetCapacityを0に設定

まず、AWS CLIで直接SpotFleetのTargetCapacityを0に設定しました:

# SpotFleetリクエストIDを取得
aws ec2 describe-spot-fleet-requests \
  --query 'SpotFleetRequestConfigs[*].[SpotFleetRequestId,SpotFleetRequestState]' \
  --output table

# 各SpotFleetのTargetCapacityを0に設定
aws ec2 modify-spot-fleet-request \
  --spot-fleet-request-id sfr-xxxxxxxxx \
  --target-capacity 0

5つのSpotFleetすべてに対して実行しました。

2. 起動中のインスタンスを終了

TargetCapacityを0にしても、既に起動しているインスタンスはすぐには停止しないため、手動で終了させました:

# 起動中のインスタンスIDを取得
aws ec2 describe-instances \
  --filters "Name=instance-state-name,Values=running" \
  --query 'Reservations[*].Instances[*].[InstanceId,InstanceType]' \
  --output table

# インスタンスを一括終了
aws ec2 terminate-instances \
  --instance-ids i-xxxxxxxxx i-yyyyyyyyy i-zzzzzzzzz ...

3. CloudFormationテンプレートを修正

Resources:
  JenkinsAgentSpotFleet:
    Type: AWS::EC2::SpotFleet
    Properties:
      SpotFleetRequestConfigData:
        TargetCapacity: 0  # Jenkins EC2 Fleet Plugin に管理を委ねる
        IamFleetRole: !GetAtt SpotFleetRole.Arn
        AllocationStrategy: lowestPrice
        LaunchTemplateConfigs:
          - LaunchTemplateSpecification:
              LaunchTemplateId: !Ref JenkinsAgentLaunchTemplate
              Version: !GetAtt JenkinsAgentLaunchTemplate.LatestVersionNumber
            Overrides:
              - InstanceType: c8i.xlarge
              - InstanceType: c7i.xlarge
              - InstanceType: c6i.xlarge

コメントも追加して、なぜ0なのかを明記しました。

4. スタック更新を再実行

修正したテンプレートでスタック更新を実行し、インスタンスが起動しないことを確認しました。

コスト影響

約50分間(気づくまで40分 + 対応10分)、50台が稼働しました。

推定コスト(Spot料金):約 $1.41 USD

内訳:

  • c8i.xlarge × 10台:$0.57
  • t3.nano × 10台:$0.009
  • t3.small × 10台:$0.037
  • t3.medium × 10台:$0.073
  • t3.large × 10台:$0.147

Spot料金のため、オンデマンドと比較して大幅に安く済みました。

再発防止策

今回の問題は、複数のチェックポイントをすり抜けた結果でした。

1. デフォルト値を「安全側」に設定

容量・サイズに関するパラメータは、デフォルト値を0または最小値にします:

Resources:
  AutoScalingGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      MinSize: 0
      MaxSize: 10
      DesiredCapacity: 0  # 初期状態では何も起動しない

2. 重要パラメータのレビュー徹底

差分が大きい場合でも、以下のパラメータは必ず確認します:

  • TargetCapacity
  • MinSize / MaxSize / DesiredCapacity
  • その他、インスタンス数に影響するパラメータ

3. スタック更新完了の通知設定

EventBridgeとSNSで、スタック更新完了を自動通知します:

Resources:
  NotificationTopic:
    Type: AWS::SNS::Topic
    Properties:
      Subscription:
        - Endpoint: your-email@example.com
          Protocol: email

  StackUpdateNotificationRule:
    Type: AWS::Events::Rule
    Properties:
      EventPattern:
        source:
          - aws.cloudformation
        detail-type:
          - CloudFormation Stack Status Change
        detail:
          stack-id:
            - !Ref AWS::StackId
          status-details:
            status:
              - UPDATE_COMPLETE
              - UPDATE_ROLLBACK_COMPLETE
      Targets:
        - Arn: !Ref NotificationTopic
          Id: NotificationTarget

4. 確認の習慣化

通知が来たら、すぐにAWSコンソールまたはCLIで確認する習慣を作ります。

まとめ

  • Jenkins EC2 Fleet Pluginと連携するSpotFleetの初期TargetCapacityは0にする
  • デフォルト値は「安全側」に倒す(何もしない状態が安全)
  • スタック更新完了の通知を設定し、確認を忘れない仕組みを作る
  • 複数のチェックポイントを設けることで、一つが失敗しても他でカバーできる

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?