Docker: Dockerfile の基本

最終更新:2026-08-26

Dockerfile はイメージの「ソースコード」です — Dockerfile を書くことで、1 つのコマンドで再現可能なアプリケーションイメージをビルドできます。

1. 学べること



2. Python 開発者の実話

(1) 問題点: デプロイのたびに環境を手動で構築する必要がある

Alice は Python Web アプリケーションを書きました。デプロイのたびに Python のインストール、仮想環境のセットアップ、依存関係のインストール、コードのコピーを手動で行う必要があります。テストサーバー、ステージング環境、本番サーバーの 3 回のために、このプロセスを 3 回繰り返す必要があり、それぞれに微妙な違いがありました。

(2) Dockerfile による自動化のソリューション

Bob は言いました。「Dockerfile を書いて、アプリケーションをイメージに『パッケージ化』すれば、どこでも 1 つのコマンドで動かせるよ」

DOCKERFILE
# Flask アプリケーション用のシンプルな Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]

(3) メリット: 一度ビルドすれば、どこでも動く

Alice が docker build -t myapp:1.0 . でイメージを構築した後、3 つの環境すべてが同じイメージで動作し、デプロイ時間は 30 分から 3 分に短縮され、環境の不整合が解消されました。



3. Dockerfile の基本構造

Dockerfile は、イメージをビルドするための一連の手順を含むプレーンテキストファイルです。各命令はイメージレイヤーを生成します。

100%
graph LR
    DF["Dockerfile<br/>一連の命令"] -->|docker build| IMG["イメージ<br/>読み取り専用オーバーレイ"]
    IMG -->|docker run| CTN["コンテナ<br/>書き込み可能レイヤー + 読み取り専用レイヤー"]

(1) 命令実行のタイミング

タイミング コマンド 説明
ビルド中 FROM / RUN / COPY / ADD / ARG イメージレイヤーを生成
実行時 CMD / ENTRYPOINT / ENV / EXPOSE / USER コンテナの動作を定義

(2) Dockerfile 基本テンプレート

DOCKERFILE
# 1. ベースイメージ
FROM python:3.12-slim

# 2. 作業ディレクトリの設定
WORKDIR /app

# 3. 依存関係ファイルを最初にコピー(キャッシュ最適化)
COPY requirements.txt .

# 4. 依存関係をインストール
RUN pip install --no-cache-dir -r requirements.txt

# 5. アプリケーションコードをコピー
COPY . .

# 6. デフォルトコマンドを定義
CMD ["python", "app.py"]


4. FROM: ベースイメージの選択

FROM は Dockerfile の最初の命令であり、ビルドのベースイメージを指定します。

(1) ベースイメージ選択戦略

戦略 イメージ サイズ ユースケース
公式言語ミラー python:3.12-slim 155 MB Python プロジェクト
Alpine バリアント python:3.12-alpine 50 MB ディスク容量が極めて限られる
マルチステージビルド golang:1.22alpine 12 MB Go/Rust などのコンパイル言語
ミニマル OS debian:bookworm-slim 74 MB カスタム環境が必要

▶ サンプル: 最もシンプルな Dockerfile (難易度: ⭐)

DOCKERFILE
# 最小限の Dockerfile: hello を出力するだけ
FROM alpine:3.19
CMD ["echo", "Hello from Docker!"]
BASH
# ビルドして実行
docker build -t hello:1.0 .
docker run --rm hello:1.0
💻 出力:

TEXT 📖 参照専用
Hello from Docker!


5. RUN: ビルドコマンドの実行

RUN はビルド中にコマンドを実行し、結果を新しいイメージレイヤーに書き込みます。

(1) 2 つのフォーマット

フォーマット 構文 特徴
シェルフォーマット RUN apt-get install nginx デフォルトで /bin/sh -c を実行; パイプをサポート
Exec モード RUN ["apt-get", "install", "nginx"] シェルを起動せずに直接実行

▶ サンプル: RUN で依存関係をインストール (難易度: ⭐⭐)

DOCKERFILE
# ベストプラクティス: RUN を結合してレイヤーを削減
FROM debian:bookworm-slim

RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        curl \
        nginx && \
    rm -rf /var/lib/apt/lists/*
📌 重要: 複数の apt-get コマンドを 1 つの RUN にまとめてイメージレイヤーの数を減らします。rm -rf /var/lib/apt/lists/* で APT キャッシュをクリアしてイメージサイズを抑えます。

(2) RUN チェーン併合の原則

方法 結果 備考
複数の RUN 各命令がレイヤーを作成 レイヤーが多く、ファイルサイズが大きい
RUN&& で併合 1 行につき 1 レイヤー レイヤーが少なく、サイズがコンパクト
キャッシュクリア rm -rf apt/lists 同じレイヤー内でキャッシュをクリア
別途クリーンアップ 次の RUN rm 無効 — 前のレイヤーはすでに確定
DOCKERFILE
# 悪い例: 2 つのレイヤーを作成、apt キャッシュがレイヤー 1 に焼き込まれる
RUN apt-get update
RUN apt-get install -y nginx

# 良い例: 1 つのレイヤーを作成、同じレイヤー内でキャッシュをクリア
RUN apt-get update && \
    apt-get install -y nginx && \
    rm -rf /var/lib/apt/lists/*


6. CMD と ENTRYPOINT

CMD と ENTRYPOINT はどちらもコンテナ起動時に実行するコマンドを定義しますが、動作が異なります。

(1) 3 つの起動コマンドの比較

観点 CMD ENTRYPOINT
目的 デフォルトコマンドを提供 固定エントリーポイントを定義
上書き可能 docker run パラメータで直接上書き --entrypoint で上書きが必要
組み合わせ ENTRYPOINT と併用可能 CMD のコマンドライン引数と併用可能
複数指定 最後のもののみ有効 最後のもののみ有効

(2) CMD の 3 つのフォーマット

フォーマット 構文 推奨度 説明
Exec フォーマット CMD ["python", "app.py"] ⭐⭐⭐ 直接実行; シグナルが正しく伝わる
シェルフォーマット CMD python app.py /bin/sh -c の子プロセスとして動作、SIGTERM が伝播しない
パラメータフォーマット CMD ["--port", "8080"] ⭐⭐ ENTRYPOINT と併用

▶ サンプル: CMD と ENTRYPOINT の違い (難易度: ⭐⭐)

DOCKERFILE
# CMD を使用した Dockerfile: コマンドを簡単に上書き可能
FROM alpine:3.19
CMD ["echo", "Hello default"]
BASH
# デフォルト: CMD を実行
docker run --rm test-cmd
# 出力: Hello default

# カスタムコマンドで CMD を上書き
docker run --rm test-cmd echo "Custom message"
# 出力: Custom message
DOCKERFILE
# ENTRYPOINT を使用した Dockerfile: コマンドは固定、引数が追加される
FROM alpine:3.19
ENTRYPOINT ["echo"]
CMD ["Hello default"]
BASH
# デフォルト: ENTRYPOINT + CMD を実行
docker run --rm test-entry
# 出力: Hello default

# 引数を追加(ENTRYPOINT は上書きしない)
docker run --rm test-entry "Custom message"
# 出力: Custom message

# ENTRYPOINT を上書き(稀なケース)
docker run --rm --entrypoint sh test-entry -c "ls /"

▶ サンプル: ENTRYPOINT + CMD の組み合わせ (難易度: ⭐⭐⭐)

これがベストプラクティスです — ENTRYPOINT が実行プログラムを指定し、CMD がデフォルト引数を提供します:

DOCKERFILE
# Entrypoint + CMD パターン
FROM python:3.12-slim
WORKDIR /app
COPY app.py .
ENTRYPOINT ["python", "app.py"]
CMD ["--host", "0.0.0.0", "--port", "5000"]
BASH
# デフォルト: CMD の引数を使用
docker run --rm myapp
# 次と等価: python app.py --host 0.0.0.0 --port 5000

# 引数のみを上書き
docker run --rm myapp --port 8080
# 次と等価: python app.py --port 8080


7. コンテキストと .dockerignore の設定

(1) コンテキストの設定

docker build の最後のパラメータ . は「現在のディレクトリ」ではなく、ビルドコンテキスト を指します — Docker クライアントはこのディレクトリのすべてのファイルをデーモンに送信します。

100%
graph LR
    CTX["ビルドコンテキスト<br/>(. ディレクトリ)"] -->|ファイル送信| D["Docker デーモン"]
    D -->|COPY/ADD の Dockerfile| IMG["イメージレイヤー"]
⚠️ 注意: ディレクトリに 1 GB の node_modules がある場合、ビルド中に 1 GB がデーモンに送信されます(Dockerfile が COPY しなくても)。.dockerignore で不要なファイルを除外できます。

▶ サンプル: .dockerignore の仕組み (難易度: ⭐⭐)

TEXT 📖 参照専用
# .dockerignore - ビルドコンテキストからファイルを除外
node_modules
.git
__pycache__
*.pyc
.env
Dockerfile
docker-compose*.yml
README.md
.vscode

(2) なぜ依存関係ファイルの COPY をソースコードの COPY より前に書くべきか?

DOCKERFILE
# 良い例: 依存関係の変更頻度 < コードの変更頻度
# コード変更時、依存関係レイヤーはキャッシュを使用
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .

# 悪い例: ファイル変更で pip install レイヤーが無効化される
COPY . .
RUN pip install -r requirements.txt
変更シナリオ 良いバージョン 悪いバージョン
コードのみ変更 ✅ pip キャッシュ使用(数秒) ❌ pip 再ビルド(数分)
依存関係を変更 ✅ pip レイヤー再ビルド(必要) ❌ pip レイヤー再ビルド(同上)


8. docker build の一般的なパラメータ

パラメータ 機能
-t イメージ名:タグ -t myapp:1.0
-f Dockerfile パスを指定 -f Dockerfile.prod .
--build-arg ビルドパラメータを渡す --build-arg VERSION=2.0
--no-cache キャッシュを使用しない --no-cache
--target 指定したステージまでビルド --target builder
--platform ターゲットプラットフォームを指定 --platform linux/arm64

▶ サンプル: イメージのビルドとレイヤー履歴の確認 (難易度: ⭐⭐)

BASH
# タグ付きでビルド
docker build -t myapp:1.0 .

# イメージレイヤーを表示
docker history myapp:1.0


9. 完全なサンプル: Flask アプリケーション用 Dockerfile の作成

DOCKERFILE
# ============================================
# Flask Web アプリケーション用 Dockerfile
# デモ: FROM、WORKDIR、COPY、RUN、CMD
# ============================================

# 公式 Python slim イメージを使用
FROM python:3.12-slim

# コンテナ内の作業ディレクトリを設定
WORKDIR /app

# 依存関係ファイルを最初にコピー(キャッシュ最適化)
COPY requirements.txt .

# 依存関係をインストール(同じレイヤー内でキャッシュをクリア)
RUN pip install --no-cache-dir -r requirements.txt

# アプリケーションソースコードをコピー
COPY . .

# アプリケーションポートを公開(ドキュメント目的のみ)
EXPOSE 5000

# Flask アプリケーションを実行
CMD ["python", "app.py"]
BASH
# イメージをビルド
docker build -t flask-app:1.0 .

# コンテナを実行
docker run -d -p 5000:5000 --name my-flask flask-app:1.0

# アプリケーションをテスト
curl http://localhost:5000

# イメージサイズとレイヤーを表示
docker images flask-app
docker history flask-app:1.0
💻 出力(抜粋):

TEXT 📖 参照専用
# docker images flask-app
REPOSITORY   TAG   IMAGE ID       SIZE
flask-app    1.0   a1b2c3d4e5f6   180MB

# docker history flask-app:1.0
IMAGE          CREATED        CREATED BY                          SIZE
a1b2c3d4e5f6   5 seconds ago  CMD ["python" "app.py"]             0B
<missing>      5 seconds ago  COPY . .                            2.5kB
<missing>      5 seconds ago  RUN pip install --no-cache-dir...   45MB
<missing>      5 seconds ago  COPY requirements.txt .             58B
<missing>      5 seconds ago  WORKDIR /app                        0B

❓ よくある質問

Q なぜ依存関係ファイルを先に COPY してソースコードを後に COPY するのですか?
A レイヤーキャッシュ機構を活用するためです。依存関係はコードよりもはるかに変更頻度が低いです。依存関係ファイルを先に COPY してインストールすることで、このレイヤーがキャッシュされます。後でコードのみが変更された場合、依存関係レイヤーは直接キャッシュを使用するため、ビルド時間が数分から数秒に短縮されます。すべてのファイルを先に COPY すると、ファイル変更ごとに pip install レイヤーのキャッシュが無効化されます。
Q CMD と ENTRYPOINT は併用できますか?
A はい、併用が推奨されます。ENTRYPOINT は固定の実行可能ファイル(例: python app.py)を定義し、CMD はデフォルト引数(例: --port 5000)を提供します。docker run に渡された引数は CMD を上書きしますが ENTRYPOINT は上書きしないため、「固定の実行ファイル + 柔軟な引数」という構成が可能です。
Q 「ビルドコンテキスト」とは何ですか?
A docker build コマンドの末尾の . がビルドコンテキストディレクトリを指定します。Docker クライアントはこのディレクトリのすべてのファイルをパッケージ化してデーモンに送信します。Dockerfile 内の COPYADD コマンドはビルドコンテキスト内のファイルのみを参照できます。.dockerignore を使って不要なファイルを除外すれば、ビルドが高速化されビルドコンテキストサイズも削減できます。
Q .dockerignore ファイルはどのように書きますか?
A 構文は .gitignore と同じです。必ず除外すべきもの: node_modules、.git、pycache
Q ビルド失敗のトラブルシューティングはどうしますか?
A 次の 3 ステップに従ってください: ① エラーメッセージを確認 — Docker は失敗したコマンドと行番号を示します; ② コンテキストを確認 — COPY で指定されたファイルが存在するか確認; ③ 対話的にデバッグ — docker run -it <最後に成功したレイヤー> bash で最後に成功したイメージレイヤーに入り、手動でトラブルシューティングします。
Q RUN の 1 行が長すぎる場合はどうしますか?
A \ で行を継続し、&& でコマンドを連結します。これが Dockerfile の標準的な書き方で、レイヤー数を抑えつつ可読性を維持します。例: RUN apt-get update && \ + apt-get install -y nginx && \ + rm -rf /var/lib/apt/lists/*

📖 まとめ

  • Dockerfile はイメージの「ソースコード」であり、各命令が読み取り専用レイヤーを生成する
  • FROM: ベースイメージの選択 — 「slim」は互換性が良好、「alpine」は最小だが互換性の問題がある場合もある
  • RUN コマンドを併合してレイヤー数を削減し、各レイヤー内でキャッシュをクリアしてファイルサイズを抑える
  • CMD はデフォルトコマンドを提供(上書き可能)、ENTRYPOINT は固定エントリーポイントを定義(上書きしにくい)
  • ENTRYPOINT + CMD の組み合わせがベストプラクティス: 固定プログラム + 柔軟なパラメータ
  • ソースコードの前に依存関係ファイルを COPY し、レイヤーキャッシュでビルドを高速化する

📝 練習問題

  1. 基本問題 (難易度: ⭐): Node.js アプリケーション用の Dockerfile を書き、ベースイメージに node:20-alpine を使い、npm install で依存関係をインストールし、node server.js でアプリケーションを起動してください。
  2. 応用問題 (難易度: ⭐⭐): イメージをビルドしてコンテナを実行します。docker history を使ってイメージ内の各レイヤーのサイズを分析し、最も大きいレイヤーを特定してその理由を説明してください。
  3. 挑戦問題 (難易度: ⭐⭐⭐): .dockerignore ファイルを作成し、node_modules と .git を除外した上で、.dockerignore がある場合とない場合のビルドコンテキストサイズの違いを比較してください(ヒント: docker build の出力で「Sending ビルド context to Docker デーモン」という行を探してください)。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%