なんかWSL2がインターネットにつながらなくなったときの解決方法


なんかWSL2でネットワークが動かなくなった。

最近、WSL2をぼちぼち使っている中で、インターネットにアクセスできなくなりました。
そこで、魔法の呪文

$ wsl --shutdown

WSL2で困ったときに使う魔法の呪文

を発動しました。だけど動かない。
で、色々試行錯誤した結果、以下の方法で解決しました。

/etc/docker/daemon.jsonに

"bip": "172.20.0.254/24"

を追記する。というだけです。
この方法が万人にうまくいくわけではないと思いますが、どんな手順で解決に至ったか解説します。
また、他の解決策と、問題の調査方法も書いてあるので、参考になればと思います。

WSL2 DNSがよく失敗する問題

WSL2が名前解決に失敗する。という問題は割と昔からありました。
名前解決とは、雑に言うと、ドメイン名からIPアドレスに変換することです。
適当なシェルで、pingを打つと、

$ ping google.com
PING google.com (142.250.206.238) 56(84) bytes of data.
64 bytes from kix06s10-in-f14.1e100.net (142.250.206.238): icmp_seq=1 ttl=116 time=4.51 ms

google.comが142.250.206.238に紐づけられていることが分かります。
これが、名前解決できないときは、

$ ping google.com
ping: google.com: Temporary failure in name resolution

という挙動になります。これが名前解決できていない状況です。しかし、これはインターネットにはつながってます。どういうことかというと、IP直指定でpingをすると

$ ping 142.250.206.238
PING 142.250.206.238 (142.250.206.238) 56(84) bytes of data.
64 bytes from 142.250.206.238: icmp_seq=1 ttl=116 time=4.75 ms

となります。したがって、IPアドレス直打ちでは動きます。これがインターネットはつながっているが、DNSが死んでいるために、ネットにつながらない状態です。

こういう状態はWSL2を使っているとしばしばあり、魔法の呪文で解決できることがありました。しかし、今回は違った。という話です。

/etc/resolv.confを変更?

こういう記事があります。

WSLの起動時にresolv.confを再生成されないようにする

大体のLinuxは/etc/resolv.confというところにDNSサーバーを記載します。

nameserver 8.8.8.8

という記述をすると、動いたりします。これは、8.8.8.8というDNSサーバーに先ほどのドメインの解決を依頼しています。

が、動かなかったというところがメインのお話。

Docker?

ふと不思議な挙動を見つけました。確かに、wsl --shutdownをした後、名前解決できているのです。しかし、自分が適当に作業をしているうちに、いつのまにか名前解決できなくなっているのです。

そこで、自分がやっていることを見ているとあることに気が付きます。

$ ping google.com #つながる
$ sudo service docker start
$ ping google.com #つながらない

という謎の挙動を見つけます。そう。Dockerを起動すると名前解決が出来なくなるのです。
ここで、Windows側のPowershellでIPがどうなっているのかみましょう。

$ ipconfig

Windows IP 構成

(中略)

イーサネット アダプター vEthernet (WSL):

   接続固有の DNS サフィックス . . . . .:
   リンクローカル IPv6 アドレス. . . . .: fe80::85e4:9ead:71bb:6d6f%28
   IPv4 アドレス . . . . . . . . . . . .: 172.17.0.1
   サブネット マスク . . . . . . . . . .: 255.255.240.0
   デフォルト ゲートウェイ . . . . . . .:

こうするとWSLには172.17.0.1割り当てられているのが分かります。ここで、WSL内の/etc/resolv.confを見ます。

/etc/resolv.conf
# This file was automatically generated by WSL. To stop automatic generation of this file, add the following entry to /etc/wsl.conf:
# [network]
# generateResolvConf = false
nameserver 172.17.0.1

なるほど、nameserverのアドレスとWindows側のIPv4は同一のものですね。
この時、ifconfigを見に行きます。

/etc/resolv.conf
$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.8.58  netmask 255.255.240.0  broadcast 172.17.15.255
        (略)

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        (略)

まぁ特に面白いところは無いです。では、sudo service docker startした後、ifconfigを見てみます。

$ ifconfig
br-fb02ec2e615e: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.18.0.1  netmask 255.255.0.0  broadcast 172.18.255.255
        (略)

docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.255.0  broadcast 172.17.0.255
        (略)

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.8.58  netmask 255.255.240.0  broadcast 172.17.15.255
        (略)

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        (略)

なんかネットワークインターフェースが増えていて、docker0に172.17.0.1のIPアドレスが振られています。

これで、原因が分かりました。

  1. WSL2のネットワークが172.17.0.1に振られている
  2. Dockerを起動すると、内部的なネットワークが172.17.0.1に割り当てられる。
  3. 名前解決しようとすると、nameserverがあるべき172.17.0.1がDockerに乗っ取られており、エラーとなる。

では、そもそもdocker0は何なのか?というのはドキュメントを参考にしてください。
https://docs.docker.jp/engine/userguide/networking/default_network/custom-docker0.html

で、このdocker0のIPアドレスの設定を変えるのは何か。というと、最初に書いた、

/etc/docker/daemon.jsonに

"bip": "172.20.0.254/24"

を追記する。という方法になります。これで、WSLに割り当てられているIPアドレスと、docker0のIPアドレスが競合しないようにすることで、ネットワークが動くように調整しています。

分からねぇよそんなもん・・・

追記:それでもつながらなかったときは・・・?

その後、ややこしい問題を引き起こしました。

$ ping google.com     #つながる
$ ping api.github.com #つながらない

Dockerのコンテナ内で特定のドメインだけつながらない。というパターンがありました。
これは、

/etc/docker/daemon.jsonに

"dns": ["8.8.8.8"]

を追記して、docker自体を再起動することで解決できるようになりました。
おそらくなんですが、WSL上でDockerを起動するとき、DockerはWSLのDNS設定を継承して、DNS設定を作ります。
しかし、WSLのDNS設定も特殊な構成になっている(Windowsとのブリッジなど)ので、その部分がうまく疎通取れないことがあるようです。そのため、/etc/docker/daemon.jsonに明示的にDNS設定を書き、コンテナはWSLのDNS設定ではなく、そちらの設定を見るように調整すると動きました。知るかよ。

感想

WSL2の魔法の呪文が微妙に伸びていたので、久々に書いてみました。
っていうか、いきなりdockerが動かなくなって、作業にならなくなったので、やらざるを得ませんでした・・・
今回、こういう方法を書いていますが、微妙にロジックが甘いところもあります。そもそもresolv.confに8.8.8.8を記述したタイミングで動く気がするんですよね。今回は、なぜ動かなかったのか・・・正直よく分からないです。
ちょこちょことWindows Insider Programで環境を破壊されたりするので、やはり玄人向きですね。私は2,3回ひどい目に遭いました。大体、WSLの機能自体は使えるようになったので、私はInsider Programは止めました。
こういう時に、技術力や自分の知識は試されるな・・・と、ひしひしと感じた事案でした。