2021/09/27
日々の手間もコストも減らす!〜アトラスのスポットインスタンス活用方法〜
目次
こんにちわ、L小川です。最近はオーディオテクニカのAT2020USB+をテレワークのお気に入りマイクとして使っています。
さて、今回はスポットインスタンスの活用方法についてご紹介します。
スポットインスタンスはいつ停止するかわからないデメリットはありますが、その安さはとても魅力的です。
前回のブログでご紹介した「AWS ECSを1インスタンス1タスク構成でスケールするための工夫」と「スポットインスタンスの定時起動/停止」を組み合わせて、手間とコストを減らす方法を紹介します。
今回のブログの内容で、以下のことができるようになります。
- 業務時間のみスポットインスタンスを起動(自動起動/停止)
- スポットインスタンスのスケールアウト/インに連動したECSタスク数の自動制御
前回ブログについて
ECSで1インスタンス1タスクの構成を維持したい場合、ECSサービスタイプを「DAEMON」で作成することが考えられますが、デプロイ方法がローリングアップデートに限られます。
前回のブログでは、Blue/GreenデプロイのECSサービスで、インスタンスの起動/停止に連動してECSタスク数を自動でスケールする方法について紹介しました。
スポットインスタンスはどれぐらい安くなる?
スポットインスタンスを利用するとどれくらい安くなるのか、t3.largeの年間コストを例に見てみます。
リザーブドインスタンス(以下RI)とスポットインスタンスを比べると、スポットインスタンスの方が50%ほど安くなります。
開発環境や評価環境のように24時間起動し続ける必要がない環境を想定して、平日の8時〜22時のみインスタンスを起動すると、年間コストはRIと比較して約77%のコストダウンになります。
スポットリクエストとECSタスクのスケールアウト
以下はスポットリクエストとECSタスクのスケールアウトの概要です。
スポットインスタンスがECSインスタンスに登録されるまで待機した後、ECSタスク数を増やし、1インスタンス1タスクの状態にします。
- スポットリクエストのキャパシティ数を増やす
- CloudWatch Eventsでインスタンスの起動通知を受け取る
- CloudWatch Eventsから受け取ったインスタンスIDがどのスポットリクエストに含まれるのかを調べ、スポットリクエストに設定したタグから、対応するプロダクトのSNSにメッセージを送る
- SNSをトリガーにLambdaを起動する
- 追加したインスタンスがまだECSに登録されていない場合は一定時間Sleepし、再度同じSNSにメッセージを送る
- インスタンスがECSに登録されたらECSのタスク数を変更(+1)する
3. ハブ用Lambdaの役割
前回ブログでは、オートスケーリンググループ(以下ASG)のインスタンス起動を検知するために、CloudWatch Eventsルールのイベントソースのサービス名に”Auto Scaling”を選択し、対象のオートスケーリンググループ名を指定していました。
この場合、インスタンスIDとASG名がSNS・Lambdaに通知されるため、インスタンス起動通知がどのASGから来たものかがわかりました。
スポットリクエストで同じことをやろうとすると、インスタンスの起動・スポットインスタンスの中断通知からはインスタンスIDしか通知できず、どのプロダクトのスポットリクエストから来た通知なのかがわかりません。
(インスタンス起動のイベント)
そこで、通知されたインスタンスIDがどのプロダクトのスポットリクエストのものかを調べ、各プロダクト用のSNSへメッセージを送るLambdaを用意します。
各スポットリクエストにはタグで各プロダクトのサービス名を設定しておきます。
Lambda内でこれらのタグの値とSNSのARNをマッピングしておき、対象のSNSに通知を送ります。
定時起動
先程のスケールアウト環境に、定時起動用のCloudWatch EventsとLambdaを追加します。
CloudWatch Eventsでスケジュールタイプのルールを作成します。
月〜金の午前8時に起動する場合は以下のように設定します。(GMT時刻で設定する必要があります)
このルールのターゲットとなるLambdaでは、各プロダクトのスポットリクエストのキャパシティ数を必要なインスタンス数に変更します。その後、スポットインスタンスが起動し、先程のスケールアウト処理に続きます。
次はスケールイン・定時停止の仕組みについて説明します。
スポットリクエスト・ECSタスクのスケールイン
スポットリクエストとECSタスクのスケールインの概要から見ていきます。
スポットリクエストでインスタンス数を減らした際に、「ALBのターゲットグループからのタスク解除」「タスク停止」「インスタンスのドレイニング」を行い、1インスタンス1タスクの状態を維持します。
- スポットリクエストでターゲット容量を減らす
- CloudWatch Eventでスポットインスタンスの中断通知を受け取る
- インスタンスIDがどのスポットリクエストに含まれるのかを調べ、対応するプロダクトのSNSにメッセージを送る
- SNSをトリガーにLambdaを起動する
- 停止するインスタンス上のタスクをELBのターゲットグループから外し、同時にECSのタスク数を変更(-1)する
- 一定時間sleep後、再度SNSにメッセージを送る
- インスタンス上のタスクを停止し、インスタンスをECSからドレイニングする
この後、インスタンスが削除されます。
※スポットインスタンスが中断される場合、停止通知から2分後に停止されるので、上記の処理が2分以内に終わるようにする必要があります。
定時停止
スケールイン環境に定時停止用のCloudWatch EventsとLambdaを追加します。
CloudWatch Eventsでスケジュールタイプのルールを作成します。
月〜金の午後10時に停止する場合は以下のように設定します。(GMT時刻)
ルールのターゲットになるLambdaでは、スポットリクエストのキャパシティ数を0に変更します。スポットインスタンスの中断通知が2つ目のCloudWatch Eventsに届き、後続のスケールアウト処理が実行されます。
手間を減らし、より安く
今回はスポットインスタンスとECSタスクを連動してスケーリングし、定時起動/停止で運用コストをより安くする方法について紹介しました。
スポットインスタンスを本番環境に適用するのは難しい……という場合でも、開発環境や評価環境のほか、一時的な負荷試験環境などには利用できるかもしれません。
技術的なアプローチでコスト削減に貢献できると、エンジニアとしても嬉しさがひとしおですね。
もしスポットインスタンスの利用を検討中でしたらぜひお試しいただければと思います。
それでは!