EC2 + SSH + dockerでvimのxdebug(vdebug)
さて、前回より内容が濃くなってくぞ。
前回使ったサーバーの掃除
apacheとかが起動しているので掃除する。新たに起動する人はここは見る必要はない
sudo apt purge libapache2-mod-php
まあ簡単に作ったり壊したりできるのがEC2のいいところではある。
dockerとdocker-composeを入れる
まあ正確にはdocker-composeは入れなくてもいいかもしれないけどまあなんとなくこれを入れれば一式入るので。
$ sudo apt install docker-compose
作業準備
で、今sshログインしてhomeにいるとする。debianのEC2のAMIはadminになっているから、そのユーザーがそうなってるのかしら?
admin@ip-172-31-46-247:~$ pwd
/home/admin
dockerを使う場合はこのユーザーをdocker groupに入れる
$ sudo adduser admin docker
Adding user `admin' to group `docker' ...
Done.
簡単なのは一度ログアウトしてsshを繋ぎ直す事だ
$ id
uid=1000(admin) gid=1000(admin) groups=1000(admin),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),110(docker)
見切れてるけど、ここでdockerグループに入ってるのを確認する必要がある。
workingディレクトリの作成
まあ、正直何でもいい
$ mkdir vdebug-test
$ cd vdebug-test/
とした
とりあえず簡単なDockerfileの用意
apache2でもいいんだけど、まあせっかくだしnginxにします?今回は
Dockerfile を作成する
FROM php:8-fpm
# Nginxのインストール
RUN apt-get update && apt-get install -y nginx
# ワークディレクトリの設定
WORKDIR /var/www/html
# PHP-FPMとNginxを前面で実行
CMD php-fpm -D && nginx -g "daemon off;"
とまあこんな感じにしよう。そしたら
docker build -t vdebug-test .
とかやるとvdebug-test とかいう名前でイメージができる。
シンプルにこれを起動してみよう。
docker run -d -p 80:80 -v $(pwd):/var/www/html vdebug-test
するとこんな感じになるはずだ
$ docker run -d -p 80:80 -v $(pwd):/var/www/html vdebug-test
f386acab14859ebe0e1d75189607655af8db2749c7711f338a3f97441495470d
こんな感じでそれぞれプロセスidを出しながら起動しているはず。当該ホストにhttpで接続してみよう。
未設定なので、まあこんなもんだろう。
nginx.confをキめる
まずさっきのdockerを止める。idを指定するといい
$ docker stop f38
3文字くらいでいい。コピペしてももちろんよいが。
最低限のnginx.confを作る
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
server {
listen 80 default_server;
root /var/www/html/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
}
これをDockerに食わせる設定をする
更新されたDockerfile
FROM php:8-fpm
# Nginxのインストール
RUN apt-get update && apt-get install -y nginx
COPY nginx.conf /etc/nginx/nginx.conf
# ワークディレクトリの設定
WORKDIR /var/www/html
# PHP-FPMとNginxを前面で実行
CMD php-fpm -D && nginx -g "daemon off;"
再構築している。
docker build -t vdebug-test . --no-cache
再度run
$ docker run -d -p 80:80 -v $(pwd):/var/www/html vdebug-test
711894d328cccfe9597e2b54abf824618c719156dc4e3778ea037718a565eee7
すると今度は
404 not foundになったことで設定が変更されているのがわかる。これはnginx.confの
root /var/www/html/public;
この設定がキいていて、かつDockerfile の
WORKDIR /var/www/html
これが設定されているので、host側にpublicディレクトリを作ればいいという事が何となくわかる。というのも
docker run -d -p 80:80 -v $(pwd):/var/www/html vdebug-test
このコマンドでPWD を /var/www/html にマウントしてるからね、というわけで
$ mkdir public
$ echo "<?php phpinfo();" > public/index.php
などというワンライナーを書いてみれば
こうなる。ちなみに
FROM php:8-fpm
とか適当な指定をしたので8.3が勝手に入ってるけど、バージョンをちゃんと書いたらそれが入るので8.2の方がよければphp:8.2-fpmになる。まあいいや。新しいのが使える分には困らんのだがねえ…(切実)
xdebugの設定
xdebugの設定はまだなされていない。ここではどういう風に行ったらいいのかを確認するためにまずdocker containerに入ってみよう。
docker exec -it 711894d328cc bash
こんな感じで入る。psのidがわからんくなったらdocker psしてね
そうすると
root@711894d328cc:/var/www/html#
みたいなdockerの中で起動されたbashが使えるようになる。
xdebugを手動で設定してみよう
php系のdockerイメージはかなりカスタムされておりosのパッケージを使う事ができないので用意されてるpeclを使う
# type pecl
pecl is /usr/local/bin/pecl
というわけでこれを使ってinstallする
# pecl install xdebug
するとビルドがはじまりこんな風味で終わるだろう
Installing '/usr/local/lib/php/extensions/no-debug-non-zts-20230831/xdebug.so'
install ok: channel://pecl.php.net/xdebug-3.3.2
configuration option "php_ini" is not set to php.ini location
You should add "zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20230831/xdebug.so" to php.ini
ここで警告されているようにphp.iniに設定していないから
zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20230831/xdebug.so
をphp.iniに書けよといっている。ただまあこれはもうちょっと横着ができてdockerのphpの設定で
Scan this dir for additional .ini files /usr/local/etc/php/conf.d
とあるので、ここにxdebugの設定を置けばよさそうだ。しかし実際にはモジュールを読みこむためにnginxに再起動をかけるとdockerが終わっちゃうので、これらの流れを踏まえてDockerfileで行う必要がある。ではDockerfileを改善する前にxdebug.ini を作る
xdebug.ini
まあ実際にはもっと単純に書ける
zend_extension=xdebug.so
この一行だけ書いとけばいい
そしてDockerfileを更新する
FROM php:8-fpm
# Nginxのインストール
RUN apt-get update && apt-get install -y nginx && pecl install xdebug
COPY nginx.conf /etc/nginx/nginx.conf
COPY xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini
# ワークディレクトリの設定
WORKDIR /var/www/html
# PHP-FPMとNginxを前面で実行
CMD php-fpm -D && nginx -g "daemon off;"
として、先程のbuild、docker停止、開始とかやって何とかこれをloadすると
が出てくるだろう。前回は3.2だったけどこっちは3.3.2だね
xdebugの設定微調整
これはxdebug.iniに書いたらいい。
zend_extension=xdebug.so
[xdebug]
xdebug.mode=debug
xdebug.start_with_request=yes
;xdebug.client_host=
xdebug.client_port=9003
client_hostは設定しない。ここでもbuildからの再起動が必要だ。もういちいちコマンドは書きませんよ。
起動すれば
まあちゃんとこの辺が切り替わることでしょう。
さて、xdebugの仕様を再度確認する
xdebugつのは
ブラウザーのリクエストがきたら(start_with_request = yesの場合)
裏で指定されたclientのhostとportにデバッグ情報をぶん投げる
という動きをする。従ってdocker containerの中でコード書いてりゃワケないんだけど今回のような構成の場合はdockerのhostからコード書いてコンテナは単に動かすだけにしたいやんか。そういう場合はcontainerからhostと通信しないといけない。
まずcontainerからhostに通信できるようにする
これに関してはdocker.host.internal ってのがwindowsとかのdocker desktopでは使えるんだけど素のlinux dockerだとうまいこといかなかったりするのではあるが、とりあえず一度dockerをstopして、いちいち見ていこう
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bc4d062148d7 vdebug-test "docker-php-entrypoi…" 6 minutes ago Up 6 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:9003->9003/tcp, :::9003->9003/tcp, 9000/tcp goofy_aryabhata
$ docker stop bc4
さて、ここで今までは
$ docker run -d -p 80:80 -v $(pwd):/var/www/html vdebug-test
なるコマンドを使っていたが、これをちょっと拡張して
docker run -d -p 80:80 -p 9003:9003 --add-host=host.docker.internal:host-gateway -v $(pwd):/var/www/html vdebug-test
で起動する。ここで一応確認のために中に入る
$ docker exec -it 062 bash
root@062abc93277a:/var/www/html#
そしたら/etc/hosts を確認しよう、すると
# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.1 host.docker.internal
172.17.0.2 062abc93277a
このようにhost.docker.internal の値が書き込まれているので、これで解決できるってわけだ。iputils-ping とか入れて確認してみてもいい
# apt install iputils-ping
# ping host.docker.internal
PING host.docker.internal (172.17.0.1) 56(84) bytes of data.
64 bytes from host.docker.internal (172.17.0.1): icmp_seq=1 ttl=64 time=0.048 ms
64 bytes from host.docker.internal (172.17.0.1):
この段階でcontainerを抜けてstopする
root@062abc93277a:/var/www/html# exit
exit
$ docker stop 062
そしてxdebug.ini に設定値を書き込もう
[xdebug]
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_host=host.docker.internal
xdebug.client_port=9003
そしてビルドして起動して値を見る
vimで待ち受ける前にnetcatとかで待ち受けてみる
これはhost側、つまりvimを起動する側でやる。
sudo apt install netcat-openbsd
そうしたら9003番で待ち受けてみる
nc -l -p 9003
そうしたら、こんな具合でxmlが飛んでくるのが見えるかと思う
こうなってれば通信できており、xdebugを使う準備は完成しているから。あとはvdebugを起動するだけだ。逆にこれが見えない場合は通信できてないので一生成功しない。
vdebugしてみる
ではpublic/test.php にこんな感じで書いてみよう。
<?php
$a = 1;
$b = 2;
echo $a + $b. "\n";
基本的にxdebugとhostが通信できているので成功するはずだ。echoの所にbreakpointを追いてF5を押したよ
ではブラウザーでtest.phpにアクセスしてみよう。すると…
このような形で微妙に動きがあやしくなるはずだ。これはpath_mappingがちゃんとおこなえていない時になる。
ここでvimrcにこの設定を足す
let g:vdebug_options['path_maps'] = { '/var/www/html': '/home/admin/vdebug-test' }
すると
このようにちゃんとつかまえられるであろう。
しかし固有の設定をいちいち.vimrcに書いて切り換えるのが面倒くさい
これに関しては後日考えてみるとして今回はここまで。