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 Stream | Lambda 実行環境 ID、ECS Task ID、EC2 インスタンス ID |
| Log Event | 1 行のテキスト + 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 日 |
aws logs put-retention-policy \
--log-group-name /aws/lambda/my-function \
--retention-in-days 30aws 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 + Lambda — CreateLogGroup イベントを受けて自動適用 (実戦でよくある)
方法 2: ポリシーで強制 — 作成時に retention を明示しないと拒否 (やや過剰)
Lambda のログ送信 #
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 のインストールが必要。
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 100fields @timestamp, @message
| filter @message like /ERROR/
| sort @timestamp desc
| limit 50fields @timestamp, @duration
| filter @type = "REPORT"
| stats avg(@duration), max(@duration), count(*) by bin(5m)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 のほぼすべてのサービスが自動送信します。
よく見るメトリクス #
| サービス | よく見るメトリクス |
|---|---|
| EC2 | CPUUtilization、NetworkIn/Out、DiskReadOps |
| RDS | CPUUtilization、DatabaseConnections、FreeStorageSpace、ReadLatency |
| Lambda | Invocations、Errors、Duration、Throttles、ConcurrentExecutions |
| ECS | CPUUtilization、MemoryUtilization (Service / Task ごとに) |
| ALB | RequestCount、TargetResponseTime、HTTPCode_Target_5XX_Count |
| API Gateway | Count、Latency、4XXError、5XXError |
| S3 | BucketSizeBytes、NumberOfObjects (1 日 1 回) |
| DynamoDB | ConsumedReadCapacity/WriteCapacity、ThrottledRequests |
メトリクスのディメンション (Dimensions) #
同じメトリクスでも ディメンション で切り分けられます。
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 分で十分。
カスタムメトリクスの送信 #
アプリケーションが直接送信。
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 が自動でメトリクスに変換。
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 されます。アラーム / ダッシュボードに活用できます。
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-errorsAlarm の状態 #
| 状態 | 意味 |
|---|---|
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 Scaling | ASG のスケール in / out |
| Systems Manager | OpsItem 作成 |
運用の 90% は SNS → Slack / メール です。
Anomaly Detection #
ベースライン (band) を自動学習 → その外なら通知。トラフィック / CPU のような パターンのあるメトリクス に効果的。静的な閾値より誤検知が少ないです。
SNS 統合 — 通知を送る方法 #
ほとんどのアラームが SNS Topic に行き、そこから次の段階に fanout します。
| 購読 | どこへ |
|---|---|
| メール | |
| HTTPS | Slack incoming webhook |
| Lambda | 加工して別の段階へ |
| SMS | スマホ (まれ) |
| SQS | キューへ |
Slack 連携 — Lambda パターン #
直接 webhook で送るか、AWS Chatbot で。
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 Insights —
fields / 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 本の糸で通して整理します。