背景
Laravelをはじめ、PHPのWebアプリケーション開発において、デバッグをする際、
var_dump(○○○○○);
のように、変数をブラウザ上に出力する方法を取られることがあります。
しかし、この方法では、
- 条件分岐やループ等の処理の流れを追うことは難しい。
※追うためには多くの個所でvar_dump
等の出力を入れなければならない。 - 文字列以外のデータ(日付、リスト、辞書など)を読みやすい形式で出力されるとは限らない。
※表示用のフォーマットを設定したり、JSON形式にするなど、手間がかかる。 - デバッグ後に
var_dump
等のコードを確実に削除しなければならない。
※万一、削除漏れがあったままリリースすると、内部的な情報が表示されてしまう。
などの問題があり、効率的なデバッグをすることができませんよね。
開発している方の中にも、苦労されている方もいらっしゃると思います。
一方で、Xdebug等によるデバッグ環境構築の手順、特に、Dockerコンテナでの環境での手順は、あまりまとまっていないのようです。
社内の開発メンバーからも、手順を知っている人がいないか?という質問がありましたが、手順を把握しているメンバーはいませんでした。
そこで、実際にデバッグ環境を作ってみることにしました。
はじめに
このサンプルは、DockerでPHPおよびNGINXのコンテナを作成し、PHPのコンテナにXdebugをインストールし、ローカルのVSCodeでステップ実行させるものです。
なお、今回は、Windows 10にWSL2(Windows Subsystem for Linux 2)でUbuntu 20.04をインストールし、その上にDockerおよびDocker Composeをインストールした環境を使用しています。
Macや、Docker Desktop for Windows等の環境については、動作するかは不明です。申し訳ありませんがご了承ください。
※Docker Compose実行シェルスクリプトのユーザー・グループの情報を変更することで、動作する可能性がありますので、よろしければお試しください。
サンプルについて
なお、この記事のために作成したサンプルは、GitHubの私のリポジトリ
https://github.com/murakami0923/laravel-remote-debug-sample
にあります。
全体条件や使用方法など、詳細は、こちらのリポジトリのREADME.md
をご参照ください。
【解説】全体の構成の概要
構成図
全体の構成を図にまとめます。
Docker Composeで下記3つのコンテナを起動します。
- NGINX
- PHP-FPM(Laravelアプリケーション)
- MariaDB
PHPの処理の流れ
このとき、NGINXコンテナのnginx-app.conf
ファイルで、\*.php
へのアクセスの場合、FastCGIでPHP-FPM(Laravelアプリケーション)のコンテナの9000ポートへアクセスを渡すように設定します。
その後、開発者がWebブラウザでNGINXにアクセスすると、PHPへのアクセスの場合、PHP-FPMコンテナに処理が渡り、処理後にレスポンスを開発者のWebブラウザに返ります。
PHP-FPMコンテナ上でのデバッグ
また、PHP-FPM内のLaravelアプリケーションにはXdebug
をインストール・設定し、開発者のVSCodeにRemote Containers
の拡張機能をインストール・設定します。
その後、開発者がWSL2でVSCodeを開き、続いて、Remote Containers
に設定したコンテナを開くと、コンテナ上にVSCode Server
がインストールされ、自動的に開きます。
次に、XCodeのデバッグ設定をしてデバッグを実行すると、コンテナ上のVSCode ServerがXdebugに接続し、デバッグ可能となります。
VSCode Server
で、Controller等にブレークポイントを設定し、Webブラウザでサイトにアクセスすると、ブレークポイントで処理が止まり、ステップ実行しながら変数等の状態を確認することができます。
Docker Composeのボリュームについて
Laravelアプリケーション(laravel-app
ディレクトリ)をボリュームに設定します。
NGINX、PHP-FPMの書くコンテナにおいて、/var/www/html
がRootディレクトリとなるため、laravel-app
をボリュームとして設定します。
なお、WSL2 Ubuntuで使用しているユーザーのuidとアプリケーション実行ユーザーのuidが合わないと、ディレクトリやファイルへの読取り・書込み・実行の権限が、Laravelアプリケーションを正しく実行できないことがあります。
PHP-FPMのアプリケーション実行ユーザは、デフォルトではwww-data
(uid : 33
)となっていますが、WSL2ユーザーのユーザーとグループをPHP-FPMコンテナ内に作成し、そのユーザーでアプリケーションを実行するようにしています。
【解説】Docker定義
NGINXコンテナ定義
定義:docker/laravel-remote-debug-sample-nginx
LaravelのPHPアプリケーションへアクセスが来た時に、PHP-FPMコンテナに処理を渡すための設定が必要になるので、下記設定ファイルにて定義しています。
設定ファイル:nginx-app.conf
server {
listen 80;
root /var/www/html/public;
index index.php index.html index.htm;
access_log /var/log/nginx/app-access.log;
error_log /var/log/nginx/app-error.log;
# 全てのアクセスに対しての処理。左からドキュメントルート配下を参照する。以下の場合は3つのステップを踏んでいる
# 1) アクセスがきたパスの静的ファイルが存在するか
# 2) アクセスがきたパスのディレクトリが存在するか(見つかったらその中の indexを参照する)
# 3) 上の二つがない場合、index.phpのファイルとする(Laravelに処理を任せる)
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# 静的ファイルの時の処理。正規表現によるパターンマッチングが使える
location ~* \.(jpg|jpeg|gif|css|png|js|ico|svg)$ {
expires 30d; # キャッシュの設定。クエリーを変更の処理がしっかりさmaxでもいい。今回は30日
log_not_found off; # ログを出さない。publicサーバは攻撃に晒されていてログがたくさん出てしまうため
access_log off; # 同じ理由でアクセスログも見えないようにしている
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass laravel-remote-debug-sample-app:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
このファイルをCOPYし、コンテナ内に設置するよう、Dockerfile
に記載しています。
COPY nginx-app.conf /etc/nginx/conf.d/
RUN mv /etc/nginx/conf.d/nginx-app.conf /etc/nginx/conf.d/app.conf
PHP-FPMコンテナ定義
定義:docker/laravel-remote-debug-sample-app
Xdebugをインストールするよう、Dockerfile
に記載しています。
COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/
RUN install-php-extensions gd xdebug
また、Xdebugの設定ファイルを作成します。
設定ファイル:docker-php-ext-xdebug.ini
zend_extension=xdebug
xdebug.client_host = localhost
xdebug.start_with_request = yes
xdebug.mode = debug
; port 番号はデフォルトで9003番ですがわかりやすいように明示的に書いています。9003ではなくても空いているポートなら大丈夫です。
; 後述する .devcontainer/devcontainer.json に書く port と同じにしてください。
xdebug.client_port = 9003
xdebug.log = /var/log/xdebug.log
この設定ファイルを設置するよう、Dockerfile
に記載しています。
RUN mv /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini /tmp/docker-php-ext-xdebug.ini.org
COPY docker-php-ext-xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
※設定ファイルの中で指定しているポート番号で、Remote Containersやデバッグの際に接続しています。
【解説】PHP-FPM実行ユーザーの作成
実行する環境によって、ユーザー名・グループ名が異なるため、コンテナ実行時のシェルスクリプトで、ユーザーの作成を定義しています。
まず、Docker Composeで、環境変数を渡すよう、定義しています。
定義ファイル:docker-compose.yaml
(抜粋)
app:
~(中略)~
environment:
- USER_NAME
- USER_ID
- GROUP_NAME
- GROUP_ID
また、Docker Compose実行の際、WSL2 Ubuntuのユーザー情報、グループ情報を変数に入れてから実行することで、コンテナに環境変数として渡されます。
Docker Compose実行シェルスクリプト:start-docker-compose.sh
USER_NAME=$(id -un) USER_ID=$(id -u) GROUP_NAME=$(id -gn) GROUP_ID=$(id -g) docker-compose up -d
※Mac等、違う環境で実行する場合は、この部分を変更してください。
(laravel-appディレクトリ以下の所有者と同じユーザーでご指定ください。)
更に、PHP-FPMコンテナ実行時のシェルスクリプトで、ユーザーを作成するようにしています。
rootユーザーのシェルスクリプト:startup-root.sh
(抜粋)
このシェルスクリプトで、アプリケーション実行ユーザーとグループを作成しています。
また、
#!/bin/bash
# 環境変数で渡されたユーザー、グループ情報をechoする。(アプリケーション実行ユーザー)
echo ${USER_NAME}
echo ${USER_ID}
echo ${GROUP_NAME}
echo ${GROUP_ID}
# 環境変数で渡されたユーザー、グループを作成する。
groupadd -g ${GROUP_ID} ${GROUP_NAME}
useradd -u ${USER_ID} -g ${GROUP_ID} -m -s /bin/bash ${USER_NAME}
※作成前にechoすることで、Dockerホスト(WSL2 Ubuntu)で、下記コマンドを実行すると、正しくユーザーとグループが作成されたか、確認できます。
docker logs laravel-remote-debug-sample-app
次に、アプリケーション実行ユーザーのシェルスクリプトを設置して実行しています。
rootユーザーのシェルスクリプト:startup-root.sh
(抜粋)
# startup-user.shを作成したユーザーのホームディレクトリへ複製し、所有者を変更する。
/bin/cp /root/startup-user.sh /home/${USER_NAME}/
chown ${USER_NAME}:${GROUP_NAME} /home/${USER_NAME}/startup-user.sh
# startup-user.shを実行する。
echo "call ${USER_NAME} startup script"
su - ${USER_NAME} -c "/home/${USER_NAME}/startup-user.sh"
echo "end ${USER_NAME} startup script"
アプリケーション実行ユーザーでは、Laravelに必要なcomposerのパッケージをインストールし、.envのAPP_KEYを生成しています。
アプリケーション実行ユーザーのシェルスクリプト:startup-user.sh
#!/bin/bash
cd /var/www/html/
# composerのパッケージをインストールする。
composer install
# .envのAPP_KEYを生成する。
php artisan key:generate
php artisan cache:clear