Docker 中級 #6 ロギングとデバッグ
Docker 中級シリーズの最後の記事です。複数のコンテナが回り始めて最初にぶつかる二つの論点 — ログがどこから出るか、そして 何かが動かないときどこから覗くか をこの記事にまとめました。
Docker 中級 シリーズでこの記事の位置:
- #1 マルチステージビルドとイメージスリミング
- #2 ビルドキャッシュ — レイヤー順序の最適化
- #3 docker compose 基礎 — web + db
- #4 compose 深掘り — depends_on, healthcheck, profiles
- #5 環境変数と secrets 管理
- #6 ロギングとデバッグ ← この記事
コンテナログの一行原則 #
Docker が推奨するロギングパターンは単純です。
アプリは stdout/stderr にだけログを出せ。その流れを Docker が受けて処理する。
ファイルにログを書かないでください。コンテナのファイルシステムは揮発性で (基礎 #4)、運用で複数コンテナのログをまとめるには標準出力に流さないとツールが拾えません。
この一原則が定着すれば — docker logs、log driver、fluentd / Loki のような外部収集器まで全て同じ出口に流れます。
言語別の短いノート #
| 言語 / フレームワーク | 確認するところ |
|---|---|
| Python | print はバッファリング → PYTHONUNBUFFERED=1 環境変数で切る。logging モジュールはデフォルト stderr |
| Node.js | console.log/error は stdout/stderr に流れる — OK |
| Go | log.Println は stderr — OK |
| Django | LOGGING 設定で 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 myappDocker が 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 |
local | json-file の効率的バリアント (圧縮、ローテーションがデフォルト) |
journald | systemd journal |
syslog | syslog daemon |
fluentd | fluentd daemon (外部収集器) |
gelf | Graylog |
awslogs | AWS CloudWatch Logs |
gcplogs | GCP Cloud Logging |
none | ログ捨て (docker logs も不可) |
コンテナごとの driver 指定 #
docker run -d --log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
myappservices:
web:
image: myapp
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"運用でほぼ必須なオプションがその二行です。
max-size: 10m— 一つのログファイルが 10MB を超えるとローテートmax-file: 3— 最大 3 つまで保管、それ以上は削除
このオプションがないとログファイルが無制限に大きくなってディスクが溢れます。Docker 事故ランキング上位の一つです。
デーモンのグローバルデフォルト #
サービスごとにログオプションを書くのが面倒なら、デーモン設定に置けます。
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}これで新しく作る全コンテナに上のデフォルトが適用されます。デーモン再起動が必要なので運用環境変更はメンテナンス時間に。
docker compose logs — 一箇所で
#
複数サービスのログを一箇所で見られるのが 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 initializedup --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 を一回限りで 立ち上げて使うパターンがあります。
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 — リアルタイムリソース使用量
#
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 / 4kBdocker stats はデフォルトで全コンテナのリアルタイム使用量を流します。一回だけスナップショットしたいなら:
docker stats --no-streamリソース限界を決めておくと MEM % が 100% に届くと OOMKilled になります。運用では限界を明示するのが定石です。
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基礎 #3 の docker system prune とペアで — ディスクが溢れるときの最初のコマンドです。
イメージの中を覗く — dive
#
イメージサイズが疑わしい、またはどのレイヤーに何が刻まれているか見るとき — 外部ツール dive がとても便利です。
brew install dive
dive myapp:latestTUI が立ち上がってレイヤーごとにどのファイルが追加 / 修正 / 削除されたかが一目で見えます。イメージスリミングの手がかりを最も速く見つける道具 です。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 df、prune |
| ビルドが大きすぎる | dive、docker history |
| Compose が意図通りマージされない | docker compose config |
| 環境変数が反映されない | docker compose run web env、#5 の優先順位表 |
シリーズの締めとして #
中級シリーズで揃えた道具を一つの絵で:
ビルド効率 実行 / 運用
──────── ─────────
#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でマージされた定義を検証- 症状ごとに最初のコマンドを手に馴染ませると追跡が速くなる