资讯动态

告别Docker Desktop:在WSL2中安装原生Docker Engine的完整指南

发布时间:2026/9/19 14:17:44 来源:尧图企业网站定制
1. 为什么我最终放弃 Docker Desktop在 WSL2 里装原生 Engine1.1 Docker Desktop 留下的三个真实痛点先把话说在前面这篇不是给纯新手的跟风教程而是记录我自己的选型过程和踩坑经验。我从 2020 年 WSL2 刚稳定那会儿就开始在 Windows 上做容器开发最早用的自然是 Docker Desktop。那时候它确实是唯一省事的方案——装好之后 Windows 侧有托盘图标WSL 里也不用管 daemon 的事默认就把 WSL2 集成了。但用久了之后三个问题让我越来越难受。第一个是资源占用。Docker Desktop 会起一个完整的虚拟化后端再加上配套的 UI 进程、权限服务、更新服务随便一开就是 2~3GB 内存。我开发机内存只有 16GB还要开 IDEA、几个微服务进程容器一多内存就说不够用。原生 Engine 的思路是直接在 Ubuntu 里跑 daemon整个环境更纯粹虚拟化开销也被压到最低。第二个是两层 Docker的混乱感。Docker Desktop 集成 WSL2 之后Windows 的 docker 命令和 WSL 里的 docker 命令共用同一个 daemon有时候确实方便但也带来概念上的混乱。你执行 docker ps 的时候到底在看哪个上下文context 切来切去CI 脚本里稍微不注意就指错了地址。原生方案没有这个问题Ubuntu 里只有一个 dockerdWindows 侧要用就通过 wsl 命令桥接过去逻辑清清楚楚。第三个是版本和更新问题。Docker Desktop 的更新机制和组件复杂度让它经常出现更新一半卡住版本回退困难这类状况。相比之下Ubuntu 里用 apt 管理 docker-ce、containerd.io、docker-buildx-plugin、docker-compose-plugin 这几个包版本可控、回滚方便完全符合我在 Linux 服务器上的运维习惯。1.2 原生 Engine 与 Docker Desktop 的本质区别很多人以为装原生 Docker Engine就是在 WSL 里执行一遍 Docker 官方教程其实没那么简单。要理解这个事先得搞清楚 Docker 的分层架构。Docker 分成 Client 和 DaemonClientdocker 命令只是把你要做的事翻译成 REST API 请求真正干活的是 Daemondockerd它负责拉镜像、管理容器生命周期、维护网络和存储。Docker Desktop 的本质是一个运行在 Windows 上的守护程序它内部通过虚拟化起了一个轻量级 VM所有容器都跑在那个 VM 里WSL 里的 docker 命令只是它的 client。而原生方案是停掉 Docker Desktop完全在 WSL2 的 Ubuntu 里安装、运行 dockerd。WSL2 本身就基于真正的 Linux 内核dockerd 和 containerd 直接跑在 Ubuntu 的用户态跟你在云服务器上跑 Docker 没有本质区别。从 Docker 的角度看WSL2 的 Ubuntu 就是一台普通 Linux 主机。好处我看重三点。第一所有配置都和服务器端一致写出来就是一份可以复用到生产环境的操作手册第二容器网络的方案更原生没有 Docker Desktop 那层额外的转发层排查端口问题时少一个变量第三apt 管理让升级、回滚、锁定版本都变得标准化不用跟 GUI 的更新并重启较劲。1.3 什么情况下不建议折腾原生方案话虽如此原生方案并不是所有人都需要。如果你满足下面任一条件我建议继续用 Docker Desktop你只是想在 Windows 上跑起来一个容器玩一下对 daemon 原理、systemd、内核网络没有兴趣也没有精力了解你重度依赖 Docker Desktop 的 Kubernetes 单机集群、资源面板、镜像扫描等 GUI 功能你的项目同事统一用 Docker Desktop且共享 compose 配置中依赖了 Desktop 特有的挂载方式。说到底这是一个想要更可控还是更省心的选择。后面我讲的整套流程方向是把 WSL2 里的 Ubuntu 当服务器来用。如果你认同这个理念再往下走不吃亏。2. 动手前必做把 WSL2 和 Ubuntu 环境理顺2.1 版本检查是第一步不是可选步骤我在十几台机器上装过 WSL2第一件事永远是检查 Windows 版本。WSL2 要求 Windows 10 2004build 19041或更高Windows 11 直接支持。如果你的系统还在 1903、1909先别急着装 Ubuntu去 Windows 设置里把系统更新做完再说否则后面各种内核不兼容问题会把人折磨疯。检查命令很简单管理员 PowerShell 里执行wsl --status如果输出里能看到默认版本2说明环境已经就绪如果显示适用于 Linux 的 Windows 子系统没有安装或者默认版本是 1就需要往下处理。还需要确认 BIOS 里打开了虚拟化Intel VT-x / AMD SVM。任务管理器 - 性能 - CPU右下角会显示虚拟化已启用。很多装不上 WSL2 的案例最后查下来都是因为虚拟化没开Windows 功能装好了也白搭。2.2 用一条命令装好 Ubuntu 发行版从较新的 Windows 版本开始装 Ubuntu 最省事的方式是一条命令搞定wsl --install -d Ubuntu-22.04这条命令会自动开启适用于 Linux 的 Windows 子系统和虚拟机平台两个 Windows 功能下载并安装 Ubuntu然后提示你设置用户名和密码。首次执行后一般会要求重启重启后再启动 WSLUbuntu 才会真正初始化。如果你希望用更新的 LTS 版本可以把发行版参数换成 Ubuntu-24.04。我个人的建议是不要盲目追新Docker Engine 对 22.04 的兼容性最成熟搜社区问题也最容易找到答案。24.04 也不是不行只是某些第三方 apt 源可能会晚一些适配。装好之后检查一下版本wsl -l -v输出类似这样NAME STATE VERSION * Ubuntu-22.04 Running 2这里 VERSION 必须是 2如果显示 1执行wsl --set-version Ubuntu-22.04 2转换。从 WSL1 转 WSL2 的过程比较慢它会重新复制整个文件系统耐心等几分钟。2.3 内核升级与 systemd 预检如果你之前装过老版本的 WSL或者 wsl --status 有提示内核更新务必执行wsl --update这会把 WSL 内核更新到最新版。为什么要做这一步因为新版内核才支持 systemd而 systemd 正是后面 Docker 守护进程能不能随系统自动启动的关键。很多人装完 Docker 后手动启动没问题一重开 WSL 就找不到 dockerd绝大多数是这个原因。进到 Ubuntu 里再顺手做两件事。第一是更新 apt 索引sudo apt update sudo apt upgrade -y第二是确认 systemd 状态。在 WSL 较新版本里可以直接查看systemctl list-unit-files --typeservice | head如果能正常列出服务列表说明 systemd 已经在运行如果报错 System has not been booted with systemd as init system稍后我会专门讲怎么开启它。3. 原生 Docker Engine 安装全流程3.1 先清掉可能冲突的历史残留不管你是全新 Ubuntu 还是以前折腾过 Docker第一步都建议执行清理。网上教程喜欢直接跳到添加源但我在实际维护机器时发现一旦和旧版本冲突排查成本比安装本身高得多。for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove -y $pkg done注意这段命令不会动到你 Docker 的数据/var/lib/docker如果之前有容器数据需要保留这里可以放心执行清理之后再把残留的 /var/lib/docker 目录另外归档避免旧数据干扰新 daemon 的初始化。3.2 添加 Docker 官方 apt 源按四步走官方文档现在是推荐用 apt 源安装。我把它拆成四步每步都说说为什么。第一步安装依赖证书工具sudo apt-get update sudo apt-get install -y ca-certificates curl第二步创建 keyrings 目录并导入 GPG 密钥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 ar /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这里有个细节我故意不用 lsb_release -cs而是用 /etc/os-release 里的 VERSION_CODENAME。因为 lsb_release 可能没装而且 /etc/os-release 是从系统底层读的更可靠。这里也提醒一下别为了下载快随便把官方源替换成第三方源。先保留官方源下载速度的问题后面用 registry mirror 解决那条路径更干净、更可控。如果执意用内网 apt 镜像请确保镜像完整同步了 docker-ce 仓库否则 apt update 会报 404。第四步更新并安装核心包sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugindocker-buildx-plugin 和 docker-compose-plugin 看着像可选项我建议一起装上。Buildx 是现在构建镜像的标准前端新版 Dockerfile 的很多特性比如 heredoc 语法都要靠它compose plugin 则让你不需要单独装 docker-compose 二进制直接用 docker compose 子命令。3.3 用户组与开机自启装完先别急着用 root 跑 docker。把当前用户加进 docker 组避免每条命令都踩 sudosudo usermod -aG docker $USER这个操作要重新登录才生效我一般是直接执行newgrp docker临时切换或者干脆关掉终端重开。然后是 systemd 服务。在 WSL2 里如果 systemd 已启用直接sudo systemctl enable docker sudo systemctl start docker如果 systemd 没启用手动启动 daemonsudo service docker start3.4 用一次真实拉取验证整套链路验证不是跑一下 docker --version 就完事。我习惯用一条实操命令验证完整链路docker run --rm hello-world这条命令背后会发生这几件事docker client 和 daemon 建立连接daemon 从镜像仓库拉取 hello-world 镜像镜像被解包到 /var/lib/docker 的 overlay2 存储层containerd 创建并启动容器容器输出一段欢迎文字。只要这一步能跑通说明 client、daemon、containerd、网络、存储五层都没问题。如果卡在 pulling image 阶段那多半是网络问题直接看镜像加速配置那一章。4. systemd 与守护进程启动失败的完整排查4.1 错误背后的第一道坎WSL2 默认不跑 systemd很多人在 WSL2 里装完 Docker 后发现手动 start 没问题但是重开 WSL 后 dockerd 就不见了。这个问题的历史原因在于 WSL2 早期版本默认用 init 而不是 systemd 作为启动进程systemd 服务管理器压根没跑起来systemctl 命令装了也用不了。解决方法是开启 WSL2 的 systemd 支持。先编辑 /etc/wsl.conf[boot] systemdtrue然后在 Windows 管理员 PowerShell 里重启 WSLwsl --shutdown重新进入 Ubuntu 后执行systemctl is-system-running如果输出是 running 或 degraded就说明 systemd 已经接管。这一步是整个 Docker 自启的基础我建议在做完这一步之后再回去 enable docker否则即使设置了开机启动下次冷启动也会失效。4.2 从 Docker Desktop 的报错反推原生环境问题网上搜starting the docker engine无法启动跳出的大多是 Docker Desktop 的截图。但如果你已经决定走原生路线遇到启动失败时要换一套排查语言docker: Cannot connect to the Docker daemon和systemctl start docker一直卡在启动中才是你要关注的原生报错。我会这么定位。先看 daemon 有没有进程ps aux | grep dockerd如果没有 dockerd 进程说明 daemon 根本没起来。再尝试手动启动并看日志sudo dockerd --debug--debug 模式会把日志打到终端信息量比 journalctl 大得多。启动失败的原因十有八九会在这里暴露缺内核模块、iptables 配置失败、overlayfs 挂载失败、磁盘空间不足等等。我第一次排查时就发现是 /var/lib/docker 的某个索引文件损坏导致 daemon 启动即退出删掉对应目录让 daemon 重建就好了。4.3 两个高频坑iptables 和 cgroup在我折腾过的机器里原生 dockerd 启动失败最常见的就是 iptables 问题。WSL2 的内核默认是 nftables 后端而 dockerd 历史上更习惯用 legacy iptables两者冲突时启动日志里会出现比较明显的报错比如 Failed to load iptables module 或者 could not delete default network。解决方法是把系统默认的 iptables 切换回 legacy 模式sudo update-alternatives --config iptables选择 iptables-legacy 后重启 docker 服务再试。注意这个操作只影响 WSL 发行版内部的网络栈对 Windows 侧没有任何副作用。另一个是 cgroup 驱动。新版 Ubuntu 默认 cgroup v2Docker 20.10 已经默认支持一般不需要动。但如果你从老教程里复制过 exec-opts: [native.cgroupdriversystemd] 之类的配置并且发现容器启动后资源限制不生效就要确认 daemon.json 里没有写死一个和内核不匹配的驱动删除这行配置让 Docker 自动探测通常更省心。4.4 一个完整的排查链路示例我在帮朋友排查时就遇到过一个典型情况docker run 报 error during connectsystemctl status docker 显示 active (running)但 docker ps 就是连不上。后来发现他以前装过 Docker Desktop之后卸载不干净PATH 里的 docker client 还是 Desktop 版本。排查链路如下先确认 client 连的是哪个 socketdocker context ls看 socket 是否存在ls -l /var/run/docker.sock如果 socket 文件不在说明 daemon 可能没起来或者监听了别的地址用sudo dockerd --debug看日志对比 client 和 daemon 版本差异。这类问题用文字不好一次讲清但排查方法论是一致的先确认进程再看 socket最后看日志。把这三步走完90% 的启动问题都能定位到具体原因。5. 镜像加速、数据持久化与磁盘瘦身5.1 daemon.json 配置国内镜像加速原生方案装好之后第一个体验痛点就是拉 Docker Hub 镜像慢。这个问题在国内开发机上几乎人人都会遇到解决方案是给 dockerd 配置 registry mirror。修改 /etc/docker/daemon.json{ registry-mirrors: [ https://docker.m.daocloud.io, https://docker.mirrors.ustc.edu.cn ] }如果你有阿里云账号也可以在阿里云容器镜像服务里申请一个专属加速地址那个地址形如 https://xxxxx.mirror.aliyuncs.com效果通常更稳定。配置完重启sudo systemctl daemon-reload sudo systemctl restart docker验证是否生效sudo docker info | grep -A5 Registry Mirrors有一点必须提醒镜像加速只对 Docker Hub 官方仓库生效Google 的 gcr.io、GitHub 的 ghcr.io 这些第三方仓库是加速不到的。如果你要拉 ghcr.io 的镜像常见做法是在云服务器上提前 pull 好再导出然后导入到本机或者把镜像重新 tag 后推到自己的私有仓库再用加速地址拉取。同时镜像加速地址并不是永久有效的公共镜像站时不时会调整服务策略。我一般每季度检查一次把失效的地址移除换成可用的。这个检查很简单docker pull 一个公共镜像试试速度就知道。5.2 容器数据的正确存放姿势WSL2 里最常犯的错误是把数据放到 /mnt/c 或 /mnt/d 这种 Windows 挂载目录下。比如有人习惯把 docker 的>wsl --export Ubuntu-22.04 D:\wsl-backup\ubuntu.tar wsl --unregister Ubuntu-22.04 wsl --import Ubuntu-22.04 D:\wsl\Ubuntu-22.04 D:\wsl-backup\ubuntu.tar注意 unregister 会清掉原来的用户和文件务必先确认已经用 export 备份好。5.3 vhdx 无限膨胀与精简WSL2 的虚拟磁盘默认是动态增长的用久了之后即使删掉大量镜像vhdx 文件也不会自动缩水。我见过一个开发机docker 里删了几十 GB 镜像但 C 盘空间完全没回来。精简的方法是先清掉 Docker 的无用数据再压缩虚拟磁盘。先清 Docker 数据sudo docker system prune -a --volumes这一步会删掉所有未使用镜像、停止的容器、未使用的网络和卷执行前要把需要的镜像确认好。然后在 Windows 管理员 PowerShell 里wsl --shutdown Optimize-VHD -Path C:\Users\你的用户名\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu22.04LTS_*\LocalState\ext4.vhdx -Mode Full如果系统没有 Optimize-VHD 命令说明 Hyper-V 管理工具没装可以改用 diskpart 的 compact vdisk或者干脆用 WSL 自带的精简开关wsl --manage Ubuntu-22.04 --set-sparse true这个 sparse 模式是较新版本 WSL 的功能开启后虚拟磁盘会自动瘦身算是目前最省心的方案。我自己的机器在开启 sparse 后再也没手动压缩过 vhdx。6. 日常使用磨出来的经验保活、资源限制与文件性能6.1 让 WSL2 里的 Docker 长期在线的保活方案WSL2 的一个特性是没有活跃进程就自动关闭。Windows 重启之后如果你不打开 WSL 终端dockerd 不会自己起来Windows 上依赖容器服务的程序就会连不上。我这边总结了三层保活方案按复杂度和稳定性排序。第一种最简单的手动兜底在 Windows 计划任务里建一个登录时启动任务操作设置为执行wsl.exe -d Ubuntu-22.04 -u root --exec service docker start这样每次登录 Windows都会拉起 Ubuntu 并启动 docker 服务。缺点是如果你之后手动执行了 wsl --shutdown这个任务不会自动重新触发。第二种配置 /etc/wsl.conf 让 systemd 管好服务。开启 systemd 后docker 服务本身可以设置开机自启但 WSL 虚拟机的启动时机还是取决于有没有进程请求。配合 Windows 计划任务里随便跑一条wsl.exe -d Ubuntu-22.04 true就能把虚拟机拉起来后续 dockerd 交给 systemd。第三种无限 sleep式保活。在 Ubuntu 里用 nohup 跑一个永不结束的进程nohup sleep infinity /dev/null 21 只要这个进程在WSL2 虚拟机就不会自动关。这个方法适合喜欢保留终端会话的开发者缺点是每次 WSL 冷启动后要重新拉起来。我个人推荐第二种组合systemd 管 dockerWindows 计划任务管虚拟机启动。这是目前最接近开机即用的体验。6.2 用 .wslconfig 限制内存和 CPUWSL2 默认会吃掉最多一半物理内存这对跑 Docker 的开发机来说太奢侈了。在用户目录下创建 .wslconfig 文件可以精细控制[wsl2] memory8GB processors4 swap2GB localhostForwardingtrue要注意.wslconfig 是 Windows 侧读的配置必须放在 C:\Users用户名.wslconfig改完之后执行 wsl --shutdown 重新进才生效。memory 给多少取决于你本机内存和你容器集群的规模我的经验是至少留 2GB 给 Windows 自己否则 Windows 会频繁触发内存压缩整个机器卡成幻灯片。6.3 文件性能和 I/O 的隐藏坑原生 Docker Engine 在 WSL2 里跑容器容器内部读写 ext4 文件系统是走原生内核路径性能很好。但如果你用 docker run -v /mnt/d/project:/app 这种挂载方式把 Windows 目录挂进容器性能就会断崖式下跌。原因还是前面提到的 9P 协议。我做过一个很直观的测试同样的 Node 项目依赖装在容器内 /app/node_modules 但源码从 /mnt/d 挂载进去冷启动耗时比源码放在 Ubuntu 本地慢了三倍如果项目里有大量小文件读写差距还会继续拉大。所以我的建议是代码仓库放 Windows 侧没问题但不要在容器里直接挂载 Windows 目录跑构建和安装依赖要么把项目复制到 WSL 文件系统里跑要么利用 IDE 的远程开发能力把仓库同步进去。这是使用体验差异最大的一条经验。6.4 Windows 访问容器端口与反代细节WSL2 的 localhost 转发默认是开着的。在 Ubuntu 里 docker run -p 8080:80 nginxWindows 浏览器直接访问 localhost:8080 就能看到页面这层转发是 WSL 内建的不需要任何额外配置。但如果你有 NAS、其他电脑、或者内网服务器需要访问这台开发机上的容器就不能依赖 localhost 转发了因为 WSL2 的虚拟机 IP 是动态的Windows 防火墙也不会自动放行。我目前的做法是用 Windows 侧的端口代理netsh interface portproxy add v4tov4 listenport8080 listenaddress0.0.0.0 connectport8080 connectaddressWSL2的IP然后给防火墙加一条入站规则放行 8080 端口。WSL2 IP 可以通过 Ubuntu 里的 ip addr 查看但它重启后会变所以这个方案只适合临时调试。长期稳定对外提供容器服务我更建议直接在 WSL2 里跑一个反代容器做入口或者干脆把这类服务部署到真正的服务器上别把开发机的容器当成生产环境用。6.5 与 idea wsl2 开发场景的配合如果你用 IntelliJ 系 IDE 在 WSL2 里做 Java、Go、Python 开发IDE 的 WSL 支持其实会自己识别 WSL 环境并调用 WSL 里的 docker 命令。装了原生 Engine 后IDEA 的 Docker 插件只需要把 context 切到 WSL 侧就能正常操作容器。我第一次接的时候踩了个坑IDEA 默认还连着 Docker Desktop 的 socket导致容器列表一直为空。手动执行docker context use default并把连接方式改成 WSL 里的 unix socket /var/run/docker.sock 之后一切才恢复正常。如果你也用 VS Code逻辑类似但要注意 Remote-WSL 插件打开的终端里默认环境变量会指向 WSL 侧所以 docker 命令天然走的是原生 daemon反而不会踩到 context 混用的问题。写到这里整个流程就串起来了。最后再分享一个小经验很多人遇到 WSL2 环境问题会反复重装 Ubuntu我劝你克制住这个冲动。绝大多数 docker 启动失败、自启失败、镜像拉取慢的问题都不是重装能解决的问题往往出在 systemd 没开、iptables 模式不对、或者 daemon.json 写错这类的配置项上。把这几个文件逐一检查一遍比重装系统高效得多。我也建议你养一个习惯在 Ubuntu 里改完任何 Docker 相关配置都先把 /etc/docker/daemon.json 和 /etc/wsl.conf 备份一份然后记录当时改了什么、为什么改。WSL2 环境看似简单实际上它的网络栈和 Windows 是两套体系出问题时可排查的变量比一台纯 Linux 服务器多不少。有备无患。如果这篇能让你少走我当初走过的弯路那我的折腾就值了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价