1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【実例】CloudWatch Logsの誤設定でAWS請求が3倍になった話 ─ 防御設定5選

1
Posted at

はじめに

個人開発で動かしている小規模な Web サービスで、ある月の AWS 請求がいつもの 3 倍になっていた。普段は月数千円程度のサービスなのに、その月だけ請求が跳ねていて、原因を追ったら CloudWatch Logs だった、という話を書く。

21 歳 / エンジニア歴 2 年目、業務では別件をやっていて、個人開発側の請求は完全に油断していた。「インフラ費そんなに伸びるはずないだろ」と思っていたが、ログまわりは想像以上に簡単に膨らむ。

何が起きていたか

サービスは Lambda + API Gateway + DynamoDB + S3 のよくある構成。負荷はそこまで高くなく、月のリクエスト数は数十万程度だった。

請求の内訳を Cost Explorer で見て、明らかに伸びていたのが次の 3 項目だった。

  • CloudWatch Logs Data Ingestion (取り込み): 想定の 4 倍以上
  • CloudWatch Logs Data Storage (保存): じわじわ増加
  • CloudWatch Metrics (カスタムメトリクス): Lambda 内で put_metric_data を呼びすぎていた

合計すると月数千円のサービスで CloudWatch 関連だけで数万円。完全に主役交代していた。

原因の特定方法

最初に Cost Explorer のフィルタを Service: CloudWatch に絞り、Group by: Usage Type で見る。これで「取り込み量が多いのか保存量が多いのか」が一発で分かる。

# CLI 派ならこれで日次の取り込み量が見える
aws ce get-cost-and-usage \
  --time-period Start=2026-04-01,End=2026-05-01 \
  --granularity DAILY \
  --metrics "UnblendedCost" \
  --filter '{"Dimensions":{"Key":"SERVICE","Values":["AmazonCloudWatch"]}}' \
  --group-by Type=DIMENSION,Key=USAGE_TYPE

DataProcessing-Bytes が突出していたので、次に取り込み量の多いロググループを特定する。

aws logs describe-log-groups \
  --query 'logGroups[].[logGroupName,storedBytes,retentionInDays]' \
  --output table

retentionInDaysnull (= 無期限) のロググループが大量にあり、しかも storedBytes が GB 単位のものがいくつかあった。これは「無期限保存 + DEBUG ログ垂れ流し」のサインである。

防御設定5選

1. リテンション設定を全ロググループに強制する

CloudWatch Logs はデフォルトで「無期限保存」になる。これは個人開発では確実に罠で、ロググループ作成と同時にリテンションを切るべきである。Lambda の自動生成ロググループも対象。

# 既存ロググループ全部に 14 日のリテンションを設定
aws logs describe-log-groups --query 'logGroups[].logGroupName' --output text | \
  tr '\t' '\n' | \
  while read -r LG; do
    aws logs put-retention-policy --log-group-name "$LG" --retention-in-days 14
  done

CDK / Terraform を使っているなら、リテンション無指定を lint で禁止するのが本筋。

resource "aws_cloudwatch_log_group" "lambda" {
  name              = "/aws/lambda/myfunc"
  retention_in_days = 14   # 必須にする
}

2. ログレベルでサンプリング

DEBUG ログを本番で出しっぱなしにしていたのが最大の戦犯だった。Lambda の場合、POWERTOOLS_LOGGER_SAMPLE_RATE でサンプリングできる。

from aws_lambda_powertools import Logger

logger = Logger(service="myapp")  # 環境変数 POWERTOOLS_LOGGER_SAMPLE_RATE=0.1 で 10% サンプリング

def handler(event, context):
    logger.debug("incoming event", extra={"event": event})
    # ... 通常処理

10% に絞るだけで取り込み量が桁で減る。

3. ロググループの分離

「全部 /aws/lambda/myfunc に流す」をやめて、リクエスト系・バッチ系・エラー系で分ける。リテンションをロググループ単位で変えられるので、エラーは長期保存、デバッグは短期、と運用しやすくなる。

import logging

req_logger = logging.getLogger("req")
err_logger = logging.getLogger("err")
# ハンドラを別ロググループ向けに分ける (CloudWatchLogsHandler 等)

4. Metric Filter の乱用を避ける

Metric Filter は便利だが、1 ロググループに対して大量に貼ると「カスタムメトリクスの数」で課金が跳ねる。PutMetricData を Lambda 内で都度呼ぶ実装も同じ罠を踏む。

EMF (Embedded Metric Format) で 1 回のログ出力に複数メトリクスを乗せる方が圧倒的に安い。

import json

print(json.dumps({
    "_aws": {
        "Timestamp": 1715000000000,
        "CloudWatchMetrics": [{
            "Namespace": "MyApp",
            "Dimensions": [["Service"]],
            "Metrics": [{"Name": "Latency", "Unit": "Milliseconds"}]
        }]
    },
    "Service": "checkout",
    "Latency": 123
}))

5. AWS Budgets でアラートを必ず設定

最後は「気づける状態にする」こと。月予算と日次の伸び率に対してアラートを貼る。

aws budgets create-budget --account-id 123456789012 --budget '{
  "BudgetName": "monthly-cloudwatch",
  "BudgetLimit": {"Amount": "5", "Unit": "USD"},
  "TimeUnit": "MONTHLY",
  "BudgetType": "COST",
  "CostFilters": {"Service": ["AmazonCloudWatch"]}
}' --notifications-with-subscribers '[{
  "Notification": {"NotificationType":"ACTUAL","ComparisonOperator":"GREATER_THAN","Threshold":80},
  "Subscribers":[{"SubscriptionType":"EMAIL","Address":"me@example.com"}]
}]'

CloudWatch 単独で budget を切っておけば、今回のような事故は数日で気づける。

まとめ

CloudWatch Logs は「とりあえず出しておく」で済ませると、簡単に AWS 請求の主役になる。個人開発レベルでも月数万円跳ねるので、リテンションとサンプリングは最初から入れておきたい。失敗してから気づいたことなので、これから個人で AWS を触る人は最初に Budgets を切ってから始めることをおすすめする。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?