2026年7月11日
2026年7月11日
cAdvisorでDockerコンテナを監視する方法|WordPressコンテナのメモリ不足・CPU制限・再起動を指標から見つける
cAdvisor を入れたのに「グラフは出ているが、何を見ればよいか分からない」という状態はよくあります。結論から言うと、WordPress を Docker で動かしている場合に最初に見るべきなのは、メモリ上限に対する使用率、CPU の制限による待ち(スロットリング)、OOM による強制終了と再起動の4つです。この記事では、cAdvisor の指標をこの4つの観点で読み、Prometheus のクエリとアラートにして、原因の切り分けまでつなげる方法を説明します。
cAdvisor の導入手順そのものより、「導入した後の読み方」に重点を置いています。指標名やラベルは cAdvisor のバージョンや cgroup v1/v2 の違いで変わることがあるため、記事中のクエリは手元の /metrics の出力と照らし合わせて調整してください(2026年10月現在の一般的な構成を前提にしています)。
前提の構成と、上限を設定しておく理由
ここでは、WordPress(PHP-FPM か Apache 版イメージ)、MariaDB/MySQL、Nginx を Docker Compose で動かし、cAdvisor と Prometheus で収集している構成を想定します。
cAdvisor の指標を「異常かどうか」で判断するには、比較対象となる上限が必要です。コンテナにメモリや CPU の上限を設定していないと、使用量の数字は出ても「あとどれくらい余裕があるか」が分かりません。上限なしのコンテナはホストのメモリを使い切るまで増え続け、最終的にホスト側の OOM Killer が別のプロセス(多くはデータベース)を止めてしまうこともあります。まずは各サービスに上限を付けておきます。
# compose.yaml(抜粋)
services:
wordpress:
image: wordpress:php8.3-fpm
mem_limit: 768m
cpus: 1.5
db:
image: mariadb:11
mem_limit: 1g
cpus: 1.0
値は例です。PHP-FPM なら「1プロセスあたりのメモリ × pm.max_children」、MariaDB なら「innodb_buffer_pool_size + 接続ごとのバッファ」が上限の中に収まるかを先に見積もってください。上限だけを小さくすると、次に説明する OOM が頻発します。
指標1:メモリ使用率は working set で見る
cAdvisor にはメモリ関連の指標が複数あります。よく誤解されるのが container_memory_usage_bytes で、これはページキャッシュを含むため、ファイルを多く読むコンテナでは上限近くまで増えて見えても問題ないことがあります。上限超過による強制終了の判断に近いのは container_memory_working_set_bytes です。
# 上限に対する working set の割合(上限を設定したコンテナのみ)
container_memory_working_set_bytes{name=~".+"}
/ (container_spec_memory_limit_bytes{name=~".+"} > 0)
> 0 で割っているのは、上限を設定していないコンテナでは上限値が 0(または非常に大きな値)として出るためです。この割合が常に 0.9 を超えているなら、上限が小さすぎるか、アプリ側でメモリを使いすぎています。
WordPress コンテナで割合が高い場合は、次の順に確認します。
- PHP-FPM の子プロセス数が多すぎないか。
pm.max_childrenを上限メモリから逆算した値に下げます。 - 特定のページやプラグインの処理(画像の一括再生成、大きな CSV のインポートなど)の時間帯に跳ねていないか。グラフの山と、アクセスログや WP-Cron の実行時刻を突き合わせます。
- PHP の
memory_limitを上げすぎていないか。1リクエストの上限を上げると、同時実行時の合計も増えます。
指標2:CPU は使用量よりスロットリングを見る
cpus: 1.5 のように CPU を制限すると、コンテナは一定の周期ごとに割り当てられた CPU 時間を使い切った時点で待たされます。これがスロットリングです。CPU 使用量のグラフが上限に張り付いていなくても、短い時間に処理が集中すると待ちが発生し、管理画面や REST API の応答が遅くなります。
# CPU 使用量(コア数換算)
sum by (name) (rate(container_cpu_usage_seconds_total{name=~".+"}[5m]))
# スロットリングされた周期の割合
sum by (name) (rate(container_cpu_cfs_throttled_periods_total{name=~".+"}[5m]))
/ sum by (name) (rate(container_cpu_cfs_periods_total{name=~".+"}[5m]))
スロットリングの割合が継続して数十%ある場合、体感の遅さの原因になっている可能性があります。対処は、CPU 上限を上げるか、同時に走る PHP プロセス数を抑えるか、重い処理をキャッシュで減らすかのいずれかです。上限を上げる前に、ページキャッシュが効いているか(ログイン中のユーザーやキャッシュ除外ページが多すぎないか)を確認すると、無駄に資源を増やさずに済みます。
指標3:OOM と再起動を検知する
メモリ上限を超えたコンテナは、カーネルによってプロセスを強制終了されます。Compose で restart: unless-stopped などを指定していると自動で再起動するため、気付かないまま「ときどき 502 が出る」状態が続きがちです。
# 直近1時間に OOM が起きたコンテナ(指標がある cAdvisor のバージョンのみ)
increase(container_oom_events_total{name=~".+"}[1h]) > 0
# 直近1時間に再起動したコンテナ(開始時刻が変わったものを検出)
changes(container_start_time_seconds{name=~".+"}[1h]) > 0
container_oom_events_total は比較的新しい cAdvisor で追加された指標で、環境によっては出ていません。その場合は再起動の検出で代用し、ホスト側で原因を確認します。
# 直前の終了が OOM によるものか
docker inspect --format '{{.Name}} OOMKilled={{.State.OOMKilled}} Restarts={{.RestartCount}}' $(docker ps -aq)
# カーネルログに OOM の記録があるか
sudo journalctl -k --since "1 hour ago" | grep -i -E "oom|killed process"
OOMKilled=true なら上限超過です。データベースのコンテナで起きている場合は、データ破損のリスクを避けるため、上限を上げる前にバックアップが取れているかを確認してください。
指標4:ディスク I/O とネットワークは比較で見る
ディスクの読み書き(container_fs_reads_bytes_total、container_fs_writes_bytes_total)とネットワーク(container_network_receive_bytes_total、container_network_transmit_bytes_total)は、数値そのものより「いつもと比べてどうか」で見るのが実用的です。
sum by (name) (rate(container_fs_writes_bytes_total{name=~".+"}[5m]))
sum by (name) (rate(container_network_transmit_bytes_total{name=~".+"}[5m]))
ディスク指標はストレージドライバーやボリュームの種類によって値が出ない、またはデバイス単位で分かれて出ることがあります。値が 0 のままならラベルの device を確認し、必要ならホスト側の node_exporter のディスク指標で補います。ネットワークの送信量が普段の何倍にも増えていたら、画像の直リンクやクローラーの集中、あるいは不正利用を疑い、アクセスログを確認します。
アラートにする
見るべき指標が決まったら、Prometheus のアラートルールにします。しきい値と継続時間は例なので、数日分のグラフを見てから調整してください。短い継続時間で設定すると、一時的な山で通知が鳴り続けます。
# rules/containers.yml
groups:
- name: containers
rules:
- alert: ContainerMemoryNearLimit
expr: |
container_memory_working_set_bytes{name=~".+"}
/ (container_spec_memory_limit_bytes{name=~".+"} > 0) > 0.9
for: 10m
labels:
severity: warning
annotations:
summary: "{{ $labels.name }} のメモリが上限の90%を超えています"
- alert: ContainerCpuThrottled
expr: |
sum by (name) (rate(container_cpu_cfs_throttled_periods_total{name=~".+"}[5m]))
/ sum by (name) (rate(container_cpu_cfs_periods_total{name=~".+"}[5m])) > 0.3
for: 15m
labels:
severity: warning
annotations:
summary: "{{ $labels.name }} のCPUスロットリングが30%を超えています"
- alert: ContainerRestarted
expr: changes(container_start_time_seconds{name=~".+"}[15m]) > 0
labels:
severity: critical
annotations:
summary: "{{ $labels.name }} が再起動しました"
ルールファイルを追加したら promtool check rules rules/containers.yml で構文を確認してから Prometheus を再読み込みします。
つまずきやすい点
- **
nameラベルが空の系列が混ざる**: cAdvisor はコンテナ以外の cgroup(システムのサービスなど)の指標も出します。クエリにname=~".+"を付けているのはこれを除くためです。 - コンテナ名が変わってグラフが途切れる: Compose のプロジェクト名やサービス名を変えると
nameラベルが変わります。ダッシュボードではcontainer_label_com_docker_compose_serviceのようなラベルで集計すると、作り直しに強くなります(ラベルが出るかは cAdvisor の設定によります)。 - cAdvisor 自体が重い: 収集間隔が短すぎたり、不要な指標まで出していたりすると、cAdvisor が CPU を使います。
--housekeeping_intervalを延ばす、--docker_onlyを付ける、使わない指標を無効にする、といった調整を検討してください。 - ホストの cgroup のバージョン: cgroup v2 の環境では一部の指標の出方が v1 と異なります。古いダッシュボードのクエリがそのまま使えない場合は、
/metricsの実際の出力で指標名を確認します。
まとめ
- 上限を設定していないと「余裕」が判断できないため、まず各コンテナにメモリと CPU の上限を付ける
- メモリは
container_memory_working_set_bytesを上限で割った割合で見る - CPU は使用量よりスロットリングの割合が遅さの手がかりになる
- OOM と再起動は自動復旧で見逃しやすいので、アラートで拾い、
docker inspectとカーネルログで原因を確認する - ディスクとネットワークは普段との比較で見て、急増したらログで裏付けを取る