Xdebug×Docker×Laravel:コンテナの壁を撃ち抜け。IDEを完全同期させる極限のデバッグアーキテクチャ
開発現場でこんな絶望を味わったことはないか。
「ローカル環境なら動くのに、Dockerコンテナ内に入れた途端にブレークポイントがスルーされる」
「`host.docker.internal` を設定したのに、なぜかMacでは動いてLinux(Ubuntu)のCIやWSL2環境で沈黙する」
「パフォーマンスが激重になり、`docker-compose up` するだけでCPU使用率が天井知らずに跳ね上がる」
ネットを検索すれば「`xdebug.mode=debug` を書きましょう」「`client_host=host.docker.internal` です」という、誰が書いたか分からないコピペ記事が溢れている。しかし、それらの設定は「なぜその通信が発生するのか」「OSごとのネットワークバウンダリをどう超えるのか」「本番環境のビルド成果物にどう影響を与えないようにするか」という本質的なアーキテクチャを語っていない。
私は何十年も開発環境の最適化とCI/CDパイプラインの構築に向き合ってきた。
結論から言おう。Docker上のLaravelでXdebugを完璧に手なずけるには、OSのネットワークトポロジの理解と、プロセス空間のライフサイクル管理が不可欠だ。
今回は、単なる「動いた」で終わらせない。Dockerコンテナ内部のPHPプロセスとホストマシンのIDE(PhpStorm / VS Code)を完全に同期させ、開発体験を極限まで引き上げる「プロダクション・グレード」のコンフィギュレーションを解き明かす。
—
1. 内部アーキテクチャ:XdebugとDockerネットワーキングの深層
まず、Xdebugが裏側で何をしているのかを正確に把握する必要がある。
Xdebugは単なる「ログ出力ツール」ではない。PHPの実行エンジン(Zend Engine)のC拡張モジュールであり、スクリプトの実行を一時停止(ブレーク)させ、DBGP(Debug Protocol)という専用プロトコルを用いて、TCPソケット経由で外部のIDEと双方向通信を行う「セッションコントローラー」だ。
ここで問題になるのが、Dockerのネットワーク分離である。
[ ホストマシン (IDE) ] <--- (TCP 9003) ---> [ Docker ブリッジネットワーク ] —> [ PHP-FPM コンテナ ]
通常、Dockerコンテナはホストから独立したネットワーク名前空間(Network Namespace)に存在する。コンテナ側から見れば、ホストマシンは「外部のゲートウェイ(ルーター)」であり、ホスト側で待ち受けているIDE(PhpStorm等)のリスニングポート(デフォルト 9003)に直接アクセスすることは、デフォルトのルーティングでは許可されていないか、IPアドレスが動的に変わるため固定できない。
この壁を突破するために、Dockerが提供する特殊なDNS解決やルーティング機構、そしてOSごとの差異を完全に理解し、コードに落とし込む必要がある。
—
2. 【OS別決定版】`host.docker.internal` の限界と正しいネットワーク設計
よくある失敗例として、すべての環境で `xdebug.client_host=host.docker.internal` と記述する手法がある。
確かにDocker Desktop(Mac / Windows)環境ではこれで動く。しかし、Linuxネイティブ環境(CI/CDや本番同等のLinux検証サーバー)では、このホスト名はデフォルトで解決できない。
Linux版Dockerでは、`–add-host=host.docker.internal:host-gateway` をコンテナ起動時に明示的にバインドするか、ブリッジネットワークのゲートウェイIPを動的に取得するアーキテクチャが必要となる。
ここでは、環境差異を完全に吸収し、Mac/Linux/WSL2のどこであっても一発で動く `docker-compose.yml` の極限設定を提示する。
堅牢な `docker-compose.yml` の実装例
version: ‘3.8’
services:
app:
build:
context: .
dockerfile: docker/php/Dockerfile
image: laravel-xdebug-master:latest
container_name: laravel_app_core
# ホスト側のソースコードをリアルタイムでコンテナ内にマウント
volumes:
- .:/var/www/html:delegated
environment:
- APP_ENV=local
- XDEBUG_MODE=debug,develop
- XDEBUG_CONFIG=client_host=host-gateway client_port=9003 start_with_request=yes
# Linux環境でも host.docker.internal を確実にホストIPとして名前解決させる神設定
extra_hosts:
- “host.docker.internal:host-gateway”
networks:
- laravel-net
networks:
laravel-net:
driver: bridge
【アーキテククトの解説】
- `extra_hosts: – “host.docker.internal:host-gateway”`: これにより、Linux環境であっても `host.docker.internal` がホストマシンのIP(Dockerブリッジのゲートウェイ)を指すようになり、OS依存のコンフィギュレーション分岐が不要になる。
- `XDEBUG_CONFIG`: 環境変数経由で動的にXdebugの挙動を制御する。`start_with_request=yes` にすることで、HTTPリクエストやArtisanコマンドが走った瞬間に自動でデバッグセッションが張られる。
—
3. Laravelプロジェクト最適化:Dockerfileとphp.iniの完全調停
次に、PHPイメージ(Dockerコンテナ内)の構築と、Xdebugモジュールのビルド・設定を行う。
ここで重要なのは、「開発環境の利便性」と「本番環境の安全性・軽量性」を完全に分離することだ。Xdebugはメモリ消費が激しく、本番環境に残したままにすると深刻な脆弱性やパフォーマンス劣化を引き起こす。
1. 多段ビルド(Multi-stage Build)を意識した Dockerfile の構成
ベースイメージとして公式の PHP 8.2 FPM を採用
FROM php:8.2-fpm
依存パッケージとビルドツールのインストール
RUN apt-get update && apt-get install -y \
git \
curl \
libpng-dev \
libonig-dev \
libxml2-dev \
zip \
unzip \
&& apt-get clean && rm -rf /var/lib/apt/lists/
Composerのインストール
COPY –from=composer:latest /usr/bin/composer /usr/bin/composer
PECLを通じて最新の安定版 Xdebug をインストール
RUN pecl install xdebug \
&& docker-php-ext-enable xdebug
ワークディレクトリの設定
WORKDIR /var/www/html
権限の適切な管理(www-dataユーザー)
RUN chown -R www-data:www-data /var/www/html
2. `xdebug.ini` による詳細な振る舞い制御
コンテナ内の `/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini` に以下の設定を流し込む。
; 拡張モジュールのロード(docker-php-ext-enableで自動有効化されるが明示的に記載)
zend_extension=xdebug
[xdebug]
; デバッグモードと例外・エラー発生時の詳細な開発者用出力(develop)を有効化
xdebug.mode = debug,develop
; IDEがリクエストを待ち受けるポート(デフォルト: 9003)
xdebug.client_port = 9003
; 自動的にデバッグを開始するトリガー設定(リクエスト毎に強制接続)
xdebug.start_with_request = yes
; ログ出力先を指定することで、接続エラー時の原因究明を秒速で行う
xdebug.log = /var/log/xdebug.log
xdebug.log_level = 7
; IDE Keyの設定(PhpStorm等のIDE側と一致させる)
xdebug.idekey = PHPSTORM
【ここがプロの知見】
`xdebug.log` のパスを必ずコンテナ内に指定し、かつログレベルを `7`(connectionやhandshakeの詳細)に設定せよ。もしデバッグが繋がらないトラブルシューティングの際、`/var/log/xdebug.log` を `tail -f` すれば、「なぜIDEとコネクションが確立できなかったのか(タイムアウトなのか、IP拒否なのか)」が1秒で判明する。勘に頼ったデバッグ設定からの完全な脱却だ。
—
4. Laravel特有の罠:ArtisanコマンドとQueueワーカーのデバッグ術
Webブラウザからのリクエスト(HTTP)であれば、上記の基本設定で簡単にブレークポイントで止まる。しかし、シニアエンジニアが本当にデバッグしたいのは、Laravelの裏側で動くDaemonプロセスやCLIコマンドではないか?
- `php artisan queue:work`(非同期キューワーカー)
- `php artisan schedule:run`(スケジューラー)
- カスタムArtisanコマンド
これらはHTTPリクエストを伴わないため、通常の `start_with_request=yes` だとセッションが正しく確立されない場合がある。あるいは、非同期で大量に走るジョブのすべてでデバッガーが起動すると、開発環境が完全にフリーズする。
CLI環境でXdebugを自在に操るためのハック
ホストマシンのシェル、またはDockerコンテナ内部のCLIからArtisanを実行する際、環境変数を一時的に書き換えてXdebugをアクティブにするシェルスクリプト(またはエイリアス)を用意するのが最もスマートだ。
!/bin/bash
—————————————————————–
任意のLaravel ArtisanコマンドをXdebug有効の状態でコンテナ内で実行するスクリプト
使用法: ./artisan-debug.sh queue:work –once
—————————————————————–
docker-compose exec \
-e XDEBUG_MODE=debug \
-e XDEBUG_TRIGGER=1 \
app php artisan “$@”
【解説:なぜ `XDEBUG_TRIGGER` なのか】
すべてのCLI実行でXdebugを有効にすると、Composerのインストールやテスト実行(PHPUnit)のたびにデバッガーが割込みを試み、パフォーマンスが劇的に低下する。
そのため、普段は `xdebug.mode=off` または `develop` にしておき、デバッグが必要なピンポイントのコマンド実行時のみ環境変数で `XDEBUG_MODE=debug` を注入するのが、高速な開発環境を維持する究極の最適化ハックである。
—
5. IDE(PhpStorm / VS Code)側の受入設定とリバース・コネクションの極意
コンテナ側の準備が整ったら、ホスト側のIDEを「待ち受け状態(Listener)」にする必要がある。
PhpStormでの設定要件
1. `Settings (Preferences) > Languages & Frameworks > PHP > Xdebug` を開き、Debugポートが `9003` になっていることを確認する。
2. 右上の 「Start Listening for PHP Debug Connections」アイコン(電話のマーク) を緑色(有効)にする。
3. `Settings > Languages & Frameworks > PHP > Servers` にて、以下を設定する:
- Name: `laravel-docker`
- Host: `localhost` (またはドメイン)
- Port: `80` (NginxやLaravel内蔵サーバのポート)
- Use path mappings: チェックを入れ、プロジェクトのルートディレクトリと、コンテナ内の絶対パス (`/var/www/html`) を完全にマッピングする。
Visual Studio Code (`launch.json`) の場合
VS Codeでデバッグを行う場合は、`PHP Debug` 拡張機能を導入し、プロジェクト直下の `.vscode/launch.json` に以下を記述する。
{
“version”: “0.2.0”,
“configurations”: [
{
“name”: “Listen for Xdebug (Docker Laravel)”,
“type”: “php”,
“request”: “launch”,
“port”: 9003,
“pathMappings”: {
“/var/www/html”: “${workspaceFolder}”
},
“ignore”: [
“/vendor//.php”
]
}
]
}
- `ignore` プロパティに `/vendor//.php` を指定することで、Laravelのフレームワーク内部やサードパーティ製パッケージのコードに入り込んで迷子になるのを防ぎ、自作のアプリケーションロジック(Controllers, Models等)のみにブレークポイントを集中させることができる。
—
6. まとめ:最高峰の開発体験を手に入れろ
ここまでの設定を実装した環境のメリットを整理する。
1. OS非依存: Mac, Linux, WSL2のどこであっても `host-gateway` により同一の `docker-compose.yml` でシームレスに動く。
2. パフォーマンスの最適化: 普段は無駄なオーバーヘッドを削ぎ落とし、必要なとき(CLIデバッグや特定のリクエスト)だけXdebugをトリガーする。
3. 完全なパス同期: パスマッピングの正確な定義により、IDEが迷うことなくコンテナ内のソースコードとホストのファイルを一致させ、変数の中身をリアルタイムに覗き見ることができる。
コードを書く。保存する。ブラウザをリロードする、あるいはAPIを叩く。
その瞬間、手元のIDEのブレークポイントで処理がピタリと止まり、変数のスタックトレースが美しく展開される——。
この環境が構築できた瞬間から、あなたのデバッグ速度はこれまでの何倍にも跳ね上がるはずだ。妥協のないアーキテクチャで、真の高速開発を手に入れてほしい。