はじめに
どーも!shihopowerです!
今回はAWSのライフサイクルフック(Lifecycle Hooks) についてお話します。
AWS Certified Solutions Architect - Professional(SAP)の対策を進めていたところ、ライフサイクルフックに関する模擬問題に出くわしました。この機能、私にとってははじめましてだったので「いったい何者なんだ?」と気になり、調べてみることに。
調べてみるとEC2 Auto Scalingを使いこなす上で重要な機能だったので、同じくSAP対策中の方や、Auto Scalingを運用している方の役に立てばと思い、記事にまとめます。
目次
- きっかけとなった模擬問題の紹介
- ライフサイクルフックとは何か
- なぜライフサイクルフックが必要なのか
- まとめ:ライフサイクルフックの要点
1. きっかけとなった模擬問題の紹介
まずは、私がライフサイクルフックと出会うことになった模擬問題のシナリオをざっくり紹介します。
問題のシナリオ(要約)
・Auto Scalingグループの背後にあるEC2インスタンスでアプリを運用している
・負荷の変動に応じてスケールイン・スケールアウトが頻繁に発生する
・EC2インスタンス上のログファイルは60分ごとにS3バケットへコピーする運用
・ところが、終了したインスタンスのログが一部欠落していることが発覚
・終了したEC2インスタンスのログを確実にS3へコピーするにはどうすればよいか?
この問題の本質
問題の本質はとてもシンプルです。
ログのコピー周期(60分)よりも前にインスタンスが終了してしまうと、最後のログがS3に転送されないまま消えてしまう
例えば、ログを15分ぶん貯めたタイミングでスケールインが発生したら、その15分ぶんのログは永遠に失われます。これでは監査もトラブルシュートもできません。
そこで必要になるのが「インスタンスを終了させる前に、ログをS3にアップロードしてから終了させる」仕組みです。
そして、この仕組みを実現するためにAWSが用意しているのが、今回の主役 ライフサイクルフック なのです。
2. ライフサイクルフックとは何か
一言で言うと
EC2インスタンスの起動・終了の途中に「待った!」をかけて、カスタム処理を差し込める機能
これがライフサイクルフックです。
通常のAuto Scalingでは、スケールアウト時はサクッとインスタンスが起動してサービスインし、スケールイン時はサクッとインスタンスが終了します。この「サクッと」の間に処理を挟みたいときに、ライフサイクルフックが活躍します。
インスタンスの状態遷移
ライフサイクルフックを使ったときの状態遷移は以下のようになります。
スケールアウト時(起動時)
Pending → Pending:Wait → Pending:Proceed → InService
↑
ここでカスタム処理を実行
(例:ソフトウェアインストール、設定ファイル配置)
スケールイン時(終了時)
InService → Terminating → Terminating:Wait → Terminating:Proceed → Terminated
↑
ここでカスタム処理を実行
(例:ログをS3に退避、接続のドレイン)
Pending:WaitやTerminating:Waitという待機状態で処理を実行できるのがポイントです。
2種類のライフサイクルトランジション
ライフサイクルフックには2種類しかありません。シンプル!
| トランジション名 | いつ発火? | 主な用途 |
|---|---|---|
autoscaling:EC2_INSTANCE_LAUNCHING |
インスタンス起動時 | ソフトウェアインストール、ヘルスチェック待ち |
autoscaling:EC2_INSTANCE_TERMINATING |
インスタンス終了時 | ログ退避、接続切断、状態保存 |
今回の問題で使うのは、もちろん下の EC2_INSTANCE_TERMINATING です。
待機時間はどれくらい?
デフォルトでは1時間(3600秒) 待機できます。
それでも足りない場合は RecordLifecycleActionHeartbeat APIを呼べばタイマーを延長できますが、最大でも48時間までとなっています。
通知先(カスタム処理の起動方法)
インスタンスが待機状態に入ったとき、以下のいずれかに通知を飛ばせます。
- Amazon EventBridge(推奨)→ Lambdaを起動するのが一般的
- Amazon SNS トピック
- Amazon SQS キュー
3. なぜライフサイクルフックが必要なのか
「ユーザーデータやシャットダウンスクリプトでいいのでは?」問題
ライフサイクルフックを調べていて、最初に湧いた疑問がこれでした。
ユーザーデータとは
ユーザーデータ(User Data) は、EC2インスタンスの初回起動時に1回だけ自動実行されるスクリプトを仕込める機能です。起動テンプレートや起動設定に書いておくと、cloud-initが起動時に実行してくれます。Apacheのインストール、設定ファイルの配布、cronの登録など、初期セットアップに使われるのが定番です。
ただし起動時しか動かないのがポイントで、終了時の処理には使えません。
シャットダウンスクリプトとは
シャットダウンスクリプトは、OSがシャットダウンする際に実行されるスクリプトのことです。Linuxなら/etc/rc0.dやsystemdのサービス停止フック、Windowsならグループポリシーのシャットダウンスクリプトなどで実現します。一見「終了時の処理」に使えそうに見えます。
なぜこれらでは不十分なのか
スケールイン時、Auto Scalingは容赦なくインスタンスを終了させます。シャットダウンスクリプトでログをS3へアップロードしようとしても、以下の問題があります。
- S3アップロードが終わる前にインスタンスが消滅する可能性がある(OSのシャットダウンタイムアウトは短い)
- Auto Scaling側はスクリプトの完了を待ってくれない
- ネットワークインターフェースが先にデタッチされると、そもそもS3に到達できない
ライフサイクルフックを使えば、Terminating:Wait状態で最大48時間待たせられるので、確実に処理を完了させてから終了できます。Auto Scalingに対して「処理中だから待って」と明示的に伝えられるのが、ユーザーデータやシャットダウンスクリプトとの決定的な違いです。
Auto Scalingに「処理中だから待って」と伝える仕組み
処理が完了したら CompleteLifecycleAction APIを呼んで、結果を CONTINUE または ABANDON で返します。
ABANDON と CONTINUE の違い
ここはSAP試験で混同しやすいポイントなので強調します。
終了時(TERMINATING)の動作
| 結果 | 挙動 |
|---|---|
CONTINUE |
終了する(他のライフサイクルフックも実行してから) |
ABANDON |
終了する(残りのフックはスキップして即終了) |
重要:終了時はどちらを送ってもインスタンスは終了します。
「ABANDONを送れば終了を防げる」という解釈は完全な誤りです。
起動時(LAUNCHING)の動作
| 結果 | 挙動 |
|---|---|
CONTINUE |
カスタム処理が成功 → InServiceへ進める |
ABANDON |
カスタム処理が失敗 → インスタンスを破棄して再作成 |
起動時はCONTINUE/ABANDONで結果が大きく変わります。ここは混同しないように注意です。
Lambda + SSM Run Command との組み合わせ
ライフサイクルフックは単体ではただの「待機状態」を作るだけなので、その間に実際の処理を実行する仕組みとセットで使います。AWS公式ブログでも紹介されている定番構成が、Lambda + Systems Manager Run Commandの組み合わせです。
全体の流れ
[スケールイン発生]
↓
[ライフサイクルフックがインスタンスを Terminating:Wait に]
↓
[EventBridgeが検知]
↓
[Lambda起動]
↓
[SSM SendCommand でインスタンス側のスクリプト実行]
↓
[ログをS3にコピー]
↓
[Lambda が CompleteLifecycleAction で CONTINUE 送信]
↓
[インスタンス正常終了]
Systems Manager Run Command(SendCommand API)とは
AWS Systems Manager Run Command は、EC2インスタンスに対してSSHやRDPを使わずにコマンドやスクリプトをリモート実行できる機能です。インスタンスに事前にSSM Agent(Amazon Linux 2やWindows ServerのAMIにはプリインストール済み)が動いていて、適切なIAMロールが付与されていれば、LambdaなどからSendCommand APIを呼ぶだけで任意のコマンドを実行できます。
実行するスクリプトは「SSMドキュメント」というJSON/YAML形式の定義としてあらかじめ登録しておきます。例えば「/var/log/app/配下のログファイルをaws s3 cpコマンドで指定のS3バケットにアップロードする」というシェルスクリプトをSSMドキュメントに記述しておけば、Lambdaからは「このドキュメントをこのインスタンスIDで実行して」と指示するだけでOKです。
この構成のメリット
- エージェント追加不要:SSM Agentは多くの公式AMIに最初から入っているので、追加インストールが不要
- SSH不要・セキュア:踏み台もSSHキー管理も不要。IAMで権限管理できる
- スクリプトの一元管理:スクリプトはSSMドキュメントとして管理するので、変更時にAMIを焼き直す必要がない。Gitで管理してCI/CDから更新することも可能
- 実行ログが自動的に残る:実行結果はCloudWatch LogsやS3に自動転送できるので、トラブルシュートしやすい
- 複数インスタンスへの一斉実行も可能:今回のユースケースとは別ですが、タグ指定で一括実行できる柔軟性もある
Lambdaの役割
Lambdaは「司令塔」として動きます。具体的には以下を担当します。
- EventBridgeから受け取ったイベントから、終了対象のインスタンスIDとライフサイクルアクショントークンを取り出す
- SSM
SendCommandAPIを呼び、対象インスタンスでログコピー用のSSMドキュメントを実行する - コマンドの実行状態をポーリングまたは別のイベントで監視する
- 完了したら
CompleteLifecycleActionAPI をCONTINUEで呼び出し、Auto Scalingに「処理が終わったので終了させてOK」と通知する
この一連の処理をLambdaにまとめておけば、運用者は触らなくても自動でログ退避が完了します。
4. まとめ:ライフサイクルフックの要点
長くなったので、要点を絞ってまとめます。
機能のポイント
- ライフサイクルフックは、EC2 Auto Scalingの起動・終了の途中に「待ち時間」を作って、カスタム処理を差し込める機能
- トランジションは2種類だけ:
EC2_INSTANCE_LAUNCHINGとEC2_INSTANCE_TERMINATING - デフォルトの待機時間は1時間、最大48時間まで延長可能
使い方のポイント
- 通知先はEventBridge / SNS / SQSから選べる。EventBridge → Lambda → SSM SendCommand の組み合わせが王道
- 処理完了は
CompleteLifecycleActionAPIで通知する - スクリプトはSystems Managerドキュメントで管理するとメンテしやすい
試験で間違えやすいポイント
- 終了時のABANDONは「終了を防ぐ」ものではなく、「残りのフックをスキップして即終了させる」シグナル
- 終了時はABANDONもCONTINUEもどちらも結局インスタンスは終了する
- 起動時のABANDONはインスタンスを破棄して再作成する
おわりに
いかがでしたか。この記事を通してライフサイクルフックに関する理解が深まっていれば幸いです。
最後まで読んでいただきありがとうございました 🙌