AWS基礎 #7 CloudWatch 入門 — ログ / メトリクス

#1 ~ #6 で AWS セットアップの基盤が揃いました。次は運用のもう 1 つの軸 — 何がどこで何をしているか を見るツールです。

CloudWatch は AWS の Observability 標準です。AWS 内のほぼすべてのサービスがデフォルトでメトリクスを CloudWatch に送り、ログも CloudWatch Logs が受け取ります。運用の最初の視野はここから始まります。

この記事では CloudWatch の 4 つの構成 — Logs / Metrics / Alarms / Dashboards — を一気に整理します。

全体像 — CloudWatch の 4 つの構成 #

構成要素何かよくある使い方
Logsテキストログの保存 / 検索EC2 / Lambda / ECS / API Gateway のログ
Metrics時系列の数値 (CPU%、リクエスト数など)すべての AWS サービスが自動送信
Alarmsメトリクスが閾値を超えたら通知 / アクション運用通知、自動スケール
Dashboardsグラフ / ウィジェットページチーム / サービスごとに一目で

この 4 つが 1 つの流れで繋がります。ログ → メトリクス → アラーム → ダッシュボード。

CloudWatch Logs #

ロググループとログストリーム #

構造
Log Group           — 普通 1 つのアプリケーション / サービス単位
  └── Log Stream    — 普通 1 つのプロセス / コンテナ単位
        └── Log Event — 1 行
項目
Log Group/aws/lambda/my-function/ecs/my-service/var/log/myapp
Log StreamLambda 実行環境 ID、ECS Task ID、EC2 インスタンス ID
Log Event1 行のテキスト + timestamp

Lambda / ECS Fargate は 自動で ログを CloudWatch Logs に送ります。EC2 は CloudWatch Agent または補助ツール (fluent-bit など) が必要です。

Retention — もっとも重要な 1 つ #

デフォルトの retention は永続です。 そのままにするとログが永遠に積もりコスト暴騰。登録直後 / 新しいロググループごとに retention を決める必要があります。

項目推奨 retention
一般アプリケーションログ30~90 日
デバッグ / 開発ログ7 日
セキュリティ / 監査ログ (CloudTrail)1~7 年 (S3 に送る方が安い)
Lambda ログ14~30 日
retention 設定
aws logs put-retention-policy \
  --log-group-name /aws/lambda/my-function \
  --retention-in-days 30
すべてのロググループに一括適用
aws logs describe-log-groups --query 'logGroups[].logGroupName' --output text \
  | tr '\t' '\n' \
  | while read name; do
      aws logs put-retention-policy --log-group-name "$name" --retention-in-days 30
    done

この 1 行が CloudWatch のコスト事故の半分以上 を防いでくれます。

新しいロググループの自動 retention #

新しく作られるロググループに自動で retention を適用する方法は 2 通り。

方法 1: EventBridge + LambdaCreateLogGroup イベントを受けて自動適用 (実戦でよくある)

方法 2: ポリシーで強制 — 作成時に retention を明示しないと拒否 (やや過剰)

Lambda のログ送信 #

Lambda — ただ print
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)

def handler(event, context):
    logger.info("Received event: %s", event)
    return {"ok": True}

stdout / stderr が自動で CloudWatch Logs に流れます。ロググループ名は /aws/lambda/<関数名>

EC2 / ECS — CloudWatch Agent #

EC2 は自動ではありません。CloudWatch Agent のインストールが必要。

Amazon Linux 2023 / Ubuntu
sudo yum install -y amazon-cloudwatch-agent       # AL
# または
wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb
sudo dpkg -i amazon-cloudwatch-agent.deb

設定ファイル /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json:

シンプル設定
{
  "logs": {
    "logs_collected": {
      "files": {
        "collect_list": [
          {
            "file_path": "/var/log/myapp/*.log",
            "log_group_name": "/myapp/server",
            "log_stream_name": "{instance_id}",
            "retention_in_days": 30
          }
        ]
      }
    }
  },
  "metrics": {
    "metrics_collected": {
      "mem":  { "measurement": ["mem_used_percent"] },
      "disk": { "measurement": ["used_percent"], "resources": ["*"] }
    }
  }
}

ECS の Fargate はコンテナ定義に awslogs ドライバを書けば自動 — これは 上級 #1 で詳しく。

Logs Insights — クエリで検索 #

CloudWatch Logs の検索 / 分析ツール。SQL ライクな独自文法。

もっともシンプルなクエリ
fields @timestamp, @message
| sort @timestamp desc
| limit 100
ERROR だけ
fields @timestamp, @message
| filter @message like /ERROR/
| sort @timestamp desc
| limit 50
リクエスト時間の分布 (Lambda)
fields @timestamp, @duration
| filter @type = "REPORT"
| stats avg(@duration), max(@duration), count(*) by bin(5m)
API Gateway — 5xx 比率
fields @timestamp, status, path
| filter status >= 500
| stats count(*) as errors by path
| sort errors desc

よく使うコマンド:

コマンド何か
fields表示するフィールド
filter条件フィルタ
parse文字列からフィールド抽出
stats集計 (count、avg、max、percentile)
sortソート
limit最大結果数
bin(5m)時間バケット

Logs Insights のコスト注意 #

クエリ時に スキャンしたデータの GB あたり課金 (~$0.005/GB)。大きなロググループを無制限の時間でクエリするとコスト事故。常に時間範囲を狭く。

CloudWatch Metrics #

メトリクスは 時系列の数値。AWS のほぼすべてのサービスが自動送信します。

よく見るメトリクス #

サービスよく見るメトリクス
EC2CPUUtilizationNetworkIn/OutDiskReadOps
RDSCPUUtilizationDatabaseConnectionsFreeStorageSpaceReadLatency
LambdaInvocationsErrorsDurationThrottlesConcurrentExecutions
ECSCPUUtilizationMemoryUtilization (Service / Task ごとに)
ALBRequestCountTargetResponseTimeHTTPCode_Target_5XX_Count
API GatewayCountLatency4XXError5XXError
S3BucketSizeBytesNumberOfObjects (1 日 1 回)
DynamoDBConsumedReadCapacity/WriteCapacityThrottledRequests

メトリクスのディメンション (Dimensions) #

同じメトリクスでも ディメンション で切り分けられます。

例: CPUUtilization
Service: AWS/EC2
Metric:  CPUUtilization
Dimensions:
  - InstanceId: i-1234567890
  - InstanceId: i-2345678901
  - InstanceId: i-3456789012

ディメンションが違えば別メトリクス。メトリクスの数 = コスト なのでディメンションが爆発するとコスト事故。

統計 (Statistic) #

統計何か
Sum合計 — Invocations、RequestCount
Average平均 — CPU、Latency
Maximum最大 — スパイク検知
Minimum最小
p95 / p99パーセンタイル — Latency
SampleCountデータポイント数

ほとんどの場面で Average + p95 が意味があります。p99 / p99.9 は SLA / ユーザー体験に直接影響します。

標準 vs 高解像度 #

種類解像度コスト
標準1 分標準
高解像度1 秒高い — スパイクが短い場面のみ

ほとんどは標準 1 分で十分。

カスタムメトリクスの送信 #

アプリケーションが直接送信。

Python — boto3
import boto3

cloudwatch = boto3.client("cloudwatch")
cloudwatch.put_metric_data(
    Namespace="MyApp",
    MetricData=[{
        "MetricName": "OrderCreated",
        "Value": 1,
        "Unit": "Count",
        "Dimensions": [
            {"Name": "Environment", "Value": "prod"},
            {"Name": "Region", "Value": "ap-northeast-1"},
        ],
    }],
)

コスト: メトリクスあたり $0.30 / 月。ディメンションの組み合わせごとに別 — ユーザー ID のような高カーディナリティのディメンションは絶対禁止。

EMF (Embedded Metric Format) — Lambda 用パターン #

Lambda では put_metric_data の呼び出し自体がコスト / レイテンシ負担。ログに特定の JSON 形式で書く と CloudWatch が自動でメトリクスに変換。

EMF 形式
import json
print(json.dumps({
    "_aws": {
        "Timestamp": int(time.time() * 1000),
        "CloudWatchMetrics": [{
            "Namespace": "MyApp",
            "Dimensions": [["Environment"]],
            "Metrics": [{"Name": "OrderCreated", "Unit": "Count"}],
        }],
    },
    "Environment": "prod",
    "OrderCreated": 1,
}))

aws-embedded-metrics-python のような SDK がこれをよりきれいにしてくれます。

Metric Filter — ログからメトリクスを作る #

すでにログにある情報 (ERROR 発生、レスポンス時間など) をメトリクスに変換。

コンソールで作る
CloudWatch → Log groups → グループ選択 → Metric filters → Create
- Filter pattern: ERROR
- Metric namespace: MyApp
- Metric name: ErrorCount
- Metric value: 1

これで ERROR が 1 回発生するたびにメトリクスが +1 されます。アラーム / ダッシュボードに活用できます。

Filter pattern の例
ERROR                       # ERROR 単語を含む
[..., level="ERROR", ...]   # 構造化ログのフィールド
{ $.level = "ERROR" }       # JSON ログのキー

CloudWatch Alarms #

メトリクスが閾値を超えたらアクションを実行します。通知の本丸です。

最初のアラーム — Lambda Errors #

コンソール
CloudWatch → Alarms → Create alarm
- Metric: AWS/Lambda → Errors → Function: my-function
- Statistic: Sum
- Period: 1 minute
- Threshold: > 0 for 1 datapoint within 5 minutes
- Action: SNS → 通知トピック
- Name: lambda-my-function-errors

Alarm の状態 #

状態意味
OK閾値内
ALARM閾値超過 — アクション発動
INSUFFICIENT_DATAデータ不足 — 新しいケース / メトリクスが来ていない

INSUFFICIENT_DATA をアラーム扱い (== 通知) するかはオプションです。新規アラームの評価中によく出る 状態なので一般的には無視します。

Composite Alarms #

複数のアラームの AND / OR で組み合わせ。「ALB 5xx ≥ 1% AND CPU > 80%」のようなパターンです。

組み合わせ例
ALARM("alb-5xx") AND ALARM("ec2-high-cpu")

誤検知を減らすのに効果的です。

Alarm のアクション #

アクション何か
SNS Topicメール / Slack / SMS / Lambda などへ fanout
EC2 Actionインスタンスの stop / terminate / reboot / recover
Auto ScalingASG のスケール in / out
Systems ManagerOpsItem 作成

運用の 90% は SNS → Slack / メール です。

Anomaly Detection #

ベースライン (band) を自動学習 → その外なら通知。トラフィック / CPU のような パターンのあるメトリクス に効果的。静的な閾値より誤検知が少ないです。

SNS 統合 — 通知を送る方法 #

ほとんどのアラームが SNS Topic に行き、そこから次の段階に fanout します。

購読どこへ
Emailメール
HTTPSSlack incoming webhook
Lambda加工して別の段階へ
SMSスマホ (まれ)
SQSキューへ

Slack 連携 — Lambda パターン #

直接 webhook で送るか、AWS Chatbot で。

Lambda — SNS → Slack 変換
import json, os, urllib.request

WEBHOOK = os.environ["SLACK_WEBHOOK"]

def handler(event, context):
    msg = json.loads(event["Records"][0]["Sns"]["Message"])
    payload = json.dumps({
        "text": f"🚨 *{msg['AlarmName']}* — {msg['NewStateValue']}",
        "blocks": [...]
    }).encode()
    req = urllib.request.Request(WEBHOOK, data=payload,
                                  headers={"Content-Type": "application/json"})
    urllib.request.urlopen(req)

上級 #4 API Gateway + Lambda上級 #5 EventBridge / SQS / SNS でさらに深く扱います。

CloudWatch Dashboards #

ウィジェットページです。チーム / サービスごとに一目で見られます。JSON で定義してコードで管理できます。

よく作るダッシュボード #

種類何か
サービスダッシュボード1 つのサービスの核となるメトリクス (リクエスト、レイテンシ、エラー、インフラ)
インフラダッシュボードEC2/RDS の CPU / メモリ / ネットワーク
ビジネスダッシュボード登録 / 決済 / 注文のようなビジネスメトリクス
オンコールダッシュボードアクティブアラーム / 最近の事故 / 核となる指標

ウィジェットの種類 #

  • メトリクスグラフ (line、stacked、number)
  • ログ (Logs Insights クエリ結果)
  • テキスト (Markdown — ダッシュボード説明)
  • Alarm status

良いダッシュボード = 「この 1 ページを 30 秒見ればシステムの状態が分かる。」

登録直後に ON にすべき設定 #

このシリーズが終わる時点で次の設定が整っている必要があります。

項目どこで
新しいロググループに retention 自動適用コンソール / EventBridge + Lambda
Lambda Errors アラーム関数ごとに
RDS FreeStorageSpace アラームDB ごとに
ALB 5xx アラームLB ごとに
ビリングアラーム (#3)アカウント単位
GuardDuty findings アラーム (#6)アカウント単位

この 6 つが小さな運用の通知基盤です。

よく出会う落とし穴 #

1) ログの retention 永続 #

もっともありがちなコスト事故。ロググループごとに retention 明示 + 新しいグループの自動化。この 1 行でコストを半分以上削減します

2) 高カーディナリティのカスタムメトリクス #

Dimensions: [{Name: 'UserId', Value: user_id}] — ユーザー 1 万人ならメトリクス 1 万個 × ディメンションの組み合わせ。1 ヶ月 $3000+。ユーザーごとはログ (Logs Insights) が答え。

3) Logs Insights の無制限時間クエリ #

大きなロググループに時間範囲を決めないと GB 単位のスキャンコスト。常に時間範囲を狭く。

4) Alarm の Period が短すぎる #

1 分 1 データポイントでアラーム → 一時的なスパイクごとにアラーム暴走。普通 5 分 / 3 データポイント くらいがノイズ適度。

5) Alarm のアクションを付けない #

アラームを作って SNS / アクションを付けないとコンソールで赤くなるだけ — 誰も気付きません。作るときにアクションも一緒に。

6) ダッシュボードを作るだけで見ない #

作った直後はみんな見ますが時間が経つと忘れられます。オンコール / 日次スタンドアップの最初のチェック項目 として定着させます。

7) Metric Math 未使用 #

複数のメトリクスの比率 / 合計 / 変換を計算できる Metric Math。「5xx / 総リクエスト = エラー率」のような計算です。うまく使えばダッシュボード / アラームがずっとシンプルになります。

まとめ #

今回つかんだもの:

  • CloudWatch の 4 つの構成 — Logs / Metrics / Alarms / Dashboards
  • Logs — グループ → ストリーム → イベント。retention は登録直後に設定。CloudWatch Agent で EC2 収集
  • Logs Insightsfields / filter / stats / sort / parse + bin(5m)。時間範囲を狭く
  • Metrics — AWS サービスが自動送信。ディメンション / 統計 (Avg、p95、p99)。高カーディナリティのディメンション禁止
  • Metric Filter — ログからメトリクス抽出 (ERROR など)
  • EMF — Lambda でログからメトリクス (put_metric_data の代替)
  • Alarms — 閾値 + Period + Datapoints。SNS / EC2 アクション / ASG。Composite / Anomaly
  • SNS — アラームの fanout。Slack / メール / Lambda
  • Dashboards — サービス / インフラ / ビジネス / オンコール
  • 落とし穴 — retention 永続、高カーディナリティのディメンション、Insights の無制限時間、Period が短すぎる、アクション未連携、ダッシュボードを見ない

次シリーズ — AWS 中級 #

これで AWS 基礎 7 編 が終わりました。コンソール / アカウント / IAM / コスト / CLI / SSO / セキュリティ / CloudWatch — AWS の上で何かを始めるのに必要な道具箱が 1 ヶ所に揃いました。

次は本物のリソースを作る番です。AWS 中級 7 編はバックエンド運用のメイン要素を整理します。

AWS 中級 #1 EC2 と VPC 基礎 では仮想マシン EC2、そしてその EC2 が住む仮想ネットワーク VPC の構造を、Subnet / Internet Gateway / Route Table / Security Group / NACL まで 1 本の糸で通して整理します。

X