はじめに
コンテナイメージを軽量化するメリット
コンテナイメージを軽量化することには、以下のメリットがあります。
- デプロイ速度の向上: イメージサイズが小さくなれば、レジストリからのダウンロードが速くなり、デプロイ時間が短縮されます。特にCI/CDパイプラインにおいて、この差は顕著です。
- ディスク容量の節約: 本番サーバーや開発環境のディスク容量を節約できます。
- 環境負荷の低減: データ転送量の削減は、ネットワークやストレージへの負荷を減らし、省電力にもつながります。
なぜLaravelプロジェクトのコンテナは肥大化しやすいのか
LaravelプロジェクトのDockerコンテナが肥大化する主な原因は、ビルドプロセスにあります。
開発用ツールの残留: composerやnpm、gitなどの開発用ツールが最終的なイメージに残ってしまうことがあります。
ビルドキャッシュの蓄積: パッケージインストール時の一時ファイルやキャッシュが削除されずに残ってしまいます。
不要なファイルの混入: .envや.gitディレクトリなど、本番環境では不要なファイルがCOPYされてしまいます。
これらの問題を解決するために、Dockerfileを最適化する必要があります。
本記事で解説するDockerfile最適化の基本原則
本記事では、以下の3つの基本原則に基づいた最適化テクニックを解説します。
マルチステージビルド: ビルドと実行のプロセスを分離する。
ベースイメージの見直し: より軽量なベースイメージを選択する。
レイヤーキャッシュの最適化: Dockerのキャッシュ機構を効率的に活用する。
これらの原則を理解し、実践することで、コンテナサイズを大幅に削減できます。
マルチステージビルドを活用する
マルチステージビルドとは何か?その仕組みを理解する
マルチステージビルドは、一つのDockerfile内で複数のFROM命令を使い、中間的なイメージ(ステージ)を作成する手法です。最終的なイメージは、最後のステージから作成され、前のステージで生成されたビルド成果物だけをコピーします。これにより、ビルドに必要なツールや一時ファイルを最終イメージから除外できます。
ビルド用ステージと実行用ステージを分離する
マルチステージビルドは、主に「ビルド用」と「実行用」の2つのステージに分かれます。
ビルド用ステージ: 開発用ツール(composer、npm、gitなど)をインストールし、依存関係の解決やフロントエンドのビルドを行います。
実行用ステージ: php-fpmなど、アプリケーションの実行に最低限必要なものだけを含む軽量なベースイメージを使用します。
不要な開発ツールを最終イメージから排除する
この手法を使うことで、最終的な本番用イメージに、composerやnpmといったビルド用ツールが残ることはありません。
以下は、マルチステージビルドを使ったDockerfileの例です。
Dockerfile
# --- Stage 1: ビルドステージ ---
FROM composer:2.5 as composer_stage
WORKDIR /app
COPY composer.json composer.lock ./
# 開発環境のパッケージを除外してインストール
RUN composer install --no-dev --optimize-autoloader --no-scripts
# --- Stage 2: 実行ステージ ---
FROM php:8.2-fpm-alpine
# ビルドステージから必要なファイルをコピー
COPY --from=composer_stage /app ./
# Laravelの実行に必要なファイルをコピー
COPY . .
# 実行時に不要なファイルを削除
RUN rm -rf vendor/phpunit vendor/fakerphp
# 最終的なイメージサイズを減らすためのキャッシュクリアなど
RUN composer clear-cache \
&& rm -rf /root/.composer/cache \
&& apk del --purge composer
この例では、composer_stageで依存関係を解決し、最終的な実行ステージには、インストールされたvendorディレクトリのみをコピーしています。これにより、composerコマンド自体は最終イメージに含まれません。
ベースイメージを見直す
公式イメージと軽量イメージ(alpine版)の違い
Dockerの公式PHPイメージには、Debianベースのイメージ(例:php:8.2-fpm)と、Alpine Linuxをベースにしたイメージ(例:php:8.2-fpm-alpine)があります。
- Debianベース: 多くのライブラリがプリインストールされており、安定していますが、イメージサイズが大きくなりがちです。
- Alpineベース: 非常に軽量で、イメージサイズを劇的に小さくできます。ただし、必要なライブラリを手動でインストールする必要があります。
alpine版イメージのメリットとデメリット
メリット:
- 圧倒的な軽量さ: イメージサイズが最小限に抑えられます。
- セキュリティ: 最小限のパッケージしか含まないため、攻撃に晒される範囲が狭まります。
デメリット:
- 互換性の問題: 一部のライブラリやツールがalpine版では動作しないことがあります。
- インストール手順: 必要なライブラリを手動でapk addコマンドでインストールする必要があります。
プロジェクト要件に合わせた最適なベースイメージの選択
イメージサイズを重視するならalpine版、互換性や開発のしやすさを重視するならDebianベースのイメージを選択します。本番環境用にはalpine版を、開発用にはデバッグツールなどが揃ったDebianベースのイメージを使うなど、使い分けるのが一般的です。
レイヤーキャッシュを最適化する
Dockerのレイヤーとキャッシュの仕組み
Dockerは、Dockerfileの各命令をレイヤーとしてキャッシュします。Dockerfileに変更がない限り、そのレイヤーは再利用されます。
- レイヤーの再利用: RUN, COPYなどの命令は、キャッシュキー(命令のハッシュ値)が同じであれば再利用されます。
- キャッシュの無効化: 一つの命令が変更されると、それ以降のすべてのレイヤーのキャッシュが無効化され、再ビルドされます。
変更頻度の高いコマンドをDockerfileの下部に配置する
このキャッシュの仕組みを理解すると、Dockerfileの記述順序が重要であることがわかります。変更頻度の高いコマンド(COPY . .など)を下に配置することで、変更の影響範囲を最小限に抑え、キャッシュを効率的に再利用できます。
COPYコマンドの効率的な使い方(composer.jsonのみ先にコピーなど)
composer installを実行する前に、composer.jsonとcomposer.lockだけを先にコピーし、依存関係をインストールすることで、アプリケーションコードに変更があってもcomposer installのキャッシュが再利用されるようになります。
Dockerfile
FROM php:8.2-fpm-alpine
WORKDIR /var/www/html
# composer.jsonとcomposer.lockのみ先にコピー
COPY composer.json composer.lock ./
# 依存関係をインストール
RUN composer install --no-dev --optimize-autoloader --no-scripts
# アプリケーションコードをコピー
COPY . .
# ...
このようにすることで、コードの変更があった場合でも、composer installのレイヤーキャッシュが再利用され、ビルド時間を短縮できます。
実行時に不要なファイルを削除する
ビルドキャッシュや一時ファイルを削除するコマンド
パッケージのインストール時などに生成される一時ファイルは、ビルド後に削除すべきです。
Dockerfile
# パッケージインストール後にクリーンアップ
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
libzip-dev \
&& rm -rf /var/lib/apt/lists/*
#&& rm -rf /var/lib/apt/lists/*を同じRUNコマンドに含めることで、キャッシュ削除も一つのレイヤーにまとめられ、コンテナサイズが大きくなるのを防ぎます。
Laravelの.envファイルや.gitディレクトリを除外する
本番環境のコンテナに、.envファイルや.gitディレクトリは不要です。.dockerignoreファイルを使うことで、これらのファイルをCOPYの対象から除外できます。
.dockerignoreファイルの活用
プロジェクトのルートディレクトリに.dockerignoreファイルを作成し、除外したいファイルやディレクトリを記述します。
.git
.gitignore
.env
.env.example
node_modules
これにより、COPY . .コマンドが実行される際、これらのファイルはコンテナにコピーされなくなります。
まとめと応用
最適化されたDockerfileの完成形
これまでのテクニックを組み合わせた、Laravelプロジェクトの最適化されたDockerfileの完成形です。
Dockerfile
# --- Stage 1: ビルドステージ(Laravel)---
FROM composer:2.5 as composer_stage
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-scripts
# --- Stage 2: ビルドステージ(Vue.js / フロントエンド)---
FROM node:18-alpine as frontend_stage
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build
# --- Stage 3: 実行ステージ ---
FROM php:8.2-fpm-alpine as final_stage
# 実行に必要なパッケージをインストール
RUN apk add --no-cache nginx
# Composerの成果物をコピー
COPY --from=composer_stage /app/vendor /var/www/html/vendor
# Laravelのアプリケーションコードをコピー
COPY . /var/www/html
WORKDIR /var/www/html
# フロントエンドのビルド成果物をコピー
COPY --from=frontend_stage /app/dist /var/www/html/public
# 実行時に不要なファイルを削除
RUN rm -rf vendor/phpunit vendor/fakerphp \
&& rm -rf /var/cache/apk/* \
&& rm -rf /tmp/*
# Laravelのキャッシュを生成
RUN php artisan config:cache \
&& php artisan route:cache \
&& php artisan view:cache
# 権限設定
RUN chown -R www-data:www-data /var/www/html
EXPOSE 9000
CMD ["php-fpm"]
このDockerfileを使えば、最終的なコンテナイメージは最小限のサイズになり、デプロイ速度や運用コストを大幅に改善できます。
軽量化と開発体験のバランスを考える
コンテナの軽量化は重要ですが、開発時の利便性とのバランスも考慮すべきです。例えば、デバッグに必要なツールや、開発中に頻繁に書き換わるファイルは、開発用のイメージでは残しておいても問題ありません。本番環境と開発環境で異なるDockerfileを使い分けることが、最も現実的な解決策です。
CI/CDパイプラインでの実践と効果測定
最適化されたDockerfileは、CI/CDパイプラインでその真価を発揮します。docker buildコマンドの実行時間を計測したり、ビルド後のイメージサイズをチェックするステップを追加することで、継続的な改善を図ることができます。ビルド時間の短縮は、開発者のフィードバックループを速め、生産性向上に直結します。
今回の記事では、LaravelプロジェクトのDockerfileを軽量化するための実践的なテクニックを解説しました。マルチステージビルド、ベースイメージの見直し、キャッシュの最適化、そして不要ファイルの削除といった手法を駆使することで、あなたのコンテナイメージは劇的にスリムになり、より効率的な開発・運用が可能になります。
安心安全のホワイト高還元SESに転職を考えている方へ
新しい挑戦に踏み出すことは、人生において重要な一歩です。 転職活動は自分自身を知り、成長する貴重な機会でもあり、夢や成長を追求するためには必要な要素の一つ になるかと思います。 どんな選択をされるにせよ、その決断があなたに取って素晴らしい未来を切り開くことを願っています! グラディートと一緒に誇れるエンジニアを目指しましょう!
■『株式会社グラディート』では受託開発・SES・ブランディングデザイン・事業コンサルティングなどを事業として行う都内のIT企業です。現在、不遇な待遇で困っているエンジニアさんは、ぜひ一度グラディートに相談してみてね!(年収査定・SESへの転職相談も承っております!)
株式会社グラディート採用情報はこちら▼
https://en-gage.net/gradito/
株式会社グラディート公式サイトはこちら▼
https://www.gradito.co.jp/