スポットインスタンスを利用するシステムの堅牢化

スポットインスタンス中断への対処

あるアベイラビリティゾーンのあるインスタンスタイプにおいてキャパシティが不足するとき、EC2サービスはキャパシティを回復させる必要があります。このとき、対象のアベイラビリティゾーンにおけるインスタンスタイプにスポットインスタンスが起動していれば、EC2サービスは該当するスポットインスタンスに2分前の中断通知を送付し、スポットインスタンスを中断することでEC2キャパシティの回復に充てます。2分前の中断通知はインスタンスメタデータサービス、およびCloudWatch Eventsから受け取ることができます。中断通知の詳細はこちらのドキュメントを参照してください。 ここでは、中断通知イベントをCloudWatch Events経由で受信し、それをトリガーにLambda関数を実行する仕組みを構築します。中断通知イベントはEC2 Spot Instance Interruption Warningという名称です。Lambda関数には、中断対象とマークされたスポットインスタンスをAuto Scalingグループからデタッチ(DetachInstances)する動作を記述します。デタッチの詳細はEC2 Auto ScalingユーザーガイドのAuto Scaling グループからの EC2 インスタンスのデタッチを参照してください。

  1. まず検討すべきポイントは、スポットインスタンスをデタッチするタイミングでAuto Scalingグループの希望容量を1台減らすのか、それとも台数変更なしにするのか、という点です。もし希望容量を変更しない場合、デタッチの直後にAuto Scalingサービスが新しいインスタンスを自動的に起動します。

  2. 次に、今回のワークショップのようにAuto Scalingグループがロードバランサー配下に登録される場合、インスタンスがAuto Scalingグループからデタッチされるとき、ロードバランサーからも登録解除(deregister)されます。このとき、ロードバランサー(あるいはターゲットグループ)にConnection Draining(あるいは登録解除の遅延とも呼ばれます)が設定されていれば、Auto Scalingは処理中のコネクションが終了するまでデタッチを待ちます。今回、このタイムアウト値は中断通知の長さに合わせて120秒を設定しています。

EC2 Auto Scalingのデタッチ動作についてはこちらのドキュメントを参照してください。

ここでは、Lambda関数とCloudWatch Eventsを作成し、それぞれを関連付けるCloudFormationテンプレートを用いてこの仕組みを構築します。

  1. CloudFormationテンプレートの内容を確認し、次のコマンドでスタックを作成します。
```
aws cloudformation deploy --template-file spot-interruption-handler.yaml --stack-name spotinterruptionhandler --capabilities CAPABILITY_IAM
```
  1. スタックの作成が完了したらばLambdaコンソールを開き、新規作成された関数名をクリックします。作成完了の目安は2分以内を想定しています。

  2. 作成されたLambda関数の内容を確認します。Cloud9のインラインコードエディタを活用してください。

これでスポットインスタンスの中断通知を受け取り、そのインスタンスを自動的にAuto Scalingグループからデタッチさせることができるようになりました。中断そのものをシミュレーションすることはできませんが、ここではLambda関数のテスト機能を使い、処理が正しく実行されるかを確認します。

  1. Lambdaコンソール右上にあるドロップダウンメニューから「テストイベントの選択」をクリックしてドロップダウンメニューを表示させ、「テストイベントの設定」を選択します。グレーアウトされている場合にもそのままクリックし、ドロップダウンメニューを表示できます。
  2. 「テストイベントの設定」ダイアログボックスではイベント名に任意の名前を設定し(TestSpotInterruptionなど), 次のjsonを入力します。
```json
{
  "version": "0",
  "id": "92453ca5-5b23-219e-8003-ab7283ca016b",
  "detail-type": "EC2 Spot Instance Interruption Warning",
  "source": "aws.ec2",
  "account": "123456789012",
  "time": "2019-11-05T11:03:11Z",
  "region": "eu-west-1",
  "resources": [
    "arn:aws:ec2:eu-west-1b:instance/<instance-id>"
  ],
"detail": {
  "instance-id": "<instance-id>",
  "instance-action": "terminate"
  }
}
```
  1. このjsonに2箇所存在する**"<instance-id>"**を、作成したAuto Scalingグループ内の任意の1台のインスタンスIDで置き換えます。インスタンスIDはEC2 Auto Scalingコンソールのインスタンスタブから確認できます。

  2. 作成を押します。

  3. 「テストイベントの選択」で先ほど選択したテストイベントを選択し、「テスト」ボタンをクリックします。

  4. 画面上部に実行結果の成功が表示されます。「詳細」をクリックして内容を確認します。

  5. EC2 Auto Scalingコンソールから対象Auto Scalingグループの「アクティビティ履歴」タブを開き、成功ログとして"Instance i-01234567890123456 belongs to AutoScaling Group runningAmazonEC2WorkloadsAtScale. Detaching instance…“のようなメッセージが出ていることを確認します。またその直後のログに新しいインスタンスの起動が記録されているのを確認します。

  6. EC2 ELBターゲットグループコンソールから今回のターゲットグループを選択し、ターゲットタブを開きます。該当インスタンスがdrainingのステータスになっていることを確認します。

ここまでの手順で、スポットインスタンスの中断通知を受けたタイミングでシームレスに新しいスポットインスタンスを起動し、スポットインスタンスが終了する前に安全に入れ替える仕組みを構築することができました。

実際に中断が発生する場合、Auto ScalingグループからデタッチしたインスタンスはEC2スポットサービスにより自動的に終了(Terminate)されます。今回は単に中断イベントをシミュレーションしただけであったためインスタンスは終了されません。デタッチしたインスタンスを忘れずに終了させてください。

スポットインスタンスを利用するシステムの堅牢化

これまでスポットインスタンスの起動に際して、アベイラビリティゾーンごとに最も安い順に4種類のインスタンスタイプを9種類のインスタンスタイプから選択できるように構成してきました。ここからさらに、使用するインスタンスタイプとアベイラビリティゾーンの組み合わせを増やすことで、特定のスポットキャパシティプール(アベイラビリティゾーンとインスタンスタイプの組み合わせ)での中断の影響を緩和していくことができます。

チャレンジしてみましょう

今回作成したスポットインスタンスによる音楽ストリーミングアプリケーションをより堅牢にするために、どのような構成の改善を検討したらよいでしょうか。

考え方の一例

チャレンジしてみましょう

スポットインスタンスの配分戦略には、今回選択したlowest-priceの他にどのようなものがあるでしょうか。今回のワークロードに最も適した配分戦略はどれになるでしょうか。 ヒント:新しい配分戦略についての[記事] (https://aws.amazon.com/blogs/compute/introducing-the-capacity-optimized-allocation-strategy-for-amazon-ec2-spot-instances/)を参照してください。

Click here for the answer