Dockerfile の書き方ひとつで、コンテナイメージのサイズは 10 倍、ビルド時間は 20 倍、脆弱性の数は 5 倍の差がつきます。「動けばいい」から「本番で使える」Dockerfile への進化は、2025 年の DevOps エンジニアにとって必須スキルです。
Kubo では、Kubernetes 上で稼働する多数のコンテナワークロードを管理しており、Dockerfile の品質がクラスタ全体のパフォーマンスとセキュリティに直接影響します。本記事では、2025 年の最新知見に基づいた Dockerfile ベストプラクティスを体系的に解説します。
1. ベースイメージの選択: 軽量化の第一歩
ベースイメージの選択は、最終イメージのサイズとセキュリティに最も大きく影響します。Docker 公式ドキュメントでは、信頼できるソースからのイメージを推奨しています。
ベースイメージ比較
| ベースイメージ | サイズ | CVE 数(目安) | シェル | 用途 |
|---|---|---|---|---|
ubuntu:24.04 | ~77MB | 30-50 | bash | 汎用(非推奨) |
debian:bookworm-slim | ~52MB | 20-40 | bash | Debian 系の軽量版 |
alpine:3.21 | ~5MB | 5-10 | sh (busybox) | 軽量 + デバッグ可能 |
distroless/static | ~2MB | 0-2 | なし | Go/Rust 静的バイナリ |
distroless/base | ~20MB | 2-5 | なし | glibc 依存あり |
scratch | 0MB | 0 | なし | 完全カスタム |
Google の distroless イメージは、アプリケーションとそのランタイム依存のみを含み、シェルやパッケージマネージャが存在しないため攻撃対象領域を最小化します。
# 推奨: 用途に応じた最小イメージを選択
# Go アプリケーション
FROM gcr.io/distroless/static-debian12
# Node.js アプリケーション
FROM gcr.io/distroless/nodejs20-debian12
# Python アプリケーション
FROM python:3.12-slim
バージョンピニングは再現性のために必須です:
# 悪い例: タグが変わる可能性
FROM node:20
# 良い例: 具体的なバージョン指定
FROM node:20.18-alpine3.21
# 最良: ダイジェストで完全固定
FROM node:20.18-alpine3.21@sha256:abc123...
Captain.AI は、プロジェクトの言語とフレームワークを分析し、最適なベースイメージを自動推薦します。
2. レイヤー構造の最適化
Docker はレイヤーキャッシュを活用してビルドを高速化します。命令の順序を最適化することで、キャッシュヒット率を最大化できます。
命令の順序: 変更頻度の低い順に
# 1. ベースイメージ(ほぼ変更なし)
FROM node:20-alpine AS builder
# 2. システム依存のインストール(まれに変更)
RUN apk add --no-cache python3 make g++
# 3. 依存定義のコピー(パッケージ追加時のみ変更)
WORKDIR /app
COPY package.json package-lock.json ./
# 4. 依存のインストール(パッケージ追加時のみ再実行)
RUN npm ci
# 5. ソースコードのコピー(頻繁に変更)
COPY . .
# 6. ビルド(ソースコード変更のたびに再実行)
RUN npm run build
この順序にすることで、ソースコード変更時の再ビルドはステップ 5-6 のみで完了し、依存のインストール(数分かかる場合がある)がキャッシュされます。ByteScrum のベストプラクティス記事では「間違った順序 = 20 分のリビルド、正しい順序 = 20 秒のリビルド」と表現されています。
RUN 命令の結合
# 悪い例: 3 つのレイヤーが作成され、中間ファイルが残る
RUN apt-get update
RUN apt-get install -y curl wget
RUN rm -rf /var/lib/apt/lists/*
# 良い例: 1 つのレイヤーに統合、キャッシュも削除
RUN apt-get update && \
apt-get install -y --no-install-recommends \
curl \
wget \
&& rm -rf /var/lib/apt/lists/*
3. セキュリティ強化の必須対策
Sysdig の調査によると、本番コンテナの 76% が root で実行されており、これは重大なセキュリティリスクです。
非 root ユーザーで実行
# アプリケーション用ユーザーを作成
RUN addgroup -g 10001 -S appgroup && \
adduser -u 10001 -S appuser -G appgroup
# 必要なディレクトリの所有権を設定
COPY --chown=appuser:appgroup . /app
# 非 root ユーザーに切り替え
USER appuser
UID 10000 以上を使用することで、ホスト OS のシステムユーザーとの ID 衝突を回避します。
シークレット管理
# 絶対にやってはいけない: イメージレイヤーにシークレットが残る
COPY .env /app/.env
ENV API_KEY=sk-secret-key
# 正しい方法: BuildKit シークレットマウント
RUN --mount=type=secret,id=api_key \
export API_KEY=$(cat /run/secrets/api_key) && \
./setup.sh
BuildKit のシークレットマウントは、シークレットをイメージレイヤーに残さずにビルド時のみ利用可能にします。
.dockerignore の活用
# .dockerignore
.git
.gitignore
node_modules
.env
.env.*
*.md
docker-compose*.yml
Dockerfile*
.DS_Store
__pycache__
*.pyc
coverage/
.pytest_cache/
.dockerignore はビルドコンテキストのサイズを削減し、意図しないファイル(特にシークレット)のイメージへの混入を防ぎます。GeeksforGeeks の Dockerfile ガイドでも必須対策として紹介されています。
Kubo のセキュリティポリシーでは、非 root 実行と .dockerignore の使用を標準として推奨しています。
4. BuildKit の活用
BuildKit は Docker の次世代ビルドエンジンで、並列ビルド、改善されたキャッシュ、セキュリティ機能を提供します。
BuildKit の有効化
# 環境変数で有効化
export DOCKER_BUILDKIT=1
# docker buildx を使用(推奨)
docker buildx build -t myapp:latest .
キャッシュマウント
依存パッケージのキャッシュをビルド間で共有し、再インストールを回避します:
# pip のキャッシュを保持
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt
# npm のキャッシュを保持
RUN --mount=type=cache,target=/root/.npm \
npm ci
# Go モジュールのキャッシュを保持
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
go build -o /app/server .
# apt のキャッシュを保持
RUN --mount=type=cache,target=/var/cache/apt \
--mount=type=cache,target=/var/lib/apt \
apt-get update && apt-get install -y curl
ヒアドキュメント構文
# 従来の方法(エスケープが煩雑)
RUN echo "server {" > /etc/nginx/conf.d/default.conf && \
echo " listen 80;" >> /etc/nginx/conf.d/default.conf && \
echo " location / { proxy_pass http://app:8080; }" >> /etc/nginx/conf.d/default.conf && \
echo "}" >> /etc/nginx/conf.d/default.conf
# BuildKit のヒアドキュメント(読みやすい)
COPY <<EOF /etc/nginx/conf.d/default.conf
server {
listen 80;
location / {
proxy_pass http://app:8080;
}
}
EOF
5. イメージサイズ削減のテクニック集
COPY を ADD より優先
# 避ける: ADD はリモート URL ダウンロードと tar 自動展開を行う
ADD https://example.com/app.tar.gz /app/
# 推奨: COPY は明示的なローカルファイルコピーのみ
COPY app/ /app/
ADD は予期しない動作を引き起こす可能性があるため、ローカルファイルのコピーには常に COPY を使用します。
不要パッケージの排除
# --no-install-recommends で推奨パッケージを除外
RUN apt-get update && \
apt-get install -y --no-install-recommends \
ca-certificates \
curl \
&& rm -rf /var/lib/apt/lists/*
# Alpine では --no-cache で apk キャッシュを無効化
RUN apk add --no-cache curl
マルチステージビルドの活用
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --production
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
USER node
CMD ["node", "dist/index.js"]
DevOps Training Institute の最適化ガイドでは、これらのテクニックを組み合わせることで 80% 以上のサイズ削減が達成可能と報告されています。
定期的なイメージ再ビルド
本番イメージは少なくとも月次で再ビルドし、ベースイメージの最新セキュリティパッチを適用します。Dependabot や Renovate で自動化するのが Docker 公式の推奨です。
6. ヘルスチェックの定義
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8080/healthz || exit 1
HEALTHCHECK 命令は Docker がコンテナの健全性を判断するために使用します。Kubernetes では liveness/readiness Probe に置き換えられますが、Docker Compose 環境では依然として有効です。
まとめ: Dockerfile チェックリスト 2025
本番品質の Dockerfile を書くための最終チェックリスト:
- 最小限のベースイメージを選択(alpine - distroless - scratch)
- バージョンピニングで再現性を確保
- 変更頻度の低い命令を先に配置(キャッシュ最適化)
- RUN 命令を結合し、キャッシュを削除
- 非 root ユーザーで実行(UID 10000+)
- .dockerignore でビルドコンテキストを最小化
- シークレットを BuildKit マウントで管理
- マルチステージビルドで本番と開発を分離
- COPY を ADD より優先
- HEALTHCHECK を定義
- 定期的な再ビルドスケジュールを設定
Kubo では、これらのベストプラクティスに準拠したコンテナイメージを Kubernetes クラスタ上で効率的に運用しています。Captain.AI を活用すれば、Dockerfile の自動レビューと最適化提案により、チーム全体の Dockerfile 品質を底上げできます。
Dockerfile の最適化やコンテナ運用のご相談は、お問い合わせよりお気軽にどうぞ。