Contents
この記事の目的と完成イメージ・前提条件
このガイドは php-fpm + nginx + MySQL を軸にした Docker Compose 開発環境を、実務で使えるテンプレートとして短時間で構築できるよう示します。デバッグ、権限、パフォーマンス、セキュリティ、CI を含めた運用上の注意点と再現性の高いサンプルを提供します。前提は Docker(Engine または Docker Desktop)と Compose plugin、Git です。
推奨アーキテクチャ、ディレクトリ構成、PHPバージョン選定
ここではサービス構成とディレクトリ設計、PHP バージョン選定の方針を示します。実務で扱いやすく、拡張・本番移行しやすい設計を意識しています。
サービス一覧/役割
以下は開発フェーズで最低限用意すると良いサービスです。責務を分離し、公開範囲は最小化します。
- app(php-fpm): PHP 実行環境。CLI、テスト、composer 実行も担う。
- web(nginx): 静的配信とリバースプロキシ。ドキュメントルートは /var/www/html/public を想定。
- db(MySQL / MariaDB): 永続化ボリュームを持つ DB。ホスト公開は原則しない。
- admin(phpMyAdmin / Adminer): 開発用 DB 管理ツール(profiles で切替)。
- redis: キャッシュ/セッション用。開発時は profiles で切替。
- mail(MailHog 等): 送信メールのキャプチャ。
- worker: queue ワーカー用(必要に応じて追加)。
推奨ディレクトリ構成
プロジェクトの最低構成例です。ファイル配置と名前は後述のサンプルに合わせて統一してください。
- project-root/
- docker/
- php/
- Dockerfile
- php.ini.d/
- 20-xdebug.ini
- 99-dev.ini
- nginx/
- default.conf
- mysql/
- initdb.d/
- src/
- public/
- index.php
- tests/
- docker-compose.yml
- .env.example
- composer.json
- .gitignore
PHP バージョン選定の方針
サポート期間とプロジェクト要件を優先してバージョンを固定してください。運用上はパッチレベルまで固定すると安全です。Docker イメージは .env で明示的に指定して運用することを推奨します。
- 開発向け: 安定したマイナーバージョン(例: 8.2.x の明示的なパッチ)。
- イメージ基盤: debian-slim 系(php:
-fpm)を推奨。Alpine は軽量だが一部拡張で追加のビルドパッケージが必要になります。
参考リンク(代表的な資料)
- Composer 公式ダウンロードと検証手順(署名/ハッシュの検証): https://getcomposer.org/download/
- Docker + PHP 開発の実践記事(例): https://zenn.dev/topics/docker-php (Zenn のタグ一覧ページ)
実務向け Dockerfile と docker-compose の実例と解説
ここでは、実際に動く最小構成と重要な注意点を示します。ビルド依存の一時導入と削除、Composer の安全な扱い、ドキュメントルートの整合性に重点を置いています。
Dockerfile(開発用: ビルド依存を一時的に導入して削除する例)
この Dockerfile はビルドツールを一時的に入れて拡張をコンパイルし、その同じ RUN レイヤー内で不要なビルド依存を purge して削除します。これにより最終イメージに不要なツールを残しません。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# docker/php/Dockerfile ARG PHP_VERSION=8.2.16 FROM php:${PHP_VERSION}-fpm ENV COMPOSER_ALLOW_SUPERUSER=1 \ COMPOSER_HOME=/home/app/.composer # ランタイム依存とビルド依存を同一 RUN 内で処理し、 # ビルド後に削除して攻撃面を減らす RUN set -eux; \ apt-get update; \ apt-get install -y --no-install-recommends \ ca-certificates \ git curl zip unzip \ libzip-dev zlib1g-dev \ libpng-dev libjpeg-dev libfreetype6-dev \ libicu-dev libonig-dev libxml2-dev pkg-config \ # 以下はビルドにのみ必要なパッケージ build-essential autoconf gcc make ; \ docker-php-ext-configure gd --with-freetype --with-jpeg; \ docker-php-ext-install -j"$(nproc)" pdo_mysql mbstring intl zip gd opcache; \ pecl install redis && docker-php-ext-enable redis; \ # ビルド用ツールを削除してイメージをクリーンにする apt-get purge -y --auto-remove build-essential autoconf gcc make pkg-config; \ rm -rf /var/lib/apt/lists/* /tmp/pear /usr/share/doc/* # Composer を安全にインストール(ハッシュ検証) RUN set -eux; \ EXPECTED_SIGNATURE="$(curl -sS https://composer.github.io/installer.sig)"; \ php -r "copy('https://getcomposer.org/installer', '/tmp/composer-setup.php');"; \ ACTUAL_SIGNATURE="$(php -r "echo hash_file('sha384','/tmp/composer-setup.php');")"; \ if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]; then >&2 echo 'ERROR: Invalid composer installer signature' ; exit 1; fi; \ php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer; \ rm -f /tmp/composer-setup.php # PHP 設定を読み込む(開発用) COPY docker/php/php.ini.d/ /usr/local/etc/php/conf.d/ # 非 root ユーザー作成(ホストの UID/GID を合わせる) ARG HOST_UID=1000 ARG HOST_GID=1000 RUN groupadd -g ${HOST_GID} app || true \ && useradd -u ${HOST_UID} -g ${HOST_GID} -m -s /bin/bash app \ && mkdir -p /var/www/html /home/app/.composer \ && chown -R app:app /var/www/html /home/app/.composer WORKDIR /var/www/html USER app CMD ["php-fpm"] |
ポイントの補足:
- ビルド用パッケージは必ず同じ RUN レイヤーで purge して削除してください。別レイヤーだと残ってしまいます。
- Composer はインストーラのハッシュ(署名)検証を行ってからインストールしています。安全性の面で必須です。
- default-mysql-client 等の不要なクライアントは入れないでください。攻撃面が増えます。
docker-compose.yml(ドキュメントルートの整合とイメージ固定)
ここではドキュメントルートを /var/www/html/public に統一し、ソースはホストの ./src を /var/www/html にマウントします。イメージは .env で明示的なタグを指定する形にして再現性を高めます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# docker-compose.yml version: "3.9" services: app: build: context: . dockerfile: docker/php/Dockerfile args: PHP_VERSION: ${PHP_VERSION} HOST_UID: ${HOST_UID} HOST_GID: ${HOST_GID} volumes: - ./src:/var/www/html:delegated - composer_cache:/home/app/.composer environment: - COMPOSER_HOME=/home/app/.composer - DB_HOST=db - XDEBUG_MODE=off extra_hosts: - "host.docker.internal:host-gateway" networks: - internal web: image: nginx:1.24.0-alpine ports: - "127.0.0.1:8080:80" volumes: - ./src:/var/www/html:ro - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro depends_on: - app networks: - internal db: image: ${MYSQL_IMAGE} environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: ${DB_DATABASE} MYSQL_USER: ${DB_USERNAME} MYSQL_PASSWORD: ${DB_PASSWORD} volumes: - db_data:/var/lib/mysql - ./docker/mysql/initdb.d:/docker-entrypoint-initdb.d:ro healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s timeout: 5s retries: 5 networks: - internal admin: image: ${PHPMYADMIN_IMAGE} environment: PMA_HOST: db ports: - "127.0.0.1:8081:80" profiles: - dev depends_on: - db networks: - internal redis: image: ${REDIS_IMAGE} volumes: - redis_data:/data profiles: - dev networks: - internal mail: image: mailhog/mailhog:1.0.1 ports: - "127.0.0.1:8025:8025" profiles: - dev networks: - internal volumes: db_data: composer_cache: redis_data: networks: internal: driver: bridge |
- イメージは .env で具体的なパッチレベルやタグを指定してください(例: MYSQL_IMAGE=mysql:8.0.33、PHPMYADMIN_IMAGE=phpmyadmin:5.2.0)。major タグや latest は避けてください。
- extra_hosts の host-gateway を使うと Linux や Docker Desktop でホストへのルーティング設定が簡単になります(Xdebug の接続先設定で後述)。
- ボリュームのマウントオプション(:delegated/:cached)は macOS 向けの最適化です。後述のプラットフォーム別注意を参照してください。
Nginx の最小設定(ドキュメントルート整合)
以下は docker/nginx/default.conf の例です。ドキュメントルートは /var/www/html/public に統一しています。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
server { listen 80; server_name localhost; root /var/www/html/public; index index.php index.html; location / { try_files $uri /index.php$is_args$args; } location ~ \.php$ { fastcgi_pass app:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; } |
サンプルファイル一覧(名前と配置を統一)
- docker/php/Dockerfile
- docker/php/php.ini.d/20-xdebug.ini
- docker/php/php.ini.d/99-dev.ini
- docker/nginx/default.conf
- docker/mysql/initdb.d/*.sql
- docker-compose.yml
- .env.example
- src/public/index.php
ファイル名・配置は上記で統一してください。特に php.ini と xdebug のファイル名・パスは一致させてください。
開発ワークフロー、デバッグ、ファイル権限とパフォーマンス対策
運用時に混乱しやすいポイントをまとめます。Xdebug の接続先や Composer 実行、ボリュームの権限調整について実例を示します。
Xdebug(v3)と接続先の扱い
Xdebug 設定例と接続先の注意点を示します。開発環境ごとに接続先の指定方法が変わるため、複数方法を用意しておくと便利です。
|
1 2 3 4 5 6 7 8 |
# docker/php/php.ini.d/20-xdebug.ini(開発用) zend_extension=xdebug.so xdebug.mode=develop,debug xdebug.start_with_request=trigger xdebug.client_port=9003 xdebug.client_host=host.docker.internal xdebug.log=/tmp/xdebug.log |
- Mac/Windows (Docker Desktop): host.docker.internal が使えます。
- Linux: Docker のバージョンが新しければ docker-compose の extra_hosts に "host.docker.internal:host-gateway" を追加すると動作します。
- サーバ/古い環境: ホストの IP を固定して extra_hosts に明示的に設定するか、IDE とコンテナのネットワーク設計を見直してください(例: extra_hosts: ["host.docker.internal:172.17.0.1"])。
- セキュリティ上、Xdebug は開発プロファイルでのみ有効にしてください。
Composer の運用と vendor の扱い
Composer は基本的にコンテナ内で実行し、キャッシュは named volume に置くのが実務的です。vendor の扱いはプロジェクト方針次第です。
- ローカルでの実行例(UNIX 系):
|
1 2 |
docker compose run --rm --user "$(id -u):$(id -g)" app composer install |
-
Windows(PowerShell / WSL2)ではユーザー指定方法が異なります。うまくいかない場合は root で実行してからボリュームの所有権を調整してください。
-
vendor をホストと共有すると権限・パフォーマンス問題を招きます。大きなプロジェクトは CI でビルドし、成果物をコンテナに組み込む運用(マルチステージ)を推奨します。
バインドマウントのパフォーマンス対策(macOS / Linux の違い)
マウントオプションや代替手段を明示します。macOS と Linux で挙動が異なります。
- Docker Desktop(macOS)の最適化: マウントに :delegated や :cached を付けて改善する場合があります(例: ./src:/var/www/html:delegated)。
- Linux: :delegated/:cached は効果が限定的で無視されます。代替として NFS、rsync、または Mutagen(ファイル同期ツール)を検討してください。
- 長期的には Mutagen や docker-sync を使った同期が大規模アプリで効果的です。
運用・CI・セキュリティ・保守・トラブルシューティング
本番移行や CI、セキュリティ運用、よくある障害と対処をまとめます。イメージの最小化とスキャン、自動化が重要です。
CI とマルチステージビルド(本番イメージの例)
CI や本番イメージではマルチステージビルドで依存とランタイムを分離します。以下は概念例です。
|
1 2 3 4 5 6 7 8 9 10 11 |
# build stage (composer を使って vendor を準備) FROM composer:2.6 AS vendor WORKDIR /app COPY composer.json composer.lock ./ RUN composer install --no-dev --optimize-autoloader --no-interaction # runtime stage FROM php:8.2.16-fpm COPY --from=vendor /app/vendor /var/www/html/vendor # 以降はランタイムに必要な最小構成を追加 |
- CI ではキャッシュレイヤ(actions/cache など)や buildx のキャッシュを活用してください。
- テストはサービスを定義して行い、Xdebug は CI で無効にします。
セキュリティ強化と脆弱性スキャン
- イメージにビルドツールや余分なクライアントを残さないでください(Dockerfile の purge を参照)。
- シークレット (.env 等) をリポジトリにコミットしないこと。代わりに Docker Secrets やクラウドの Secret Manager を使用してください。
- 定期的に Trivy や Clair でイメージスキャンを実行してください。例:
|
1 2 |
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy image --severity HIGH,CRITICAL your-image:tag |
- 最小権限の原則を守り、運用アカウントは必要最小限の権限に制限してください。
ボリュームとパーミッション運用手順
named volume(例: composer_cache 等)は初期作成時に root 所有になることが多いです。運用手順の例を示します。
- 初回作成後に一度だけ chown を実行する例(ホスト上で):
|
1 2 3 |
# コンテナ内で root を使って所有権を変更 docker compose run --rm --user root app sh -c "chown -R ${HOST_UID}:${HOST_GID} /home/app/.composer" |
- またはコンテナの entrypoint で起動時に存在しなければ所有権を調整するスクリプトを用意すると運用が楽になります。
- CI や自動化スクリプトで定期的に権限を検査する運用ルールを設けてください。
よくある障害と対処(要点)
- ポート競合: 127.0.0.1 バインドやポート変更で回避。
- DB 接続エラー: アプリ側はサービス名(db)を使う。環境変数を再確認。
- 権限エラー: HOST_UID/HOST_GID を見直し、必要なら chown を実行。
- パフォーマンス低下(macOS): :delegated/:cached、Mutagen、gRPC FUSE の検討。
まとめ
- php-fpm + nginx + MySQL の開発環境はサービスの責務分離とドキュメントルートの整合が重要です。
- Dockerfile ではビルド依存を一時インストールして同一レイヤーで削除し、不要なツールを残さないでください。
- Composer はインストーラの署名/ハッシュ検証を行うか、公式 composer イメージを使うことで安全に扱います。
- ボリュームの権限は初期 chown や entrypoint スクリプトで調整し、macOS のマウント最適化はプラットフォーム差を考慮して運用してください。
- CI ではマルチステージビルドとキャッシュ活用、脆弱性スキャン(Trivy 等)を組み込み、最小権限の原則を守ってください。