【テクニカル・上級編】Docker環境でXdebugを動かすには?Laravel開発での設定テンプレを公開 – デバッグ・コード品質・テストツール生産性向上バイブル

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のブレークポイントで処理がピタリと止まり、変数のスタックトレースが美しく展開される——。

この環境が構築できた瞬間から、あなたのデバッグ速度はこれまでの何倍にも跳ね上がるはずだ。妥協のないアーキテクチャで、真の高速開発を手に入れてほしい。

タイトルとURLをコピーしました