Docker 中級 #6 ロギングとデバッグ

読了 8分

Docker 中級シリーズの最後の記事です。複数のコンテナが回り始めて最初にぶつかる二つの論点 — ログがどこから出るか、そして 何かが動かないときどこから覗くか をこの記事にまとめました。

Docker 中級 シリーズでこの記事の位置:

コンテナログの一行原則 #

Docker が推奨するロギングパターンは単純です。

アプリは stdout/stderr にだけログを出せ。その流れを Docker が受けて処理する。

ファイルにログを書かないでください。コンテナのファイルシステムは揮発性で (基礎 #4)、運用で複数コンテナのログをまとめるには標準出力に流さないとツールが拾えません。

この一原則が定着すれば — docker logs、log driver、fluentd / Loki のような外部収集器まで全て同じ出口に流れます。

言語別の短いノート #

言語 / フレームワーク確認するところ
Pythonprint はバッファリング → PYTHONUNBUFFERED=1 環境変数で切る。logging モジュールはデフォルト stderr
Node.jsconsole.log/error は stdout/stderr に流れる — OK
Golog.Println は stderr — OK
DjangoLOGGING 設定で handler が StreamHandler か確認
Nginxデフォルトは /var/log/nginx/...access_log /dev/stdout; error_log /dev/stderr; でリダイレクトするのが定石

docker logs を再び見る #

基礎 #3 で触れたコマンドを運用視点でもう一度。

よく使う形
docker logs -f myapp                      # follow
docker logs --tail 200 myapp              # 直近 200 行
docker logs --since 30m myapp             # 直近 30 分
docker logs --since 2026-04-18T10:00 myapp  # 絶対時刻
docker logs --until 1h myapp              # 1 時間前まで
docker logs -t myapp                      # タイムスタンプ込み

障害時刻の周辺を切り取りたいときは --since + --until の組合せが便利です。

障害時刻の追跡
docker logs --since 14:00 --until 14:10 myapp

Docker が stdout/stderr をどう保存するかについては、次の節で説明します — log driver です。

Log driver — ログがどこへ行くか #

Docker はコンテナの stdout/stderr を log driver というモジュール経由でどこかに流します。デフォルトは json-file — ホストのディスクに JSON で溜まる形です。

Driverどこへ
json-file (デフォルト)/var/lib/docker/containers/<id>/<id>-json.log
localjson-file の効率的バリアント (圧縮、ローテーションがデフォルト)
journaldsystemd journal
syslogsyslog daemon
fluentdfluentd daemon (外部収集器)
gelfGraylog
awslogsAWS CloudWatch Logs
gcplogsGCP Cloud Logging
noneログ捨て (docker logs も不可)

コンテナごとの driver 指定 #

docker run
docker run -d --log-driver json-file \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  myapp
compose.yaml
services:
  web:
    image: myapp
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

運用でほぼ必須なオプションがその二行です。

  • max-size: 10m — 一つのログファイルが 10MB を超えるとローテート
  • max-file: 3 — 最大 3 つまで保管、それ以上は削除

このオプションがないとログファイルが無制限に大きくなってディスクが溢れます。Docker 事故ランキング上位の一つです。

デーモンのグローバルデフォルト #

サービスごとにログオプションを書くのが面倒なら、デーモン設定に置けます。

/etc/docker/daemon.json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

これで新しく作る全コンテナに上のデフォルトが適用されます。デーモン再起動が必要なので運用環境変更はメンテナンス時間に。

docker compose logs — 一箇所で #

複数サービスのログを一箇所で見られるのが Compose の大きな利点です。

Compose ログコマンド群
docker compose logs                # 全サービス
docker compose logs -f             # follow
docker compose logs --tail 100     # 直近 100 行 (サービスごと)
docker compose logs web pg         # 特定サービスだけ
docker compose logs --since 10m -f web   # 組み合わせ
docker compose logs --no-color     # 色なし (ファイルリダイレクト時)
docker compose logs --no-log-prefix web  # "web | " prefix なし

サービスごとに色付きの prefix が付いて、どのサービスのログか一目でわかります。

出力例
web-1  | INFO     127.0.0.1 - - [20/May/2026 14:32:01] "GET / HTTP/1.1" 200
pg-1   | LOG:  database system is ready to accept connections
redis-1| 1:M 20 May 2026 14:32:00.123 # Server initialized

up --attach / --no-attach #

docker compose up でどのサービスのログを追うかを選べます。

ログフィルタ
docker compose up --attach web --attach worker     # 二つのサービスだけ
docker compose up --no-attach pg --no-attach redis # DB / キャッシュ除外

DB ログがうるさすぎたり、デバッグ中のサービスだけに集中したいときに便利です。

外部ログ集約 — 一段落 #

運用コンテナが増えるとホストのログファイルを一つずつ見るのが不可能になります。一段落だけ触れておくと:

  • Loki + Promtail + Grafana — 軽量なセルフホスト。compose 一束で立ち上げられる
  • Elastic Stack (ELK) — 強力だが重い。Elasticsearch + Logstash + Kibana
  • Datadog / New Relic / Grafana Cloud — マネージド SaaS
  • CloudWatch Logs / Cloud Logging — クラウドネイティブ環境

ほとんどが コンテナ stdout を受けてインデックス → 検索 / アラートに繋ぐ構造です。今シリーズの一歩先のテーマなので名前だけ触れて進みます。

デバッグの最初の道具 — docker exec #

基礎 #3 で見たコマンドですが、デバッグ視点でもう一度。

コンテナの中で環境点検
docker compose exec web sh

# 中で
ps aux                    # プロセスツリー
env | sort                # 環境変数
cat /etc/resolv.conf      # DNS 設定
ls -la /app               # 作業ディレクトリ

docker compose exec は立ち上がっているコンテナに入る道。distroless / scratch ベースだとシェルがなくて使えません。 (中級 #1 のトレードオフ。) そんなときは debug image を一回限りで 立ち上げて使うパターンがあります。

distroless コンテナの隣にデバッグコンテナ
docker run -it --rm \
  --network container:myapp-web-1 \
  --pid container:myapp-web-1 \
  nicolaka/netshoot

--network container:X--pid container:X で対象コンテナのネットワークとプロセス名前空間を共有します。すると nicolaka/netshoot (ネットワークデバッグツール集) の中で curl localhost:8000 のようなコマンドが対象コンテナの 8000 ポートに届きます。隔離は外して道具だけ借りるパターン。

docker inspect — 精密診断 #

JSON で展開された状態情報を覗くとき。

よく取り出す値
# 状態
docker inspect myapp-web-1 --format '{{.State.Status}}'

# Health
docker inspect myapp-pg-1 --format '{{json .State.Health}}' | jq

# ネットワーク
docker inspect myapp-web-1 --format '{{json .NetworkSettings.Networks}}' | jq

# マウント
docker inspect myapp-web-1 --format '{{json .Mounts}}' | jq

# 開始時刻
docker inspect myapp-web-1 --format '{{.State.StartedAt}}'

# OOM で死んだか
docker inspect myapp-web-1 --format '{{.State.OOMKilled}}'

OOMKilled: true が見えればメモリ限界でコンテナが死んだということ — 次に見る docker stats と一緒に追えば答えが出ます。

docker stats — リアルタイムリソース使用量 #

全コンテナの CPU/メモリ/IO
docker stats
# CONTAINER ID   NAME           CPU %   MEM USAGE / LIMIT     MEM %   NET I/O
# a1b2c3d4...    myapp-web-1    1.2%    120MiB / 7.7GiB       1.5%    12kB / 8kB
# e5f6g7h8...    myapp-pg-1     0.1%    35MiB / 7.7GiB        0.4%    5kB / 4kB

docker stats はデフォルトで全コンテナのリアルタイム使用量を流します。一回だけスナップショットしたいなら:

一スナップショットだけ
docker stats --no-stream

リソース限界を決めておくと MEM % が 100% に届くと OOMKilled になります。運用では限界を明示するのが定石です。

compose.yaml — リソース限界
services:
  web:
    image: myapp
    mem_limit: 512m
    cpus: 1.0
    # または deploy キー (Swarm) — 普通の compose では上の形式が動く

ディスク占有 — docker system df #

イメージ / コンテナ / ボリュームがディスクをどれだけ占めているか。

一箇所要約
docker system df
# TYPE           TOTAL   ACTIVE   SIZE      RECLAIMABLE
# Images         18      4        3.2GB     2.1GB (65%)
# Containers     6       1        12MB      5MB (41%)
# Local Volumes  8       3        420MB     280MB (66%)
詳細
docker system df -v

基礎 #3docker system prune とペアで — ディスクが溢れるときの最初のコマンドです。

イメージの中を覗く — dive #

イメージサイズが疑わしい、またはどのレイヤーに何が刻まれているか見るとき — 外部ツール dive がとても便利です。

dive インストール (Homebrew)
brew install dive

dive myapp:latest

TUI が立ち上がってレイヤーごとにどのファイルが追加 / 修正 / 削除されたかが一目で見えます。イメージスリミングの手がかりを最も速く見つける道具 です。CI で肥大化回帰を防ぐゲートとしても使われます (イメージ効率スコアが閾値以下ならビルド失敗)。

docker compose top — コンテナ内のプロセス #

中のプロセスツリー
docker compose top
# myapp-web-1
# UID    PID    PPID    CMD
# root   1234   1200    python app.py
# root   1456   1234    python app.py (worker)

exec で入って ps aux と同じことですが、一つのコマンドで全サービスをまとめて見られます。ゾンビプロセス / 意図しない子プロセスがあるか素早く確認。

docker events — Docker が起きたことを流す #

障害追跡 / 自動化にたまに使うコマンド。

リアルタイムイベントストリーム
docker events --filter container=myapp-web-1
# 2026-04-18T14:32:01 ... container die ...
# 2026-04-18T14:32:01 ... container start ...

コンテナがしきりに立ち上がっては死ぬのを繰り返すとき、これでパターンを掴みます。

よく出会うデバッグフロー #

症状最初のコマンド
コンテナが繰り返し終了docker logs <c>docker inspect <c> --format '{{.State.OOMKilled}}'
外部からポートに届かないdocker port <c>、コンテナ内で 0.0.0.0 バインド確認
コンテナ間通信不可同じネットワークか (inspect)、サービス名のタイポ
ディスクが満杯docker system dfprune
ビルドが大きすぎるdivedocker history
Compose が意図通りマージされないdocker compose config
環境変数が反映されないdocker compose run web env#5 の優先順位表

シリーズの締めとして #

中級シリーズで揃えた道具を一つの絵で:

Docker 中級の道具マップ
   ビルド効率                     実行 / 運用
   ────────                       ─────────
   #1 マルチステージ              #3 compose — 複数コンテナ
   #2 BuildKit キャッシュ         #4 healthcheck, depends_on, profiles
   #2 mount cache                 #5 環境変数と secrets
   #2 外部キャッシュ              #6 ロギングとデバッグ

基礎シリーズで掴んだ一コンテナのサイクルの上に、中級シリーズは 複数コンテナ + 運用感覚 を重ねました。compose 一ファイル・一コマンドで立ち上げ、healthcheck で意味のある開始順序を確立し、秘密をイメージの外に置いて、ログを一箇所で見てデバッグするところまで扱いました。

次のシリーズは Docker 上級 です。BuildKit のさらに深い機能、マルチアーキテクチャビルド、イメージセキュリティ (non-root、distroless、Trivy スキャン、SBOM、cosign 署名)、リソース制限と cgroups、そしてプロダクション運用の細かい点を扱います。今シリーズで掴んだ compose の上にもう一段積み上げる内容です。

まとめ #

この記事で掴んだ絵:

  • アプリは stdout/stderr にのみ ログを出して、Docker が処理する — ファイルロギング禁止
  • docker logs --since/--until/--tail で時刻 / 量を制限して見る
  • max-size + max-file ログオプションが運用必須 — ディスク暴走防止
  • docker compose logs で複数サービスを一箇所で、prefix 色で区別
  • docker exec + inspect + stats がデバッグ最初の三つの道具。distroless なら --network container: トリックでデバッグコンテナを隣に立ち上げ
  • dive でイメージの中を、docker system df + prune でディスクを、docker compose config でマージされた定義を検証
  • 症状ごとに最初のコマンドを手に馴染ませると追跡が速くなる
X