こんにちは。NewsPicksのWeb Reader Experience Unitで学生インターンをしている西(@yukinissie)です。
昨年の 9 月までSREチームに所属しており、その頃にNewsPicksの全ての常駐バッチサーバーをAWS CDKを用いてAWS FargateとAWS Fragate Spotを併用するようにさせましましたのでコストの変化や工夫した点についてご紹介します。
AWS Fargate Spotとは?
AWS Fargate Spot(以降、Fargate Spot)とは、AWS re:Invent 2019にてAWSが発表したAWS Fargate(以降、Fargate)の新しい機能です。この機能を有効にすると通常のFargateよりもコスト(料金)が最大70%割引で利用することができます。
AWS公式ブログ「AWS Fargate Spotの発表 – Fargateとスポットインスタンスの統合」 aws.amazon.com
実際どれくらいのコストを削減できたか
下記図1はNewsPicksの常駐バッチサーバーのFargate Spotを含むFargateのコストと使用量の推移です。順を追って説明します。
0. ECSコンテナ化
SREチームでは8月の1ヶ月間を利用して徐々に常駐バッチサーバーをEC2からECSに移行していました。そのためグラフは山を登るような形でFargate全体の料金が上昇していました。(図1中、黒い縦線の間の期間)
1. 使用量はほぼ横ばい
ECSコンテナ化が終わり、使用量が横ばいとなり、コストの山の頂上が見えてきました。通常のFargateであればコストのグラフの最大値の部分を払い続けるはずでした。
2. Fargate Spotの適用
図のオレンジ色の線で表している日にFargate Spotを適用しました。
3. コストが約47%削減!!!
驚いたことにFargate Spotを適用したその日から直前の最もコストが掛かっていた日と比べて約47%ものコストが削減されました!しかも、今日までほぼ変わらずです。やったー!!
AWS CDKでAWS Fargate Spot機能を適用する際に工夫したこと
課題
Fargate Spotを適用することでコスト削減できるので、全てのタスクに対して適用したいと思いました。しかし、Fargate Spotは通常のFargateと違い、AWSの都合でタスクが中断されることがあります。今回Fargate Spot化させる常駐バッチサーバーは中断自体は問題ないのですが、全てのタスクをFargate Spotにするとタスク数が0になってしまう可能性があり、可用性の観点から本番環境に関してはそれを許容したくありませんでした。また、開発環境に関してはコスト削減を優先し、全てのタスクをFargate Spotにしたいと考えました。それから、NewsPicksではAWSリソースをAWS CDK(以降CDK)で管理しているので上記の課題をCDKで実装したいと考えました。
解決策
FargateとFargate Spotを併用するようにキャパシティープロバイダー戦略を工夫して設定することで可用性とコストのバランスを取ることができると考えました。そして、それを自動で決定することや条件によって全てFargate SpotタスクにするようなIaCモジュールをCDKで実装することにしました。
FargateとFargate Spotの併用
Fargate Spotを適用する上でキャパシティープロバイダー戦略を工夫して設定することにしました。その工夫とはタスクの必要数(desiredCount)がNの時、キャパシティープロバイダーのFargateのウェイトを1にして、Fargate SpotのウェイトをN-1に設定することです。(図2)
これによってFargateタスクが必ず1つ起動するので常駐バッチサーバーが継続動作しつつ、並列稼働で2タスク目以降はFargate Spotでコスト削減ができる状態になります。
キャパシティープロバイダーのベースやウェイトの数の考え方についてはクラスメソッドさんの下記記事を参考にしました。ありがとうございます。
DevelopersIO「Fargate Spotにおける各Provider毎のタスク数起動推移を検証してみた #reinvent」 dev.classmethod.jp
キャパシティープロバイダー戦略を自動で決定するIaCモジュール
キャパシティープロバイダー戦略を工夫して設定することで可用性を保たせたままコスト削減ができることが明らかになりましたので、続いてはCDK(TypeScript)でキャパシティープロバイダー戦略を自動で決定するIaCモジュールの実装について紹介します。
以下は今回作成したキャパシティープロバイダー戦略を自動生成するメソッドです。
// 常にFargate Spotを利用するキャパシティープロバイダー戦略 function createFargateSpotCapacityProviderStrategies( desiredCount = 1 ): CapacityProviderStrategies { return [ { capacityProvider: "FARGATE", base: 0, weight: 0, }, { capacityProvider: "FARGATE_SPOT", base: desiredCount, weight: 1, }, ]; } // 少なくとも1つはFargateタスク、それ以外はFargate Spotを併用するキャパシティープロバイダー戦略 function createFargateHybridCapacityProviderStrategies( desiredCount = 1 ): CapacityProviderStrategies { return [ { capacityProvider: "FARGATE", base: 0, weight: 1, }, { capacityProvider: "FARGATE_SPOT", base: 0, weight: desiredCount - 1, }, ]; } // フラグによってキャパシティープロバイダー戦略を切り替える function createCapacityProviderStrategies( useSpotAlways: boolean, desiredCount = 1 ): CapacityProviderStrategies { if (useSpotAlways) { return createFargateSpotCapacityProviderStrategies(desiredCount); } return createFargateHybridCapacityProviderStrategies(desiredCount); }
createFargateHybridCapacityProviderStrategies()
メソッドではタスクの必要数(desiredCount)を与えると、最低1タスクのFargateと、それ以外のタスクはFargate Spotを併用するキャパシティープロバイダー戦略を返します。また、createFargateSpotCapacityProviderStrategies()
メソッドは開発環境のような常にFargate Spotを利用する場合に使うキャパシティープロバイダー戦略を返します。そして createCapacityProviderStrategies()
メソッドは上記2つの戦略の内どちらかを返すラッパー関数で、常にFargate Spotを使う戦略を返すかどうかのフラグ(useSpotAlways)を指定することで切り替えられるようになっています。
これにより複数環境に十数台ある様々なタスク必要数を持ったタスクのキャパシティープロバイダー戦略を柔軟にかつ自動的にCDKで設定することができるようになりました。
まとめ
AWS Fargate Spotを適用したおかげで通常のAWS Fargateの半額で利用することができました!キャパシティープロバイダー戦略を工夫して設定することで常にタスクを動かしつつコストの節約をすることができます。また、AWS CDKを利用すれば様々なタスクの要件に対して柔軟に対応することも可能です。AWS Fargateを利用している方はコスト削減のためにAWS Fargate Spot機能の有効化を検討をしてみるのはいかがでしょうか?