Dockerfileデバッグの完全手順【2026年版】–progress=plain・BuildKit・exec対応

2016年5月10日


DockerfileのビルドがRUN命令の途中でエラーになる。ログを確認しても「どの行が原因か」が特定できない。修正して再ビルドしても同じエラーが再現するだけ—そんなループに入ってしまっているエンジニアは多い。

本記事では、Dockerfileデバッグに必要な手順を体系的に解説します。ビルドログの完全表示から、ビルド失敗時の中間コンテナへの侵入、起動後のコンテナへのlive attach、BuildKitキャッシュの活用まで、実際に動作するコマンド例を使って説明します。

このガイドを読み終えると、以下のことができるようになります。

  • --progress=plainでビルドログを完全表示し、省略されていたエラーの詳細を把握する
  • ビルド失敗時の中間コンテナに入って、失敗直前のファイルシステム状態を直接調査する
  • docker execで起動中のコンテナにアタッチしてリアルタイムで内部を調べる
  • docker inspectでコンテナの設定・状態・ネットワーク情報を一覧する
  • BuildKitのキャッシュを活用してデバッグの反復サイクルを高速化する
  • docker run --entrypointでCMD/ENTRYPOINTを無視して任意のコマンドを実行する

本記事の手順はすべてDocker Desktop 4.28 / Docker Engine 26.x(BuildKitデフォルト有効)で実機検証済みです。

docker build --progress=plain の出力例。各RUN命令のステップごとに詳細ログが表示されている様子


前提条件

このガイドを進める前に、以下の環境・知識が必要です。

  • 環境: macOS / Linux(Windowsも同様手順、WSL2推奨)
  • 必要なツール: Docker Engine 20.10以上(BuildKitデフォルト有効)
  • 前提知識: Dockerfileの基本構文(FROM、RUN、COPYなど)
# バージョン確認
docker --version
# 期待する出力例: Docker version 26.1.4, build 5650f9b

docker buildx version
# 期待する出力例: github.com/docker/buildx v0.14.0 cloud/user

BuildKitについて: Docker 23.0以降はBuildKitがデフォルト有効です。それ以前のバージョンではDOCKER_BUILDKIT=1 docker build ...として明示的に有効化してください。


全体の流れ

このガイドは以下のステップで構成されています。

  1. ビルドログを完全表示する(–progress=plain) — 5分
  2. ビルド失敗時の中間コンテナを調査する — 10分
  3. docker execで起動中のコンテナにアタッチする — 5分
  4. docker inspectでコンテナの詳細情報を確認する — 5分
  5. docker run –entrypointで内部を直接検証する — 5分
  6. BuildKitキャッシュを活用してデバッグを高速化する — 10分
  7. 応用編:ヘルスチェック・マルチステージビルドのデバッグ

どのステップから始めるか: ビルドが失敗している場合はステップ1→2の順に進む。コンテナは起動するが動作がおかしい場合はステップ3→4から始める。


ステップ1: ビルドログを完全表示する

デバッグの第一歩はエラーの全体像を把握することです。デフォルトのdocker buildは出力を圧縮・省略するため、詳細なエラーメッセージが見えないことがあります。

–progress=plainオプションを使う

# BuildKit有効(Docker 23.0以降はデフォルト)
docker build --progress=plain -t myimage .

# 環境変数で指定する方法(より確実)
BUILDKIT_PROGRESS=plain docker build -t myimage .

--progress=plainを付けることで、各RUN命令の標準出力・標準エラー出力がそのまま表示されます。

#8 [4/5] RUN apt-get install -y curl
#8 0.342 Reading package lists...
#8 2.105 Building dependency tree...
#8 2.341 E: Unable to locate package curl
#8 ERROR: process "/bin/sh -c apt-get install -y curl" did not complete successfully
------
 > [4/5] RUN apt-get install -y curl:
------
Dockerfile:8
--------------------
   7 |
   8 | >>> RUN apt-get install -y curl
   9 |
--------------------
ERROR: failed to solve: process "/bin/sh -c apt-get install -y curl" did not exit successfully: exit code: 100

--progress=auto(デフォルト)では上記の詳細が省略され、最後の ERROR: 1行だけが表示されます。エラーの根本原因を把握するには必ず--progress=plainを使います。

–progress=plainと–progress=autoの違い

項目 –progress=auto –progress=plain
各RUNの標準出力 省略 すべて表示
エラー発生箇所 最終行のみ 詳細コンテキスト付き
視認性 高い(正常時) 低い(正常時)
デバッグ用途 不向き 推奨

–no-cacheと組み合わせる

キャッシュが原因で古い状態でビルドされている疑いがある場合は、--no-cacheを加えてクリーンな状態でビルドします。

docker build --progress=plain --no-cache -t myimage .

よくあるミス: キャッシュが効いているせいでRUN命令が再実行されず、修正が反映されていないように見えることがあります。特にパッケージインストール系のRUNを変更した場合は--no-cacheを試してください。

ログをファイルに保存する

ビルドログが長い場合や、CI/CDで失敗ログを保存したい場合はファイルに出力します。

# ログをファイルに保存しながらターミナルにも表示
docker build --progress=plain -t myimage . 2>&1 | tee build.log

# エラー行だけを確認
grep -E "ERROR|error" build.log

ステップ1完了の確認: エラー発生時に具体的なエラーメッセージ(E: Unable to locate package ...exit code:)がログに表示されている。


ステップ2: ビルド失敗時の中間コンテナを調査する

ログを見てもわからない場合は、ビルドが失敗した直前の状態のコンテナに入って、実際にファイルシステムやコマンドを確認します。

BuildKit有効時の中間コンテナを取り出す

BuildKitが有効な場合(Docker 23.0以降のデフォルト)、旧来のdocker ps -aで中間コンテナを探す方法は使えません。代わりに以下のアプローチを使います。

方法A: 問題箇所の直前までを–targetでステージ分けする

Dockerfileに一時的な調査用ステージを追加します。

# 元のDockerfile(問題箇所を切り出して調査)
FROM ubuntu:22.04 AS debug_stage
RUN apt-get update
# RUN apt-get install -y curl  ← ここが失敗している場合、この行の前まで
RUN echo "ここまでは成功"

FROM ubuntu:22.04 AS main_stage
# 本来の処理...
# debug_stageだけビルドしてコンテナに入る
docker build --target debug_stage -t debug_image .
docker run --rm -it debug_image /bin/bash

# コンテナ内で問題のコマンドを手動実行
root@container:/# apt-get install -y curl
# → 実際のエラーメッセージが表示される

方法B: –outputでファイルシステムをエクスポートする

# 成功しているステップまでのファイルシステムをローカルに取り出す
docker build --target debug_stage --output type=local,dest=./debug_output .

# または tar として取り出す
docker build --target debug_stage --output type=tar,dest=debug.tar .
tar xf debug.tar -C ./debug_output
ls ./debug_output

ファイルシステムをローカルに展開して、ファイルの存在確認・内容確認ができます。

BuildKit無効時(旧来の方法)

DOCKER_BUILDKIT=0でビルドした場合、失敗したコンテナがdocker ps -aに残ります。

DOCKER_BUILDKIT=0 docker build -t myimage .

# 停止しているコンテナを確認
docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS                     PORTS     NAMES
377562f0f1c3   7d75b95648f2   "/bin/sh -c 'apt-get…"  30 seconds ago  Exited (100) 25 seconds ago           focused_heisenberg

このコンテナIDを使って中間イメージを作成し、bashに入ります。

# 失敗した中間コンテナをイメージ化
docker commit 377562f0f1c3 builddebug

# イメージからbashで起動
docker run --rm -it builddebug /bin/bash

# コンテナ内で失敗したコマンドを手動実行して確認
root@container:/# apt-get update
root@container:/# apt-get install -y curl
# → 具体的なエラーメッセージを直接確認できる

BuildKit無効時の注意: DOCKER_BUILDKIT=0でビルドするとBuildKitの最適化(並列ビルド・キャッシュマウント等)が無効になります。あくまでデバッグ目的で使用してください。

docker ps -aで中間コンテナを確認し、docker commitでイメージ化してbashに入るデバッグフロー

ステップ2完了の確認: 失敗したRUN命令と同じコマンドをコンテナ内で手動実行でき、エラーの詳細を確認できている。


ステップ3: docker execで起動中のコンテナにアタッチする

ビルドは成功したが、コンテナを起動してから問題が発生する場合に使うのがdocker execです。コンテナを停止せずに追加のプロセスを起動して内部を調査できます。

docker execの基本

# 起動中のコンテナ一覧を確認
docker ps

# コンテナ名またはIDを使ってbashを起動
docker exec -it <container_id_or_name> /bin/bash

# bashがない軽量イメージの場合
docker exec -it <container_id_or_name> /bin/sh

-i(インタラクティブ)と-t(疑似TTY割り当て)を組み合わせることで、インタラクティブなシェルが使えます。

実践的な使用例

ログをリアルタイムで確認する

# アプリのログを直接確認
docker exec -it mycontainer tail -f /var/log/app/error.log

# journaldのログを確認
docker exec -it mycontainer journalctl -f

プロセスとリソース使用量を確認する

# 起動中のプロセス一覧
docker exec -it mycontainer ps aux

# メモリ・CPU使用量
docker exec -it mycontainer top

# ディスク使用量
docker exec -it mycontainer df -h

ネットワーク接続状況を確認する

# リスニングポートの確認
docker exec -it mycontainer ss -tlnp
# または
docker exec -it mycontainer netstat -tlnp

# DNS解決の確認
docker exec -it mycontainer nslookup google.com

# 他サービスへの接続テスト
docker exec -it mycontainer curl -v http://db:5432

ファイルシステムの確認

# 設定ファイルの内容確認
docker exec -it mycontainer cat /etc/myapp/config.yml

# マウントされているボリュームの確認
docker exec -it mycontainer mount | grep /data

# 権限・オーナーの確認
docker exec -it mycontainer ls -la /app/

docker execとdocker run –entrypointの使い分け

状況 使うコマンド
コンテナが起動中 docker exec
コンテナが起動していない docker run --entrypoint
ビルド失敗の調査 docker run --entrypoint--targetで中間イメージ作成後)
起動後の動作異常調査 docker exec

ステップ3完了の確認: 起動中のコンテナにシェルで入り、内部のプロセス・ファイル・ネットワーク状態を確認できている。


ステップ4: docker inspectでコンテナの詳細情報を確認する

docker inspectはコンテナやイメージの詳細な設定・状態をJSON形式で出力するコマンドです。エラーログに表示されない設定の問題(環境変数の未設定、ボリュームの誤設定など)の特定に役立ちます。

コンテナの詳細を確認する

# コンテナの全設定をJSON出力
docker inspect <container_id_or_name>

# 特定の情報だけを取り出す(--formatオプション)
# 環境変数の確認
docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' mycontainer

# IPアドレスの確認
docker inspect --format='{{.NetworkSettings.IPAddress}}' mycontainer

# マウントされているボリュームの確認
docker inspect --format='{{json .Mounts}}' mycontainer | python3 -m json.tool

よく確認する項目と確認方法

環境変数が正しく設定されているか

docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' mycontainer
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
DATABASE_URL=postgres://db:5432/myapp
NODE_ENV=production

ポートマッピングの確認

docker inspect --format='{{json .NetworkSettings.Ports}}' mycontainer | python3 -m json.tool
{
  "8080/tcp": [
    {
      "HostIp": "0.0.0.0",
      "HostPort": "3000"
    }
  ]
}

コンテナの終了コードとエラーメッセージ

コンテナが即終了する場合、終了コードで原因を絞り込めます。

docker inspect --format='{{.State.ExitCode}} {{.State.Error}}' mycontainer
1 OCI runtime create failed: container_linux.go:380: ...
終了コード 意味
0 正常終了
1 アプリケーションエラー
125 docker run自体の失敗
126 コマンドが実行できない
127 コマンドが見つからない
137 SIGKILL(OOMキラーなど)
143 SIGTERM(正常な停止要求)

イメージのレイヤー構成を確認する

# イメージのレイヤー・Dockerfile命令を確認
docker inspect myimage

# イメージの履歴(Dockerfileの各命令とサイズ)
docker history myimage

# サイズが大きいレイヤーを特定
docker history --no-trunc myimage | sort -k4 -hr | head -10
IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
a3b2c1d4e5f6   2 hours ago     RUN /bin/sh -c npm ci                          245MB     buildkit.dockerfile.v0
...

ステップ4完了の確認: コンテナの環境変数・ポートマッピング・終了コードを確認でき、設定ミスの有無を特定できている。


ステップ5: docker run –entrypointで内部を直接検証する

ビルドが途中まで成功しているイメージが存在する場合、--entrypointオプションを使ってCMD/ENTRYPOINTを無視し、直接bashを起動できます。

–entrypointを使う基本

# CMDやENTRYPOINTを無視してbashで起動
docker run --rm -it --entrypoint /bin/bash myimage

# shしか入っていない場合(Alpine等の軽量イメージ)
docker run --rm -it --entrypoint /bin/sh myimage

よくある使用シーン

シーン1: ENTRYPOINTがバイナリの場合に内部を確認したい

ENTRYPOINT ["/app/server"]

このようなDockerfileでは普通にdocker runしてもサーバーが起動するだけです。--entrypointでbashに切り替えて内部を調べます。

docker run --rm -it --entrypoint /bin/bash myimage

# コンテナ内で
ls -la /app/
file /app/server          # バイナリの種類を確認
/app/server --help        # ヘルプを表示
ldd /app/server           # 依存ライブラリを確認

シーン2: 環境変数・設定ファイルの確認

docker run --rm -it --entrypoint /bin/bash \
  -e DATABASE_URL=postgres://localhost/mydb \
  -e NODE_ENV=production \
  myimage

# コンテナ内で
printenv DATABASE_URL
cat /etc/myapp/config.yml

シーン3: ボリュームをマウントして設定ファイルを差し替える

docker run --rm -it --entrypoint /bin/bash \
  -v $(pwd)/config:/etc/myapp \
  myimage

# コンテナ内で
cat /etc/myapp/config.yml   # マウントした設定ファイルを確認

シーン4: 特定のコマンドだけ実行して終了する

# インタラクティブなシェルを使わず、コマンドを直接実行
docker run --rm --entrypoint /bin/sh myimage -c "ls /app && cat /app/package.json"

よくあるミス: bashが入っていない軽量イメージ(alpine等)では/bin/bashが存在せず起動に失敗します。その場合は/bin/shを使ってください。

ステップ5完了の確認: 目的のコンテナ内でインタラクティブなシェルが起動でき、内部の状態を確認できている。


ステップ6: BuildKitキャッシュを活用してデバッグを高速化する

デバッグ中は同じビルドを何度も繰り返します。BuildKitのキャッシュを正しく使うと、変更のないレイヤーはキャッシュされてデバッグのイテレーション速度が大幅に向上します。

RUN命令のキャッシュの仕組みを理解する

BuildKitはDockerfileの各命令について、命令文字列・ベースイメージ・COPYされるファイルのハッシュを元にキャッシュキーを生成します。いずれかが変わるとそれ以降のキャッシュはすべて無効になります。

パッケージインストールはDockerfileの早い段階に移動する

# NG: COPYの後にapt-getがあるとCOPYのたびにキャッシュが壊れる
FROM ubuntu:22.04
COPY . /app
RUN apt-get update && apt-get install -y curl

# OK: パッケージインストールをCOPYの前に置く
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y curl
COPY . /app

–mount=type=cacheでパッケージキャッシュを永続化する

BuildKitのRUN –mountオプションで、ビルド間をまたいでパッケージキャッシュを再利用できます。

# apt-getのキャッシュをビルド間で再利用
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
    --mount=type=cache,target=/var/lib/apt,sharing=locked \
    apt-get update && apt-get install -y curl git build-essential

# pipのキャッシュを再利用
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install -r requirements.txt

# npmのキャッシュを再利用
RUN --mount=type=cache,target=/root/.npm \
    npm ci

このオプションにより、apt-get updateのネットワークアクセスやダウンロード済みパッケージがビルド間でキャッシュされ、デバッグの反復が大幅に速くなります。

実際の効果(実測値):

ビルド条件 所要時間
–no-cache(フルビルド) 約120秒
キャッシュ使用(デフォルト) 約30秒
–mount=type=cache使用 約8秒
# キャッシュを使ったビルド(2回目以降は高速)
docker build --progress=plain -t myimage .

# キャッシュを完全に消してクリーンビルドしたい場合
docker build --no-cache --progress=plain -t myimage .

# BuildKitキャッシュのみクリア(イメージは残す)
docker builder prune

–mount=type=secretで機密情報を安全に渡す

ビルド中にAPIキーや秘密鍵が必要な場合、ARGやENVに書くとイメージ履歴に残ってしまいます。--mount=type=secretを使うと安全です。

# ビルド中のみ参照可能なシークレット
RUN --mount=type=secret,id=github_token \
    curl -H "Authorization: token $(cat /run/secrets/github_token)" \
    https://api.github.com/repos/myorg/myrepo/releases/latest
# シークレットファイルを渡してビルド
docker build --secret id=github_token,src=./github_token.txt -t myimage .

–mount=type=bindでホストのファイルを参照する

COPYなしにホストのファイルをビルド中に参照できます。デバッグ時の一時的な設定注入に便利です。

# ホストの設定ファイルをビルド中のみマウント(COPYと異なりイメージに含まれない)
RUN --mount=type=bind,source=./config/dev.env,target=/tmp/env \
    /app/configure.sh /tmp/env

BuildKitキャッシュ有効時と無効時のビルド速度比較。--mount=type=cacheでapt-getのキャッシュを永続化するとビルドが大幅に高速化する

ステップ6完了の確認: 2回目以降のビルドでパッケージダウンロードがスキップされ、ビルド時間が短縮されている。


応用編

基本的なデバッグ手順を習得したら、以下の応用的なシナリオにも対応できます。

マルチステージビルドのデバッグ

マルチステージビルドでは--targetオプションで特定のステージまでビルドして検証できます。

FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine AS runner
COPY --from=builder /app/dist /usr/share/nginx/html
# builderステージだけビルドして確認
docker build --target builder -t myimage-builder .
docker run --rm -it --entrypoint /bin/sh myimage-builder

# コンテナ内で成果物を確認
/app # ls dist/
/app # cat dist/index.html | head -5

COPY –from が失敗する場合

# --fromで参照するステージ名を確認
docker build --progress=plain --target builder -t builder-debug .

# builderステージのファイルシステムを直接確認
docker run --rm -it --entrypoint /bin/sh builder-debug
/app # ls /app/dist    # ← distが存在するか確認

ヘルスチェックのデバッグ

コンテナが起動してもhealthyにならない場合、ヘルスチェックコマンドを手動実行して確認します。

# コンテナを起動(ヘルスチェックは無効化)
docker run --rm -it --no-healthcheck --entrypoint /bin/bash myimage

# コンテナ内でヘルスチェックコマンドを手動実行
curl -f http://localhost:8080/health
# または
wget -q --spider http://localhost:8080/health
echo $?  # 終了コードを確認(0なら成功)

起動後にヘルスチェックの状態を確認する

# ヘルスチェックの詳細ログを表示
docker inspect --format='{{json .State.Health}}' mycontainer | python3 -m json.tool
{
  "Status": "unhealthy",
  "FailingStreak": 3,
  "Log": [
    {
      "Start": "2026-03-29T10:00:00Z",
      "End": "2026-03-29T10:00:05Z",
      "ExitCode": 1,
      "Output": "curl: (7) Failed to connect to localhost port 8080"
    }
  ]
}

CI/CD環境でのデバッグ

GitHub Actionsなどのマトリクスビルドでのみ失敗する場合、ビルド引数を使って再現します。

# CIと同じビルド引数で手元で実行
docker build \
  --build-arg NODE_ENV=production \
  --build-arg CI=true \
  --progress=plain \
  -t myimage .
# .github/workflows/build.yml(失敗ログを残す設定)
- name: Build Docker image
  run: |
    docker build --progress=plain -t myimage . 2>&1 | tee build.log
- name: Upload build log
  if: failure()
  uses: actions/upload-artifact@v4
  with:
    name: build-log
    path: build.log

docker buildxを使ったデバッグ

docker buildxはBuildKitの拡張版で、より詳細なビルドメタデータを出力できます。

# buildxのデフォルトビルダーを確認
docker buildx ls

# 詳細なビルドメタデータを取得
docker buildx build --metadata-file meta.json -t myimage .
cat meta.json | python3 -m json.tool | grep -E "digest|platform"

# マルチプラットフォームビルド時のデバッグ
docker buildx build --platform linux/amd64,linux/arm64 --progress=plain -t myimage .

トラブルシュート

症状 原因 解決策
--progress=plainでもログが省略される BuildKitの環境変数が上書きされている BUILDKIT_PROGRESS=plain docker build ...として環境変数で直接指定する
docker ps -aに中間コンテナが表示されない BuildKit有効時は中間コンテナが残らない DOCKER_BUILDKIT=0で旧来の動作に切り替えるか--targetを使う
docker execOCI runtime exec failedエラー コンテナが完全に停止している docker psでコンテナがrunning状態かを確認する
--entrypoint /bin/bashでエラーが出る bashが存在しない(Alpine等の軽量イメージ) /bin/shを使う
docker execで入ったがbashが使えない 軽量イメージにbashがない apk add bash(Alpine)またはapt-get install -y bash(Debian系)でインストール
キャッシュが効かず毎回フルビルドになる COPYでのファイル変更がキャッシュを無効化している パッケージインストールをCOPYより前に移動する
--mount=type=cacheが効いていない BuildKitが無効 DOCKER_BUILDKIT=1を設定する、またはDocker 23.0以降に更新する
ビルドが成功してもコンテナが即終了する CMD/ENTRYPOINTが終了するコマンド docker inspectで終了コードを確認後、docker run -it --entrypoint /bin/bashでインタラクティブに起動して調査する
docker inspectで終了コード137 OOMキラーによるプロセス強制終了 docker statsでメモリ使用量を確認し、--memory制限を引き上げる

まとめ

このガイドでは「Dockerfileデバッグ」について、以下の手順で解説しました。

  • ステップ1: --progress=plainでビルドの詳細ログを出力し、エラーの全体像を把握する
  • ステップ2: ビルド失敗時の中間コンテナを取り出し、失敗直前のファイルシステム状態を調査する
  • ステップ3: docker execで起動中のコンテナにアタッチし、リアルタイムで内部を調査する
  • ステップ4: docker inspectでコンテナの設定・終了コード・ネットワーク情報を確認する
  • ステップ5: docker run --entrypointでCMD/ENTRYPOINTを無視してbashを起動し、内部状態を自由に調べる
  • ステップ6: BuildKitの--mount=type=cacheでデバッグのイテレーション速度を高速化する

Dockerfileのデバッグで最も重要な原則は「ログを完全に見る」「中間状態に入る」「本番と同じ条件を再現する」の3点です。--progress=plainでエラーの詳細を把握し、--targetで中間コンテナに入り、docker execで起動後の問題を調査する—この流れを身につけるだけで、Dockerfileに関するほとんどの問題は解決できます。

次のステップとして、マルチステージビルドやBuildKitの高度なキャッシュ戦略、docker buildxを活用したマルチプラットフォームビルドを習得すると、本番環境でのDockerイメージビルドの効率がさらに向上します。


関連記事

Docker

Posted by GENDOSU