这段时间我把开发环境从 Docker Desktop 迁到了 WSL2 Ubuntu 里的原生 Docker Engine迁移完第一感受是命令行反馈明显快了一截而且 docker 命令的行为和我在 Linux 服务器上完全一致不用再担心“本地能跑、线上跑不起来”的差异问题。整个过程从 WSL2 环境准备到 Docker Engine 装好能正常拉镜像跑容器差不多花了一个下午中间踩了虚拟化未启用、GPG 密钥失效、镜像拉取超时这几个典型坑。这篇文章就把这套完整流程拆开讲清楚每个操作背后的原因也一并说透适合想在 Windows 下获得接近真实 Linux 服务器体验、又不想被 Docker Desktop 拖慢节奏的开发者参考。1. 先在“运行环境”上做决策为什么是 WSL2 原生 Docker Engine很多 Windows 用户的第一反应是装 Docker Desktop这个东西确实省事装上之后托盘图标点一下就能用。但如果你在 Linux 服务器上部署过应用很快会感觉到 Docker Desktop 那一层封装带来的隔阂docker 命令的实际执行环境、网络模型、文件挂载行为都和裸 Linux 上装的 Docker Engine 存在细微差别。另一个现实问题是资源占用Docker Desktop 在 Windows 上会额外跑一个完整的 Linux 虚拟机哪怕你已经在 WSL2 里配置了后端它仍然需要维持自己的管理进程和图形界面对于内存只有 16GB 的笔记本来说压力不小。1.1 Docker Desktop 与 WSL2 原生引擎的本质差异Docker Desktop 在 Windows 上的工作方式是先启动一个 Hyper-V 虚拟机再在这个虚拟机里跑一个精简版 LinuxDocker Engine 实际上跑在这个精简系统里。你 Windows 侧的 docker CLI 通过命名管道或 TCP 连接到虚拟机内的 daemon。这套方案的最大好处是屏蔽了底层差异装完就能用但代价也在这里——中间隔了一层很多网络和存储行为要经过虚拟化转换性能有损耗排查问题时也不够直观。WSL2 本身也是一个轻量虚拟机但它的内核启动速度和内存占用优化比传统 Hyper-V 虚拟机好得多。直接在 WSL2 的 Ubuntu 发行版里安装原生 Docker Engine意味着 docker CLI 和 dockerd 都运行在同一个 Linux 内核之上没有额外的管理层。我在实际使用中明显感觉到docker build时的文件监听和缓存命中率更稳定尤其在使用 bind mount 做本地开发时改动文件后的热更新延迟比原来低了不少。1.2 一份方案对比表格与我的选择逻辑对比维度Docker DesktopWSL2 后端WSL2 原生 Docker Engine安装复杂度低图形界面引导中全部命令行操作资源占用较高存在管理进程与 GUI较低仅 dockerd 与容器进程与 Linux 服务器行为一致性较一致但有大版本跟随周期完全一致可直接复用生产配置Docker Compose / Buildx自带并自动更新通过组件包安装版本可控命令行体验依赖 Windows 侧 CLI 转发直接使用 Linux 侧 CLI反馈更原生适合场景快速上手、需要图形管理界面追求性能、贴近生产环境、脚本化部署我的选择逻辑其实很简单既然平时部署目标就是 Linux 服务器那本地开发环境越接近目标环境越好。而且 WSL2 原生方式的 daemon.json、容器网络、存储驱动都能直接沿用生产环境的配置省去了一层“本地没问题、上线就出幺蛾子”的中间环节。如果你只是偶尔跑一两个容器做实验Docker Desktop 确实更方便但如果你想长期在 Windows 下做容器化开发我建议直接上原生 Engine。2. WSL2 前置检查虚拟化、版本与发行版这三道门槛在装 Docker 之前得先把 WSL2 本身跑起来。这里最容易卡住的就是启动时提示“WSL2 无法启动因为此计算机上未启用虚拟化”这个报错我在两台机器上遇到过原因和处理方式不太一样值得单独说一下。2.1 未启用虚拟化的报错与排查路径这个报错出现时第一反应应该是进 BIOS 确认虚拟化是否开启。Intel 平台对应 Intel VT-xAMD 平台对应 SVM Mode不同主板叫法不一样但基本都在 Advanced / CPU Configuration 这类菜单下。开启并保存重启后虚拟化就位了。还有一类情况是 BIOS 里开了虚拟化但 Windows 侧缺少必要功能组件。需要确保“虚拟机平台”Virtual Machine Platform和“适用于 Linux 的 Windows 子系统”这两个可选功能都被启用。在管理员 PowerShell 里执行以下命令可以快速确认和启用# 检查 WSL 功能状态 Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux Get-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform如果某个功能显示为 Disabled就用管理员权限开启dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart执行结束后必须重启系统。重启后在 PowerShell 里执行wsl --status如果看到默认版本是 2说明基础环境就位了。如果仍然提示虚拟化问题可以再检查一下是否有旧版本 Hyper-V 与第三方虚拟机软件冲突比如部分版本 VMware Workstation 与 WSL2 的虚拟化层互斥需要升级到支持嵌套虚拟化的版本。2.2 用 wsl --install 快速建立 Ubuntu 环境从较新的 Windows 10 版本开始安装 WSL2 已经不需要手动下载安装包直接执行wsl --install这个命令默认会安装 WSL2 内核和 Ubuntu 最新 LTS 发行版。如果你像我一样想指定 Ubuntu 22.04可以先查看可用发行版列表wsl --list --online然后安装指定版本wsl --install -d Ubuntu-22.04这里有个实际经验如果你之前已经停用或删除过某个发行版重新安装可能会遇到分发目录残留的问题。稳妥的做法是先执行wsl --shutdown然后在设置里删除旧的 Ubuntu 虚拟磁盘或者用wsl --unregister Ubuntu-22.04清理干净再装。我自己遇到过注册表里残留导致新安装的 Ubuntu 启动后密码不一致的情况折腾了一阵子。所以安装前如果系统里有过旧版 WSL建议先彻底清理。2.3 确认 WSL2 版本与 Ubuntu 发行版信息装好后进入 Ubuntu 终端先做两个确认# 确认 WSL 内核版本 uname -r # 确认 Ubuntu 版本 cat /etc/os-release如果uname -r输出的是以microsoft-standard-WSL2结尾的内核版本说明当前确实运行在 WSL2 上。接下来更新软件源与基础包sudo apt update sudo apt upgrade -y这一步很重要。Docker Engine 的安装依赖ca-certificates、curl等基础工具而 WSL2 的全新发行版往往只包含最小基础软件包。先把系统更新到最新可以避免后续安装过程中出现依赖版本过旧导致的诡异问题。3. 安装 Docker Engine从 GPG 密钥到四个组件的完整命令Docker Engine 在 Ubuntu 上的官方安装方式是通过 apt 仓库而不是直接下载 deb 包。这样做的最大好处是后续可以用apt upgrade统一管理 Docker 组件升级不用每次手动去拉二进制文件。这里我把安装过程拆成三步每一步都有明确的执行目标和验证手段。3.1 apt 仓库安装与 GPG 密钥的建立首先安装必要的基础依赖sudo apt update sudo apt install -y ca-certificates curl然后创建 keyrings 目录并下载 Docker 官方的 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这里的install -m 0755 -d是创建一个权限为 755 的目录用于存放 GPG 密钥。指定这一权限的目的是让 apt 在验证仓库签名时能够正常读取密钥文件同时又不会给普通用户不必要的写权限。chmod ar则是确保所有用户都能读取密钥内容属于 apt 官方文档明确要求的权限设置。接下来添加 Docker 仓库。这里有一个容易踩的坑不能直接写死 Ubuntu 版本代号而应该动态读取当前系统的版本代号因为不同 Ubuntu 版本的仓库路径不同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$(dpkg --print-architecture)会输出当前 CPU 架构比如 amd64 或 arm64确保仓库地址与架构匹配。$(. /etc/os-release echo $VERSION_CODENAME)会读取系统版本代号22.04 对应 jammy24.04 对应 noble。这样即使在将来升级了 Ubuntu 版本这条配置也能自动适配前提是 Docker 官方仓库已支持新版本。3.2 docker-ce、containerd、buildx 与 compose 插件各自的职责添加完仓库后执行sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin很多人第一次看到这么多包名会疑惑一个 Docker 装这么多东西干什么其实每个包都有明确分工docker-ceDocker 社区版核心守护进程dockerd负责管理容器生命周期、镜像、网络和存储。docker-ce-cli命令行客户端工具就是你在终端里敲的 docker 命令。containerd.io容器运行时负责真正启动和停止容器进程。dockerd 把容器操作下发给 containerd 执行。没有它docker 命令只会报错“Cannot connect to the Docker daemon”。docker-buildx-pluginBuildx 插件提供多平台镜像构建能力比如在 x86 机器上构建 ARM 镜像。docker-compose-pluginCompose 插件让你可以直接用docker compose up启动多容器应用不需要额外安装 python-docker-compose。安装完成后先不要急着用还需要确认 daemon 能否正常启动。在 WSL2 环境下默认情况下 dockerd 不会自动运行所以先手动启动并设为开机自启sudo service docker start sudo systemctl enable docker如果在较新版本且开启了 systemd 的环境里systemctl enable docker会正常生效。如果提示 System has not been booted with systemd as init system (PID 1)说明当前还没开启 systemd这篇文章后面专门有一节讲怎么处理。3.3 首次验证hello-world 背后的完整链路安装配置完成后跑一个最小的验证sudo docker run hello-world正常情况会输出一段介绍文字说明 Docker 工作正常。这一步验证的不只是“能跑起来”而是整条链路CLI 发出请求 - dockerd 接收 - containerd 拉取镜像 - 创建容器 - 运行并输出日志 - 退出。如果到这里一切顺利说明最困难的部分已经完成了。如果输出的是Cannot connect to the Docker daemon先检查 dockerd 是否在运行sudo service docker status如果服务是停止状态启动服务前先查看一下日志找到失败原因sudo dockerd --debug我遇到过一次 dockerd 无法启动的情况日志提示网络桥接创建失败。这是因为 WSL2 的网络模型与物理机不同docker0 网桥的创建依赖 iptables 正常工作。排查后确认是 Ubuntu 24.04 里默认的 nftables 与 Docker 的 iptables 规则冲突临时方案是在/etc/docker/daemon.json里显式指定 iptablesfalse但更好的做法是检查iptables -L能否正常输出并确保iptables-nft包已安装。4. 两项收尾配置镜像加速与免 sudoDocker Engine 装好并能跑通 hello-world 后接下来这两项配置是我认为“不做会很难受”的事情一个是镜像加速一个是把当前用户加入 docker 用户组。4.1 daemon.json 中配置 registry mirror在部分网络环境下直接从 Docker Hub 拉取镜像经常出现超时或下载中断尤其是比较大的镜像比如 node、python、postgres 这类常用镜像每个动辄几百 MB下载中断一次就得从头再来。解决办法是在/etc/docker/daemon.json里配置 registry mirror镜像加速地址。先创建配置文件sudo mkdir -p /etc/docker sudo nano /etc/docker/daemon.json写入如下内容{ registry-mirrors: [ https://docker.mirrors.ustc.edu.cn, https://hub-mirror.c.163.com ] }需要注意公共镜像加速地址的可用性会随时间变化有的已经停止服务有的需要注册账号获取专属地址。比较可靠的做法是去云服务商的控制台申请一个专属加速地址格式类似https://xxxx.mirror.aliyuncs.com拿到后填入即可。我这里列出的两个地址仅供验证思路实际使用时建议以各家官网最新公告为准。配置完成后重启 Dockersudo systemctl daemon-reload sudo systemctl restart docker用以下命令验证加速是否生效docker info | grep -A 5 Registry Mirrors如果输出里列出了你配置的地址说明已经生效。实测下来配置加速后拉取同样的 postgres:16 镜像时间从十几分钟降到几分钟差距很直观。4.2 将当前用户加入 docker 用户组每次敲 docker 命令都要加 sudo这不仅是手累的问题还会导致一些意想不到的权限边界。比如在使用 Dockerfile 时某些 COPY 指令对宿主机文件的读取权限会受 sudo 影响容易踩坑。把当前用户加入 docker 用户组sudo usermod -aG docker $USER执行后必须注销当前终端会话或重启 WSL让用户组变更生效exit wsl --shutdown重新进入 WSL 后直接执行docker ps不再需要 sudo 就说明配置成功了。关于这个操作有一个安全提醒docker 用户组的权限等价于 root因为容器可以挂载宿主机目录并执行任意命令。所以只建议在单机开发环境里这么做如果是多人共享服务器需要审慎评估。4.3 验证配置是否生效重启 WSL 后我建议一次性执行这几个命令确认整体状态docker version docker info docker run --rm hello-worlddocker version会输出 Client 和 Server 两部分信息。如果只有 Client 没有 Server说明 daemon 没起来。docker info会显示存储驱动、内核版本、镜像加速地址等关键信息。hello-world 再跑一遍确认用户组配置没有破坏原有功能。5. 让 Docker 服务开机自启WSL2 的 systemd 与 Windows 侧联动WSL2 早期的发行版没有 systemd服务管理全靠 init.d 脚本Docker 每次开机都需要手动service docker start很烦人。好在新的 WSL2 内核已经支持 systemd只需要在 /etc/wsl.conf 里打开开关。5.1 在 wsl.conf 中开启 systemd编辑 /etc/wsl.confsudo nano /etc/wsl.conf写入[boot] systemdtrue保存后在 Windows PowerShell 里执行wsl --shutdown重新进入 WSL 后执行systemctl is-system-running如果输出running说明 systemd 已正常接管。此时 Docker 服务会由 systemd 管理开机自动启动systemctl enable docker systemctl start docker开启 systemd 之后有一个连带好处之前很多依赖 systemd 的服务管理命令都可以正常使用了比如systemctl status ssh对于在 WSL 里跑开发服务的人来说非常方便。5.2 从 Windows 侧直接调用 docker 命令如果你已经习惯在 Windows PowerShell 里操作不想每次切到 WSL 终端可以安装 Windows 版的 Docker CLI。安装后通过环境变量把 DOCKER_HOST 指向 WSL2 里的 daemon这样 PowerShell 里的 docker 命令和 WSL 里的 docker 命令操作的是同一个 daemon。先确保 WSL2 里的 Docker daemon 监听 TCP 端口。在 /etc/docker/daemon.json 里增加 hosts 配置{ registry-mirrors: [ https://docker.mirrors.ustc.edu.cn, https://hub-mirror.c.163.com ], hosts: [ unix:///var/run/docker.sock, tcp://0.0.0.0:2375 ] }重启 Docker 后在 Windows PowerShell 里设置环境变量$env:DOCKER_HOST tcp://localhost:2375 docker ps如果能看到 WSL2 里的容器列表说明联通成功。可以把这行环境变量写入 PowerShell 的 profile省得每次设置。这里要强调一下安全性2375 端口默认没有任何身份验证监听 0.0.0.0 意味着局域网内其他机器也能访问。只建议在纯本机开发环境这么干如果需要更安全的远程访问应该配置 TLS 证书认证或者干脆使用 SSH 转发。5.3 在 Windows Terminal 中固化 WSL 默认终端我个人的使用习惯是把 WSL 设为 Windows Terminal 的默认配置文件这样每次打开终端直接进入 Ubuntu无需切换。然后在 Ubuntu 的~/.bashrc里加几个常用别名alias ddocker alias dcdocker compose alias dpsdocker ps alias dpsadocker ps -a alias dlogdocker logs -f --tail 100这些小东西用惯了之后工作效率提升明显。我还在~/.bashrc里加了一段自动判断 docker 是否在运行的逻辑如果docker info失败就自动启动服务省得每次开机都要手动敲。6. 踩坑实录安装与使用过程中的六个典型问题任何环境搭建过程中都会遇到各种报错把我在 WSL2 上安装和使用 Docker Engine 过程中遇到过的典型问题整理出来按排查思路写清楚希望你能少走弯路。6.1 安装 docker 的 GPG 密钥无效或下载失败执行apt update时如果提示The following signatures couldnt be verified because the public key is not available多半是 GPG 密钥没有正确下载。常见原因有两个一是下载时网络不通导致文件内容为空二是文件名或路径与 source list 里的 signed-by 不一致。排查方式很直接ls -l /etc/apt/keyrings/docker.asc file /etc/apt/keyrings/docker.asc如果文件大小是 0 或者内容不是 ASCII 文本删除后重新下载。如果网络下载失败可以考虑换个时间段重试或者使用代理镜像源。注意不要把 source list 里的 signed-by 路径写错我之前因为把路径写成了相对路径导致 apt 找不到密钥报错信息完全牛头不对马嘴。6.2 apt update 时签名报错与 Ubuntu 版本的坑添加 Docker 仓库后apt update报错The repository ... does not have a Release file或者 404 错误大概率是仓库地址里的版本代号写错了。我在升级系统时遇到过这样的问题系统从 22.04 升级到 24.04 后旧的 Docker 仓库源还是 jammy但系统实际是 nobleapt 找不到对应的包索引。解决办法是重新生成 source list 中的版本代号sudo rm /etc/apt/sources.list.d/docker.list # 用前文的方式重新添加仓库确保 $(. /etc/os-release echo $VERSION_CODENAME) 输出的是当前版本所以在添加仓库时务必使用动态获取版本代号的写法而不是把 jammy 或 noble 写死。6.3 WSL2 内存占用过高导致构建卡死WSL2 默认会使用宿主机最多 50% 的内存对于 16GB 内存的机器来说就是 8GB。执行大型镜像构建或同时跑多个容器时内存经常被打满构建过程直接 OOM。我一开始以为是 Docker 的问题配置了 swap 也没用后来才发现是 WSL2 的全局内存限制。解决办法是在 Windows 用户目录下创建.wslconfig文件[wsl2] memory8GB processors4 swap4GB保存后执行wsl --shutdown再重新进入。这里有个小细节swap配置只在 WSL2 内核里生效如果你在 daemon.json 里也配置了 Docker 层面的 swap需要注意两层限制取最小值。实测配置.wslconfig后构建过程稳定很多不会再因为内存不足而中断。6.4 containerd 与 docker-ce 版本不匹配如果执行docker run时提示failed to create containerd task: OCI runtime create failed很大概率是 containerd 和 docker-ce 版本之间存在不兼容。这个坑在添加 Docker 官方仓库时比较容易遇到因为apt install docker-ce只安装当前仓库里最新的 docker-ce但 containerd.io 可能是旧版本缓存在本地。解决办法很简单显式升级全部 Docker 相关组件到统一版本sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin如果依然报错尝试清除缓存后重新安装sudo apt autoremove sudo rm -rf /var/lib/docker sudo apt install --reinstall docker-ce containerd.io注意最后一步会删除所有本地镜像和容器数据操作前确认没有需要保留的数据。6.5 容器端口在 Windows 侧无法访问在 WSL2 里启动了一个端口为 8080 的容器Windows 浏览器却访问不到。这个问题的根源在于 WSL2 的 NAT 网络模式从 Windows 访问 WSL2 内的服务通常是通过 localhost 转发不一定总是立即生效。我遇到的情况是 WSL2 IP 变了导致旧会话里的连接目标失效。解决思路# 在 WSL 里查看当前 IP hostname -I # 在 Windows PowerShell 里查看是否能连上 Test-NetConnection -ComputerName WSL_IP -Port 8080如果 IP 通了把浏览器地址改成 WSL IP 访问。如果 localhost 能通而 IP 不能通检查 Docker 容器的端口映射参数确认没有遗漏-p 8080:8080。另外Windows 防火墙有时候会拦截对 WSL 虚拟网卡的访问需要检查入站规则。如果希望固定 IP可以在 .wslconfig 里配置networkingModemirrored让 WSL2 直接共享宿主机的网络接口这样 localhost 转发更稳定。6.6 WSL2 文件挂载性能与代码目录选择把项目代码放在 Windows 文件系统里然后通过/mnt/c/...路径挂载进容器开发时保存代码会有明显的延迟大型项目尤其明显。这是 WSL2 跨文件系统读写9P 协议的固有开销不是 Docker 能解决的。我的经验是把项目代码放在 WSL2 内部文件系统也就是~/projects下然后用 Windows 侧的资源管理器通过\\wsl$\Ubuntu-22.04\home\user\projects路径访问。这样 Docker build 和容器挂载都在 Linux 文件系统内完成性能提升非常明显。实测在同一个项目上放在 WSL 内部后的 Docker build 时间比放在 /mnt/c 下快了三倍以上。如果你的项目仓库已经 clone 在 Windows 侧可以用git clone重新克隆一份到 WSL 内或者用cp -r复制过去之后 git 操作在 WSL 内进行。数据一致性方面WSL2 的虚拟磁盘安全性足以应对日常开发不用担心文件丢失。最后分享一个我这段时间用得最顺手的组合VS Code 的 WSL 扩展 Windows Terminal WSL2 原生 Docker Engine。在 WSL 里打开 VS Code 会自动进入远程开发模式终端直接敲 docker 命令容器管理、代码编辑、调试全部在同一个窗口完成这套组合目前是我的 Windows 开发主力方案。如果你也在 Windows 下做容器化开发希望这篇文章能帮你顺利搭好环境少踩几个坑。