BtoC大規模サービスにおけるAWSコスト最適化の実践

  • s3
    s3
  • ecs
    ecs
  • fargate
    fargate
  • sqs
    sqs
2023/09/11に公開

この投稿はでも表示されます。

まとめ

観点 内容
課題 トラフィック増に伴うコストのブラックボックス化。スパイク耐性のための過剰プロビジョニングと、不要リソースの放置が事業利益を圧迫していた。
対応 SQS導入による負荷平滑化(同期/非同期パスの分離)や、Fluent Bitを用いたログ集約によるS3リクエスト削減など、アーキテクチャの見直しで最適化。
運用 AWS Budgetsによる異常検知、EndDate タグを用いたリソース棚卸しの自動化を整備。設計初期にコスト効率を検討するF文化を醸成。
結果・成果 月間AWS利用料を 30% 削減。インフラ費用の透明性を確保し、事業成長に合わせたスケーラブルかつ低コストな基盤を実現。

課題・背景

BtoCサービスの成長に伴い、トラフィックの増大は喜ばしい一方で、インフラコストが事業利益を圧迫し始めるという課題に直面しました。当時の状況を整理すると、大きく3つの問題がありました。

BtoCサービスの成長とコストの乖離

ユーザー数の増加に比例してインフラ費用が膨らみ、当初の事業計画で想定していたコストラインを大きく上回っていました。特にキャンペーン実施時のアクセススパイクに対応するため、常にピーク時を想定したリソースを維持せざるを得ず、非効率なコスト構造が常態化していました。

管理の不透明さ

AWS Organizationsを利用して複数のプロジェクトが動いていましたが、どのサービスや機能がコスト増の主因なのかを即座に特定できない状態でした。コスト管理が「月末の請求書確認」という事後処理に終始しており、予期せぬ高額請求が発生してから調査を開始するという、常に後手に回る運用となっていました。

リソースの放置

開発速度を優先するあまり、検証環境の消し忘れや、終了したキャンペーン用のリソースが稼働し続けているケースが散見されました。いわゆる「野良アカウント」や「野良リソース」の存在が、塵も積もれば山となる形で全体のコストを押し上げていました。

対応方針

これらの課題に対し、単発の削減作業ではなく、継続的にコストを最適化するための「仕組み」を作ることを方針としました。

「仕組み」による自動検知

人的リソースを割いて毎日ダッシュボードを監視するのではなく、AWS Budgetsを活用して異常を自動検知する体制を目指しました。予算に対する進捗だけでなく、前日比での急激な変動をアラート通知することで、異常発生から24時間以内に初動対応ができる状態を目標としました。

アーキテクチャの最適化

「ピークに合わせてリソースを確保する」という固定的な考え方から、「需要に連動してコストが可変する」構成への転換を図りました。具体的には、後述するSQSを用いたキューイングの導入など、アプリケーションのアーキテクチャレベルでの改善を方針に据えました。

ガバナンスの徹底

「誰が、何の目的で、いつまで使うのか」を明確にするため、タグ付けルールの策定を最優先事項としました。特に EndDate(終了期限)タグを必須とし、期限を過ぎたリソースを可視化・削除できる運用フローを構築することで、不要なリソースの放置を構造的に防ぐ方針を立てました。

検証(調査・測定)

コスト削減を断行する前に、まずは現状のコスト構造を詳細に分解し、削減インパクトの大きい箇所を特定する作業を行いました。

コスト構造の可視化

まず、AWS Cost Explorer を用いて過去 6 ヶ月間のコスト推移を分析しました。

  • サービス別の特定: 請求額の大部分が EC2(RDS 含む)、S3、DynamoDB、およびデータ転送(Data Transfer)に集中していることを確認しました。
  • AWS Resource Groups の活用: タグ付けが不十分だったため、Resource Groups を用いて暫定的にプロジェクトごとのリソースをグルーピングし、どのビジネスユニットがコストを牽引しているかを可視化しました。
  • AWS Trusted Advisor の参照: 「低負荷の Amazon EC2 インスタンス」や「アイドル状態の DB インスタンス」を抽出し、即座に停止・縮退可能なリソースのリストを作成しました。

詳細分析:ログとAPIコール

CloudWatch Logs のインジェスト(取り込み)量と請求額を照らし合わせ、単価は低いものの「量」で圧倒しているリソースを調査しました。

  • ログ出力量の異常: 特定のサービスで本番環境にもかかわらず DEBUG レベルのログが大量に出力されており、CloudWatch Logs の取り込み料金を押し上げていることが判明しました。
  • S3 のリクエスト数: 保存容量(GB)に対するコストよりも、PUT/LIST リクエスト数による課金が高いバケットを特定。アプリケーション側で数KB単位の微細なファイルを高頻度で書き込んでいる挙動を確認しました。

DynamoDB のアクセスパターン解析

DynamoDB については、CloudWatch メトリクスと各テーブルの「ストレージの詳細」をサンプリングしました。

  • アクセス頻度の偏り: 全データのうち、直近 30 日以内にアクセスされたのは全体の数%であり、大部分が「参照はされないが保持が必要なアーカイブデータ」であることが分かりました。
  • アイテムサイズの検証: 数MBにおよぶバイナリデータ(Blob)が直接属性値として格納されており、読み込み/書き込みキャパシティユニット(RCU/WCU)を無駄に消費している箇所を特定しました。

実施内容

検証フェーズで特定したコスト要因に対し、アーキテクチャの変更を含む具体的な最適化を実施しました。

コンピューティング:スパイク対策とリソースの適正化

キャンペーン時の突発的なアクセス増に対し、DB(RDS)のスペックをピークに合わせるのではなく、負荷を平滑化する構成へ変更しました。

  • SQSによるキューイング導入: 大量のリクエストが集中するものの、数秒〜数分の処理遅延が許容される非クリティカルな書き込み処理(例:アクセスログ、アクティビティ履歴など)を SQS でバッファリングするように変更しました。ピーク時の負荷を後ろに「逃がす」ことで、RDSのスペックをスパイクの頂点に合わせる必要がなくなり、より安価なインスタンスへのダウンサイジングを実現しました。

Before: ピーク負荷に合わせた設計

After: 負荷平滑化による最適化

  • ログレベルの適正化: CloudWatch Logs のコストを抑えるため、本番環境のログレベルを DEBUG から INFO へ変更。必要な監査ログやエラーログのみを保持するようフィルタリングを強化しました。

ストレージ・DB:データ特性に応じた配置の最適化

データの「鮮度」と「サイズ」に着目し、保存場所とクラスを最適化しました。

  • DynamoDBのコスト最適化

    • アクセス頻度が低い過去データの保持用テーブルに対し、テーブルクラスを Standard-IA(低頻度アクセス) へ移行。これにより、ストレージコストを約6割削減しました。
    • 数MB単位の大きなBlobデータは、DynamoDBに直接格納するのをやめ、Amazon S3へオフロードする設計に変更。DynamoDB側にはS3のオブジェクトキー(パス情報)のみを保持することで、WCU/RCUの消費を劇的に抑えました。
  • S3のライフサイクル管理:

    • Fluent Bitによるサイドカー・バッファリング: アプリケーションから直接S3にアクセスしログを出力するのではなく、サイドカーとして配置したFluent Bitで一度ログを受け、メモリ上でMB単位に集約してからアップロードする設定に変更しました。これにより、S3へのPUTリクエスト数を物理的に激減させました。
    • Intelligent-Tieringの全面適用: 保存されたデータに対しては S3 Intelligent-Tiering を有効化し、アクセス頻度に基づいたクラス移動をAWS側に委ねることで、管理工数をかけずに「取り出した際のコスト」と「保存コスト」のバランスを最適化しています。
    • Glacierへの自動移行: コンプライアンス等で長期保存が必要なログについては、ライフサイクルポリシーを定義。作成から60日が経過したものを自動的に Glacier Instant Retrieval へ移行し、取り出し速度を維持したまま、ストレージ単価を最小化しました。

契約・運用:ガバナンスと割引オプションの適用

技術的な変更と並行して、無駄なリソースの発生を防ぐための運用ルールを導入しました。

  • EndDateタグの徹底: 開発・テスト環境の全リソースに EndDate タグを必須化。期限が切れたリソースを週次でリストアップし、不要なものを削除する運用フローを確立しました。
  • アカウントの集約: 各所に点在していた「野良アカウント」を、Organizations下の管理アカウントへ移行。一括請求(Consolidated Billing)にまとめることで、S3等のボリュームディスカウントが組織全体で適用されるようにしました。
  • 割引プランの購入: 利用実績が安定しているベースラインのリソースに対し、Savings Plans および Reserved Instances (RI) を適用。オンデマンド料金からのコスト削減を確定させました。

定量成果

一連の施策を実施した結果、インフラコストおよび運用指標において以下の変化が見られました。

  • 月間AWS利用料の削減: 対策実施前と比較し、サービス全体の月間コストを約 30% 削減しました。特に、ピーク時に合わせた過剰なRDSスペックの廃止と、DynamoDBのテーブルクラス見直しが大きな寄与となりました。
  • ストレージ単価の最適化: S3 Intelligent-Tieringとライフサイクルポリシーの適用により、保存容量あたりの単価が改善。特に長期ログ保存コストについては、Glacierへの自動移行によって 約70% のコスト抑制に成功しました。
  • 異常検知の早期化: AWS BudgetsとSlack通知の連携により、予期せぬコスト増(APIのループや設定ミス等)が発生した際、発生から 24時間以内 に検知・対応を開始できる体制が整いました。
  • リクエストコストの低減: 「MB単位での集約保存」への切り替えにより、S3へのPUTリクエスト数が大幅に減少。ログ保存用バケットにおいて、容量課金(GB)よりも高額になっていたリクエスト課金部分の費用を 80% 削減しました。

定性成果

数値的な削減だけでなく、開発チームの運用面においてもポジティブな変化がありました。

  • 「コストも設計の一部」という意識の定着: 性能や可用性だけでなく、「その設計がコストにどう響くか」を開発初期段階で検討する文化が醸成されました。性能や可用性だけでなく、「その設計がコストにどう響くか」を開発初期段階で検討する文化が醸成されました。具体的には、DB(DynamoDB)に巨大なペイロードを直接書き込むのを避け、S3へのオフロードを前提とした設計や、非同期処理(SQS)を活用したリソースの平滑化を意識した実装が自発的に行われるようになりました。

  • ガバナンスの強化: アカウントの集約とタグポリシーの適用により、いわゆる「野良リソース」の発生を構造的に抑止。セキュリティ面での一元管理にも副次的な効果をもたらしました。