📝

Auto Scalingライフサイクルフックを利用して、AMIを取得する方法

2021/06/25に公開

概要

Auto Scalingグループ内のEC2インスタンスが起動、終了する際、一時的に起動、終了処理を停止し、その間にソフトウェアのインストールやログの退避などを行うことができます。

Amazon EC2 Auto Scaling は、Auto Scaling グループにライフサイクルフックを追加する機能を提供します。これらのフックにより、Auto Scaling グループは Auto Scaling インスタンスライフサイクルのイベントを認識し、対応するライフサイクルイベントが発生したときにカスタムアクションを実行できます。ライフサイクルフックは、インスタンスが次の状態に移行する前に、ライフサイクルアクションを完了するための指定された時間(デフォルトでは 1 時間)を提供します。
Amazon EC2 Auto Scaling ライフサイクルフック

こちらを利用して、予期せぬスケールイン(インスタンス終了)が発生した際に、障害調査用のAMIを取得する流れを実装することができたので紹介します。

きっかけ

あるプロジェクトのAuto Scalingグループで予期せぬスケールインが発生したのですが、AMIの取得やログの退避をしておらず、障害調査ができなかったことがありました。

要件

  • Auto ScalingグループはCloudFormationによって自動作成される
  • Auto Scalingグループに手動で変更を加えない
  • 予期せぬスケールイン発生時のみAMIを取得し、予定されたリリースなどによるCloudFormation実行時にはAMIを取得しない

実装方法

イメージ

全体の流れは以下の図の通り。

  1. Auto ScalingグループのイベントをEventBridgeで捕捉
  2. EventBridgeからAMI取得用のLambda関数を発火
  3. Lambda関数から終了しようとしているインスタンスに対してAMI取得リクエスト

具体的な実装

要件のうち3つ目の

予期せぬスケールイン発生時のみAMIを取得し、予定されたリリースなどによるCloudFormation実行時にはAMIを取得しない

をどのように判定するかで悩みましたが、サポートに

S3,EFS,DynamoDBなどにリリース時かそうでないかを示す情報を記録しておき、Lambdaから都度参照することで判定する

という方法を提案して頂き、これをヒントに以下のような実装としました。

  • ライフサイクルフックに、インスタンス起動時と終了時のイベントを両方設定
  • EventBridgeで起動と終了のイベントを取得し、Lambdaに渡す
  • Lambdaの環境変数に現在のAuto Scalingグループ名を登録しておき、イベント発生時に参照
  • インスタンス起動時には、環境変数のAuto Scalingグループ名を、新しいグループ名に更新
  • インスタンス終了時には、
    - 環境変数のグループ名とイベントが発生したグループ名が同じ場合、予期せぬスケールインと判断し、AMIを取得
    - そうでない場合は、終了イベントを継続

コード

プログラムっぽく書くと以下のようなかたちになる。

//現在のグループ名を環境変数から取得
//イベントが発生したグループ名をEventBridge経由で取得

if(起動イベント){
    //環境変数更新
    //ALBへの登録も待機させたいので、起動イベントの継続リクエストは送信しない
}else{
    if(現在のグループ名とイベントが発生したグループ名が同じ){
        //AMI取得処理
    }else{
        //終了イベント継続リクエスト
    }
}

実際のソースコードはこちらにまとめました。

ポイント

  • 環境変数を利用する
    過去のグループ名をすべて保持しておく必要がないため、DB系は使用しませんでした。

  • イベント発生順序
    CloudFormationによる更新の場合、新規グループ起動 → 古いグループ削除の順に行われるので、起動時に先に環境変数の更新が可能となっています。先に更新をかけることで、後の終了イベント発生時にAMIを取得しない判定となります。
    予期せぬスケールイン発生時にも、先に起動イベントが発生し、環境変数更新処理が走りますが、同一グループからのイベントなので、実際は環境変数の値は保持されることになります。これにより後の終了イベント時にAMIを作成する判定となります。

  • 起動と終了のライフサイクルを使用する
    当初は終了イベントのみで考えていましたが、上記の通り、環境変数の更新が必要だったため、起動イベントのライフサイクルも使用しました。起動イベントでは5分間の待機時間を設定し、ALBへの登録を待機するようにしています。

  • イベント継続リクエスト
    ライフサイクルフックにはHeartbeatTimeoutと呼ばれる時間があり、この時間の間は起動、終了処理は一時停止されます。CompleteLifecycleAction APIで終了リクエストを送ることで、一時停止を解除することができる。

    インスタンスは一定期間、待機状態に維持できます。デフォルトは 1 時間 (3,600 秒)

    AMI取得には時間がかかる場合があるので、HeartbeatTimeoutを1800秒(30分)に設定し、継続リクエストは送信していません。

まとめ

今回は、Auto Scalingライフサイクルフックを利用して、AMIを取得する方法を紹介しました。Auto Scalingによる予期せぬインスタンス終了時に、手動でAMIを作るのは困難なので、ライフサイクルフックで自動化しておくことで、対応時間外でもAMIを取得でき、障害調査にもつなげやすくなりました。AMIを取得以外にもログの退避だけもできると思うので、今度やってみようと思います。

参考資料

Discussion