Tags
Cover Image

WSL2来了!但是能正常使用并不简单

Zor Chen
Zor
WSL2终于随Windows 10的版本2004发布了,Windows真的或成最好的Linux发行版🤣。 实际上手使用后,发现目前的WSL2版本里还有一些比较影响使用的坑,比如配置代理服务器。 在这里给大家带来一种自动化的代理配置方法,并指出一些WSL2在使用过程中可能会遇到的问题。

为什么要升级到 WSL2

用过 WSL 第一代的朋友一定都遇到过一些比较蛋疼兼容性的问题,这些问题是由于 WSL 并不“完整”导致的。说到底 WSL 还只是一个用于访问 Linux 环境的兼容层,并没有实现 Linux 内核的完整功能。比如,我遇到了这些情况:

  • 无法启动Go的 Debug 进程,导致调试Go程序很麻烦
  • 需要在 WSL 下单独安装一套 Docker 引擎,并进行额外配置
  • 在 WSL 下启动 VS Code 后,会出现目录中文件被占用的情况,导致无法重命名,必须要先退出 VS Code
  • 无法正常使用 Linux 下全部命令,比如netstat

而 WSL2 基于Hyper-V功能的子集提供了“真正的 Linux 内核”,因此上述问题也被解决了。同时,WSL2 也 支持 Windows 10 家庭版,因此之前家庭版不能用 Docker Desktop 的情况也已经成了历史。 这对于习惯使用 Linux 开发,但是又需要 Windows 玩游戏,同时不希望用盗版的有志青年们来说简直是天大的福音。

升级到 WSL2

下面的所有命令均需在有管理员权限的 PowerShell 中执行

安装 WSL2 功能模块

  1. 如果之前没有用过 WSL,那么首先需要安装 Windows 10 的 WSL 功能:
    Terminal window
    dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
  2. 安装 WSL2 功能模块。
    Terminal window
    dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
  3. 上面的命令执行完毕后,重启电脑完成 WSL2 的安装。

安装 Linux 内核

  1. 至此为止我们只是开启了 WSL2 的功能,但是还需要安装对应的 Linux 系统内核。 请按照 官方文档 中的说明下载并安装对应软件包。
  2. 在 PowerShell 中,将 WSL 默认版本设置为2,这样之后安装的 Linux 发行版才会都安装在 WSL2 中。 若没有安装上一步中的内核,此时系统会提醒你到对应的网址下载内核并安装。
    Terminal window
    wsl --set-default-version 2

安装 Linux 发行版

  1. 备份旧版 WSL 中的重要文件,比如 shell 配置文件等。卸载旧版 Linux 发行版,然后从 Microsoft Store 中重新下载安装 Linux 发行版, 比如 Ubuntu 18.04。

    虽然 WSL 提供了版本转换工具(例如可以执行,wsl --set-version Ubuntu-18.04 2),但在实际安装过程中,我的电脑转换了 3 个小时都没有成功,而且内存占用一直在 98%以上(我是 32G 内存),同时还吃掉了我所有的 C 盘空间,导致我一度无法工作。

    这是因为这个转换工具会把原发行版中的所有文件都重新复制到一个新的虚拟硬盘中(ext4 格式),而且 WSL2 对 NTFS 文件系统访问速度很慢, 耗时可想而知。因此最简单的办法,就是干掉原发行版,然后再重装一个新的。

  2. 到此为止大功告成,可以使用wsl命令启动 WSL2 了,你会发现 WSL2 的启动速度有显著提高。

    在 PowerShell 中使用如下命令可以检查是否安装成功:

    Terminal window
    wsl --list --verbose

    从输出信息中可以看到,Ubuntu-18.04的版本确实已经是2

    Linux发行版的WSL版本号Linux发行版的WSL版本号

使用新版 Docker 后端引擎

Windows 下的 Docker Desktop 一直以来都是我心中的痛。为了使用这个完整版的 Docker 引擎,我不得不:

  • 使用更贵的 Windows 10 专业版
  • 进行更复杂的 Docker 引擎配置
  • 忍受 1 分钟左右的 Docker 启动时间

而随着 WSL2 的发布,Docker Desktop 也发布了支持 WSL2 后端的新版本,上述问题都不是问题了。

如果你之前一直在用 Docker Desktop 并设置了开机启动的话,会发现在安装 WSL2 并重启电脑后,Docker Desktop 会 自动弹出一个提示框,告诉你监测到系统中已安装了 WSL2,询问是否开启 WSL2 后端引擎。此时可以点 Enable 直接启用。

如果没有在上面的情况下启用,也可以打开 Docker Desktop 的设置界面,并在左侧导航栏中进入Resources>WSL INTEGRATION 面板,然后开启界面中的开关,如下图所示:

开启Docker Desktop中的WSL2支持开关开启Docker Desktop中的WSL2支持开关

保存设置并重启 Docker,然后进入 WSL2,你会发现docker命令和docker-compose命令都可以正常使用了,无需任何额外操作!🍻️

文件访问慢 & 无法 Hot-Reloading

安装 WSL2 后我赶紧打开本博客项目,并在开发环境下继续未完成的工作。但是发现:

  • 文件系统 I/O 巨慢,Gatsby 开发环境的编译时间大大增长,git的反应也很慢
  • 更致命的是,在编辑过程中我发现保存文件后系统没有监测到文件变化,因此也没有进行 Rebuild 的过程。 这意味着我每进行一个改动,都需要完整重新编译整个项目才能看到效果。

这事儿真心忍不了。

在进行了一番调(sou)研(suo)后,发现这些都是 WSL2 的已知问题,主要还是由于 Windows 和 Linux 两种文件系统的兼容性尚未被很好的处理。 详细的讨论可参见#4197#4739这两个 issue。

目前的解决办法只有一个:将项目文件移动到 Linux 文件系统下

好吧。🤷‍♂️

配置代理服务器正常科学上网

另一个巨坑,也是本文重点所在,是在 WSL2 下如何连到 Windows 的代理服务器来走向世界。 这个问题耗费了我几乎整个下午的时间。

获取 Windows 主机 IP

你会发现 WSL2 不再能正常访问localhost的各种端口了。比如我们在 Windows 下启动一个简单的 HTTP 服务器:

Terminal window
python -m http.server

然后在 WSL2 下尝试访问:

Terminal window
curl http://localhost:8000

结果是没有响应,连接直接超时或关闭。这时候我已经疯了,于是又开始了疯狂的探(sou)索。 在这篇博客官方文档中 我找到了答案:新的 WSL2 架构会为 Linux 系统分配一个虚拟网卡,Linux 虚拟机与 Windows 组成了一个局域网,因此 若想在 Linux 下访问 Windows 的服务,必须要使用 Windows 的主机地址。

通过查看 Linux 下的 DNS 设置可以得到 Windows 主机的地址:

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 192.168.112.1

上面的输出中,nameserver之后的部分就是 Windows 主机 IP。

代理服务器自动设置工具

Windows 主机 IP 在每次重启后都会变化,因此我写了 一个 bash 脚本以便在 shell 启动时自动设置代理服务器,源码在此,也可以使用下面的命令直接获取安装:

中文版:

Terminal window
curl -o wslproxy -L https://git.io/Jfo6z
sudo bash ./wslproxy install

英文版:

Terminal window
curl -o wslproxy -L https://git.io/Jfo62
sudo bash ./wslproxy install

然后根据命令行提示使用即可。

Windows 防火墙设置

在设置代理服务器后,我发现仍然无法正常连接 Windows 上的服务。研究很长时间后才发现,

Windows 防火墙居然拦住了来自 WSL 的请求!!!

佛了。Windows 认为 WSL 的虚拟网卡是公用网络,因此任何阻止公用网络入站请求的防火墙策略都会拦住 WSL 发过来的请求。

知道了问题所在就好办了。

  1. 首先我们使用 PowerShell 命令行创建一条给 WSL 网卡的防火墙策略:

    Terminal window
    New-NetFirewallRule -DisplayName "WSL" -Direction Inbound -InterfaceAlias "vEthernet (WSL)" -Action Allow
  2. 然后进入 Windows 的防火墙和网络保护设置,在界面下方点击高级设置,提权后会打开高级安全 Windows Defender 防火墙窗口。

    打开放火墙高级设置打开放火墙高级设置

  3. 高级安全 Windows Defender 防火墙中选择入站规则,并在右侧找到你使用的代理软件,然后禁用对应的阻止公用网络的规则, 比如我的是这样:

    防火墙规则设置防火墙规则设置

  4. 防火墙规则设置完毕后,应该已经可以在 Linux 中正常连接到代理服务器了。


参考资料