今週の計算機トラブル対処記録

なんだか今週は重めの計算機トラブルが二つ発生して直すのに手間取ったので、対処方法を記録しておきます。 一つ目は、カーネルパニックが出てしまうという問題で、二つ目はWSLからインターネットにつなげないというものです。

カーネルパニック

症状

Ubuntuを起動しようとすると、TuxLinuxのペンギン)のAAと感嘆符とともに、カーネルパニックを起こしたとメッセージが表示されます(写真からの書き起こしなのでスペースの数とかが違うかもしれません。以下同様です)。

   .--.         _
  |o_o |       | |
  |:_/ |       | |
 //   \ \      |_|
 (|     | )     _
/'\_   _/`\    (_)
\___)=(___/
                  KERNEL PANIC!
           Please reboot your computer.
VFS: Unable to mount root fs on unknown-block(0,0)

SSDが壊れてしまったとかだとすると面倒なことになったようです。

解決手順

Ubuntu再起動時にKernel Panicを起こした #OS - Qiitaを参考にしました。 最初はGRUBのシェル風コマンドラインから頑張ろうとしたのですが、USB接続キーボードの反応が悪すぎて、1文字打ったつもりが2文字になるわバックスペースキーを押すと2文字分消えるわで、思ったようにコマンドが打てません。 画面はちゃんと出ている感じがするので、グラフィックドライバの問題ではなさそうです。 そこで、この手順(上記Qiita記事の1の手順)はスキップして、このUbuntuをインストールするときに使ったLive USBをつないで、何が起こっているかを確認しました。 ライブセッションでは、USB接続キーボードの反応はまともでした。 画面は黄色と白になっていて非常に見づらいですが、これは前にインストールした時もそうだったので今回の症状とは無関係と判断しました*1

まず、lsblk -fをすると、以下のように出てきました。

nvme0n1

├─nvme0n1p1
│    vfat   FAT32

└─nvme0n1p2
     ext4   1.0

ext4になっているのがUbuntuのルートパーティションらしいので、nvme0n1p2をマウントします。

sudo mount /dev/nvme0n1p2 /mnt

その後、システムディレクトリをマウントし、sudo chroot /mntでルート環境に入り、update-initramfs -u -k allをやっても直りませんでした(上記Qiita記事の2の手順が失敗)。

その次に、上記Qiita記事で成功したと書かれていた4の手順の最初のコマンド(ls /mnt/boot/)を実験してみました。

System.map-6.11.0-24-generic  memtest86+ia32.bin
System.map-6.14.0-28-generic  memtest86+ia32.efi
config-6.11.0-24-generic      memtest86+x64.bin
config-6.14.0-28-generic      memtest86+x64.efi
efi                           vmlinuz
grub                          vmlinuz-6.11.0.24-generic
                              vmlinuz-6.14.0-28-generic
initrd.img-6.11.0-24-generic  vmlinuz.old
initrd.img.old

grubの下に謎のスペースがありますが、ここには何らかの文字が出力されている可能性があります。おそらく、画面が黄背景白文字になっていることが原因で読めない配色で出力されているのでしょう。位置的には、grub2でしょうか。

これを見ると、上記Qiita記事と同様、initrdのバージョンが古い物しかない(他のは6.14.0-28-genericがあるのに、initrdだけそれがない)ことがわかります。 アップデートが途中で終わっちゃったとかでしょうか。 いずれにせよ、上記Qiita記事と類似の症状ということで、同じ手順で直せる可能性が高そうです。 そこで、以下の手順でinitrd.img-6.14.0-28-genericを作成しました。

sudo mount --bind /dev /mnt/dev
sudo mount --bind /proc /mnt/proc
sudo mount --bind /sys /mnt/sys
sudo mount --bind /run /mnt/run

update-initramfs -c -k 6.14.0-28-generic
update-grub

この後、このライブセッションを終了し、USBメモリを外してから電源を入れれば、無事に起動することができました。

WSLからインターネットにつながらない

症状

Windowsからは普通にインターネットにつながるのに、WSLからはインターネットにつながらなくなりました。

$ ping 8.8.8.8
ping: connect: Network is unreachable

解決までに試した手順(失敗したもの)

WSL側のDNS自動生成を有効化

まず、/etc/resolv.confがどうなっているかを調べました。 ChatGPTに聞いたところ、以下のどちらかを切り分けましょうと言われました。

  • /run/systemd/resolve/stub-resolv.confへのシンボリックリンクか、/run/systemd/resolve/resolv.confへのシンボリックリンク(systemd-resolved方式)
  • リンクではないファイルであるか、または、そもそも存在しない(WSL方式)

しかし、/etc/resolv.conf/mnt/wsl/resolv.confへのシンボリックリンクになっています。これは新しめの方式だそうです。

まず、wsl --updateを行って、最新のバージョンにしました(2.3.26.0→2.5.10.0)。 これにより、dnsProxyやdnsTunnelingが規定で有効になるので、うまくいくはずとのことで次の手順を試しましたが、うまくいきませんでした。

$ sudo chattr -i /etc/resolv.conf 2>/dev/null || true
$ sudo rm -f /etc/resolv.conf
PS> wsl --shutdown

systemd-resolvedに任せる

WSLの自動生成を使わないで、Ubuntuのsystemd-resolvedに作らせる方式を試しました。 まず、/etc/wsl.confに以下を書き加えました。

[network]
generateResolvConf = false

つづいて、以下のコマンドを実行しましたが、まだ駄目です。

$ sudo rm -f /etc/resolv.conf
$ sudo ln -s /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
$ sudo systemctl enable --now systemd-resolved
PS> wsl --shutdown

resolvedが上流DNSを把握できるようにする

以下を実行するように促されましたが、そもそもip route show defaultが何も返さないようです。

# 1) 既定経路のゲートウェイIPを取得(だいたい 172.29.x.1)
GW=$(ip route show default | awk '{print $3}')
IF=$(ip route show default | awk '{print $5}')   # だいたい eth0

# 2) そのゲートウェイを上流DNSとして設定し、既定の検索ルートにする
sudo resolvectl dns "$IF" "$GW"
sudo resolvectl domain "$IF" "~."
sudo resolvectl flush-caches

# 3) 動作確認
getent hosts example.com

デフォルト経路を復旧

以下を実行するように促されましたが、vEthernet (WSL)はないようです。 また、Restart-Service : サービス名 'LxssManager' のサービスが見つかりません。というエラーも出ます。

wsl --shutdown
# vEthernet (WSL) を再起動
Get-NetAdapter -Name "vEthernet (WSL)" | Restart-NetAdapter -Confirm:$false
# WSL のサービスも再起動(念のため)
Restart-Service LxssManager

ネットワークアダプターをすべて表示

PowerShellで以下のようにやると、ネットワークアダプターを全て表示することができるそうです。

# vEthernet 系や Hyper-V 仮想NICを一覧(非表示も含む)
Get-NetAdapter -IncludeHidden |
  Where-Object { $_.Name -match '^vEthernet' -or $_.InterfaceDescription -like '*Hyper-V Virtual Ethernet Adapter*' } |
  Sort-Object Name |
  Format-Table -Auto Name, Status, InterfaceDescription

すると以下のようになりました。やはりWSL用のネットワークアダプターがないのがおかしそうです。

vEthernet (Default Switch)   Up     Hyper-V Virtual Ethernet Adapter
vEthernet (内部仮想スイッチ) Up     Hyper-V Virtual Ethernet Adapter #3

WSLの中から見てみると、以下のようになっていて、eth0がないのがおかしそうです。

$ ip -br a
lo               UNKNOWN        127.0.0.1/8 10.255.255.254/32 ::1/128
enPXXXXp0s0     DOWN           XXX.XXX.XXX.XXX/32
docker0          DOWN           172.17.0.1/16
$ ip r
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown

※XXXXは数値、XXX.XXX.XXX.XXXは特定のグローバルIPアドレス

mirroredの影響を排除

次を促されましたが、これはうまくいきませんでした。

notepad $env:USERPROFILE\.wslconfig

を管理者PowerShellから実行して以下を書き込む

[wsl2]
networkingMode=NAT
dnsProxy=true

管理者PowerShellから以下をやる

# 1) いったん WSL を止める
wsl --shutdown

# 2) HNS と vmcompute を再起動(LxssManager は最新版WSLでは無いことがあるのでスキップでOK)
Stop-Service hns -Force
Start-Service hns
Restart-Service vmcompute

# 3) 壊れた WSL NAT があれば削除(無ければスキップされます)
Get-NetNAT | Where-Object Name -eq 'WSL' | Remove-NetNAT -Confirm:$false

# 4) 既存の HNS ネットワークに 'WSL' があれば削除(Default Switch は触らない)
$wslNet = Get-HnsNetwork | Where-Object Name -eq 'WSL'
if ($wslNet) { Remove-HnsNetwork -Id $wslNet.Id }

# 5) WSL を一度だけ起動してネットワークを再生成させる
wsl -e /bin/true

直った手順

Windows側を修正

以下を実行した後、Windowsマシンを再起動したところ、WSLからインターネットにつながりました。 単にWindowsマシンを再起動するだけでも直ったのかもしれず、これを実行したことが決め手となったかはわかりません。

wsl --shutdown
DISM /Online /Enable-Feature /FeatureName:VirtualMachinePlatform /All /NoRestart
DISM /Online /Enable-Feature /FeatureName:Microsoft-Windows-Subsystem-Linux /All /NoRestart
netsh winsock reset
netsh int ip reset

WSLの自動生成に戻す

試行錯誤の途中で追加された次の記述を/etc/wsl.confから取り除きました。

[network]
generateResolvConf = false

その結果、自動生成されるresolv.confは以下のようになり、正しくなりました。

$ cat /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 10.255.255.254
search XX.ad.jp

※XX.ad.jpは契約しているプロバイダのドメイン

WSLの中からは次のように見えていて、ちゃんとeth0が出現しています。

$ ip -br a
lo               UNKNOWN        127.0.0.1/8 10.255.255.254/32 ::1/128
eth0             UP             172.22.XXX.XXX/20 fe80::215:5dff:feXX:XXXX/64
docker0          DOWN           172.17.0.1/16
$ ip r
default via 172.22.32.1 dev eth0 proto kernel
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
172.22.32.0/20 dev eth0 proto kernel scope link src 172.22.XXX.XXX

XX:XXXXはWSLを起動するたびに変わります(fe80::はリンクローカルアドレスを意味していて、その後の215:5dff:feは製造元がマイクロソフトであることを意味しているので、WSLであればここは変わらないはずです)。 XXX.XXXも変わりうるようです(172.22.32.0/20を検索するとWSL関連しか出てこないので、この範囲で変わるのでしょうか)。

PowerShellから確認しても、ちゃんとWSLの仮想スイッチが出現していました。

PS C:\WINDOWS\system32> # vEthernet 系や Hyper-V 仮想NICを一覧(非表示も含む)
PS C:\WINDOWS\system32> Get-NetAdapter -IncludeHidden |
>>   Where-Object { $_.Name -match '^vEthernet' -or $_.InterfaceDescription -like '*Hyper-V Virtual Ethernet Adapter*' } |
>>   Sort-Object Name |
>>   Format-Table -Auto Name, Status, InterfaceDescription

Name                               Status InterfaceDescription
----                               ------ --------------------
vEthernet (Default Switch)         Up     Hyper-V Virtual Ethernet Adapter
vEthernet (WSL (Hyper-V firewall)) Up     Hyper-V Virtual Ethernet Adapter #2
vEthernet (内部仮想スイッチ)       Up     Hyper-V Virtual Ethernet Adapter #3

まとめ

計算機トラブルを二つ片付けました。 生成AIを使うと対話的に問題を解決できるので、なかなか便利になりました。

一つ目の問題は結果的にはQiita記事に助けられたわけですが、完全に同じ症例だったわけではないので、その手順が応用可能な範囲に関する情報も有用かなと思って書きました。 二つ目の問題は生成AIの言うがままにコマンドを実行していったら直ったわけで、その情報はきっとインターネットのどこかにあるのでしょう。 生成AIの出力を理解しないままに貼り付けて記事にするのはどうなのかなとも思いましたが、実際に試してうまくいったとかうまくいかなかったとか、どういう出力が得られるのが普通なのかとか、そういった情報はあれば役に立つこともあるかなと思って書きました。

*1:今ウェブ検索してみると直し方がたくさん出てきました。