Sansan Builders Blog

Sansanのものづくりを支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信

Eight の EC2 費用を Spotinst によって4割くらい減らした話

Eight 事業部の秋本です。

本記事では Eight における EC2 スポットインスタンスの活用について取り上げ、同時にスポットインスタンス運用をラクにするサービスである Spotinst もご紹介したいと思います。

TL; DR

  • EC2 オートスケーリンググループで運用されてきたインスタンスを Spotinst の Elastigroup という枠組みで運用するようにしました
  • Elastigroup はリッチなオートスケーリンググループといった風情です
  • Elastigroup に移行することで月々の EC2 費用が全体で4割程度削減できました
  • もっとも Elastigroup にはオートスケーリンググループとは異なるお作法があり、それなりに苦労しました

ここからは前置きが続くので、上で述べた内容の詳細が早く知りたいという方は下記リンク先に飛んでしまってください:

スポットインスタンス導入まで

Eight 内での EC2 インスタンス

Eight 内で利用される EC2 インスタンスには主に以下の4種類があります:

  1. web アプリケーションが稼動するインスタンス
  2. ECS コンテナを稼動させる ECS クラスタとして利用されるコンテナインスタンス
  3. 部門内限定で利用されるアプリケーションが稼動するインスタンス
  4. 踏み台や雑多な用途で利用されるインスタンス

1. および 2. については以前より EC2 オートスケーリンググループで運用されており、負荷状況などによってインスタンス台数が適当に調整されるようになっています。また各種リリース作業によって逐次インスタンス達がゴッソリ入れ替えられます。

一方 3. および 4. については基本的にはあまり再作成などは行われず、設定変更やパッケージの更新などを適宜行いながら長く使われ続けるものになります。

上に挙げたインスタンス達は従来リザーブドインスタンス(以下 RI とよびます)とオンデマンドインスタンンスで運用されていました。

オートスケーリンググループで運用されるインスタンスについては、最低限動作しておいて欲しい台数分を RI で確保しつつ費用を下げ、 所定の台数のみ必要なインスタンスについては全ての利用分を RI でまかなうようになっています。

世知辛いお金の話

さて RI で稼動するインスタンスについては RI の効能により4割程度 1 コストが削減できます。インスタンスも安定して稼動できるしお金も安い。嬉しいですね。

一方でオートスケーリンググループ内では基本的にオンデマンドインスタンスが稼動します。これは費用面ではあまり嬉しくありません。 オンデマンドインスタンスは1時間単位の従量課金の為、使わなければ無論費用も発生しませんが、使えば使った分だけ費用が嵩みます。

なに当たり前の事を言ってんだという感じですが、特に前述の web アプリケーションが稼動する 1. のインスタンス達はキャンペーンやパフォーマンス的な観点から往々にしてスケールアウトされがちで 2 その分だけ毎月の AWS 費用変動に怯える日々を過ごさねばなりません。しかし費用変動に怯えることでインスタンス台数を調整できず、サービスが遅くなっちゃった、止まっちゃったとなってしまえばもっと悲惨です。

EC2 の費用変動に怯えず気軽にインスタンスを横に並べたい、なにか良い方法はないでしょうか。 オートスケーリンググループ運用ができているのでインスタンスやアプリケーションのレベルではステートレスな状態にできています。ネックは費用のみです。さあどうしましょう。

そこでスポットインスタンスを使ってみようという話になります。

スポットインスタンス

スポットインスタンスとは、本来オンデマンドインスタンスとして利用されんとする為に AWS が用意した在庫をお安く利用できるという、余り物には福があるという類いのものです。 詳細は https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/using-spot-instances.html が詳しいので説明を譲ります。雰囲気としては

  1. オンデマンドインスタンスが100台分起動するように準備したよ
  2. アレッ70台分しか使われないな
  3. 30台分どうしよう、もったいないな
  4. 余ってる30台分を安くするから誰か使ってくれ

といった感じです。

さてスポットインスタンスの費用がオンデマンドインスタンスに比べてどれくらい安くなるかということですが、

を比較するとわかります。わかりますといって終わりにするのは不親切なのでいくつかのインスタンスタイプで比較してみましょう。 後述するようにスポットインスタンスの価格は時価で定まりますので以下の価格は確認時点 (2019-12-03) の ap-northeast-1 におけるものです:

インスタンスタイプ オンデマンド価格 [USD/hour] スポット価格 [USD/hour] スポットインスタンス割引率 [%]
m5.large 0.124 0.0349 71.855
c5.large 0.0333 0.107 68.889
r5.xlarge 0.0732 0.304 75.921

だいたい7割くらい安いです。嬉しいですね。9割程度引かれる例もあるようです。

スポットインスタンスはオンデマンドインスタンスとは利用する方法が異なるのみで、実際に使い始めればインスタンスとしては同じものが利用できます。 例えば m5.large なインスタンスを使用する場合、オンデマンドでもスポットでも CPU 2コアでメモリが 8GiB のインスタンスを利用できます。良いことづくめですね。

残念な事にこれで話は終わりません。良いことづくめなわけはありません。

まず、スポットインスタンスの価格はこの在庫の状況によって決定されます。なのでオンデマンドインスタンスがあまり使われず在庫が有り余っている際は安くなり、オンデマンドインスタンスが売れまくっている時は高くなります。スポットインスタンスは時価で価格が決まります。緊張感がありますね 3

そしてこれが重要な点なのですが、スポットインスタンスとして利用されるインスタンスは、オンデマンドインスタンスの在庫が不足してきてスポットインスタンスに回せる分の在庫が無くなった時点で容赦無く落とされます。つまり

  1. 100台の在庫のうち30台分余っちゃったのでスポットインスタンスとして売るよ
  2. オンデマンドインスタンスが売れ始めて70台から90台に増えちゃった
  3. スポットインスタンスに回せる在庫が30台から10台になっちゃった
  4. 20台分使ってくれてるみんな、ごめんね、没収しちゃうよ

という雰囲気で無慈悲にスポットインスタンスは落とされてしまいます。慈悲深きことに落とされる2分前に AWS から事前予告はなされます 4

料金が時価で決定されるので変動するのはともかく、AWS 側の在庫状況でインスタンスが落ちてしまうのは困りますね。 しかしオンデマンド比で7割引という価格帯はとても魅力的です。なんとかスポット価格で運用しつつもインスタンスを安定稼動させる術はないものでしょうか。

AWS の世界のみでは残念ながらその手段がありません。自分の望む環境を自前で頑張って拵える必要があります。 しかしながら AWS の外にも世界があります。そして AWS の世界の傍に望みを叶えるものがあったのでした。

おまちどうさまでした。ここから Spotinst がでてきます。

Spotinst / Elastigroup

Spotinst は AWS をはじめ Azure や GCP といったクラウドプラットフォームを一段抽象化して良い感じに使えるようにする為のサービスです。 使う事でのメリットは本家のページに譲りますが

Optimize cloud infrastructure costs by up to 90%

という素敵な文言は注目に値します。どういった訳でしょう。

我々がここで見るべきは Spotinst のプロダクトのひとつである Elastigroup です。https://spotinst.com/products/elastigroup/ に紹介がありますが、これは要約するに

  • 可用性を維持しつつクラウドプロバイダの余剰在庫をうまく使って最大9割コストを下げるぜ
  • 細かい事を考えることなしにインスタンスを良い感じにコントロールするぜ

といったものです。 前者は AWS の言葉でいえばスポットインスタンスであり、後者はだいたいオートスケーリンググループのようなものです。 よって乱暴な表現を許して頂くと Elastigroup は豪華なオートスケーリンググループみたいなものと言えるでしょう。

豪華なオートスケーリンググループというのはわかりました。しかしながらスポットインスタンスには AWS の都合で突然落とされるというデメリットがあるはずです。Elastigroup はそのあたりをどうしているのでしょう。この回答は前掲のドキュメントを読むとわかるのですが

  • スポットインスタンスの需要を独自に予測しインスタンスが落とされる15分前には対策を打つ
  • スポットインスタンスを基本的には使うが在庫不足の時はオンデマンドインスタンスにも頼る
  • もし利用できる RI 枠があるならそれを使ってインスタンスを起動させる

という内容でスポットインスタンスを使う事で生じるリスクをヘッジしていることがわかります。なかなかよさそうですね。 スポットインスタンスを基本的には使用して有事の際にはよしなにオンデマンドインスタンスを使ってくれるというのは安心感があります。また RI 枠があるのならばそれもよしなに利用してくれるというのも、購入した RI が無駄にならないという意味で嬉しさがあります。

良い事づくめな話題で終えてしまうと不公平な感じもあるのでお金の話で本節を締めましょう。 Spotinst は営利企業なのでサービスの利用には対価が必要です。その料金体系は https://spotinst.com/pricing/ で紹介されています。

プランには Free / Professional / Enterprise の3種類があり、右にゆくにつれてサポートのレベルが向上してゆきます。 Enterprise プランは応相談な価格体系なので説明を省略し、Free プランは無料なのでこれも省略します。

さて Professional プランの料金体系は「月々節約できた額の 20%」と宣言されています。つまりユーザが1ドルも節約できなければ Spotinst は儲からず、1,000 ドル節約できれば 200 ドル得られるという計算です。シンプルでよいですね。ユーザの懐事情を良くしないと自身が儲からんという一蓮托生感があります。

では実際につかってみましょう。以後はようやく Eight での事例紹介です。

オートスケーリンググループから Elastigroup への移行

Spotinst アカウント作成や請求まわりの設定などは省略します。

インスタンス起動元の変更

前述の通り Eight 内では既にオートスケーリンググループが活用されています。オートスケーリンググループで運用されるインスタンスはいずれもステートレスになるよう構成されている為、基本的にはインスタンスが作成されれば所望の動きをし、あるインスタンスが突然止まったり落ちたりしても他のインスタンスやサービスそのものへの影響はありません。

つまり移行計画としては

  1. 移行用 Elastigroup を作成しておく
  2. Elastigroup 内でインスタンスを起動させる
  3. Elastigroup 内で起動してきたインスタンスの台数分だけオートスケーリンググループ内のインスタンス数を減らす
  4. 2.3. をオートスケーリンググループ内インスタンスが0台になるまで繰り返す

という単純なものです。移行計画としては単にこれだけで、オートスケーリンググループ運用されているインスタンス達は単にこの手順によって Elastigroup への移行が完了しました。

より厳密には web アプリケーション稼動インスタンスと ECS コンテナインスタンスとでは ALB ターゲットグループの有無という違いがあり、ターゲットグループにインスタンスがひもづけられる必要のあるものについてはリクエスト断が発生しないように気を使ってあげる必要があります。 この方面から作業が正常にできるか? という検証は適宜実施しました。

なお Elastigroup をはじめ Spotinst の各種サービスは Terraform による管理が可能です。Eight の場合は https://www.terraform.io/docs/providers/spotinst/r/elastigroup_aws.html を参考に作業を行いました。 また Elastigroup を作成するには新規に作成する方法の他にオートスケーリンググループなどの定義をインポートしてやることも可能で、Elastigroup として作成されたものを Terraform 定義としてエクスポートすることも可能です。新規に Terraform るのが面倒な場合はこれを利用するとよいでしょう。

費用削減の状況

ここまで文字だらけで辟易されているかと思いますがここだけ図に溢れた節になります。

Eight 内での Spotinst 利用は2019年6月に ECS コンテナインスタンスを対象に始まりました。 その後運用実績が積み重なってくるにつれて web アプリケーションが稼動しているインスタンスでも Elastigroup を利用するという行程を踏んでいます。オートスケーリンググループからの全面移行が完了したのは同年9月です。費用削減の状況もこの流れに沿ったものとなりました。

Spotinst 利用状況

f:id:kakmt:20191205164804p:plain

諸般の事情によりグラフの縦軸は隠しています。

集計上の都合により横軸の日付は一ヶ月分ズレています。 Dec 2019 は2019年11月分の内容を示しています。またグラフの凡例は以下の通りです:

  • potential_costs:Spotinst を使わなかった場合の EC2 利用額
  • actual_costs:実際の EC2 利用額
  • spotinst_costs:Spotinst 利用額
    • 前述の通りこれは (potential_costs - actual_costs) * 0.2 で求められます
  • total_costsactual_costsspotinst_costs との合計
    • Spotinst 料金を含めてどれくらい削減できたかを示す指標

2019年9月(グラフでは Oct 2019)以降、 potential_coststotal_costs との差が激しくなっています。おおむね6割弱の削減効果がでている計算になります。

なお Spotinst は HTTP API で費用状況を取得できます。上の図はその API を使用して取得した値を Redash で可視化しているものになります。

単にスポットインスタンスを使うのみであれば7割程度の削減効果が見込まれるものが、Spotinst 利用により1割程相殺され、全体としては6割程度の削減効果となっています。1割分の旨味が減るのは惜しいですが、Spotinst によってスポットインスタンス利用にかかるリスクを減らせている点を考えれば、費用対効果は充分にあるのではないでしょうか。

さて Spotinst による費用削減効果はわかりました。が、EC2 費用全体でみるとどうなるのでしょうか。以下に続きます。

EC2 費用状況

f:id:kakmt:20191205164831p:plain

こちらも諸般の事情によりグラフの縦軸は隠しています。こちらは AWS Cost Explorer の内容となりますので横軸の日付はそのまま見たままの日付となります。 Nov 2019 であれば2019年11月の内容です。

Spotinst 導入開始月である2019年6月に比べ、オートスケーリンググループからの以降が完了した2019年9月以降で以下の違いが見て取れます:

  • 紫色の割合が激減
  • 橙色の割合が増加
  • 緑色の割合が減少
  • 黄色の割合も増加

紫色と橙色とは同一インスタンスタイプをオンデマンドで利用したかスポットで利用したかの違いであり、スポットインスタンスに移行することでだいぶ費用が下がることがみてとれます。

黄色部分は新たなインスタンスタイプをスポットインスタンスとして起動している分であり、その費用影響はグラフからみても限定的なものに落ち着いていることが観察できます。

スポットインスタンス利用は従来オートスケーリンググループで運用されているものだけですので、固定台数の利用だったりする分のインスタンス費用はもちろん削減されてはいません。 しかしながらスポットインスタンスの利用が増えるにつれて EC2 費用全体も削減できていることが観察できるかと思います。

泣いたところ

オートスケーリンググループから Elastigroup へ単に移行するだけでよかったとは前述しましたが、やはりというか両者には無視できない違いもあるのでした。またスポットインスタンス特有の問題も無視できないものがあったのでした。 そのあたりを説明します。

リリーススクリプトの変更

類似の事例はどこでもあると思いますが、Eight でもアプリケーションの変更内容を本番環境に反映する際にリリース用のスクリプトが実行されます。 このリリーススクリプトはオートスケーリンググループ操作用の HTTP API を AWS SDK を使用して叩くもので、元来 Ruby 製のものが使われていました。

オートスケーリンググループから Elastigroup に移行するにあたり、このリリーススクリプトも Elastigroup 対応版に移行する必要があります。ところが https://github.com/spotinst?q=sdk をみるとわかるのですが Spotinst 謹製の SDK としては Ruby に対応したものがありません。有志が作成したものも確認時点 (2019-12-03) で特に見当りませんでした。

Spotinst は SDK でカバーされる操作を HTTP API で行うことができます。これを Ruby から叩くようにすれば既存の Ruby 資産を流用することができますが、Ruby 資産を流用できるメリット以上に API 呼び出しを独自実装することによるデメリットが増えるだろうことが容易に想像できた為、泣く泣く同様の動きをするリリーススクリプトを再実装する羽目になりました。 新しいリリーススクリプトは筆者が唯一マトモに使える言語である Python を利用しています。

リリーススクリプトの詳細については省略します。Eight 固有の内容で汎用性が無い為です。 Ruby 製スクリプトを Python で再実装することになったという悲しさを文章にしておきたかったのでした。

ライフサイクルアクションの違い

オートスケーリンググループにはインスタンスの起動や終了などのタイミングに応じ Amazon SNS にイベント情報を通知する事ができるライフサイクルフックという機能があります: https://docs.aws.amazon.com/ja_jp/autoscaling/ec2/userguide/lifecycle-hooks.html

これと同様に Elastigroup にも Elastigroup 内インスタンスの起動 / 終了タイミングに応じて Amazon SNS に通知を送ることができるライフサイクルイベントという機能があります。 https://api.spotinst.com/lifecycle-events/

両者にはインスタンスの状態遷移を示す図が添付されています。ここで両者の図を見比べてみましょう。図の引用は諸般の事情と手間とで省略しますので上の2リンクを別タブで開いて比較してみてください。

おわかりいただけたでしょうか。文字だらけで辟易されていると思いますが、更に文字で説明すると以下のようになります:

  1. インスタンスが起動してくる
    • オートスケーリンググループ: Pending
    • Elastigroup: Instance Launch
  2. インスタンスが起動完了となるのを待つ
    • オートスケーリンググループ: Pending:Wait
    • Elastigroup: おや?
  3. インスタンスが起動完了となったのでサービスイン(ALB ひもづけ等)処理を開始する
    • オートスケーリンググループ: Pending:Proceed
    • Elastigroup: おや?
  4. 起動してきたインスタンスのサービスインが完了する
    • オートスケーリンググループ: InService
    • Elastigroup: Instance Running

……インスタンス起動中を示すステータスが Elastigroup には無いですね。不穏な匂いがします。 起動直後のインスタンスは OS レベルでは起動完了しているかもしれませんが、中で動いているアプリケーションのレベルではきちんと稼動していないかもしれません。アプリケーションレベルでの起動完了が担保できないとサービスイン、とくに ALB にぶらさげてユーザからのリクエストを受けるといった用途で使うのは怖いです。しかし Elastigroup にはそれをハンドルする仕組みがないようです。

ちゃんと検証していたつもりでしたが、後悔先に立たず、この問題に起因して Elastigroup 移行後しばらくの間インスタンス起動直後に HTTP 503 エラーの発生が散見されました。上の説明通り、インスタンス内のアプリケーションが無事に起動完了となる前に ALB からリクエストが転送されるに至り、適切にリクエストを処理できずエラーとなってしまうという状態が発生してしまうのでした。 なお適当なタイミングでのアプリケーションの実行が許容される ECS コンテナインスタンスの場合は本問題に直面することはありませんでした。

対処方法は種々あると思いますが、Eight では Elastigroup で使用するターゲットグループにスロースタートモード 5 を設定してやり、アプリケーションが無事に起動し終わるまでに時間的な猶予を設けることで解決するに至ったのでした。 それでも若干の HTTP 503 を生じさせるのんびり屋さんの存在にはヘルスチェック回数の増加で対処しました。スケールアウトの所要時間が少し延びてしまいますが、エラーを返されるよりはマシです。

Spotinst を使っていてもスポットインスタンスは落ちるときは落ちる

身も蓋もない見出しになってしまいました。残念ですがそんなもんです。

Spotinst でヘッジできるのはある程度予測が立てられる、別の言い方をすれば統計的に傾向を掴むことができるスポットインスタンスの強制終了タイミングであり、オンデマンドインスタンスの需要が突沸したときといったイレギュラーな事象には流石に対処できません。

Elastigroup には以下パラメータを設定することでスポットインスタンス利用にかかるリスクを軽減できる仕組みが備わっています:

  • Elastigroup 内インスタンスのスポット / オンデマンド比
  • スポットインスタンスとして使用するインスタンスタイプの種類
  • スポットインスタンスとして使用するインスタンスタイプの優先順位

Eight では当初以下のような設定にしていました:

  • スポット : オンデマンド == 9:1
  • インスタンスタイプの種類を2種類に設定
    • t[23].medium といった感じです
  • ひとつのインスタンスタイプだけを優先して使うよう設定
    • t3.medium だけといった感じです

さてどうなるでしょうか。Spotinst では予測できないオンデマンドインスタンス需要の突沸が生じた際、スポットインスタンスが大量に落ちてしまうという悲劇が発生しました。予想できたことですね。想像力の大切さがわかる事例でした。Spotinst のサポートに問い合わせた際も「トガった使い方してますね」とコメントを頂戴してしまいました。

しかし Elastigroup は優秀です。スポットインスタンスの在庫不足をすぐに検知し、落ちた台数分のオンデマンドインスタンスが素早く起動され、サービスにはほとんど影響ありませんでした。こうなるとオンデマンドインスタンスで稼動され続けてコスト的に嬉しくないのではと思われるかもしれませんが、心配ご無用、Elastigroup にはオンデマンドとスポットの割合を在庫とコスト観点から適宜調整する仕組みがあり 6 、時間の経過につれて適度な按分に自動調整されてゆくのでした。

とは言えインスタンスが大量に落ちるというのは悲劇であることに変わりはありません。 同じ悲劇を繰り返さず、かつコストともバランスがとれるよう、現在は以下の設定で運用を行っています:

  • スポット : オンデマンド == 7:3
  • インスタンスタイプの種類を4種類に増加
    • t[23].mediumt[23].large といった感じです
  • 2種類のインスタンスタイプを優先して使うように設定
    • t3.(medium|large) といった感じです

変更内容は些細なものですが、これでだいぶ安定してスポットインスタンスを運用することができるようになりました。 オンデマンドインスタンスの割合が3割となっているのでコスト的にあまり嬉しくないかもとは思いましたが、RI でカバーできる台数+1台程度に落ちついているので、コスト影響は無視できる範囲に収まっています。

おわりに

ここまで読んでいただき有難うございました。筆者として感謝の念に堪えません。

いくらか導入の際につまづきはありますが、導入しただけでもなかなかの効果を発揮するものが Spotinst であり Elastigroup であるという所感が得られました。 オートスケーリンググループでいいかんじに EC2 インスタンスを運用されている諸賢におかれましては Elastigroup への移行によりコスト的なメリットを比較的手軽に享受することができます。無論筆者が泣く羽目になったのと同じ轍を踏まないよう、念には念を入れた検証を事前に実施されるよう強くお勧め致します。

この記事を読んで興味を持たれた向きには Elastigroup を一度検証をしてみてはいかがでしょうか。 Free プランであれば無料で使うことができます。筆者も導入を検討している間は Free プランにお世話になりました。

ふろく:自己紹介

改めまして Eight 事業部の秋本です。 Eight インフラ担当のひとりとして日々 Eight の安定稼動やサービス品質向上の為にチョコチョコと仕事をしています。いつも執務室の隅っこでメカニカルキーボードをバチバチ叩いています。やかましくてごめんなさい。

インフラ担当としての職務は前述の通りですが、最近は web サーバよりもウォーターサーバに齧りついている時間のほうが長いように思います。最近どうも異様に喉が渇きます。困ったものです。


Eightにまつわる関連記事 buildersbox.corp-sansan.com

buildersbox.corp-sansan.com


  1. https://aws.amazon.com/jp/ec2/pricing/reserved-instances/ 本当はもっと割引かれている気がしますが確認するのが億劫なので省略しました

  2. 一方でスケールアウトでこういう事態を処理できるのは設計がうまくいっている為と言えると思います。ですが今は EC2 費用の話をしているのでちょっと悲壮なムードを出しています

  3. これを利用してスポットインスタンスを利用する予算を定義した上でスポットインスタンスの値段が予算を下回る際に利用するという方法もあります (https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/using-spot-instances.html#spot-pricing) が、Eight ではそこまでカリカリな利用をしていないので省略します

  4. https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/spot-interruptions.html#spot-instance-termination-notices

  5. https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/load-balancer-target-groups.html#slow-start-mode

  6. https://api.spotinst.com/elastigroup-for-aws/concepts/general-concepts/market-scoring-managing-interruptions/]

© Sansan, Inc.