Dockerイメージの最適化:サイズ削減とビルド・デプロイの高速化
Dockerを使用することで、アプリケーションと実行に必要な環境全体をパッケージ化できます。しかし、Dockerイメージが大きすぎると、ビルド、プッシュ、プル、デプロイのプロセスが遅くなり、より多くのリソースを消費してしまいます。
この記事では、Dockerイメージを最適化し、サイズを削減してデプロイのパフォーマンスを向上させるための重要なテクニックを解説します。
1. なぜDockerイメージの最適化が必要なのか?
大きなDockerイメージは、以下のような様々な問題を引き起こします:
- ビルド時間が長くなる。
- プッシュおよびプルが遅くなる。
- Docker Registryのストレージ容量を圧迫する。
- デプロイ時の帯域幅を消費する。
- デプロイ完了までの時間が長くなる。
- 不要なパッケージが含まれ、セキュリティリスクが高まる可能性がある。
例えば、1.5GBあるイメージを最適化して300MBまで削減できれば、プッシュ、プル、デプロイの速度を大幅に向上させることができます。
2. 適切なベースイメージの選択
ベースイメージはDockerイメージ構築の基盤であり、最終的なイメージサイズに大きな影響を与えます。
例:
FROM ubuntu:24.04
アプリケーションがPHPのみを必要とする場合、PHPのイメージを直接使用できます:
FROM php:8.4-fpm
場合によっては、Alpineを使用することもできます:
FROM php:8.4-fpm-alpine
Alpineは通常、サイズが大幅に小さくなります。ただし、一部のパッケージや拡張機能で追加設定が必要になる場合があるため、すべてのアプリケーションに適しているわけではありません。
3. マルチステージビルド(Multi-stage Build)の活用
マルチステージビルドは、Dockerイメージのサイズを削減するための最も重要なテクニックの1つです。
例えば、フロントエンドアプリケーションをビルドする際、ビルドプロセスにはNode.jsや多くの開発用パッケージが必要です。しかし、本番環境(Production)で必要なのはビルド済みのファイルのみです。
FROM node:24 AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build
FROM nginx:alpine COPY --from=builder /app/dist /usr/share/nginx/html
上記の例では、Node.jsやビルド用の全パッケージは本番用イメージに含まれません。
その結果、単一のイメージを使用する場合と比較して、本番イメージを非常に小さく抑えることができます。
4. .dockerignore の活用
以下を実行する場合:
COPY . .
Dockerはビルドコンテキストからデータを取得します。.dockerignore が存在しない場合、多くの不要なファイルがビルドプロセスに含まれてしまう可能性があります。
.dockerignore ファイルの例:
.git node_modules vendor .env .idea .vscode storage/logs *.log
.dockerignore を使用することで、ビルドコンテキストを削減し、ビルドを高速化するとともに、不要なデータがイメージに入り込むのを防ぎます。
5. Dockerキャッシュの活用
Dockerには、変更のないビルドステップを再利用するレイヤーキャッシュ機構があります。
Node.jsの例として、以下の代わりに:
COPY . . RUN npm install
依存関係を先に分離してコピーします:
COPY package.json package-lock.json ./ RUN npm ci COPY . .
ソースコードが変更されても依存関係ファイルに変更がなければ、Dockerはキャッシュされたレイヤーを再利用します。
これにより、ビルド時間を大幅に短縮できます。
6. 不要なレイヤー数の削減
RUN、COPY、ADD コマンドを実行するたびに新しいレイヤーが作成されます。
以下の代わりに:
RUN apt update RUN apt install -y curl RUN apt clean
1つのコマンドにまとめます:
RUN apt update &&
apt install -y curl &&
apt clean &&
rm -rf /var/lib/apt/lists/*
この方法により、レイヤーをより適切に管理し、不要なキャッシュを排除できます。
7. 必要な依存関係のみをインストールする
本番環境用イメージには、開発専用のパッケージを含めるべきではありません。
Laravelの例:
composer install --no-dev --prefer-dist --optimize-autoloader
Node.jsの例:
npm ci --omit=dev
開発用依存関係を除外することで、イメージが軽量化され、セキュリティチェック対象のパッケージ数も減らすことができます。
8. パッケージインストール後のキャッシュ削除
パッケージマネージャーは、インストール時に一時キャッシュを作成します。
Alpineの例:
RUN apk add --no-cache nginx
Debian/Ubuntuの例:
RUN apt update &&
apt install -y curl &&
rm -rf /var/lib/apt/lists/*
キャッシュを削除することで、最終的なイメージサイズを削減できます。
9. Dockerイメージの検証・確認
ビルド後、以下のコマンドでイメージサイズを確認できます:
docker images
レイヤーを確認する場合:
docker history image_name
さらに、Dive、Trivy、Docker Scout などのツールを使用して、レイヤーの分析やセキュリティ脆弱性のスキャンを行うことも推奨されます。
10. Dockerイメージ最適化チェックリスト
- 適切なベースイメージを選択する。
- マルチステージビルドを使用する。
- .dockerignore ファイルを作成する。
- Dockerキャッシュを活用する。
- 不要なパッケージをインストールしない。
- パッケージマネージャーのキャッシュを削除する。
- 本番用依存関係のみをインストールする。
- 各レイヤーのサイズを確認する。
- セキュリティ脆弱性のためにイメージをスキャンする。
まとめ
Dockerイメージの最適化は、容量を節約するだけでなく、ビルド、プッシュ、プル、デプロイのスピードを大幅に向上させます。
CI/CDやマイクロサービスを採用しているプロジェクトでは、Dockerイメージを小さく最適化された状態に保つことで、時間、リソース、運用コストの削減につながります。
実際には、マルチステージビルド、.dockerignore、Dockerキャッシュ、適切なベースイメージの選択を意識して適用するだけで、Dockerイメージのサイズを劇的に削減することができます。