VirtualBox上に作ったWindows環境から、ホストPCのDockerで動かしているMySQLへ接続したい。
構成としてはこんな感じ。
ホストPC(Windows)├─ WSL2├─ Docker Desktop│ └─ MySQLコンテナ│└─ VirtualBox └─ WindowsMySQLコンテナはDocker側で3307番ポートを公開していた。
0.0.0.0:3307->3306/tcpなので最初は、
ホストPCのIPアドレス:3307でVirtualBoxから接続すれば終わりだと思っていた。
終わらなかった。
この記事は、VirtualBoxからWSL2 + Docker Desktop上のMySQLへ接続できなかったときの調査ログ。
接続方法だけ知りたい場合は、手順だけを抜き出したこちらの記事へ。
まずホストPCのIPが分からん
VirtualBoxから接続するので、localhost ではない。
VirtualBoxの中でlocalhostと書いたら、それはVirtualBox自身を指す。
ということでホスト側で、
ipconfigを実行。
ここで地味に困った。
どのIPv4アドレス使うねん。
WSLやVirtualBoxを入れているWindowsでipconfigすると、ネットワークアダプターがいくつも出てくる。
今回は実際にネットワーク接続に使っているWi-Fi側のIPv4アドレスを使った。
これで、
ホストPCのIPv4アドレス:3307へ接続すればいいはず。
VirtualBox側から確認する。
Test-NetConnection <ホストPCのIPv4> -Port 3307結果。
TcpTestSucceeded : Falseはい。
Dockerのポート公開を確認する
まず疑ったのはDocker側。
docker psでMySQLコンテナを見る。
ポートは、
0.0.0.0:3307->3306/tcpになっていた。
少なくとも設定上は、
ホスト側 3307 ↓コンテナ側 3306で公開されている。
127.0.0.1:3307だけにバインドされているわけでもない。
じゃあFirewallか?
Windows Firewallを開けてみる
Windows側で3307番ポートへの受信を許可した。
New-NetFirewallRule ` -DisplayName "Docker MySQL 3307" ` -Direction Inbound ` -Protocol TCP ` -LocalPort 3307 ` -Action Allowもう一度、
Test-NetConnection <ホストPCのIPv4> -Port 3307結果は、
False変わらん。
このあたりから、単純に「VirtualBoxからFirewallで弾かれている」だけではなさそうになってきた。
localhostなら通る
次にホストWindows自身から確認した。
Test-NetConnection localhost -Port 3307これは、
TcpTestSucceeded : Trueだった。
ところが同じWindowsから、自分自身のIPv4アドレスを指定すると、
Test-NetConnection <ホストPCのIPv4> -Port 3307こちらは、
Falseだった。
これはかなり大きなヒントだった。
VirtualBoxから届かない以前に、
Windows自身が、自分のIPv4アドレス経由で3307へ接続できていない。
つまり、この段階ではVirtualBoxを疑っても仕方ない。
localhost:3307 → True
ホストIPv4:3307 → Falseこの差を見る必要がある。
LISTENしているポートを確認する
Windows側で、
netstat -ano | findstr :3307を実行してみた。
何も出ない。
さらに、
Get-NetTCPConnection -LocalPort 3307 -State Listenでも該当するオブジェクトが見つからなかった。
Dockerでは、
0.0.0.0:3307->3306/tcpと表示されている。
localhost:3307にも接続できる。
でもWindows側のnetstatでは3307をLISTENしているものが見えない。
ここで、そういえばこのDocker環境はDocker Desktop + WSL2だったことを思い出した。
Docker DesktopとWSL2ってどういう関係?
ここ、Docker Desktopを普通に使っていると割と忘れる。
Windows上でDocker Desktopを使っていて、WSL2バックエンドを利用している場合、LinuxコンテナがWindows上で直接そのまま動いているわけではない。
ざっくりイメージすると、
Windows ↓Docker Desktop ↓WSL2側のLinux環境 ↓Dockerコンテナ ↓MySQLという層がある。
普段はDocker Desktopがこの辺をうまく面倒見てくれるので、
localhost:3307と書けば普通にMySQLへ届く。
だから普段の開発では、あまり境界を意識しなくても済む。
ところが今回はさらに外側にVirtualBoxがいる。
VirtualBox ↓Windows ↓Docker Desktop / WSL2 ↓MySQLコンテナVirtualBoxから見ると、まずWindowsホストへ入って、そこからDocker Desktop側へ渡してもらう必要がある。
普段localhostで隠れていた境界が、VirtualBoxという別環境からアクセスした途端に姿を現した。
Windows側に入口を作ってみる
そこでWindowsのportproxyを使って、外から入ってきた通信をlocalhost:3307へ転送することにした。
最初はこうした。
netsh interface portproxy add v4tov4 ` listenaddress=0.0.0.0 ` listenport=3307 ` connectaddress=127.0.0.1 ` connectport=3307確認すると、
netsh interface portproxy show allには、
0.0.0.0 3307 → 127.0.0.1 3307という設定が出ている。
IP Helperサービスも確認した。
Get-Service iphlpsvcこちらはRunning。
IPv6のバインドも確認したが、有効になっていた。
設定だけ見ると動きそう。
しかし、
netstat -ano | findstr :3307には相変わらず何も出なかった。
設定はいる。
でもLISTENしてない。
入口と出口を同じ3307にするのをやめた
ここで、portproxyの待受側を別ポートにして試すことにした。
いったん3307の設定を削除。
netsh interface portproxy delete v4tov4 ` listenaddress=0.0.0.0 ` listenport=3307今度は13307を入口にする。
netsh interface portproxy add v4tov4 ` listenaddress=0.0.0.0 ` listenport=13307 ` connectaddress=127.0.0.1 ` connectport=3307Firewallにも13307の受信許可を追加。
New-NetFirewallRule ` -DisplayName "VirtualBox to Docker MySQL 13307" ` -Direction Inbound ` -Protocol TCP ` -LocalPort 13307 ` -Action Allowそして、
Test-NetConnection <ホストPCのIPv4> -Port 13307結果。
TcpTestSucceeded : True来た。
VirtualBoxからもTrueになった
ここまで来たら、いよいよVirtualBox側から確認する。
Test-NetConnection <ホストPCのIPv4> -Port 13307こちらも、
TcpTestSucceeded : Trueになった。
最終的な経路はこうなった。
VirtualBox ↓ホストPCのIPv4:13307 ↓Windows portproxy ↓127.0.0.1:3307 ↓Docker Desktop / WSL2 ↓MySQLコンテナ:3306ようやくVirtualBoxからDocker側へ橋が架かった。
しかしアプリ側にポート設定がない
ネットワークが通ったところで、もう一つ問題が出た。
接続したいアプリ側に、MySQLのポートを指定する場所が見当たらない。
13307を指定できないなら、このままでは使えない。
そこでVirtualBoxから見える入口をMySQLのデフォルトポートである3306にする。
イメージとしては、
VirtualBox ↓ホストPCのIPv4:3306 ↓Windows portproxy ↓127.0.0.1:3307 ↓Docker Desktop ↓MySQL:3306これならアプリ側はホスト名だけ指定し、通常のMySQL接続として扱える。
13307は今回の切り分けには役立ったが、最終的にアプリの仕様に合わせるなら入口側のポートも考える必要がある。
今なら最初にどこを見るか
今回、一番効いた確認はこれだった。
Test-NetConnection localhost -Port 3307と、
Test-NetConnection <ホストPCのIPv4> -Port 3307の比較。
前者がTrueで後者がFalseだった時点で、
「MySQLコンテナが動いていない」
とか、
「VirtualBoxのネットワークがおかしい」
という方向から一旦離れられる。
今回のような構成なら、最初から次の順番で見るとかなり早そう。
docker psで公開ポートを確認- Windowsから
localhost:ポートを確認 - Windowsから
Windows自身のIPv4:ポートを確認 - VirtualBoxから
WindowsのIPv4:ポートを確認
この3地点を順番に見ると、
MySQL↑Docker Desktop / WSL2↑Windows↑VirtualBoxのどこまで通信できているかが分かる。
「Dockerでポート公開してるからOK」の一歩先
今回最初に見ていたのは、
0.0.0.0:3307->3306/tcpだった。
これを見て、
「3307公開されてる。じゃあVirtualBoxからホストIPの3307に行けばええな」
と思った。
普段ならそれで終わることも多い。
でも今回は、その間にWindows、Docker Desktop、WSL2という層がいた。
Docker Desktopは普段、この辺をかなり自然につないでくれる。
自然すぎて、存在を忘れる。
たぶん、家でパンイチで過ごすのと同じなんやと思う。
家の中だけなら何の問題もない。むしろ快適ですらある。
でも、その感覚のまま玄関を開けて宅配便を受け取ろうとした瞬間、
「あ、ここから外やった」
となる。
今回もそんな感じだった。
localhostの中だけで完結している間は、WindowsとWSL2とDocker Desktopの境界なんてほとんど意識しなくていい。
でもVirtualBoxという「外」と接触した瞬間、それまで意識していなかった境界が急に効いてくる。
Docker Desktopの0.0.0.0:3307を見て安心する前に、
「今、パンツ履いてたっけ?」
くらいの確認はした方がいい。
いや、パンイチなんやからパンツは履いてるか。