Docker: Dockerfile の基本
最終更新:2026-08-26
Dockerfile はイメージの「ソースコード」です — Dockerfile を書くことで、1 つのコマンドで再現可能なアプリケーションイメージをビルドできます。
1. 学べること
- Dockerfile の基本構文と構造
- 「FROM」句によるベースイメージ選択のポリシー
- RUN: ビルドコマンド実行のベストプラクティス
- CMD と ENTRYPOINT の違い
- コンテキストと .dockerignore の設定
2. Python 開発者の実話
(1) 問題点: デプロイのたびに環境を手動で構築する必要がある
Alice は Python Web アプリケーションを書きました。デプロイのたびに Python のインストール、仮想環境のセットアップ、依存関係のインストール、コードのコピーを手動で行う必要があります。テストサーバー、ステージング環境、本番サーバーの 3 回のために、このプロセスを 3 回繰り返す必要があり、それぞれに微妙な違いがありました。
(2) Dockerfile による自動化のソリューション
Bob は言いました。「Dockerfile を書いて、アプリケーションをイメージに『パッケージ化』すれば、どこでも 1 つのコマンドで動かせるよ」
# 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 は、イメージをビルドするための一連の手順を含むプレーンテキストファイルです。各命令はイメージレイヤーを生成します。
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 基本テンプレート
# 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.22 → alpine |
12 MB | Go/Rust などのコンパイル言語 |
| ミニマル OS | debian:bookworm-slim |
74 MB | カスタム環境が必要 |
▶ サンプル: 最もシンプルな Dockerfile (難易度: ⭐)
# 最小限の Dockerfile: hello を出力するだけ
FROM alpine:3.19
CMD ["echo", "Hello from Docker!"]
# ビルドして実行
docker build -t hello:1.0 .
docker run --rm hello:1.0
Hello from Docker!
5. RUN: ビルドコマンドの実行
RUN はビルド中にコマンドを実行し、結果を新しいイメージレイヤーに書き込みます。
(1) 2 つのフォーマット
| フォーマット | 構文 | 特徴 |
|---|---|---|
| シェルフォーマット | RUN apt-get install nginx |
デフォルトで /bin/sh -c を実行; パイプをサポート |
| Exec モード | RUN ["apt-get", "install", "nginx"] |
シェルを起動せずに直接実行 |
▶ サンプル: RUN で依存関係をインストール (難易度: ⭐⭐)
# ベストプラクティス: 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 |
無効 — 前のレイヤーはすでに確定 |
# 悪い例: 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 の違い (難易度: ⭐⭐)
# CMD を使用した Dockerfile: コマンドを簡単に上書き可能
FROM alpine:3.19
CMD ["echo", "Hello default"]
# デフォルト: CMD を実行
docker run --rm test-cmd
# 出力: Hello default
# カスタムコマンドで CMD を上書き
docker run --rm test-cmd echo "Custom message"
# 出力: Custom message
# ENTRYPOINT を使用した Dockerfile: コマンドは固定、引数が追加される
FROM alpine:3.19
ENTRYPOINT ["echo"]
CMD ["Hello default"]
# デフォルト: 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 がデフォルト引数を提供します:
# Entrypoint + CMD パターン
FROM python:3.12-slim
WORKDIR /app
COPY app.py .
ENTRYPOINT ["python", "app.py"]
CMD ["--host", "0.0.0.0", "--port", "5000"]
# デフォルト: 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 クライアントはこのディレクトリのすべてのファイルをデーモンに送信します。
graph LR
CTX["ビルドコンテキスト<br/>(. ディレクトリ)"] -->|ファイル送信| D["Docker デーモン"]
D -->|COPY/ADD の Dockerfile| IMG["イメージレイヤー"]
node_modules がある場合、ビルド中に 1 GB がデーモンに送信されます(Dockerfile が COPY しなくても)。.dockerignore で不要なファイルを除外できます。
▶ サンプル: .dockerignore の仕組み (難易度: ⭐⭐)
# .dockerignore - ビルドコンテキストからファイルを除外
node_modules
.git
__pycache__
*.pyc
.env
Dockerfile
docker-compose*.yml
README.md
.vscode
(2) なぜ依存関係ファイルの COPY をソースコードの COPY より前に書くべきか?
# 良い例: 依存関係の変更頻度 < コードの変更頻度
# コード変更時、依存関係レイヤーはキャッシュを使用
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 |
▶ サンプル: イメージのビルドとレイヤー履歴の確認 (難易度: ⭐⭐)
# タグ付きでビルド
docker build -t myapp:1.0 .
# イメージレイヤーを表示
docker history myapp:1.0
9. 完全なサンプル: Flask アプリケーション用 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"]
# イメージをビルド
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
# 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
❓ よくある質問
python app.py)を定義し、CMD はデフォルト引数(例: --port 5000)を提供します。docker run に渡された引数は CMD を上書きしますが ENTRYPOINT は上書きしないため、「固定の実行ファイル + 柔軟な引数」という構成が可能です。docker build コマンドの末尾の . がビルドコンテキストディレクトリを指定します。Docker クライアントはこのディレクトリのすべてのファイルをパッケージ化してデーモンに送信します。Dockerfile 内の COPY と ADD コマンドはビルドコンテキスト内のファイルのみを参照できます。.dockerignore を使って不要なファイルを除外すれば、ビルドが高速化されビルドコンテキストサイズも削減できます。COPY で指定されたファイルが存在するか確認; ③ 対話的にデバッグ — docker run -it <最後に成功したレイヤー> bash で最後に成功したイメージレイヤーに入り、手動でトラブルシューティングします。\ で行を継続し、&& でコマンドを連結します。これが 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 し、レイヤーキャッシュでビルドを高速化する
📝 練習問題
- 基本問題 (難易度: ⭐): Node.js アプリケーション用の Dockerfile を書き、ベースイメージに
node:20-alpineを使い、npm installで依存関係をインストールし、node server.jsでアプリケーションを起動してください。 - 応用問題 (難易度: ⭐⭐): イメージをビルドしてコンテナを実行します。
docker historyを使ってイメージ内の各レイヤーのサイズを分析し、最も大きいレイヤーを特定してその理由を説明してください。 - 挑戦問題 (難易度: ⭐⭐⭐): .dockerignore ファイルを作成し、node_modules と .git を除外した上で、.dockerignore がある場合とない場合のビルドコンテキストサイズの違いを比較してください(ヒント:
docker buildの出力で「Sending ビルド context to Docker デーモン」という行を探してください)。