122. CodeDeployはデプロイをライフサイクルイベントの列にする — appspecという台本と3つの舞台
デプロイという「止めて・配って・起動して・検証する」作業を、CodeDeployがどうやって固定されたイベントの列に変え、その各ステップの中身だけをappspecという台本で差し替えるのか——EC2・Lambda・ECSの3つの舞台で台本の意味がまるで違うところまで、図で追っていきます。
① appspecは「デプロイ=ライフサイクルイベントの列」という宣言
- 公式定義:「AppSpecファイルは、CodeDeploy固有のYAML形式またはJSON形式のファイル。各デプロイを、ファイル内で定義された一連のライフサイクルイベントフックとして管理するために使われる」
- イベントフックが台本に無ければ、そのイベントでは何も実行されない(no operation)。台本は「必要なイベントだけ埋める」空欄付きの進行表
CodeDeployは「デプロイの骨格(=イベントの列)」を固定し、appspecはその各ステップに何を差し込むかだけを宣言する台本。処理の枠はプラットフォーム側が持ち中身だけを外から注入する——手順(止める→配る→起動→検証)は固定で各ステップの実装だけを差し替える、テンプレートメソッドパターン(フレームワーク×フック)の関係そのものです。
② 同じ「appspec」でも、3つの舞台で意味がまるで違う
- 公式の明記:「The CodeDeploy agent is not used in an AWS Lambda or an Amazon ECS deployment.」
- EC2/オンプレのappspecの3役割:(a)リビジョンのファイルをインスタンス上のどこに置くかのマッピング(b)配置ファイルの権限指定(c)各ライフサイクルイベントで走らせるスクリプトの指定
- Lambdaのフックは BeforeAllowTraffic と AfterAllowTraffic の2つだけ
- ECSのappspecは「新task setへトラフィックを誘導する」ためのサービス名・コンテナ名・ポートを指定する
「appspec」は1つの名前だが、EC2では箱の中身を入れ替えるための配置図+スクリプト、Lambda/ECSでは新しい実体を指定して検証関数をぶら下げる指示書。EC2だけがエージェントを前提とし、他の2つはエージェント不在という決定的な違いがあります。
③ EC2 in-placeデプロイ:イベントの列を上から下へ
- 灰色(muted)=エージェント予約でスクリプト不可の公式イベント:DownloadBundle・Install・BlockTraffic・AllowTraffic(+開始/終了のStart・End)。ただしInstallの「何を置くか」だけはappspecのfilesセクションで指定できる
- LB(Classic/Application/Network Load Balancer)をデプロイグループに指定した時だけBlock/Allowの6イベントが有効。切り離し(Block)の3つは列の先頭=ApplicationStopの前、再接続(Allow)の3つは列の末尾
- エージェントの動き:現在のイベント名をhooksから探し、あればスクリプトを記載順に実行。終了コード0=成功。無ければ次のステップへ
デプロイの骨格は「(LBから外す→)止める→配る→起動→検証(→LBへ戻す)」という固定された縦の列。灰色の箱はCodeDeployが自分で握る予約席で、開発者が触れるのは白い箱=フックだけ。これはStep Functions編で見たFSM(状態遷移の宣言)の実例で、ValidateServiceで失敗すれば先へ進まない——ガード条件付きの状態遷移そのものです。
④ ApplicationStopの罠:「まだ届いていない台本」で止める
- 公式:「ApplicationStopで使われるAppSpecファイルとスクリプトは、前回、成功裏にデプロイされたリビジョンのもの」。止めるのは「今動いている古いアプリ」で、その止め方を知っているのは古い台本。新しい台本はまだ配られていない(DownloadBundleはApplicationStopの後)
- 同じルールはLB切り離しのBeforeBlockTraffic/AfterBlockTrafficにも適用される(公式:この3つのスクリプトは前回成功デプロイのAppSpecから実行され、それ以外はすべて今回のAppSpecから実行される)
- 公式の注記:インスタンスにデプロイする前はAppSpecファイルが存在しないため、ApplicationStopフックは初回デプロイでは走らない。2回目のデプロイから使える
イベント列は上から順に進むが、ApplicationStop(とBlock系2フック)だけは過去=前回リビジョンの台本で実行される。DownloadBundleより前に位置するから当然で、「新台本で古いアプリを止めようとしても、その台本はまだ手元にない」という時系列の必然。「最後に成功した状態」を記録しておき次の操作の起点にする、追記ログと同じ発想がここにもあります。
⑤ ECSのフックはBlue/Greenの検証ポイント
- 各フックは1デプロイにつき1回実行される検証用Lambda関数。ECS(とLambda)ではエージェントが使われないので、フックは「サーバー上のスクリプト」ではなく「呼び出される関数」
- 検証LambdaはputLifecycleEventHookExecutionStatusでSucceeded/FailedをCodeDeployに返す。Failedは自動ロールバック設定時のロールバックの引き金になり得る
ECSのフックは、コンテナ編で見たBlue/Greenの「新task set(Green)を作る→テスト用トラフィックで検証→本番トラフィックを切り替える」流れのそれぞれの検証ポイントそのもの。AfterAllowTestTrafficが「テストリスナで確かめる関所」、BeforeAllowTrafficが「本番切り替えの直前ゲート」——検証に失敗すれば切り替えず旧task setへ戻す、ガード付き遷移が並んでいます。
⑥ フックのタイムアウトと、EC2の「何台ずつ進めるか」
- ★DVA頻出★ HalfAtATime:9台なら最大4台ずつ配り、5台以上成功で全体成功
- デプロイ設定を指定しない場合の既定はCodeDeployDefault.OneAtATime
- EC2の設定は「minimum healthy hosts(最低healthy台数/割合)を通じて、デプロイ中つねに使用可能でなければならないインスタンス数」を規定する仕組み
デプロイ設定は「最低これだけは生かしたまま、何台ずつ入れ替えるか」という安全弁。OneAtATimeが最も慎重(既定)で、AllAtOnceが最速・最も乱暴、HalfAtATimeの「半分配れれば成功」という一見ゆるい成功判定がDVAの定番論点です。そして——EC2は「エージェントがスクリプトで箱の中身を入れ替える」ミュータブルな更新、Lambda/ECSは「新しい実体を作ってトラフィックを切り替える」イミュータブルな更新。appspecの中身の違いがそのまま2つのデプロイ哲学の違いになっている——この対比が次のBeanstalk回への橋になります。