Dockerfileは、Dockerイメージを構築するための命令を含むテキストファイルです。この記事では、効率的で本番環境に対応したイメージを作成するための基本的なDockerfile命令とベストプラクティスを学びます。
Dockerfileとは
Dockerfileは、Dockerイメージを構築するためのレシピです。ファイル内の各命令が、最終イメージのレイヤーを作成します:
# Dockerfileの例
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
イメージのビルド
# 基本的なビルド
docker build -t myapp:v1 .
# 特定のDockerfileでビルド
docker build -t myapp:v1 -f Dockerfile.prod .
# キャッシュなしでビルド
docker build --no-cache -t myapp:v1 .
# ビルド引数付きでビルド
docker build --build-arg VERSION=1.0 -t myapp:v1 .
基本的な命令
FROM - ベースイメージ
すべてのDockerfileはFROMで始まる必要があります。ベースイメージを設定します:
# 公式イメージ
FROM node:18
# タグ付きの特定バージョン
FROM node:18.19.0-alpine
# 最小限のベースイメージ
FROM alpine:3.19
# 空のベース(静的バイナリ用)
FROM scratch
ベストプラクティス: latestではなく、常に特定のタグを使用:
# 悪い例 - 予測不可能
FROM node:latest
# 良い例 - 再現可能
FROM node:18.19.0-alpine
RUN - コマンドの実行
RUNはビルドプロセス中にコマンドを実行します:
# シェル形式
RUN apt-get update && apt-get install -y curl
# Exec形式(明確さのため推奨)
RUN ["apt-get", "update"]
# バックスラッシュで複数行
RUN apt-get update && \
apt-get install -y \
curl \
wget \
git && \
rm -rf /var/lib/apt/lists/*
ベストプラクティス: 関連するコマンドを結合してレイヤーを削減:
# 悪い例 - 複数のレイヤーを作成
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y wget
# 良い例 - 単一レイヤー、クリーンアップを含む
RUN apt-get update && \
apt-get install -y curl wget && \
rm -rf /var/lib/apt/lists/*
COPY vs ADD
両方ともイメージにファイルをコピーしますが、違いがあります:
# COPY - シンプルなファイル/ディレクトリコピー
COPY package.json /app/
COPY src/ /app/src/
# ADD - 追加機能(必要な場合のみ使用)
ADD https://example.com/file.tar.gz /app/ # URLをダウンロード
ADD archive.tar.gz /app/ # アーカイブを自動展開
| 機能 | COPY | ADD |
|---|---|---|
| ファイルをコピー | ✅ | ✅ |
| ディレクトリをコピー | ✅ | ✅ |
| URLダウンロード | ❌ | ✅ |
| アーカイブ自動展開 | ❌ | ✅ |
| 推奨 | ✅ | 必要な場合のみ |
ベストプラクティス: ADDの機能が特に必要でなければCOPYを使用。
WORKDIR - 作業ディレクトリの設定
後続の命令の作業ディレクトリを設定します:
WORKDIR /app
# 以降のすべてのコマンドは/appで実行
COPY . .
RUN npm install
CMD ["node", "server.js"]
ベストプラクティス: RUN cdの代わりにWORKDIRを使用:
# 悪い例
RUN cd /app && npm install
# 良い例
WORKDIR /app
RUN npm install
ENV - 環境変数
コンテナで利用可能な環境変数を設定します:
# 単一の変数
ENV NODE_ENV=production
# 複数の変数
ENV NODE_ENV=production \
PORT=3000 \
DEBUG=false
# 変数の使用
ENV APP_HOME=/app
WORKDIR $APP_HOME
ARG - ビルド時変数
ビルド中にのみ利用可能な変数:
ARG NODE_VERSION=18
FROM node:${NODE_VERSION}-alpine
ARG BUILD_DATE
ARG GIT_SHA
LABEL build-date=$BUILD_DATE
LABEL git-sha=$GIT_SHA
# ビルド時に値を渡す
docker build --build-arg NODE_VERSION=20 --build-arg GIT_SHA=$(git rev-parse HEAD) -t myapp .
| 側面 | ENV | ARG |
|---|---|---|
| ビルド中に利用可能 | ✅ | ✅ |
| 実行時に利用可能 | ✅ | ❌ |
| 実行時に上書き可能 | ✅ (-eで) |
❌ |
EXPOSE - ポートのドキュメント化
コンテナがリッスンするポートをドキュメント化します:
# 単一ポート
EXPOSE 3000
# 複数ポート
EXPOSE 80 443
# UDPポート
EXPOSE 53/udp
注意: EXPOSEはドキュメントのみです。ポートを公開するには-pが必要です:
docker run -p 8080:3000 myapp
CMD vs ENTRYPOINT
両方ともデフォルトコマンドを指定しますが、目的が異なります:
CMD - デフォルトコマンド
# Exec形式(推奨)
CMD ["node", "server.js"]
# シェル形式
CMD node server.js
# ENTRYPOINTのデフォルトパラメータとして
CMD ["--port", "3000"]
CMDは実行時に上書き可能:
docker run myapp node other-script.js # CMDを上書き
ENTRYPOINT - 固定コマンド
# Exec形式
ENTRYPOINT ["node", "server.js"]
# シェル形式(非推奨)
ENTRYPOINT node server.js
ENTRYPOINT引数は追加される:
docker run myapp --port 8080 # 実行: node server.js --port 8080
組み合わせパターン
ENTRYPOINT ["node"]
CMD ["server.js"]
docker run myapp # 実行: node server.js
docker run myapp other-script.js # 実行: node other-script.js
比較表
| 側面 | CMD | ENTRYPOINT |
|---|---|---|
| 目的 | デフォルトコマンド/引数 | 固定実行ファイル |
| 実行時の上書き | 置き換えられる | 引数が追加される |
| ユースケース | 柔軟なデフォルト | コンテナを実行ファイルとして |
完全な例:Node.jsアプリケーション
# ビルドステージ
FROM node:18-alpine AS builder
WORKDIR /app
# パッケージファイルを先にコピー(キャッシュ効率化)
COPY package*.json ./
# 依存関係をインストール
RUN npm ci --only=production
# ソースコードをコピー
COPY . .
# 本番ステージ
FROM node:18-alpine
# 非rootユーザーを作成
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
# ビルダーからコピー
COPY /app/node_modules ./node_modules
COPY /app .
# 所有権を設定
RUN chown -R appuser:appgroup /app
# 非rootユーザーに切り替え
USER appuser
# 環境設定
ENV NODE_ENV=production
ENV PORT=3000
# ドキュメント
EXPOSE 3000
# ヘルスチェック
HEALTHCHECK \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
# 起動コマンド
CMD ["node", "server.js"]
完全な例:Pythonアプリケーション
FROM python:3.11-slim
# Pythonが.pycファイルを書き込むのを防止
ENV PYTHONDONTWRITEBYTECODE=1
# Pythonがstdout/stderrをバッファリングするのを防止
ENV PYTHONUNBUFFERED=1
WORKDIR /app
# システム依存関係をインストール
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc && \
rm -rf /var/lib/apt/lists/*
# キャッシュのためにrequirementsを先にコピー
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# アプリケーションをコピー
COPY . .
# 非rootユーザーを作成
RUN useradd --create-home appuser && \
chown -R appuser:appuser /app
USER appuser
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]
追加の命令
LABEL - メタデータ
LABEL maintainer="developer@example.com"
LABEL version="1.0"
LABEL description="My application"
# OCI標準ラベル
LABEL org.opencontainers.image.source="https://github.com/user/repo"
LABEL org.opencontainers.image.version="1.0.0"
USER - ユーザー設定
# 非rootユーザーを作成して切り替え
RUN addgroup -S mygroup && adduser -S myuser -G mygroup
USER myuser
# または数値UIDを使用
USER 1000
VOLUME - マウントポイントの宣言
VOLUME /data
VOLUME ["/var/log", "/var/data"]
HEALTHCHECK - コンテナヘルス
HEALTHCHECK \
CMD curl -f http://localhost:3000/health || exit 1
# ヘルスチェックを無効化
HEALTHCHECK NONE
SHELL - デフォルトシェルの変更
# WindowsでPowerShellに変更
SHELL ["powershell", "-Command"]
# またはbashを使用
SHELL ["/bin/bash", "-c"]
ビルドコンテキストと.dockerignore
ビルドコンテキストはDockerデーモンに送信されるディレクトリです。.dockerignoreでファイルを除外:
# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
Dockerfile
.dockerignore
*.md
.env
.env.*
coverage/
.nyc_output/
レイヤーキャッシュ
Dockerは各レイヤーをキャッシュします。変更頻度の低いものから高いものへ命令を順序付け:
# 良い順序(安定 → 変更が多い)
FROM node:18-alpine # めったに変更されない
WORKDIR /app # めったに変更されない
COPY package*.json ./ # 依存関係が変わると変更
RUN npm install # package.jsonが変わらなければキャッシュ
COPY . . # 頻繁に変更
CMD ["node", "server.js"] # めったに変更されない
flowchart TB
subgraph Cache["レイヤーキャッシュ"]
L1["FROM node:18-alpine ✓ キャッシュ"]
L2["WORKDIR /app ✓ キャッシュ"]
L3["COPY package.json ✓ キャッシュ"]
L4["RUN npm install ✓ キャッシュ"]
L5["COPY . . ✗ 再ビルド"]
L6["CMD ✗ 再ビルド"]
end
L1 --> L2 --> L3 --> L4 --> L5 --> L6
style L1 fill:#22c55e,color:#fff
style L2 fill:#22c55e,color:#fff
style L3 fill:#22c55e,color:#fff
style L4 fill:#22c55e,color:#fff
style L5 fill:#ef4444,color:#fff
style L6 fill:#ef4444,color:#fff
ベストプラクティスまとめ
| プラクティス | 説明 |
|---|---|
| 特定のベースイメージタグを使用 | 再現可能なビルドを保証 |
| レイヤーを最小化 | RUNコマンドを結合 |
| 変更頻度で順序付け | 安定した命令を先に |
| .dockerignoreを使用 | ビルドコンテキストサイズを削減 |
| 不要なパッケージをインストールしない | 小さいイメージ、攻撃面の縮小 |
| 非rootユーザーを使用 | セキュリティのベストプラクティス |
| ADDよりCOPYを使用 | ADD機能が必要でなければ |
| 同じレイヤーでクリーンアップ | パッケージマネージャーキャッシュを削除 |
Dockerfileのリント
hadolintを使用してDockerfileをチェック:
# hadolintをインストール
brew install hadolint
# Dockerfileをリント
hadolint Dockerfile
# またはDockerを使用
docker run --rm -i hadolint/hadolint < Dockerfile
クイックリファレンス
FROM image:tag # ベースイメージ
WORKDIR /path # 作業ディレクトリ設定
COPY src dest # ファイルをコピー
ADD src dest # 追加機能付きコピー
RUN command # コマンド実行
ENV KEY=value # 環境変数設定
ARG NAME=default # ビルド引数
EXPOSE port # ポートをドキュメント化
USER username # ユーザー設定
VOLUME /path # ボリューム宣言
HEALTHCHECK CMD command # ヘルスチェック
CMD ["executable", "arg"] # デフォルトコマンド
ENTRYPOINT ["executable"] # 固定コマンド
重要なポイント
- シンプルに始める - 動作するDockerfileから始め、後で最適化
- 特定のタグを使用 - 本番環境では絶対に
latestを使わない - キャッシュを活用 - 変更頻度で命令を順序付け
- イメージサイズを最小化 - alpineイメージ、マルチステージビルドを使用
- 非rootで実行 - 非rootユーザーを作成して使用
- クリーンアップ - キャッシュと不要なファイルを削除
次のステップ
次の記事では、Docker Composeを学びます。単一の設定ファイルでマルチコンテナアプリケーションを定義して実行する方法を習得しましょう。
参考文献
- Docker Deep Dive, 5th Edition - Nigel Poulton
- Docker in Action, 2nd Edition - Jeffrey Nickoloff
- Dockerfileリファレンス