コスト削減に期待!ECS on EC2 でスポットインスタンスの利用を考える

コスト削減の最終兵器「スポットインスタンス」のお話です
2020.07.02

みなさん、スポットインスタンス使ってますか?

今回は ECS on EC2 の環境でスポットインスタンス利用について検討する機会がありましたので、記事にしてみました。

背景

ECS のスポット利用といえば ECS on EC2 としてのスポットインスタンスに加えて、最近は AWS Fargate Spot も利用可能になりましたね。

ただ、今回のスポットは ECS on EC2 についての内容となります。

背景としては、機械学習の推論をコンテナで検討する機会があり、「GPU インスタンスだと速くなるかもですねー」、「でも、GPU インスタンスお高いですよねー」、「そうだ、スポットインスタンス使ってみてはどうですかね?」という流れです。なぜ ECS on EC2 かといえば、Fargate が GPU に対応してないんですよね。。

Fargate の GPU 対応こないですかね。|д゚)チラッ

スポットインスタンスについて正しく理解する

「スポットインスタンスって怖い」

そんなイメージを持たれている方も多いかと思いますが、数年前に比べるといろんなところで改善や、機能追加によって使いやすくなっています、ということをご説明します。

なぜスポットインスタンスを使うのか

スポットインスタンスを利用する理由は 1 つしかないです。なんと言ってもお安い。

今回、ターゲットとしている GPU インスタンス g4dn.xlarge を例に、オンデマンド、リザーブドインスタンス(RI)、スポットインスタンスの料金を比較してみましょう。(いずれも執筆時点の東京リージョンでの 1 時間あたりの Linux インスタンス起動料金となります)

RI(スタンダード)は 1 年とし、前払いなし〜全額前払い の記載です。Savings Plans は価格的には RI と同等ですので割愛します。

インスタンスタイプ オンデマンド RI(1y) スポット
g4dn.xlarge $0.71 $0.485〜$0.452 $0.213

オンデマンド料金からの削減率で表すと以下のとおりです。

インスタンスタイプ オンデマンド RI(1y) スポット
g4dn.xlarge 0% 32%〜35% OFF 70% OFF

もちろんスポットインスタンスは価格が変動しますので、常に 70% 削減であることは約束されませんが、とても魅力的な価格ではないでしょうか?

また、当該インスタンスにおいては執筆時点では 70% 削減となっていますが、インスタンスタイプによっては最大 90% 削減もあり得ます。

スポットインスタンスの価格って荒ぶるときあるやん?

「大量にスポットインスタンスがリクエストされると、オンデマンド料金より高くなることありますよね?」

いいえ、それは古い認識ですのでココで知識をアップデートしておきましょう。

re:Invent 2017 の会期中にリリースされたアップデートにより、スポットインスタンス価格は緩やかな変動となるように改善されています。

また、現在ではスポットインスタンスの価格が、オンデマンド価格の 90% を超えることはありません。

Amazon EC2 は、需要と供給に基づいて定期的にスポット料金を変更しますが、オンデマンドインスタンス料金の 90% を超えることはありません。

(引用元:Amazon EC2 スポットインスタンスの例

これは執筆時点の東京リージョンにおける m5.large インスタンスタイプ 3 ヶ月間の価格推移です。m5.large は汎用的に使えるので需要が高そうですが、非常に安定していますね。概ね $0.035/h で推移しており、ときどき緩やかに上昇していますが $0.040/h に届くか届かないか、といったところでしょうか。

今回、検討している g4dn.xlarge に至っては凪ですね。(あくまで、執筆時点の環境です)

スポットインスタンスって落ちますよね?

はい。設定価格を超えた場合だけでなく、スポットプール内のリソース枯渇など AWS 側の事由によって停止される可能性もあります。

しかし、実情としてスポットインスタンス停止の 92% はユーザーによって実施されています。つまり先述のような AWS 側の事由によって停止されるのは、わずか 8% しかありません。(全体的な数字かと思いますので、オンデマンド/RIの需要が高い、または、そもそもリソースの少ないインスタンスタイプをお使いのユーザーは実体験とのギャップを感じる可能性はあるかもしれませんが。。)

(引用元:Amazon EC2 Auto Scaling によるスポットインスタンス活用講座

とはいえ、裏を返せば 8% は停止されているというのも事実です。

停止される場合は、無慈悲に即中断ということはなく、2 分前に中断通知が届きます。この 2 分間を活用して安全に停止するために何をするべきかを検討しましょう。

中断通知は以下の方法で受けることが可能です。

  • インスタンスメタデータ
    • http://169.254.169.254/latest/meta-data/spot/instance-action に格納される中断通知を cron などで 5 秒おきにチェックするような仕組みを検討します(通常時は 404 NotFound が返り、中断通知があると中断予定時刻が返ります)
    • インスタンス内部で処理しますのでシェルスクリプトでサクッと書ける点がメリットでしょうか
  • EventBridge(CloudWatch Events)
    • 利用中のスポットインスタンスすべての中断通知を受信するので、対象のインスタンス ID は判定が必要
    • Lambda で書ける
    • ELB の登録解除などインスタンス外部の処理には EventBridge のほうが柔軟に対応できる

いずれか一方に寄せる必要はなく、処理の内容によって使いやすい方を選択するのが良いと思います。

中断に備える

先述のとおり、8% とはいえ中断されるときは中断されますので、スポットインスタンスの利用する環境では以下のような点を考慮し、中断可能なシステムにすることを意識しましょう。

  • ワークロードを再開可能にする
    • 処理途中の結果を S3 などに退避させ、次に起動されるスポットインスタンスに引き継げる仕組みがあるか
    • 再実行されることを考慮した設計になっているか(冪等性) など
  • ステートレス
    • インスタンス内に情報を持たせない
  • 疎結合
    • あるインスタンスの停止によって、周辺のシステムに影響がないように設計しましょう
  • スポットプールの分散
    • 複数のインスタンスタイプ、AZ からスポットインスタンスを起動できると安定度が増します(スポットプールについては後述します)

とは書きましたが、そもそもコンテナを利用されている場合、イメージの入替えや、スケールアウト/スケールインといった運用があるため、既に上記のような点を意識した設計になっている環境は多いのではないでしょうか。

だとすれば、コンテナ運用されている環境においてスポットインスタンスは比較的導入のハードルが低いとも考えられます。(もちろん、そうでない環境もあるでしょうが。。)

ECS におけるスポットインスタンス運用

自動ドレイン

ここまで、スポットインスタンスは中断することを念頭において利用してください、ということを述べてきました。しかしながら、ECS はコンテナホスト上で稼働するタスクを管理するコンテナオーケストレーションではありますが、コンテナホストとなる EC2 インスタンスそのものは管理をしていません。

そのため、コンテナホストが 2 分後に中断することを ECS クラスターに知らせ、当該インスタンスを除外する必要があります。

従来は先述の中断通知を受けて明示的に ECS クラスターから登録解除するような仕組みを実装する必要がありましたが、現在は ECS で「自動ドレイン」が提供されています。この機能を有効にすることで、中断通知をトリガーに自動的にドレイニングしてくれます。

  • 自動ドレイン
    • 対象インスタンス上で新規タスクがスケジュールされなくなる
    • サービスタスクを停止し、利用可能なインスタンスに再配置
    • 注意) 中断動作を hibernate にしている場合、スポットインスタンスのドレインはサポートされません

自動ドレインは /etc/ecs/ecs.configECS_ENABLE_SPOT_INSTANCE_DRAINING=true を記述することで有効化できます。詳細については以下の記事を参照ください。

スポットインスタンスの起動方法めっちゃあるやん

スポットインスタンスと一言にいっても、起動方法の選択肢が複数あって非常にわかりづらいですよね。私もこの記事を書くにあたって調べましたが「え、何が違うん・・?」となかなか理解に苦しみました。

この点については AWS Summit Tokyo 2019 「EC2スポットインスタンスのすべて」のセッション資料にとても分かりやすい表がありましたので引用させていただきます。(資料 / 動画

(引用元:AWS Summit Tokyo 2019 「EC2スポットインスタンスのすべて」

少し内容が古いので(といっても 1 年前ですが)記載されていない追加機能を補足します。

インスタンスの重み付け

2019年11月に Auto Scaling Group は、リクエスト単位に重み付けを行うことが出来るようになっています。デフォルトではすべてのインスタンスは同じ重みです。そのため希望するキャパシティを 3 と設定した場合、これは 3 インスタンスという意味になります。

しかし異なるインスタンスタイプの場合、vCPU 数、メモリ量などによって重みをつけたい場合があります。例えば今回の GPU インスタンスの場合、GPU 数が肝になるので GPU 数でカウントしたい、というような場面です。GPU を 1 つ搭載したインスタンスに 重み:1、2 つ搭載したインスタンスに 重み:2 と設定した場合、希望するキャパシティの意味としてはインスタンス数ではなく、「重みの総数」となります。

つまり 希望するキャパシティ=5 としたときに、GPUx1 インスタンスが 3 つ、 GPUx2 インスタンスが 1 つ起動したとします。インスタンス数は 4 ですが、重みの総数は 5 となり希望するキャパシティを満たした、ということになります。

詳細については以下のブログを参照ください。

ECS Cluster Auto Scaling

さらに 2019年12月に ECS Cluster Auto Scaling(CAS) という新機能がリリースされており、CAS は同時期にリリースされた Capacity Provider を使って管理要素の Auto Scaling Group とリンクします。

このような状況から考えると、ECS 環境においては機能追加が行われている Auto Scaling Group を先ずは選択すると良いかと思います。

スポットインスタンスが起動しなかったらどうすんの?

スポットインスタンスは、リージョン、AZ、インスタンスタイプごとに独立した空きキャパシティからインスタンスを起動します。この空きキャパシティが「スポットプール」です。

(引用元:AWS Summit Tokyo 2019 「EC2スポットインスタンスのすべて」

もし単一 AZ、単一インスタンスタイプのみをスポットリクエストした場合、これはインスタンスが起動する対象のスポットプールは 1 つということです。では、このスポットプールに空きキャパシティが無かった場合、どうなるでしょう?

答えとしてはお察しのとおりインスタンスは起動しません。残念ながら執筆時点において、スポットインスタンスの確保に失敗したときに、動的にオンデマンドに切り替えて起動させる、といった機能は提供されていません。(この機能、すごく欲しいです!AWS に届け!)

起動時を例にあげましたが、起動中のスポットインスタンスも同樣です。オンデマンド需要によって、スポットプールの空きや、スポットインスタンス価格もスポットプールごとに変動しますので設定価格を上回った場合、スポットインスタンスは中断されます。

では、どうすれば良いのでしょうか?

スポットプールを分散させてリスクを下げる

なるべく多くのインスタンスタイプで、複数の AZ を選択することで、スポットプールが分散されますので、あるスポットプールの枯渇によってシステム全体が停止してしまうリスクを下げましょう。

インスタンスタイプは近いスペックのインスタンスタイプ、インスタンスサイズ、旧世代インスタンスを含めて検討されると良いかと思います。

スポット割り当て戦略

Auto Scaling Group の場合、[スポット割り当て戦略] という項目で、「最低価格(Lowest-price)」を重視するか「キャパシティ最適化(Capacity-optimized)」を重視するか選択ができます。

「最低価格」の場合、コストを優先するためスポットプール内の空きキャパシティ量は考慮されないません。よって、オンデマンド需要の影響を受ける可能性が高く、中断リスクとしては相対的に高いと言えます。

「キャパシティ最適化」の場合、「最低価格」に比べると全体コストはやや高くなる可能性がありますが、キャパシティの確保しやすいプールを選択しますので、中断リスクは下がります。

どちらの戦略が適しているかは、ワークロードによって異なりますので一概にどちらが良いというものではありません。

戦略タイプ ワークロードの例
最低価格 中断コストの低いワークロード(短時間で完結するワークロード、再開可能なワークロードなど)
キャパシティ最適化 中断コストの高いワークロード(再実行できるが長時間掛かるもの、分析、レンダリング、HPCなど)

注意点としてはどちらの戦略もあくまでキャパシティ確保時点での判断になります。スポットプールの状況は時間の経過とともに変わっているでしょうから、長時間稼働している場合は、現在稼働中のインスタンスが、その時点での最適なスポットプールであるとは限りません。

可能であれば先日のアップデートで Auto Scaling Group に機能追加された「インスタンスの更新」を使って、定期的にリフレッシュさせることで、スポットプールを再選択させるということも可能かと思いますし、そもそも夜間停止するのであれば、毎日、起動時点の最適なスポットプールが選択されるでしょう。

オンデマンドや RI/SP とのミックス

開発環境や検証環境など、最悪、全停止されても良い場合はすべてスポットインスタンスで運用できれば、コスト的にはかなりの削減が期待できます。

一方で本番環境など全停止だけは避けたい、という場合はオンデマンドインスタンスや、RI/SP を併用されるのが良いかと思われます。

(引用元:AWS Summit Tokyo 2019 「EC2スポットインスタンスのすべて」

RI/SP を利用する場合は、Auto Scaling Group 設定の [インスタンスタイプ] で、優先させたいインスタンスタイプを最上位に設定します。このリスト順位はオンデマンドとして起動するインスタンスのみが対象となりますので、スポットインスタンスには影響しません。

まとめ

ざざっとスポットインスタンスおよび、ECS でのスポットインスタンス運用について紹介しましたので、まとめます。

  • スポットインスタンス価格は以前のような「じゃじゃ馬」ではありません
  • スポットインスタンスの 92 % はユーザーによって停止されている
  • とはいえ中断されるのは事実なので、中断に備えた実装をする(再実行、冪等性など)
  • ECS での中断処理(ドレイニング)は自動ドレインで随分と楽になりました
  • EC2 on ECS なら Auto Scaling Group の利用がオススメ
  • スポットプールの選択肢を増やし、起動不可、中断のリスクを下げる

さいごに

今回、スポットインスタンスを検討するにあたって調べてみると、私が知っていたころのスポットインスタンスとは随分と改善されており、かなり使いやすくなったんじゃないかという印象を持ちました。

基本的に 「Design for Failure」 の設計思想を意識されている環境であれば、比較的容易にスポットインスタンスを運用にのせることが出来るのではないでしょうか?

以上!大阪オフィスの丸毛(@marumo1981)でした!

あわせて読みたい・観たい