CloudFormationの「破壊」を制御せよ:DeletionPolicyとDependsOnで挑む無停止リソース置換の極意
多くのエンジニアがCloudFormationの「リソース置換(Replacement)」の恐怖に震えた経験があるはずだ。`UpdateReplacePolicy`や`DeletionPolicy`を安易に設定し、本番環境のDBやELBがスタック更新の瞬間に消し飛ぶ……。これはIaCにおける「最大級の事故」の一つだ。
しかし、真のSREはこれを「事故」とは呼ばない。「制御可能なイベント」として設計する。今日は、CloudFormationの深い内部挙動を逆手に取り、Blue/Greenデプロイ的なリソース置換を安全に遂行するための、極めて実戦的なテクニックを伝授する。
—
1. なぜ「単純な置換」では不十分なのか
CloudFormationはリソースの更新時、`Replacement`フラグが立つと「Create New -> Delete Old」という順序で処理を行う。問題は、この「Delete」のタイミングをAWSの抽象化されたAPIが裏側で決定している点だ。
我々が求めるのは、「新リソースの健全性を確認した後に、旧リソースを意図的に解放する」という、厳密なトランザクション制御である。
2. 鋼の依存関係:DependsOnと論理IDのトリック
リソース置換を制御する鍵は、CloudFormationのグラフ構造を意図的に操作することにある。特定の条件下で新リソースを先に立ち上げ、旧リソースの廃棄をスタックのライフサイクルから切り離す手法がこれだ。
実践:安全なリソース置換パターン
例えば、RDSやDynamoDBのテーブルを無停止で置き換える場合、名前を変えた新しいリソースを作成し、旧リソースを「削除不可(Retain)」にするのが定石だ。
Resources:
# 新しいDBインスタンス
MyDatabaseNew:
Type: AWS::RDS::DBInstance
Properties:
DBInstanceClass: db.r6g.large
# …詳細設定
# 削除ポリシーをRetainにしておくことで、万が一のロールバック時もデータが残る
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
# 旧リソースを明示的に依存させて、更新フローを制御する
MyDatabaseOld:
Type: AWS::RDS::DBInstance
DependsOn: MyDatabaseNew # 新環境が確実に立ち上がってから旧を始末する
Properties:
# …
DeletionPolicy: Snapshot # 最後にバックアップを取ってから消す
Outputs:
DatabaseEndpoint:
Value: !GetAtt MyDatabaseNew.Endpoint.Address
この設計の深淵
- `UpdateReplacePolicy: Retain`: これが重要だ。Stack更新中にエラーが発生しても、CloudFormationは旧リソースを削除しない。
- `DependsOn`の多段活用: APIリクエストの順序を強制することで、アプリケーションの接続先切り替え(DNSやAppConfigの更新)が完了するまで旧リソースを生存させ続けることが可能になる。
—
3. CLI/APIを駆使した「ステートレス・オーケストレーション」
CloudFormation単体で完結しない場合、最終兵器は「独自のライフサイクル管理スクリプト」だ。私は、CloudFormationを単なるプロビジョニングエンジンとして使い、その上の「切り替え」をAWS SDK (boto3) で制御するアプローチを強く推奨する。
究極の自動化:冪等性を担保するPythonラッパー
import boto3
def safe_replace_and_switch(stack_name, new_template_body):
cf = boto3.client(‘cloudformation’)
# 1. 変更セットの作成と検証 (ChangeSet)
# ここで「Replacement」が発生するかを事前に解析する
changeset = cf.create_change_set(
StackName=stack_name,
TemplateBody=new_template_body,
ChangeSetName=’deployment-v2′
)
# 2. 変更内容を解析し、破壊的変更が含まれる場合のみフックをかける
# ここで「リソースの削除」が含まれるなら、事前にSnapshotを取得する等のロジックを挿入
# 3. 実行
cf.execute_change_set(ChangeSetName=’deployment-v2′, StackName=stack_name)
# 4. ヘルスチェック(ここが重要)
# リソース置換完了後、即座にアプリケーションの疎通を確認し、失敗ならロールバックをトリガー
—
4. エンジニアへの極限のアドバイス
1. Stateの外部化: リソースの「物理ID」をCloudFormationに依存させるな。SSM Parameter StoreやAppConfigを使い、リソースの所在を抽象化せよ。CloudFormationがリソースを入れ替えても、アプリケーション側は「参照先を切り替えるだけ」の状態にする。
2. DeletionPolicyの自動付与: 私はCI/CDパイプラインのプリプロセッサ(cfn-lintのカスタムルール)で、本番環境の重要リソースに`DeletionPolicy: Retain`が設定されていない場合はCIを落とすようにしている。人間を信用するな、コードで縛れ。
3. メモリ消費の最適化: テンプレートが巨大になりすぎると、CloudFormationの解析フェーズでメモリ消費が跳ね上がる。スタックを論理単位でネストさせよ。ただし、`Nested Stacks`の循環参照には細心の注意を払うこと。
結びに代えて
CloudFormationを使いこなすということは、AWSが提供する「不可視のAPI制御」を理解することに他ならない。`DeletionPolicy`と`DependsOn`は単なる設定値ではない。それは、「ビジネスの継続性」という、エンジニアが守るべき最後の砦を担保するための防波堤である。
君たちがコードを書くとき、その一行が「本番環境の破壊」を招くのか、「無停止のアップデート」を実現するのか。その責任を、論理IDの一つひとつに刻み込んでほしい。