VPN 开着时 WSL 冷启动断网,我最后换成了 VirtioProxy
原创 · 约 12 分钟阅读 · 阅读 --
最后更新于

VPN 开着时 WSL 冷启动断网,我最后换成了 VirtioProxy

作者: Alex Xiang


最近碰到一个很挑启动顺序的 WSL 故障:Windows 的 VPN 已经运行时,再启动 WSL,Ubuntu 完全没有网络;先启动 WSL,再连接 VPN,却一切正常。

这种问题最容易把人带到 /etc/resolv.conf。但这次 DNS 只是最后倒下的那一环,真正失败的是 WSL 虚拟网络本身。

WSL 冷启动时 Mirrored 与 VirtioProxy 两条网络路径

先看接口和路由,不要先改 DNS

故障出现后,我在 Ubuntu 里先跑了四组检查:

ip -brief address
ip route
ls -l /etc/resolv.conf
getent hosts github.com
curl -I --connect-timeout 10 https://github.com

结果很明确:除了 lo 没有其他可用接口,也没有默认路由;WSL 自动维护的 DNS 文件不存在,域名解析和 HTTPS 自然都失败。

如果只是 DNS 配置错了,通常还能看到以太网接口和默认路由。这里连二层、三层网络都没建起来,反复重写 resolv.conf 不可能修好。

Windows 侧同时记录了 WSL 网络配置错误:

CreateInstance/CreateVm/ConfigureNetworking/0x8007054f

把两边证据放在一起,故障链条就清楚了:

Windows 网络环境已变化

WSL 冷启动并创建 Mirrored 网络

虚拟网络端点配置失败

WSL 退化到 networkingMode=None

Ubuntu 只有 lo,没有默认路由和 DNS

这也是为什么启动顺序会改变结果。WSL 先启动时,虚拟网络已经完成初始化;反过来,Windows 网络栈先进入另一种状态,Mirrored 端点的创建路径便可能触发兼容性问题。

升级 WSL 有必要,但不能把升级当成结论

排查时本机 WSL 是 2.6.1.0。我先执行了常规升级:

wsl --update

常规下载没有顺利完成,最后改用 WSL 官方 GitHub Release 提供的 x64 MSI。手工安装前,我先验证了 Authenticode 签名:

Get-AuthenticodeSignature .\wsl.2.7.10.0.x64.msi |
    Select-Object Status, SignerCertificate

签名状态为 Valid,签名者为 Microsoft Corporation,才继续安装。完成后关闭全部 WSL 实例并核对版本:

wsl --shutdown
wsl --version

WSL 升级到了 2.7.10.0,Linux 内核、WSLg 和远程桌面组件也一起更新。对应版本可以在 WSL 2.7.10 Release 核对。

但升级后,VPN 已运行时的 Mirrored 冷启动仍会失败。这个结果反而很有价值:旧版本因素被排除了,问题更集中地落在本机网络环境与 Mirrored 初始化路径的组合上。

升级是基础动作,不是“已经修好”的同义词。每次升级后都应该重新跑原始复现步骤。

检查 .wslconfig 的层级

继续检查 %USERPROFILE%\.wslconfig 时,我还发现了一个容易被忽略的问题:sparseVhdautoMemoryReclaim 被放进了 [wsl2],其中一个配置还有重复项。

它们应该位于 [experimental]。整理后,最终配置是:

[wsl2]
dnsTunneling=true
firewall=true
networkingMode=virtioproxy

[experimental]
sparseVhd=true
autoMemoryReclaim=dropcache

这里没有照抄原环境的应用层网络配置。那部分与具体 Windows 软件和安全策略有关,不适合当成通用答案。真正需要保留的结论只有一个:网络模式、DNS、应用层访问是三件不同的事,应该分开验证。

修改 .wslconfig 后必须执行:

wsl --shutdown

只是关闭终端窗口不够。只要还有发行版或 Docker Desktop 后端在使用 WSL,轻量虚拟机就可能没有真正退出,新配置也不会完整生效。

为什么这台机器最后选 VirtioProxy

Microsoft 的 WSL 网络文档 将 NAT、Mirrored、DNS tunneling 等能力分开说明。Mirrored 的优势很明显:IPv6、局域网访问、Windows 与 WSL 之间的 localhost 互通,以及更好的复杂网络兼容性。

但“理论上更合适”不代表在每台机器上都一定稳定。这台机器的 Mirrored 冷启动路径已经连续复现失败,而 VirtioProxy 不走同一套端点创建路径。切换后,即使 Windows 网络环境已经变化,Ubuntu 仍能正常获得:

  • 非回环网络接口;
  • 指向虚拟网关的默认路由;
  • 自动生成的 DNS 配置;
  • 正常的域名解析和 HTTPS 访问。

这里的选择不是说 VirtioProxy 普遍优于 Mirrored,而是根据故障边界做取舍:先恢复可重复的基础联网,再单独处理某些需要 localhost 互通或特殊路由的应用。

验证要覆盖多次冷启动

网络初始化问题带有明显时序性,只成功一次没有说服力。我保持 Windows 网络环境不变,每轮都关闭 WSL,再从零启动 Ubuntu:

wsl --shutdown
wsl -d Ubuntu -- bash -lc '
  test "$(find /sys/class/net -mindepth 1 -maxdepth 1 | wc -l)" -gt 1 &&
  ip route | grep -q "^default " &&
  getent hosts github.com >/dev/null &&
  curl -fsSI --connect-timeout 10 https://github.com >/dev/null
'

这段测试覆盖了四层:

层次验证内容
网络设备lo 外还有其他接口
三层路由存在默认路由
名称解析getent 能解析域名
真实流量HTTPS 请求能完成 TCP 与 TLS 握手

连续三轮全部通过。之后又验证 Docker Desktop 服务端能正常响应,WSLg 相关的磁盘日志问题也没有复发。

我没有把 ping 当作唯一指标。ICMP 可能被策略屏蔽,而 HTTPS 更接近日常开发流量,也能同时覆盖路由、DNS、TCP 和 TLS。

这次排障留下的几个判断顺序

以后再碰到 WSL 断网,我会保持下面的顺序:

先看网卡,再看路由

只有 lo 时,不要先折腾 DNS。网络设备都没有,解析配置只是下游症状。

再看 DNS,最后测 HTTPS

接口和默认路由存在,才有必要检查 /etc/resolv.confgetent 和 DNS tunneling。最后用一个真实 HTTPS 请求收尾。

配置改完必须冷启动

.wslconfig 不是热加载配置。Docker Desktop、编辑器或后台服务都可能让 WSL 保持运行,验证前要确认系统虚拟机真正退出。

把网络模式和应用访问分开

VirtioProxy 解决了“WSL 能否建立基础网络”,不代表所有 Windows 本地服务都会自动对 WSL 可达。后者需要按具体服务的绑定地址、防火墙和安全边界单独设计。

结语

这次故障最有用的信号不是某条神奇配置,而是“VPN 先启动才失败”这个时序特征。它把问题从普通 Linux DNS 错误,指向了 WSL 虚拟网络初始化。

最终方案是在升级 WSL、修正配置层级后,根据实测把网络模式改为 VirtioProxy,并以三次冷启动、接口、路由、DNS、HTTPS 和 Docker 回归测试确认结果。

排查 WSL 网络时,先问“网卡和默认路由有没有”,比先问“该把 DNS 改成多少”有效得多。

打开原图 ↗