はじめに
上司から「担当サービスのCloudWatch、コストが高いから見直して」と言われました。
確認してみると、ECS(PHP/Apache)で運用しているサービスのCloudWatch請求が月数十万円。
「こんなに高くなるのか…」というのが第一印象です。
何から手をつければいいか分からなかったので、まずコストの内訳を調べるところから始めました。
本記事では、その調査からコスト削減するまでの流れを紹介します。
調査・対応の流れ
どのログを削減すれば効果があるか分からなかったので、まずコストの内訳を見て原因を特定し、そこから不要なログを削減しました。
- Cost Explorerでコストの内訳を確認する
- 取り込み量が多いロググループを探す
- 該当のロググループ内で不要なログを探す
- 不要なログを削減する
- 効果を測定する
Step1:Cost Explorerでコストの内訳を確認する
Cost Explorerで下記を設定します。
- Service:AmazonCloudWatch
- Group by:Usage type
| Usage type | 割合 |
|---|---|
| APN1-DataProcessing-Bytes | 約98% |
| APN1-TimedStorage-ByteHrs | 約2% |
請求の約98%が取り込み(DataProcessing-Bytes)でした。
保存(TimedStorage)は全体の1〜2%程度なので、保持期間を短くしても効果は薄そうです。
そのため、取り込み量を削減する方針で進めました。
補足:Usage typeの見方
Cost ExplorerのUsage typeは「リージョン接頭辞 + 課金カテゴリ」で表示されます。
CloudWatch Logsでよく見るカテゴリは次のとおりです。
DataProcessing-Bytes:ログ取り込み(Operation: PutLogEvents)
TimedStorage-ByteHrs:ログストレージ(Operation: HourlyStorageMetering)
DataScanned-Bytes:Logs Insightsのスキャン(Operation: StartQuery)
Step2:取り込み量が多いロググループを探す
ロググループごとの取り込み量はCloudWatch Logsのメトリクス(IncomingBytes)で確認できます。
参考:AWS re:Post - Determine which log group is causing a bill increase
マネジメントコンソールで確認する手順
- CloudWatchコンソールを開く
- 左メニューで「メトリクス」>「すべてのメトリクス」を選ぶ
- 名前空間の一覧から「Logs」>「ロググループメトリクス」を選ぶ
- 検索ボックスに「IncomingBytes」と入力してフィルタ
- 該当サービスのロググループにチェックを入れる
- 「グラフ化したメトリクス」タブを開く
- グラフの設定を下記に変更する
- グラフの種類:数値
- 統計:合計
- 期間:30日
これで取り込み量が多いロググループを特定できます。
Step3:該当のロググループ内で不要なログを探す
コストを抑えるにはログの取り込み量を削減する必要があります。
Step2で特定したロググループに対し、Logs Insightsで出力回数が多いログ、またはサイズの大きいログを探します。
Logs Insightsはスキャン量に応じて課金されるため、今回は3日間に絞って調査しました。
コストを抑える方法は記事末尾の補足にまとめています。
出力回数が多いログを探すクエリ
今回はmsgフィールドでグルーピングしました。
ログの種別を判別できるフィールドであれば、levelやactionなど他のフィールドでもOKです。
fields msg
| filter ispresent(msg)
| stats count(*) as lines by msg
| sort lines desc
| limit 50
上位に表示されやすいログの例:
- リトライ・ループ内で出力されるログ
- 定期処理の「開始/終了」ログ
- 正常系でも毎回出力されるアクセスログ・処理完了ログ
サイズの大きいログを探すクエリ
出力回数が少なくても、1行あたりのサイズが大きいとコストに影響します。
msgごとに@messageの文字数合計を算出し、大きい順に並べます。
fields msg, @message
| stats
count(*) as lines,
sum(strlen(@message)) as bytes_est,
avg(strlen(@message)) as avg_line_size
by msg
| sort bytes_est desc
| limit 50
| カラム | 説明 |
|---|---|
| lines | 出力回数 |
| bytes_est | 文字数合計(取り込み量の目安) |
| avg_line_size | 1行あたりの平均文字数 |
avg_line_sizeが大きいログは、巨大なペイロードを含んでいる可能性があるので見直した方が良いかもしれません。
Step4:不要なログを削減する
Step3で候補を出せたら、不要なログを削減します。
削減対象とする基準と、今回実施した具体的な対応を記載します。
削減対象の基準
| 項目 | 説明 |
|---|---|
| debug/trace系 | 調査には便利だが、本番で常時出力するとコストが増加する |
| ループ・リトライ内のログ | 1行あたりが小さくても、総量が増える |
| 巨大ペイロード | リクエスト/レスポンス本文や巨大スタックトレースなど |
今回実施した対応
- ログレベルの変更
- PHPのログレベルをDEBUGからWARNに変更
- 調査のためにログレベルを上げたまま戻し忘れていた箇所
- PHPのログレベルをDEBUGからWARNに変更
- ループ内ログの集約
- 1件ごとに出力していた処理完了ログを、処理件数のサマリーログ1行に集約
- 巨大ペイロードの改善
- レスポンスボディをログ出力していた箇所を、調査に必要なフィールドのみ出力するよう変更
Step5:効果を測定する
Step4の対応後、Cost ExplorerでDataProcessing-Bytesの推移を確認したところ、約7割のコスト削減を達成できました。
今回の削減、ほとんどがログレベルの戻し忘れによるものでした…。
調査でDEBUGに上げたまま放置していただけで、これほどコストが膨らんでいたとは思いませんでした。
「ログくらい大したことない」と思いがちですが、調べてみると意外と無駄は見つかります。
やはり定期的なコストの見直しは大事ですね。
まとめ
CloudWatchのコストが高い場合は、以下の手順で対応します。
- Cost ExplorerのUsage typeで内訳を確認する
- CloudWatch Logsは保存より取り込みが高いケースが多いため、取り込み量の削減を優先する
- Logs Insightsで不要なログ(出力回数が多い・サイズが大きい)を特定し、削減する
今回は不要なログの削減でコストを抑えることができましたが、まだ改善の余地があるので少しずつ進めていきたいですね〜
補足:Logs Insightsのスキャンコストについて
Logs Insightsのスキャンは圧縮前のデータ量に対して課金されます。(東京:0.0076 USD/GB)
例えば、調査に必要な期間が3日で十分なのに1週間に設定し、スキャン量が500GBになった場合、0.0076 × 500 = $3.8(約600円)かかります。
1回あたりの金額は小さくても積み重なると結構痛いため、
- スキャン期間を必要最小限にする
- フィールドインデックス機能を使用する
などの対策をするのがオススメです。
