WSL2 的第二版架构抛弃了初代系统调用翻译层,改用轻量级虚拟机运行完整 Linux 内核。这一变化让 Docker 在 Windows 上的运行方式发生了根本改变:容器不再依赖 Windows 侧的 Hyper-V 模拟,而是可以直接使用 WSL2 提供的 Linux 内核能力。不过很多开发者在配置时发现,WSL2 中 Docker 并非只有一种形态,Docker Desktop 的 WSL2 后端和发行版内原生 Docker Engine 在文件性能、守护进程位置、资源占用上差异很大。理解清楚这两类后端的区别,再根据自己的开发场景选择并优化,能避免后续大量权限和性能问题。

先分清 Docker 在 WSL2 里的两种后端模式
WSL2 本身是一个轻量级虚拟机,每个发行版都运行在同一个 Linux 内核之上,但各自拥有独立的用户态环境。Docker 的后端可以放在两个不同的位置:一种是 Windows 侧安装 Docker Desktop,启用 WSL2 backend 后,由 Docker Desktop 启动一个专门的 Linux 虚拟机或直接使用某个 WSL2 发行版运行 Docker 守护进程;另一种是在某个 WSL2 发行版内部直接安装 Docker Engine,把该发行版当作一台完整 Linux 主机来管理容器。
这两种模式的核心区别在于守护进程由谁管理。Docker Desktop 的 WSL2 backend 会自动将 Docker CLI 注入到所有已启用的 WSL2 发行版中,你只要在 WSL2 终端里执行 docker ps 就能连接到 Windows 侧托管的 Docker 引擎。这种方式适合需要 Docker Desktop 图形界面、Kubernetes 单机集群、或者同时使用多个发行版共享同一个 Docker 守护进程的场景。缺点是有时会出现 Windows 与 WSL2 之间的文件系统转发开销,以及 Docker Desktop 自身升级带来的兼容问题。
原生 Docker Engine 方案则更接近云服务器上的使用体验:所有容器、镜像、卷都存储在该 WSL2 发行版的 ext4 文件系统内,文件读写速度比通过 /mnt/c 共享盘要好很多。它不依赖 Docker Desktop,也不需要图形界面,适合追求轻量、习惯纯命令行的开发者。但需要注意的是,这种方式下 Docker 服务不会随 Windows 启动而自动运行,通常需要在 WSL2 内启用 systemd 或手动启动服务。
你可以先执行 wsl -l -v 查看当前发行版和 WSL 版本。如果版本号是 2,说明已经运行在轻量虚拟机上;如果还是 1,需要执行 wsl --set-version Ubuntu-22.04 2 升级。
wsl -l -v
在 WSL2 发行版内安装原生 Docker Engine
如果决定使用原生 Docker Engine,首先需要在一个 WSL2 发行版中安装 Docker。以 Ubuntu 22.04 为例,先更新软件包索引并安装必要的证书工具,然后添加 Docker 官方 GPG 密钥和 apt 源。下面这段命令可以直接复制执行,唯一需要注意的是其中 && 在 HTML 源码里已经做过转义,实际终端中会自动还原为命令连接符。
sudo apt-get update sudo apt-get install -y ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
安装完成后,Docker 服务并不会自动启动,尤其是 WSL2 默认没有启用 systemd。较新的 WSL2 版本支持 systemd,只需在发行版的 /etc/wsl.conf 文件中加入以下内容,然后重启该发行版。这样 Docker 服务就能像普通 Linux 主机一样由 systemd 管理,开机自启、状态检查、日志查看都更加自然。
[boot] systemd=true
保存后,在 Windows 的 PowerShell 中执行 wsl --shutdown 等待几分钟,再重新打开该发行版。此时可以运行 sudo systemctl status docker 查看服务状态。如果服务没有启动,执行 sudo systemctl enable docker --now 即可。为了让普通用户免 sudo 运行 docker 命令,建议把当前用户加入 docker 组,并重新登录 WSL2 终端。
sudo usermod -aG docker $USER
原生方案的优势在此时已经体现出来:所有容器数据都存储在 WSL2 发行版自己的 ext4 文件系统里,例如 /var/lib/docker。如果你在 /mnt/c 下托管代码,再通过 bind mount 挂载到容器,性能会显著低于把代码放在 Linux 文件系统内。下一节会具体说明如何迁移数据目录并配置镜像加速。
配置镜像加速、数据目录与资源限制
国内网络环境下直接访问 Docker Hub 经常出现超时或拉取速度极慢,镜像加速几乎是必配项。Docker 的守护进程配置统一放在 /etc/docker/daemon.json,修改后需要重启 docker 服务。下面是一份常用配置,其中 registry-mirrors 可以使用你实际能访问的镜像源,data-root 可以指向 WSL2 发行版内的某个目录,例如家目录下的 docker-data。注意不要把它设置到 /mnt/c 下面,那样会再次经过 Windows 文件系统转发,失去原生性能优势。
{
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com"
],
"data-root": "/home/username/docker-data",
"storage-driver": "overlay2",
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}修改完 daemon.json 后,执行 sudo systemctl restart docker 使配置生效。如果之前已经拉取过镜像,这些镜像不会自动迁移到新数据目录,需要手动停止容器、导出镜像或直接重新拉取。对于新环境来说,提前设置好数据目录可以避免后续磁盘空间不足。
WSL2 的虚拟机默认允许使用宿主机 50% 到 80% 的内存,当 Docker 容器数量增加时,vmmem 进程的内存占用会非常可观。可以通过 Windows 用户目录下的 .wslconfig 文件限制 WSL2 的整体资源分配,例如限制内存为 4GB、处理器为 2 核、交换空间为 8GB。这个文件位于 C:\Users\你的用户名\.wslconfig,修改后需要在 PowerShell 中执行 wsl --shutdown 才会完全生效。
[wsl2] memory=4GB processors=2 swap=8GB localhostForwarding=true
除了内存和 CPU,文件系统性能也值得专门调整。如果你必须使用 Windows 侧的项目目录,建议将项目复制到 WSL2 内部,而不是通过 /mnt/c 直接挂载。对于 Docker Desktop 用户,可以在设置中关闭不需要的发行版集成,减少不必要的文件监听开销。
Docker Desktop 与 WSL2 实例的交互及端口映射
如果你已经安装了 Docker Desktop,并希望让 WSL2 发行版使用它的后端,操作会简单很多。打开 Docker Desktop 设置,进入 Resources 页面,确认 WSL integration 中已勾选目标发行版。之后在这些发行版内部输入 docker version,客户端会自动连接 Docker Desktop 托管的守护进程,无需额外配置。这种方式下,所有 WSL2 发行版共享同一套镜像和容器,适合快速切换不同 Linux 环境的开发场景。
如果需要在 Windows 侧直接操作 Docker,可以使用 docker context 管理不同的守护进程端点。执行下面的命令可以查看当前上下文,并根据需要切换。例如在 PowerShell 中切换回 Windows 默认的 Docker Desktop 后端,或切换到某个 WSL2 内的原生 Docker Engine。
docker context ls docker context use default
端口映射方面,无论使用哪种后端,容器映射到 0.0.0.0 的端口通常都可以通过 Windows 的 localhost 访问,因为 WSL2 默认启用了 localhost 转发。但如果遇到 Windows 侧无法访问容器端口的情况,可以先确认容器是否监听在 0.0.0.0,再检查 WSL2 的 IP 地址。对于原生 Docker Engine,如果没有 localhost 转发,还可以使用 socat 将 Docker 的 Unix socket 转发为 TCP 端口,方便从 Windows 侧远程调用。
sudo apt-get install socat -y socat TCP-LISTEN:2375,fork,bind=0.0.0.0 UNIX-CONNECT:/var/run/docker.sock &
不过开启 TCP 2375 端口存在安全风险,只建议在可信局域网或本机调试时短时间使用。更安全的做法是继续使用 Docker Desktop 的集成能力,或者通过 SSH 登录到 WSL2 后执行 docker 命令。
常见故障排查与性能优化思路
权限错误是最常见的问题之一。如果你在 WSL2 内执行 docker ps 时看到 Got permission denied while trying to connect to the Docker daemon socket,通常是因为当前用户没有加入 docker 组,或者重新登录后权限仍未刷新。可以先执行 groups 确认是否包含 docker 组,没有的话重新运行 sudo usermod -aG docker $USER,然后彻底退出 WSL2 终端再重新进入。
另一个高频问题是服务无法启动。如果在原生 Docker Engine 模式下,sudo service docker start 提示失败,先检查 WSL2 是否已经启用 systemd。如果未启用,可以继续使用 service 命令,但开机不会自启。如果启用了 systemd,则优先使用 systemctl 管理服务。运行 sudo journalctl -u docker 可以查看详细日志,定位是 iptables 冲突、存储驱动不兼容还是内核模块缺失。
性能优化方面,尽量不要在 /mnt/c 目录下构建镜像或运行容器。WSL2 访问 Windows 文件系统需要经过 9p 协议转发,大量小文件读写会非常慢。把代码放到 ~/projects 这类 Linux 原生目录下,构建速度通常能提升数倍。对于已经在 Windows 侧用 VS Code 打开的项目,可以通过 Remote-WSL 扩展直接编辑 WSL2 内的文件,既保留图形编辑体验,又避免跨文件系统开销。
最后,定期清理无用的镜像、容器和卷也能显著改善 WSL2 的磁盘占用。因为 WSL2 虚拟磁盘通常只会增长不会自动收缩,即使容器删除了,虚拟磁盘文件也可能不变小。可以使用 docker system prune -a --volumes 清理无用数据,再配合 wsl --shutdown 让虚拟磁盘有机会被压缩。如果使用 Docker Desktop,可以在设置中查看磁盘使用情况并执行清理。