🔍

Xdebug+Ubuntu+Docker+PHP-FPM+VSCodeでステップデバッグしたい

2022/03/10に公開

課題

PHP-FPMのDocker containerにXdebug3.1をインストールしたい。
Xdebug3.1でステップデバッグしたい。
VSCodeでXdebugを動かしたい。

環境

OS:Ubuntu20.04 LTS
PHP-FPMとNginxのDocker containerをDocker Composeで動かしています。

記事のターゲット

Ubuntu+Docker+PHP-FPM+VSCode環境下で、Xdebug3系でステップデバッグしたい人に向けた記事です。

Docker+PHP-FPM+VSCodeでの環境構築などは既に出来ている人を対象にしています。

また、Xdebugでリモートデバッグすることを理解している人を対象にしています。DockerでXdebugを使うことはリモートデバッグをすることなので。リモートデバッグのイメージ自体がよく分からない人は、この記事がおすすめです。Xdebug2系の記事なので設定項目は違いますが、とても分かりやすいです。

解決策

実際のプログラム例とともに以下説明します。

Dockerfile

Dockerfile
FROM php:8.1-fpm

# PECLでx-debugのインストールする。
RUN pecl install xdebug

PECLを用いて、PHP-FPMコンテナにXdebugをインストールしています。

PECLとは

PECLとは、PHPの拡張モジュールのリポジトリです。

apt-get install php-xdebugを用いない理由

Xdebugの公式ドキュメントではDebian系はapt-get install php-xdebugでインストールするように案内されています。しかし、このベースイメージでapt-get install php-xdebugを実行するとエラーが出ます。PHP-FPM8.1のコンテナはDebian11のスリム版をベースイメージに作られていることが原因かと思います。だから、PECLを用いてインストールしています。

docker-php-ext-enable xdebugを実行しない理由

pecl install xdebugの実行後に、docker-php-ext-enable xdebugを実行することを紹介している記事もあると思います。docker-php-ext-enable xdebug/usr/local/etc/php/conf.d/docker-php-ext-xdebug.iniを作成するだけのコマンドです。docker-php-ext-xdebug.iniをコンテナにバインドマウントしたい人には不要です。この記事ではdocker-php-ext-xdebug.iniをカスタマイズしてバインドマウントするので、docker-php-ext-enable xdebugを実行しません。

docker-compose.yml

docker-compose.yml
version: '3.8'
services:
  php:
    build:
      context: ./
      dockerfile: Dockerfile
    image: your-image:latest
    container_name: your-container
    # コンテナの属するネットワークを設定
    networks:
      your-network:
        ipv4_address: 172.18.0.2
    volumes:
      # Xdebugの設定ファイルをマウント
      - type: bind
        source: ./docker-php-ext-xdebug.ini
        target: /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
# ネットワークの定義
networks:
  your-network:
    name: your-network
    driver: bridge
    ipam:
      config:
        - subnet: 172.18.0.0/16
          gateway: 172.18.0.1

例として、docker-compose.ymlを長々と書きました。
が、この中で重要なのは以下の2点です。

Xdebugの設定ファイルのマウント

docker-php-ext-xdebug.iniはXdebugの設定ファイルです。
この設定ファイルを/usr/local/etc/php/conf.d/docker-php-ext-xdebug.iniにバインドマウントして下さい。
docker-php-ext-xdebug.iniの詳細については後述します。

ネットワークの定義

コンテナとホストコンピューターの間でのリモートデバッグでは、それぞれの住所であるプライベートIPアドレスが重要になります。
ホストコンピューターとPHPコンテナそれぞれにプライベートIPアドレスを定義して下さい。
空いているプライベートIPアドレスであればなんでもいいです。
今回の例では、172.18.0.0/16でネットワークを定義しています。ホストコンピューターには、172.18.0.1を割り当てています。また、PHP-FPMコンテナには、172.18.0.2を割り当てています。

docker-php-ext-xdebug.ini

docker-php-ext-xdebug.ini
zend_extension=xdebug
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_host=172.18.0.1
;xdebug.discover_client_host=1
xdebug.client_port=9003

docker-php-ext-xdebug.iniはXdebugの設定ファイルです。
以下に設定項目の解説を書きます。

zend_extension

zend_extensionはXdebugの実行ファイルのパスの設定項目です。zend_extension=xdebugがデフォルトですが、上手くいかなかったら変えてみて下さい。

xdebug.mode

xdebug.modeはXdebugのどの機能を使うかの設定項目です。ステップデバッグだけを使いたい場合は、xdebug.mode=debugを指定します。var_dumpの機能だけを使いたい時はxdebug.mode=developと指定します。複数の機能を使いたい時は、カンマ区切りで指定します。全部の機能を使いたい時は、xdebug.mode=develop,coverage,debug,gcstats,profile,traceです。

xdebug.start_with_request

xdebug.start_with_requestは、どんなときにXdebugが作動するかの設定項目です。PHPが実行される時はいつでも実行したいので、xdebug.start_with_request=yesを設定します。

xdebug.client_host

xdebug.client_hostは、docker containerから見たホストコンピューターのプライベートIPアドレスを指定します。
この記事の例の場合は、docker-compose.ymlで定義したgatewayのアドレスの172.18.0.1です。

xdebug.discover_client_host

xdebug.discover_client_hostは、ホストコンピューターのプライベートIPアドレスを自動で検索してくれる設定項目です。xdebug.discover_client_host=1で自動的に検索してくれます。

xdebug.client_hostxdebug.discover_client_hostはどちらか一方だけを設定すれば良いです。どちらも設定した場合は知りません。

この例では、xdebug.client_hostを設定しているのでxdebug.discover_client_hostはコメントアウトしていますが、好きな方を使って下さい。

xdebug.client_port

ホストコンピューターが通信に使うportを指定します。デフォルト通りにxdebug.client_port=9003を指定したら良いと思います。

PHP Debug

PHP Debugは、VSCodeでXdebugを実行する為の拡張機能です。

PHP Debugの設定方法

VSCodeの拡張機能からPHP Debugをインストールして下さい。

VSCodeの左端の『実行とデバッグ』をクリックし、『create a launch.json file』をクリックし、『PHP』を選んで下さい。

launch.jsonを下記のように編集して下さい。

launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Listen for Xdebug",
            "type": "php",
            "request": "launch",
            "port": docker-php-ext-xdebug.iniで設定したポート,
            "pathMappings": {
                "/コンテナのディレクトリ": "${workspaceRoot}/ホストのディレクトリ"
            }
        },
    ]
}

重要なのは、portとpathMappingsです。
portには、docker-php-ext-xdebug.iniで設定したportを指定して下さい。今回の例だと、9003です。
pathMappingsでは、ホストコンピューターのディレクトリとコンテナのディレクトリの対応関係を定義します。ホストとコンテナの対応するパスを書いて下さい。

Firewall

コンテナとホストコンピュータで通信が行われるので、Firewallで通信を許可してください。コンテナからホストコンピューターへの通信さえ許可すれば良いです。
この例だと、コンテナ(172.18.0.2)からホストコンピューター(172.18.0.1:9003)への通信を許可すれば良いです。

Xdebug自体のログについて

Xdebug自体のログを見るには2種類の方法があります。

Dockerのログ

特別な設定をしなくても、docker logs -f コンテナ名でエラーログを見ることが出来ます。
私自身もこのログを見ながら作業しました。

ちなみに、私の作業中に出たログは下記の通りです。

NOTICE: PHP message: Xdebug: [Step Debug] Time-out connecting to debugging client, waited: 200 ms. Tried: xxx.xxx.xxx.xxx:9003 (through xdebug.client_host/xdebug.client_port) :-(
xxx.xxx.xxx.xxx -  10/Mar/2022:06:57:25 +0000 "GET /index.php" 200

通信が出来ずにタイムアウトしていますね、、、
Firewallで通信を遮断していたことが原因でした、、、

Xdebugのログ

Dockerfileとdocker-php-ext-xdebug.iniに下記の行を加えると、コンテナ内にXdebug固有のログを出力出来ます。

Dockerfile
# xdebugのログファイルを作成する
RUN touch /var/log/xdebug.log && chmod a+w /var/log/xdebug.log
docker-php-ext-xdebug.ini
xdebug.log=/var/log/xdebug.log

Xdebugのログでは、上記で紹介した私の作業中のログは下記のようになります。

[125] [Step Debug] INFO: Connecting to configured address/port: xxx.xxx.xxx.xxx:9003.
[125] [Step Debug] ERR: Time-out connecting to debugging client, waited: 200 ms. Tried: xxx.xxx.xxx.xxx:9003 (through xdebug.client_host/xdebug.client_port) :-(

最後に

久々にXdebugを使うことになって、若干設定に苦戦しました。
昔はXdebug2系をVagrantでVirtual BoxにいれてEclipseでデバッグしていたので、、、
良い開発ライフを!

Discussion