Windows下WSL2原生Docker安装实战:从环境配置到网络故障排查

发布时间:2026/9/15 6:26:46
Windows下WSL2原生Docker安装实战:从环境配置到网络故障排查
1. 为什么我选择WSL2原生Docker而不是继续用Docker Desktop先说结论如果你只是想在Windows上跑几个容器做开发测试Docker Desktop足够省心但如果你需要在Linux环境下做完整的技术验证、追求更贴近生产环境的运行方式或者受不了Docker Desktop那套虚拟化层带来的资源消耗和许可限制那么WSL2里装原生Docker Engine是更值得走的一条路。WSL2本质上是一个轻量级虚拟机运行的是完整Linux内核。Docker Engine原生跑在WSL2的Ubuntu里流程和你在云服务器、虚拟机、裸机Linux上部署Docker几乎一模一样。这意味着你学的、配的、踩的坑全都直接映射到生产环境不会有Docker Desktop那种基于Windows的Hyper-V虚拟化层“包了一层”的隔阂感。我身边不少朋友问我Docker Desktop不是也能在WSL2后端跑吗确实能但它和WSL2里原生装Docker是两回事。Docker Desktop用的是它自己的WSL2发行版docker-desktop以及一套k8s编排全家桶覆盖场景更广但同时也更重。还有一个现实因素Docker Desktop对大型企业用户开始收费后很多工程师都在寻找替代路径。原生Docker Engine配合WSL2正是最主流、最干净的替代方案之一。适合谁看这篇文章已经在Windows上装了WSL2但还在纠结怎么跑Docker的人用Docker Desktop觉得卡、觉得资源占用高、觉得绑定太死的人想从零开始在Windows 10/11上完整搭一套Linux Docker开发环境的人只想用命令行装环境不想依赖GUI配置的“键盘流”用户这篇文章覆盖从WSL2环境准备、Ubuntu安装、Docker Engine安装、systemd配置、网络问题排查到磁盘膨胀优化的完整链路。全部步骤基于Ubuntu 22.04/24.04 LTS版本实测Windows侧是Windows 11 22H2以上或Windows 10 21H2以上建议。2. 装WSL2的硬性前置虚拟化固件、Windows功能和内核更新包2.1 先确认虚拟化到底开没开跳过这一步的人大概率会在后边遇到一个经典报错WSL2 无法启动因为此计算机上未启用虚拟化。这行字不知道劝退了多少新手。其实排查起来很简单。打开任务管理器切到“性能”选项卡看CPU一栏。如果“虚拟化”显示“已启用”说明BIOS/UEFI层面的虚拟化Intel VT-x或AMD-V是开着的。如果显示“已禁用”就需要重启电脑进BIOS设置不同主板的入口不一样常见的是开机按Del或F2。在BIOS里找到类似“Intel Virtualization Technology”“SVM Mode”“VT-x”的选项设为Enabled。保存重启。还有一种情况是虚拟化正常但Windows的“虚拟机平台”功能没开也会导致同样的问题。这种情况直接往下走。2.2 开启Windows功能面板里那两个关键项在Windows搜索框输入“启用或关闭Windows功能”打开后勾选适用于Linux的Windows子系统虚拟机平台勾完点确定系统会提示重启。这里要注意这两项缺一不可。WSL1模式下只需要第一项但WSL2依赖Hyper-V虚拟化平台所以第二项也必须开。如果你之前装过WSL1升级到WSL2时忘开“虚拟机平台”就会一直报错。重启后用管理员身份打开PowerShell确认一下WSL版本wsl --status如果默认版本不是2执行wsl --set-default-version 2此时如果提示需要安装WSL2内核更新去微软官网搜“WSL2 Linux kernel update package”下载对应的MSI包手动安装。这个包的作用是把WSL2的Linux内核从Windows可选功能里独立出来方便单独更新。国内网络下载这个包偶尔会比较慢多试几次或换个网络环境就好。2.3 新式安装命令与老命令的差别Windows 11上其实可以直接用wsl --install这个命令会自动开启所需功能、下载WSL2内核、安装Ubuntu默认发行版。但在Windows 10上这条命令的行为视系统版本而定有些早期版本并不支持自动安装还是建议手工执行上面1、2两步更稳妥。我个人的建议是不管Windows 11还是10都先执行一遍wsl --install如果系统提示功能未开启就按提示重启之后再用wsl --update把内核更新到最新版。这样既省事又能确保拿到的是新版WSL对后续Docker的兼容性更好。2023年之后的WSL版本还引入了wsl --version命令可以查看WSL本身而不是某个发行版的版本号。如果WSL版本过旧运行wsl --update升级。3. 安装Ubuntu发行版与默认路径迁移别让C盘变成定时炸弹3.1 选Ubuntu版本与安装命令查看当前可用的Linux发行版列表wsl --list --online输出里会列出Ubuntu、Ubuntu-22.04、Ubuntu-24.04、Debian、Kali等。建议直接装Ubuntu-22.04或Ubuntu-24.04 LTS版本LTS意味着五年以上的官方支持Docker的apt源对这两个版本的兼容性也做得最到位。安装命令wsl --install -d Ubuntu-24.04首次启动会让你设置Linux用户名和密码。这里有个很多人忽略的细节这个用户名/密码和你的Windows账号完全独立只对WSL里那个Ubuntu环境生效。你之后跑需要sudo权限的命令输的是这个Linux用户的密码Windows密码在这里不管用。3.2 ext4.vhdx默认装C盘真实案例分析WSL2的整个Linux文件系统都存放在一个叫做ext4.vhdx的虚拟磁盘文件里。默认情况下这个文件被放到C:\Users\你的用户名\AppData\Local\Packages\发行版包名\LocalState\下面。问题在于Docker的镜像、容器、卷全都存在这个虚拟磁盘里。几个镜像加几个容器随随便便十几个GB。如果你C盘本来就不宽裕这个文件会快速膨胀。我遇到过最夸张的一次一个同事的数据分析环境装了5个Python镜像、两个数据库镜像跑了三个月ext4.vhdx直接膨胀到56GB。他C盘总共才240GB最后整个系统被撑到卡死。所以安装完Ubuntu之后第一件事就是把它迁移到非系统盘。3.3 迁移到D盘的具体操作确保WSL里没有重要的未保存工作然后关闭所有WSL窗口。管理员PowerShell执行wsl --shutdown导出当前发行版为一个tar包这里的Ubuntu-24.04必须是准确的发行版名通过wsl -l -v查看wsl --export Ubuntu-24.04 D:\wsl-backup\ubuntu-24.04.tar注销当前发行版wsl --unregister Ubuntu-24.04注意unregister会彻底删除该发行版但导出的tar包还在所以不会丢数据。重新导入到D盘目标目录wsl --import Ubuntu-24.04 D:\WSL\Ubuntu-24.04 D:\wsl-backup\ubuntu-24.04.tar --version 2这里D:\WSL\Ubuntu-24.04是新的存放目录tar包路径放在后面。导入完成后wsl -l -v可以看到发行版状态。导入后默认会以root用户登录和之前设置的普通用户不一致。可以进到WSL里后手动改一下默认用户。有两种方式最简单的是在PowerShell里ubuntu2404.exe config --default-user 你的用户名不同发行版对应的exe文件名不同Ubuntu-24.04对应的exe可能是ubuntu2404.exe不确定的话进到C:\Users\你\AppData\Local\Microsoft\WindowsApps\或直接用where命令看一下。迁移完成后删除D盘那个tar备份包C盘的旧数据也已经被unregister清掉了。3.4 /etc/wsl.conf三个推荐项进到WSL里编辑/etc/wsl.conf[automount] enabled true options metadata,umask22,fmask11 [network] generateResolvConf true [interop] enabled true appendWindowsPath truemetadata选项让挂载的Windows目录能正确显示Linux文件权限对在/mnt/c下跑Docker卷虽然不推荐有帮助。generateResolvConf true保证DNS配置由WSL自动生成避免装完Docker后拉镜像时报DNS解析错误。4. Docker Engine本体安装为什么不用docker.io和curl脚本4.1 三个安装路径的取舍在WSL2的Ubuntu里装Docker市面上主流有3条路方式优点缺点推荐度Ubuntu官方仓库的docker.io安装简单版本旧落后于上游进度对新特性支持差不推荐curl -fsSL get.docker.comsh最快一条命令脚本会直接以root执行来源不可控生产环境风险高Docker官方apt仓库安装版本官方、可控、更新及时需要手动配源和GPG key强烈推荐docker.io最核心的问题不是不能用而是太旧。Docker Engine迭代速度不慢新版本对containerd、buildx、compose的整合都在持续变化你用老版本跑新镜像偶尔会碰到兼容性问题排查起来很费劲。curl脚本方式用来快速搭个测试环境没问题我偶尔也会在临时机器上这么干。但在这个教程场景里既然我们的最终目标是打造一个长期使用的开发环境配一次apt源也就两三分钟的事没必要直接用脚本。4.2 完整安装步骤先卸载可能存在的旧包同时更新apt索引sudo apt update sudo apt remove docker docker-engine docker.io containerd runc注意如果系统之前没装过Docker这里会提示没有可卸载的包正常现象忽略即可。安装必要的前置软件sudo apt install ca-certificates curl添加Docker的GPG keysudo 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 ar /etc/apt/keyrings/docker.asc这里把GPG key存放在/etc/apt/keyrings而不是传统的/etc/apt/trusted.gpg.d原因在于keyring文件的权限管理更严格也避免key被全局信任带来的安全隐患。添加apt源。Ubuntu 22.04和24.04的代号分别是jammy和noble直接用下面的变量自动判断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执行完后用cat /etc/apt/sources.list.d/docker.list确认源内容无误。再次更新索引然后安装Docker全家桶sudo apt update sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugindocker-compose-plugin值得单独提一下它提供的是docker compose新版插件命令和老的docker-compose独立二进制是两回事。我们装的是新版以后用docker compose命令别再加横杠。4.3 把当前用户加入docker组这一步很多人会漏掉。如果不做每次执行docker命令都要加sudo非常烦人sudo usermod -aG docker $USER执行后退出当前WSL会话重新打开一个新的终端。如果还是提示权限不足大概率是新会话没生效或者你的WSL发行版没有正确继承组的改动。检查一下groups输出里应该包含docker。注意加入docker组意味着该用户拥有等同于root的socket访问权限这在单用户开发机上是安全可接受的但在多用户共享机器上要慎重。4.4 验证安装docker --version docker compose version docker run hello-worldhello-world镜像如果跑通输出会显示一段说明文字。如果卡在拉取镜像阶段先检查网络和DNS后面有一节专门讲网络问题。5. WSL2环境下的Docker服务管理systemd开还是不开5.1 为什么WSL2里Docker服务经常“启动不了”这是WSL2用户最容易卡住的点之一。很多教程直接写sudo systemctl start docker但在WSL2里会报错System has not been booted with systemd as init system (PID 1). Cant operate.原因是WSL2默认的init进程不是systemd而是微软自己实现的一个轻量init主要职责是适配WSL的启动和回收机制。没有systemd在PID 1systemctl自然无法工作。5.2 启用systemd较新版本的WSL0.67.6支持在/etc/wsl.conf里直接开启systemd。编辑该文件[boot] systemdtrue然后在PowerShell里重启WSLwsl --shutdown再重新进入WSLps -p 1 -o comm输出应该是systemd就说明转换成功了。这时执行sudo systemctl enable docker sudo systemctl start dockerenable让Docker服务在WSL每次启动时自动运行省去每次手动start的麻烦。5.3 如果不想开systemdservice命令兜底如果因为某些原因不想启用systemd比如你的WSL版本较旧或者跑了别的依赖自定义init的软件可以用传统方式启动sudo service docker startservice命令会调用/etc/init.d/docker脚本在无systemd环境下也能正常工作。但要注意这种方式每次进入WSL都要手动执行一次。想省事可以把启动命令追加到~/.bashrc或~/.zshrc里这样每次打开终端自动拉起if ! pgrep -x dockerd /dev/null; then sudo service docker start fi这段脚本先检查dockerd进程是否存在存在就不重复启动避免每次打开终端都白执行一次。5.4 docker info会连接失败吗不管用哪种方式只要dockerd没起来执行docker info就会提示Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?这种情况不要先怀疑Docker装错了先确认dockerd进程在不在pgrep -x dockerd进程存在但还是连不上再看下socket文件ls -l /var/run/docker.sock如果socket文件存在且权限是rw-rw----用户又在docker组里基本就能连上。6. 最容易翻车的网络问题Windows防火墙拦住了容器互通6.1 一个典型现象Docker装好了镜像能拉docker run -p 8080:80 nginx也能从Windows浏览器访问到。看起来一切正常但你在容器里想访问另一个容器的服务时突然卡住了。具体表现为容器A ping不通容器B的IPcurl一个映射了端口的服务时一直超时curl: (28) Failed to connect to 172.17.0.2 port 8080 after 130 seconds更诡异的是宿主机也就是WSL2里的Ubuntu访问这两个容器的端口都正常偏偏容器之间互相不通。这就是WSL2的网络特性和Windows防火墙的组合拳在捣乱。6.2 根因Hyper-V虚拟交换机流量被防火墙拦了WSL2的网络模式是NATHyper-V会创建一个虚拟交换机通常叫vEthernet (WSL)WSL2的流量走这个交换机。Windows Defender防火墙默认拦截了这个Hyper-V虚拟交换机上的入站流量导致WSL内部容器之间通过NAT转发的通信被Windows防火墙拦下来。由于WSL2的虚拟网络交换机本身承担着容器间互访的流量转发这个拦截会直接影响容器之间的连接。Docker Desktop自动把相关规则写好了原生Docker可没人管这些。6.3 解决方案给vEthernet (WSL)放行用管理员身份打开PowerShellSet-NetFirewallProfile -Name (Get-NetFirewallProfile).Name -DisabledInterfaceAliases vEthernet (WSL)这个命令把“vEthernet (WSL)”这个网络接口从防火墙的生效接口列表里去掉等于对这个虚拟交换机网卡放行。执行后重启WSL和Dockerwsl --shutdown再进WSLsudo systemctl restart docker然后测试容器互通。如果你用的PowerShell版本比较旧可能不支持-DisabledInterfaceAliases这个参数更通用的做法是手动加防火墙规则New-NetFirewallRule -DisplayName WSL -Direction Inbound -InterfaceAlias vEthernet (WSL) -Action Allow注意这会在防火墙里增加一条入站规则允许所有从vEthernet (WSL)接口进来的流量。对开发环境来说这没问题但在安全性要求高的网络环境里建议缩小规则范围。6.4 端口映射到宿主机的访问路径理解解决了容器互通问题再厘清一下端口访问的路径。在WSL2里执行docker run -d -p 8080:80 nginx这个8080端口是绑定在WSL2的虚拟网卡IP上的。WSL2有一个localhost转发机制Windows侧访问localhost:8080会自动转发到WSL2里所以你会觉得“像本机服务一样”。实际上Windows和WSL2是两个独立的网络栈真正的转发是靠Hyper-V的本地主机回环服务完成的。如果想从局域网其他设备访问WSL2里的容器情况要更复杂一些需要在Windows上做端口转发netsh interface portproxy add v4tov4 listenport8080 listenaddress0.0.0.0 connectport8080 connectaddressWSL2的IPWSL2的IP每次重启都可能变化所以这种方案最好配合静态IP或固定WSL2 IP的配置但那就是另一个话题了。6.5 DNS问题resolveconf被覆盖WSL2里/etc/resolv.conf这个文件默认由systemd-resolved管理但如果你配过一些网络工具文件内容可能被覆盖成无效的DNS服务器导致docker拉镜像报error pulling image configuration: Get https://registry-1.docker.io/v2/: dial tcp: lookup ...快速处理方式sudo rm /etc/resolv.conf echo nameserver 223.5.5.5 | sudo tee /etc/resolv.conf sudo chattr i /etc/resolv.confchattr i给文件加上不可修改属性防止系统再把它覆盖回去。这是临时做法不建议长期用因为会影响系统对DNS配置的自动管理。更稳妥的办法是检查为什么systemd-resolved没有正常工作systemctl status systemd-resolved7. 磁盘无限膨胀ext4.vhdx只增不减的压缩方案7.1 为什么Docker越用C盘或WSL所在盘空间越少Docker的镜像分层、构建缓存、容器可写层、日志文件都保存在/var/lib/docker下。使用WSL2时这些数据全部落在ext4.vhdx里。问题在于ext4.vhdx是一个动态扩展的虚拟磁盘文件删除后它不会自动把空出来的空间还给Windows宿主。所以你会看到这个奇观WSL里面df -h显示已用空间从60GB降到了20GBWindows侧ext4.vhdx文件还是那60GB。7.2 从Docker层面清理先清理Docker自身遗留的垃圾docker system df这个命令能看到Docker的磁盘占用分布。然后一键清理所有未使用的镜像、容器、卷、构建缓存docker system prune -a --volumes-a会连没被任何容器引用的镜像也一起清掉--volumes会删除未使用的数据卷。这两个参数叠加效果很强执行前想清楚确认没有需要保留的通用数据卷。只清理构建缓存的话docker builder prune7.3 压缩vhdx文件的完整步骤Docker清理完接下来处理虚拟磁盘文件本身。关闭WSLwsl --shutdown确认发行版的vhdx文件路径。默认位置C:\Users\你的用户名\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu24.04LTS_*\LocalState\ext4.vhdx如果之前迁移过就去D:\WSL\Ubuntu-24.04\ext4.vhdx找。用diskpart压缩打开PowerShell进入vhdx所在目录启动diskpartcd D:\WSL\Ubuntu-24.04 diskpartdiskpart交互式界面里依次执行select vdisk fileD:\WSL\Ubuntu-24.04\ext4.vhdx attach vdisk readonly compact vdisk detach vdisk exitattach vdisk readonly以只读方式挂载compact vdisk会压缩虚拟磁盘文件释放其中未使用的空间。压缩时间取决于磁盘大小20GB的文件通常几十秒再大一些就要等一会儿。压缩完成后检查文件大小是否明显缩小。如果没变小说明WSL里可能还有大量文件被删但未被Docker清理覆盖到比如你手动删除过日志或临时文件可以在WSL里执行一次sudo fstrim /这个命令会通知底层存储这些块已经空闲然后再重复上面的diskpart压缩步骤。7.4 从源头控制膨胀与其等磁盘炸了再清理不如一开始就控制膨胀速度。几个实用建议定期执行docker image prune避免本地堆积大量无用的中间层镜像给容器日志设置大小上限在/etc/docker/daemon.json里加{ log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 } }不用的容器及时删除而不是只docker stop不docker rm这些操作配合定期压缩能把WSL2 Docker的磁盘占用控制在一个稳定水平。8. 实际使用中的几个关键体验别把Windows目录挂进容器8.1 /mnt/c挂载的性能陷阱WSL2里访问Windows文件系统的路径在/mnt/c。你可以直接在WSL里操作Windows文件但性能差距非常明显。9P协议的文件传输速度远低于ext4本地文件系统尤其是大量小文件的读写场景差距能拉到数量级。如果你把Windows目录直接挂载到容器里docker run -v /mnt/c/project:/app ...那么这个容器的文件读写走的是9P drvfs编译速度、包管理器的安装速度都会明显变慢。我在一个Node.js项目上实测npm install时间从27秒拖到2分多钟。正确做法把项目放在WSL2的Linux文件系统里即~/project代码编辑用VSCode的WSL远程模式需要和Windows交换文件时再用cp或docker cp显式复制。8.2 资源限制用.wslconfig控制内存和CPUWSL2默认会使用Windows物理内存的50%作为上限。你只是跑Docker开发环境可能没法接受这么大吃内存。在Windows用户目录下创建.wslconfig文件[wsl2] memory8GB processors4 swap2GB改完重启WSL生效wsl --shutdown这个文件我觉得非常有价值的一点是如果你机器配置不高限制WSL2内存上限可以让Windows主系统更流畅不至于Docker跑几个容器就卡得没法办公。8.3 和Docker Desktop能否共存原生Docker Engine和Docker Desktop可以同时存在但同一时刻只能有一个在监听/var/run/docker.sock。如果你装了Docker Desktop它默认设置会用WSL2发行版里的docker服务可能和原生Engine冲突。最简单的处理给~/.bashrc加个环境变量用DOCKER_HOST切换目标alias docker-nativeexport DOCKER_HOSTunix:///var/run/docker.sock alias docker-desktopexport DOCKER_HOSTunix:///mnt/wsl/docker-desktop/shared-sockets/guest-services/docker.sock我个人实际上把Docker Desktop卸载了因为原生Docker Engine在这个环境下已经足够覆盖我90%的需求包括多容器编排、自定义网络、数据库、中间件测试等等。8.4 VSCode Remote-WSL的开发闭环最后分享一个能让这套环境体验拉满的组合VSCode的WSL远程扩展。装好扩展后在WSL终端里直接执行code .VSCode会在Windows侧启动但整个项目上下文、终端、扩展都运行在WSL2里。这样一来Docker命令、文件读写、环境变量全都切到Linux侧你不会再感受到“我在Windows上跑Linux命令”的割裂感。配合Docker扩展容器查看、日志跟踪、终端内直连容器都可以在IDE里完成。我用了这套组合大概两年中间踩过的坑主要集中在网络和磁盘两个问题上。现在这套环境长期稳定运行既是开发环境也是测试环境偶尔还把一些临时任务直接丢到容器里跑比在Windows原生环境里折腾要干净得多。如果你的目标是搭建一套贴近Linux生产环境的Windows开发环境这条路径值得花一个下午走完。