こんにちはNewsPicks SREチームの美濃部です。
NewsPicksのSREのミッションの1つに「コストを適正化する」というものがあります。サービスの規模拡大に比例してインフラコストが増えないようにし、売上に対するコストの割合を低く維持していくのがミッションになります。
今回はこのミッションに対するアクションとして開発環境のインフラコストを適正化した話をします。
NewsPicksの開発環境について
まず、NewsPicksの開発環境について概要を説明します。
インフラ基盤は本番環境と同様にAWSを利用しており開発チームは現在10以上のチームが存在し、それぞれのチーム専用に用意された開発環境を利用しています。
2年程前までは開発環境はIaC化されておらず手動で構築していたのでチーム毎に環境を構築するのは手間と時間がかかりなかなか増やせない状態でした。その課題を解決する為にcdkでIaC化してからはそれほど手間もなく構築できるようになった事もあり、チーム毎の環境を必要に応じて増やしていきました。
簡単に環境を増やしていけるのはIaCのおかげでとても素晴らしい事なのですが、環境を増やしていった結果当然コストは比例して増加していきました。
開発環境なのでコンピューティングリソースは低めで構築しているのですが、積もり積もってこれはさすがに無視できないコストになってきたので対策した方が良いのではないかという話がでてきました。
開発環境のコストをどうやって適正化したか
とても単純な話ですが「開発に使っていない時間帯」は無駄なので料金タイプが従量課金のリソースについては停止する対応を行いました。
この対応はよくある話なので一般的な対応だと思いますが「開発に使っていない時間帯」はチームによって違う可能性があるのはもちろんのこと、日によっても時期によっても違う可能性があります。よって「開発に使っていない時間帯」というのを固定化するのは開発者体験として悪いですし、その日だけ時間帯を変える運用が面倒であれば誰も変更せずにストレスをかかえたまま日々の開発を行う事になると思います。
弊社SREのミッションには「開発者体験を高める」というものもあります。コスト削減の為だからといって開発者体験は可能な限り下げたくありません。(もちろんトレードオフになるケースもあります)
そこで各チームの判断で簡単に利用時間(社内では稼働時間というキーワードを使っているので以降、稼働時間と表現します)を変更する良い方法がないか考えた結果、Googleカレンダーで調整できたらいいよねって話になりました。弊社ではグループウェアとしてGoogle Workspaceを利用しており日常的にGoogleカレンダーを利用しているのでGoogleカレンダーで調整するのが自然な流れだと考えました。この対応であれば開発者体験を維持しつつコストを適正化する事ができると思いました。
実際の稼働時間カレンダーはこんな感じになります。現在14の開発環境が存在するのでそれぞれにカレンダーを用意しています。以下はそのうちの1つのカレンダーを表示している状態です。稼働時間を変更したい場合はドラッグ&ドロップするだけです。最高のUIです。
稼働時間対応を実現する仕組みについて
構成は以下の通りです。とてもシンプルです。
ポイントは以下の3点です。
- EventBridgeを利用して15分毎にStep Functionsをスケジュール実行するルールを設定(参考: スケジュールに従って実行する Amazon EventBridge ルールの作成)
- Google Calendar APIのEvents:listを利用して今現在リソースが起動中であるべきなのか、停止中であるべきなのかを判断するLambdaを実行
- 2の判断によって各リソースの起動もしくは停止を行う子Step Functionsを起動
3について補足です。
- Step Functionsでは「AWS SDK のサービスの統合」を利用して各AWSサービスの API アクションを呼び出すことができるのでほぼ設定だけで実現しています
- ElastiCacheについては停止の概念がないので停止の時はスナップショットを取得してから削除し、起動の時はスナップショットから作成しています
- 親Step Functionsにて各サービス毎にリソースのMapを作成し、その中でリソース毎に子Step Functionsを呼び出しています
- 実行時間を短縮する為に並列で実行できるものはParallelを使って並列で実行しています
- 起動時はデータベースリソースを先に起動した後にコンピューティングリソースを起動する事によって、データベースが起動していない状態でアプリケーションが起動しプロセスが異常終了しないようにしています
- データベースリソース起動用の子Step Functionsでは起動のAPIをコールした後、ステータスがAvailableになるまでステータスチェックをループ処理しており、起動中のままコンピューティングリソースが起動しないように制御しています。以下はRDS起動用Step Functionsの実行例です。
RdsCheckStatusAfterStart
でAvailableかどうかを判定しています。
この仕組みは14個ある開発環境毎に作成していますが、cdkで管理しているので特に負担もなく作成できています。修正が入ってもcdk deploy
で全ての環境をさっと更新できるのは本当に素晴らしいです。
実際どれくらい削減できたのか
結論からいうと稼働時間対応したリソースについては約70%削減できています。
前提
稼働時間はデフォルトで平日の9:30から18:30の9時間で設定しています。(現状は平日が祝日であっても稼働時間として設定されていますが、こちらについても設定されないように対応したいと考えています)
算出方法
稼働時間を対応する前に事前にどれくらい削減できるかは以下の計算で行っていましたが、実際その通りになっています。
- 祝日を考慮しない平日は年間約260日存在するので平日の総稼働時間は 9時間 x 260日 = 2,340時間
- 1年の総時間は24時間 x 365日 = 8,760時間
- 稼働していない時間の割合は1 - (2,340時間 / 8,760時間) = 約73%
- 実際には稼働時間は多少延長されたりして増えるので約70%くらいになる
まとめ
今回の開発環境の稼働時間対応について、開発工数はそれほどかからないにも関わらずコストが約70%削減できたので効果は絶大でした。稼働時間をデフォルトで設定しつつ各開発チームの判断で柔軟に稼働時間を調整できる事は開発者体験としてとても良いです。
コストが削減できた分の予算は開発者の生産性向上のためのサービスやツールの購入にあてたり採用にあてる事ができ、結果的に開発者体験の向上につながります。
NewsPicksのSREメンバーは日々コストの異常検知を確認したり、定期的に削減できる余地がないかを模索して「コストを適正化する」ミッションを遂行しています。現状は他にもコスト削減の為のアクションが残っているので地道に対応していこうと思います。