この記事はWSL2上のLinuxからホストのWindowsのIPを知るにはmDNSを使うのが楽の関連記事です。
前提条件
一口にWSL2上でDocker使ってるぞ!と言っても、組み合わせがかなり多彩であることに注意する必要があります。
- Docker Desktopを使いWSL2バックエンドが有効で、エディタはWindows上にある
- Docker Desktopを使いWSL2バックエンドが有効で、エディタはWSL2上にある
- Docker Desktopを使わずWSL2上に直接Dockerが起動しており、エディタはWindows上にある
- Docker Desktopを使わずWSL2上に直接Dockerが起動しており、エディタはWSL2上にある
本記事は WSL2上に直接Dockerが起動しており、エディタはWindows上にある
ユーザーがメインターゲットです。Xdebug自体の構築等については他記事を参照してください。
mDNS
利用編
mDNSを使えば特に設定不要でXdebugが利用できます。
xdebug.client_host=${Windowsのホスト名}.local
コンテナ再起動で利用できるようになります。接続がうまくいかない場合はファイアウォールでブロックされていないか確認してください。元記事も参考になります。
WSL2上のLinuxからホストのWindowsのIPを知るにはmDNSを使うのが楽
これを設定してブレイクポイントに止まればこの記事の残り部分は読む必要ありません。お疲れさまでした。
extra-hosts
&& SSH Port Forwarding
編
何らかの原因がありmDNSを利用できないときは、 extra-hosts
と SSH Port Forwarding
を組み合わせることによって目的を達成できます。強いファイアウォールが設定されており例外設定をできない時にはこちらを使用する必要があるかもしれません。
実施環境
> sudo docker version
Server: Docker Engine - Community
Engine:
Version: 20.10.17
API version: 1.41 (minimum version 1.12)
各種設定
最初に結論を述べてしまうと、コンテナ内からWSL2へのアクセスは extra-hosts
の設定が、WSL2からWindowsへのアクセスは SSH port forwarding
の設定がそれぞれ必要になってきます。
+---------------------------------------------------------+
| +------------------------------------+ Host/Windows |
| | +-----------------+ WSL2/Ubuntu | |
| | | Docker/PHP | | +--------------+ |
| | | | | | IntelliJ | |
| | | | | | Port:9099 | |
| | | -----+----- | +--------------+ |
| | | extra-hosts | |
| | | (1) ------+------ |
| | | -----+----- Port Forwarding |
| | | | (2) |
| | +-----------------+ ------+------ |
| +------------------------------------+ |
+---------------------------------------------------------+
それぞれについて見ていきます。
(1) extra-hosts
まず初めに、Dockerコンテナ内とWSL2上のUbuntuの通信について考えていきます。
すでにDocker Desktop上でXdebugを動かしたことがあるプロジェクトであれば、おそらくxdebugの設定ファイルに xdebug.client_host=host.docker.internal
と記述されているのではないかと思います。
xdebug.client_host=host.docker.internal
しかし、このホスト名そのままではLinux上のDocker(=WSL2上のDocker)では正常に名前解決を行うことができません。
root@3cc27b6f3582:/# ping host.docker.internal
ping: host.docker.internal: Name or service not known
そこで、名前解決がされるように docker-compose.yaml
に extra_hosts
の追記を行います。
php-fpm:
image: php:8.0.13-fpm
# 中略 #
extra_hosts:
- "host.docker.internal:host-gateway"
💡
extra_hosts
は通常、記述した通りにコンテナ内の/etc/hosts
を書き換える動作を行いますが、ここにhost-gateway
と指定した場合に限り、ホストにつながるIPアドレスを書き込んでくれます。コードを見る限り、ホスト名が必ずhost.docker.internal
である必要はなさそうですね。Support host.docker.internal in dockerd on Linux · moby/moby@92e809a
これで、コンテナ内から host.docker.internal
にアクセスすることでWSL2上のUbuntuに到達できるようになります。 /etc/hosts
の値を確認してpingを飛ばしてみます。
root@30f3efe237ed:/usr/share/nginx# 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.18.0.7 30f3efe237ed
root@30f3efe237ed:/usr/share/nginx# 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.037 ms
64 bytes from host.docker.internal (172.17.0.1): icmp_seq=2 ttl=64 time=0.064 ms
64 bytes from host.docker.internal (172.17.0.1): icmp_seq=3 ttl=64 time=0.064 ms
64 bytes from host.docker.internal (172.17.0.1): icmp_seq=4 ttl=64 time=0.116 ms
64 bytes from host.docker.internal (172.17.0.1): icmp_seq=5 ttl=64 time=0.061 ms
Pingが通ればOKです。
(2) Port Forwarding
extra-hosts
でコンテナ内とWSL2上のUbuntuとの通信が可能になりました。次はWSL2上のUbuntuからWindowsのエディタとの通信について考えていきます。なお、この作業はWSL上にエディタがある場合は不要です。
まず、セキュリティの観点からデフォルトの設定では外部からのアクセスをSSHを用いて別のホストに転送することができません。仮にポートフォワーディングを行ったとしても、下記のように localhost
のみがLISTENされます。これでは、Dockerコンテナ内からアクセスがあった際にフォワーディングされることはありません。
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
sshd 27086 kznrluk 9u IPv4 66507 0t0 TCP localhost:9002 (LISTEN)
これを解決するために、実際にポートフォワーディングを行う前に /etc/ssh/sshd_config
の GatewayPorts
の値を clientspecified
に変更しておきます。
- #GatewayPorts no
+ GatewayPorts clientspecified
設定が終わったら sshd
を起動させます。
$ sudo service sshd start
次に、Windowsのコンソールに戻ります。コマンドプロンプトでもパワーシェルでもOKです。
実際にWindowsからポートフォワーディングを行います。コマンドの構文とサンプルを以下に記します。
ssh -R ${LISTENするアドレス}:${ポート}:${どのホストに転送するか}:${どのポートに転送するか} ${sshユーザ名}@${sshホスト名}
// kznrluK@localhostのすべてのアドレス(0.0.0.0)の9002ポートへの通信をlocalhost:9099に転送する
ssh -R 0.0.0.0:9002:localhost:9099 kznrluk@localhost
接続すると通常のSSHと同様にシェルが起動します。SSHが疎通している間、ポートフォワーディングも行われています。
sudo lsof -i
コマンドで、きちんとポートフォワーディングが行われているか確認できます。
$ sudo lsof -i
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
sshd 11806 kznrluk 8u IPv4 27830 0t0 TCP *:9002 (LISTEN)
ポート番号が変換されていることに注意してください! Ubuntu上のポート9002はWindows上のポート9099に変換されています。Xdebugの設定ファイルで xdebug.client_port=9099
と指定したり、エディタ上でLISTENするポートを 9002
としないように注意してください。
トラブルシューティング
telnet
コマンドを使うことで正常に接続が疎通するかを実際にPHPを動かすことなく確認できます。WSL2上のシェルで確認してみましょう。
ポートフォワーディングができており、かつエディタ側で 9099
ポートをLISTENしている場合は接続できます。 telnet
で接続できるのにブレイクポイントで止まらない場合は別の問題がありそうです。ポートの設定は合っていますか?ポート変換していることを忘れていませんか?
$ telnet localhost 9002
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
// すぐ切断されなければOK
接続できるがすぐ切断されてしまう場合はエディタ側の設定が誤っているかもしれません。ポートはきちんとSSHの接続で指定した値になっていますか?この記事を見て設定したのであれば 9099
をLISTENしているか確認してください。
$ telnet localhost 9002
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Connection closed by foreign host.
そもそもポートフォワーディングがうまく言っていない場合は接続できません。SSHの設定から見直してください。
$ telnet localhost 9003
Trying 127.0.0.1...
telnet: Unable to connect to remote host: Connection refused
まとめ
Xdebugはデバッガー側にリクエストが飛ぶような仕組みなのでそこを意識しないと深みにはまってしまいそうです。
extra-hosts
&& SSH Port Forwarding
編を書いている間に、より良いmDNSを使った方法を知ったため、記事の構成があやふやになってしまいました。記事を書く前に前提を調べておくのは重要ですね。
以上です。