リリースラッシュの裏側で地道に積み重ねてきたコスト最適化施策を振り返る – Uzabase for Engineers

こんにちは。ソーシャル経済メディア「NewsPicks」のSREチームの飯野です。

2025年はNewsPicksの使い方が変わるような機能が立て続けにリリースされた一年でした。

  • 3月:BookPicks
  • 5月:コメントタイムライン
  • 6月:番組フォロー、記者フォロー
  • 7月:「業界ウォッチ」タブ、オリジナル記事のAI読み上げ
  • 10月:プレミアム会員向けにNewsPicks Learning動画を公開

これだけのリリースをしているということは、複数の開発チームが多くの試行錯誤を重ねているということでもあります。開発が活発になればビルド回数も増え、コンピューティングリソースも必要となります。AI(LLM)を活用すればその費用もかかります。BookPicksなら書影を保存するためのストレージ、NewsPicks Learningなら動画の再変換も必要です。

SRE視点ではAWSの運用コストへの影響が気になります。サービスの進化が続く限り、コストは積み上がっていきます。安心して開発を続けるため、利益を守るためにもコスト最適化は必要です。ここでは2025年のコスト最適化施策を振り返ろうと思います。

コスト削減余地を探す

2023年と2024年に大規模な削減を実施済み*1だったため、まずは新たな削減余地を探すところから始めました。

探し方はいろいろあると思いますが、今回は地道に細かい部分を見直していくことにしました*2

  • コストの大きいAWSサービスを見つける
    • コストエクスプローラーで「ディメンジョン:サービス」を選択
  • コストの大きいリソースを見つける
    • 「フィルターをサービス+連結アカウント」、「粒度:日別」を選択
    • 「ディメンジョン:リソース」or「ディメンジョン:タグ(コスト分配タグ)*3」を選択
  • システムの変更の履歴やドメイン知識を使って削減の目星をつける
    • 削減余地としては約$3/日(約$90/月)を目安とする
      • 日本円で1万円/月を超えるので10万円/年以上の削減となる
      • 11個削減できれば$1,000/月に。積み重ね、毎月継続して削減を大事にしていく
  • 予想削減金額と対応難易度を元に優先順位をつける
コストエクスプローラーで条件を絞り込みコスト削減余地の目星をつける

ここでつけた優先順位に従って、通常の業務と並行して地道に対応していきます。

今回の見直しで見つかった代表的なものをテーマ別に紹介します。

CloudWatch Logs に不要なログを出力しない

まずは基本からです。短時間で解決できることが多いので小さい金額でも優先して対応していきました。

ここで紹介した以外も含めると全体では$600/月(約9万円/月)ほどコスト発生を抑えることができました。年間だと$7,200/年(約108万円/年)。積み重ねが大事ですね。

AI(LLM)のPoCに利用していたLambdaのログ

オリジナル記事のAI読み上げなど、PoCではLambdaを使うことが多いのです。時折ログが爆発しているので声かけをしつつ対応していきます。

  • 状況:LLMへの入力、LLMの出力が全てログに出力されていました。
  • 対応:ログ出力はデバッグ時だけでよかったので抑制しました。
  • 効果:$6/日、$180/月の削減

ECSタスクでのログ

SREは開発チームに本番データをマスク&データ削減して開発環境に持ってくる仕組み*4を提供しています。古いデータを削除して開発環境の小さなインスタンスでも利用できるようにしているのですが、この削除処理で思わぬコストが発生していました。

tech.uzabase.com

  • 状況:Redisの過去データを削除する処理のログ出力コストが高い。
    • 次のようなシェルスクリプト*5でkey指定でデータを削除していました。
    • 呼び出し側で set -x を実行していたため、削除対象のkeyが全てログに出力されていました。
function delete_redis_key() {
    chunk_size=10000

    
    patterns=$(redis-cli -h "${ENDPOINT}" -p "${REDIS_PORT}" --raw keys $3)
    
    item_line=$(echo $patterns | tr '\n' ' ')
    
    read -a item <<< $item_line
    
    array_length=${#item[@]}
    
    for ((start=0; start<$array_length; start+=chunk_size)); do
        chunk=("${item[@]:$start:$chunk_size}")
        chunk_line=$(IFS=" "; echo "${chunk[*]}")

        
        redis-cli -h "${ENDPOINT}" -p "${REDIS_PORT}" del $chunk_line
    done
}
  • 対応: set -x を止める。
  • 効果: $5/日、$150/月の削減

RDSのスロークエリーログ

これはSREの不注意です。2024年11月にMySQL 8へ移行した時に細かいクエリの性能変化をみるためにlong_query_timeの値を変更していたのですが、これによってコストが増えていました。

  • 状況:スロークエリーログのコストが高い。
    • 移行時の検証で小さくしたlong_query_timeがそのままでした。
        new rds.CfnDBClusterParameterGroup(this, "DBClusterParameterGroup", {
            dbClusterParameterGroupName: `${prefix}np-cluster-parameter-group-aurora-mysql-80`,
            family: "aurora-mysql8.0",
            description: "newspicks",
            parameters: {
               long_query_time: "0.5", # デフォルト値は"5"10分の1のままだった}
       });
  • 対応: 値を戻しました。
  • 効果: $3/日、$90/月の削減

Next.jsの標準エラー出力のログ

動作には関係ない console.log が思わぬコストを発生させていました。10万円/年のコスト削減が行えるガード節です。

  • 状況:エラーログに大量の ReferenceError: window is undefined が記録されていた。
    • イメージとしては次のようなコードがサーバーサイドレンダリング時に呼び出されており、特定のページアクセス時に常に ReferenceError が発生する状態となっていました。
function getItem(keyName: string)  {
  try {
     return window.localStorage.getItem(keyName);
  } catch(e) {
     console.error(e);
     return undefined;
  }
}
  • 対応: try catchの前にガード節を追加しました。
function getItem(keyName: string)  {
  if (typeof window === 'undefined') {
    return undefined;
  }
  try {
     return window.localStorage.getItem(keyName);
  } catch(e) {
     console.error(e);
     return undefined;
  }
}
  • 効果: $2/日、$60/月(≒10万円/年)の削減

利用しなくなったDynamoDBテーブルを整理する

サービスの新陳代謝に伴って利用しなくなるテーブルも登場します。
今回の対応では$500/月=$6,000/年(約90万円/年)ほど削減できました。

データ移行実施済みのテーブル

2023年12月に5000万件のDynamoDBテーブルのデータ移行を行っているのですが、この移行元テーブルが残り続けていることがわかりました。

tech.uzabase.com

  • 状況:データ移行元のDynamoDBテーブルが残っている
    • 新旧DynamoDBテーブルへのダブルライトも継続しており、わずかながら参照も残っていました
  • 対応:参照を削除、その後テーブルも削除
    • 開発環境についてはすぐに対応してコスト発生を防ぎつつ、順番に対応していきました
      1. 開発環境のDynamoDBテーブルの再作成(ストレージコスト発生を抑える)
      2. APIの改修、DHWの対応(分割後のテーブルからビューを作成)。これは別チームに依頼しました
      3. 本番のDynamoDBテーブルを削除
  • 効果: $400/月の削減

DHWの開発環境に巨大なテーブルが残っている

  • 状況: DHWの開発環境にかつて利用していた追記専用テーブルが残っていた
    • メトリクスをみる限り1年更新されていないテーブルが残っていました。
    • DHWのワークフロー構築時はスケジュール実行されていたのですが、現在は停止しているのが理由でした。
    • このテーブルはDHWのワークフローでしか参照していないため、他の開発環境には影響がありませんでした。
  • 対応:開発環境のテーブルの再作成(ストレージコスト発生を抑える)
  • 効果: $100/月の削減

スケールダウンする、Fargate Spotを利用する

2020年ごろにNewsPicksのトップページを刷新する際に登場したフィードと呼ばれるマイクロサービスがあります。

tech.uzabase.com

このフィードですが、レコメンドのベースとなるデータの最適化やシステムの利用傾向の変化により、スケールダウンが可能ということがわかりました。

  • 状況: CPU使用率、メモリ使用率、ともに25%以下で安定している。
    • ピーク時のスループット、平均のCPU使用率ともに減ってきていました。
    • レコメンドのベースとなるベクトルデータの最適化が進み、ファイルサイズも半分になっていました。
  • 対応:スケールダウンとFargate Spot化を行う
    • 本番環境のスケールダウン(メモリのみ)
    • 開発環境のスケールダウン(CPU/メモリ)
    • 開発環境のFargate Spot化
  • 効果: $800/月の削減
  • 後日談
    • フィードについてはコメントタイムラインとの兼ね合いもあり機能がNewsPicksに移植され、マイクロサービスとしては終了しました。
    • これにより、さらに $1,100/月ほど削減できました。

全ての環境の成果物を作成しない

NewsPicksでは15環境の開発環境を用意しています。12-factor appでは1イメージを全ての環境で利用することが推奨されています。しかし、現実ではそこまで手が回らず、15環境(+本番)のマトリックスビルドが実行されてしまうアプリケーションも存在しています。

  • 状況:15環境分のビルドが動いてしまう

    • 1イメージで済む場合と比べ、CodeBuildでのビルド時間が15倍、ECRのイメージサイズが15倍になり、開発が活発になるほどコストが発生します。
  • 対応:環境ごとにビルドを分割

    • 根本対応は1イメージを全ての環境で実行可能にすること*6です。
    • 時間も人でも足りないため、ここでは場当たりな対応を行いました。
    • main用、各開発環境用で分割
      • 本番イメージの作成は常に行う
    • ブランチ名に環境名を入れることで環境用イメージのビルドを実行する
# 特定のブランチ名でビルドを実行
const envName = 'dev-1';
const invokeByPushEnvBranch =
    codebuild.FilterGroup.inEventOf(codebuild.EventAction.PUSH).andBranchIs(`${envName}/.*`);
  • 効果: 開発の活発度にもよるが $200/月の削減
  • 後日談
    • 数か月後、アプリケーションをメンテナンスしているチームでシングルアーティファクト化が実施され、根本解決しました
    • $400/月ほどの削減

延長サポート料金の発生を防ぐため計画的にデータベースを更新する

Amazon RDS、Amazon ElastiCache、Amazon OpenSearch Serviceなどで古いエンジンを利用していると延長サポート料金が発生します。

このコストの発生はNewsPicksがMySQL 5.7からMySQL 8へ移行するための強い動機となりました。

この延長サポート費用ですが、MySQL 5.7だけではなくPostgreSQL 12, Redis 5, OpenSearch 1.2などにも発生します。今後も適用範囲が増えていくようなので、計画的な更新が必要です。

NewsPicks本体では、2025年11月にサービス停止を伴うメンテナンスを実施しRedis 7へ移行を行いました。コストをきっかけにした実施でしたが、メモリ使用率が下がるなど性能面での恩恵もありました。

そのほかにやった細かいこと

今回紹介した以外にも次のようなコスト削減施策を行いました。

  • Redis から Valkey への移行(別記事で扱います)
  • 利用しなくなったWAFの削除(25個ほどで $150/月)
  • 役目を終えた常駐ワーカーの削除
  • などなど

まとめ

2025年は新機能の連続リリースという攻めの年でした。その裏でSREでは地道なコスト最適化という守りの取り組みも並行して進めてきました。

  • 紹介した施策だけでも$2,000/月(約360万円/年)以上のコスト削減できました。
  • 開発チームの協力でさらに$1,500/月(約270万円/年)以上のコスト削減できました。
  • 合計では$3,500/月(約630万円/年)以上のコスト削減となりました。

コスト最適化は一度やって終わりではなく継続的に見直していく必要があります。サービスが進化し続ける限り、新たな最適化の余地は必ず生まれます。今回の取り組みで得られた知見を活かし、2026年も安心して開発を続けられる基盤を維持していきます。




元の記事を確認する

関連記事