简介:在Windows 10的WSL(Windows Subsystem for Linux)中运行Docker时,常会遇到“Cannot connect to the Docker daemon at unix:///var/run/docker.sock.”的报错,本资源针对该问题提供了一份详细的排查与解决文档,特别适合使用Ubuntu 18.04子系统的开发者和运维人员。内容基于实际安装Docker过程中遇到的权限、守护进程及Cgroups配置等问题,梳理了完整的错误成因分析及多种解决思路,涵盖使用systemctl或service检查服务状态、添加用户到docker组后的权限生效、通过cgroupfs-mount挂载控制组、重启守护进程及更新重装Docker等操作指引,能为读者提供清晰的排错方向。资源以1个PDF文件呈现,整体大小仅38KB,便于快速查阅。已有超过14000人学习浏览,是一份针对WSL环境下Docker启动失败的实用参考笔记。
1. 不是命令写错了,是 WSL 里 Docker daemon 压根没起来:先搞清这个报错再说
在 Windows 10 的 WSL(Ubuntu 18.04)里装完 Docker,执行docker images却甩来一句Cannot connect to the Docker daemon at unix:///var/run/docker.sock,遇到这个报错的十有八九是刚接触 WSL 的新手。我最初也以为是安装命令敲错了,实际上apt install docker.io、usermod -aG docker这套流程本身没问题,真正的坑在于:WSL 不是完整 systemd 环境,Docker daemon 经常处于“看似装了、实际没跑”的状态,连带着 cgroup 挂载也是坏的。这篇文章把诊断思路、修复命令和 WSL1/WSL2 的版本差异一次说透,适合想在 Win10 里把 Docker 真正用起来、而不是只装个壳的从业者。
2. 拆解 Docker daemon 连接链路:socket、权限和 cgroup 三件事挨个查
2.1 先理解unix:///var/run/docker.sock是什么
Docker 采用客户端-守护进程架构,docker这个命令行工具只是个客户端,真正干活的是后台的dockerd守护进程。客户端和守护进程之间通过一套 REST API 通信,在 Linux 默认走的就是 Unix socket:/var/run/docker.sock。
这句话翻译过来是:你敲docker images时,客户端尝试连接这个 socket 文件,但连接失败。失败只有两种可能:socket 文件不存在(也就是守护进程没跑),或者 socket 文件在但没有访问权限。很多人一看到报错就去重装 Docker,从头折腾一遍,其实方向完全跑偏了。正确做法是先确认 daemon 是否活着。
用以下命令查看守护进程状态:
# 查看 docker daemon 进程是否存在(更贴近底层) ps aux | grep dockerd # 或者用 service 命令查服务状态 sudo service docker status如果ps输出里看不到dockerd,或者service docker status显示dockerd is not running,那就可以判定:daemon 根本没启动,后续所有权限问题都谈不上。常见做法是先sudo service docker start,再立刻看ps aux | grep dockerd确认进程是否稳定存活。有些情况下进程启动了但瞬间退出,说明配置或环境有问题,这就要继续往下查。
2.2 用户组权限:加了 docker 组不一定立刻生效
在我的排查顺序里,第二步是确认当前用户在不在 docker 组里。你之前执行过sudo usermod -aG docker leo,这个命令本身是对的,但存在一个容易忽略的细节:用户组变更不作用于当前已打开的会话。
# 查看当前用户属于哪些组 id leo # 如果输出中看不到 docker,说明组变更没生效 # 如果能看到 docker 组,继续检查 socket 文件权限 ls -l /var/run/docker.sock正常情况下 socket 文件权限是srw-rw---- root docker,也就是 root 用户和 docker 组成员才有权访问。如果你id命令里能看到 docker 组,但访问仍被拒绝,多半是当前 WSL 会话的用户组信息没刷新。解决办法是彻底重启 WSL,不是简单关掉窗口,而是在 Windows 的 PowerShell 里执行wsl --shutdown,然后再重新进入 Ubuntu 子系统。我一般会再跑一遍id确认 docker 组已经生效,再做下一步。
2.3 WSL 的 cgroup 挂载:最容易翻车的隐藏关卡
WSL 不是一个完整 Linux 内核环境,它通过一组内核接口和 Windows 内核沟通,很多原本由 systemd/init 系统自动完成的初始化工作都得手动补。cgroup(控制组)文件系统挂载就是最典型的一项。
Docker 依赖 cgroup 来管理容器的 CPU、内存等资源配额,如果 cgroup 没挂载,daemon 即使强行启动也会运行失败。检查挂载状态:
# 查看 cgroup 是否已经挂载 mount | grep cgroup如果输出为空或者只有零散几条,说明挂载不完整。在 WSL 里手动挂载的常见做法是使用cgroupfs-mount这个工具,它把标准 cgroup 文件系统按固定规则挂载到/sys/fs/cgroup下。你可以在安装了该工具后用一行命令修复:
# 安装 cgroupfs-mount(如果尚未安装) sudo apt install -y cgroupfs-mount # 执行挂载 sudo cgroupfs-mount执行完手动挂载后,必须重启 Docker 守护进程才能让它重新读取 cgroup 状态。一句话总结这一章:报错是表象,daemon 没起来是主因,权限和 cgroup 是常见的隐性帮凶,按这个顺序排查能省下大量试错时间。
3. 三个命令把 Docker daemon 救活:从挂载 cgroup 到重启服务
3.1 标准修复流程:cgroupfs-mount 与 service docker restart
先说你最关心的可抄作业环节。整套修复流程在我的 WSL(Ubuntu 18.04)上验证过,核心就是两个命令加一次 WSL 重启,操作全部在 WSL 终端里执行。
# 第一步:以 root 权限重新挂载 cgroup sudo cgroupfs-mount # 第二步:重启 docker 服务 sudo service docker restart # 第三步:验证 daemon 是否可连接 docker images这段逻辑很直白:cgroupfs-mount修复 Docker daemon 启动依赖的底层资源限制框架,service docker restart让守护进程带着完整的 cgroup 视图重新初始化,最后用docker images做连通性验证。如果你的环境里没有cgroupfs-mount,会提示 command not found,需要先安装:
sudo apt update sudo apt install -y cgroupfs-mount这里有一个参数层面的细节值得展开:service docker restart这个命令在 WSL 里往往比systemctl restart docker更可靠,原因在于 WSL 的 init 进程是定制的,systemd 的那套 unit 管理机制在多数 WSL 配置下并不完整,service命令走的是 SysV init 脚本路径,对 WSL 的适配更成熟。少部分 WSL 版本里 systemd 也是可用的,但那通常需要额外编辑/etc/wsl.conf开启,不是默认状态。
3.2 为什么必须先重启 WSL 再重启 Docker
在上面修复流程里有一个容易被跳过的关键动作:执行完sudo usermod -aG docker leo之后,需要重启 WSL 才能让用户组变更生效。重启用的是 Windows 侧的命令,不是在 WSL 里执行shutdown。
# 这是在 Windows 的 PowerShell 或 CMD 里执行,不是在 WSL 里 wsl --shutdown # 然后重新打开 Ubuntu 终端wsl --shutdown会终止当前所有 WSL 发行版的运行实例,包括正在后台跑的任何进程。下次启动 Ubuntu 时会重新初始化内核态环境,用户组信息也会重新加载。如果不做这一步,即使你执行了sudo cgroupfs-mount和sudo service docker restart,后续用docker images时仍然可能因为组权限没刷新而连接失败。
3.3 修复失败时的诊断手段
这套流程大概率能解决你当前的问题,但如果你遇到的是变体场景,比如 cgroup 挂载后 Docker 依然无法启动,那就需要看守护进程日志了:
# 前台启动 dockerd,让日志直接打到终端 sudo dockerd --debug # 或者查看系统日志 sudo tail -f /var/log/docker.log我一般用第一种方式,因为--debug参数会输出完整调用链,能明确看出是 cgroup 报错、iptables 报错还是网络命名空间创建失败。注意如果dockerd已经在运行,直接执行上述命令会报端口被占用,需要先sudo service docker stop停掉再试。日志中如果出现failed to mount cgroup,说明 cgroupfs-mount 没有真正生效,需要手动检查/sys/fs/cgroup目录是否存在;如果出现iptables相关错误,多半是 WSL 网络层配置问题,这个问题我会在第 5 章详谈。
4. WSL1 与 WSL2 的 Docker 差异:同一条报错,两种处理思路
4.1 WSL1 里 Docker 只能算“半可用”
说实话,你现在能跑通 cgroupfs-mount 并让 Docker 工作,说明用的是 WSL1 或旧版内核兼容层。WSL1 的架构是 API 翻译层,不走虚拟化,对 Linux 内核特性的支持并不完整。Docker 依赖的 cgroup、overlayfs、iptables 都属于比较底层的内核能力,WSL1 里要么缺失、要么模拟得别别扭扭。
在 WSL1 下,Docker 的典型表现就是:安装没问题、启动时好时坏、容器网络经常不通。解决报错靠的是各种手工补丁(cgroupfs-mount 就是其中最重要的一个),这也是为什么你搜到的答案几乎都指向这个工具。但也正因如此,WSL1 里的 Docker 只适合做初学者练习或轻量验证,真跑复杂服务我强烈不建议。
4.2 WSL2 的 Docker:优先装 Docker Desktop 还是直接在发行版里装 docker.io
WSL2 用了真正的轻量虚拟机,内核几乎完整,cgroup、overlayfs 都原生支持,理论上直接apt install docker.io也能跑通。但这里有个实际体验差异:在 WSL2 里我一般优先用 Windows 侧安装的 Docker Desktop,开启 WSL2 backend 之后,在 Ubuntu 子系统里直接执行docker命令就能连通到 Windows 侧 daemon,省去了 WSL 内部管理服务进程的麻烦。
下表列出两种方案的差异,方便按自己的使用习惯选择:
| 对比项 | 方案 A:Docker Desktop + WSL2 backend | 方案 B:WSL2 内直接装 docker.io |
|---|---|---|
| daemon 运行位置 | Windows 服务,独立于 WSL 生命周期 | WSL 发行版内部进程 |
| 启动方式 | Docker Desktop 自启,WSL 里即开即用 | 需要手动service docker start |
| cgroup 处理 | 由 Docker Desktop 接管,无需手动挂载 | 可能需要 cgroupfs-mount 或 systemd 支持 |
| 资源占用 | 较高(要跑一个虚拟机) | 较低 |
| 适合人群 | 希望零配置、高度稳定的从业者 | 对资源占用敏感、愿意手动维护的人 |
方案 A 的一个明显优势是:你不会再遇到Cannot connect to the Docker daemon这个报错,因为 daemon 的启动不依赖 WSL 内部 init 机制。方案 B 则保留了更纯粹的 Linux 环境,适合模拟生产环境时练习。
4.3 如何确认你当前用的是 WSL1 还是 WSL2
判断版本很简单。在 Windows 的 PowerShell 里执行:
wsl -l -v输出会列出每个发行版的名称和版本号。看到VERSION列是 1 就表示 WSL1,是 2 就是 WSL2。如果你的发行版是 WSL1 且想升级到 WSL2,可以用以下命令:
# 先确保 Windows 的虚拟化平台功能已开启 wsl --set-version Ubuntu-18.04 2升级过程中需要把整个根文件系统从 API 翻译层转换为虚拟磁盘文件,耗时从几分钟到十几分钟不等。升级前注意备份重要数据,我见过有人在这步因为磁盘空间不足导致转换失败,所以先检查一下 C 盘剩余空间是否充足。
5. 避坑记录:Docker 在 WSL 里翻车的四个高频问题与处理
5.1 现象:service docker start 显示进程已启动,但 docker images 依然报连接失败
原因:service docker start只是触发了 init 脚本,但 dockerd 在启动过程中因为 cgroup 挂载缺失等原因崩退。WSL1 环境下很常见。
解决:先执行sudo cgroupfs-mount补挂载,再执行sudo service docker restart,不要只 start 不 restart。如果仍然反复崩溃,用sudo dockerd --debug前台启动,观察具体报错点是 cgroup 还是 iptables。
5.2 现象:用户已经在 docker 组里但仍提示 permission denied
原因:WSL 的会话生命周期和普通 Linux 不同,usermod执行后的组信息不会自动同步到当前会话。即使新开一个 Ubuntu 窗口,有时跑到的仍是旧会话缓存。
解决:在 PowerShell 执行wsl --shutdown,等待几秒后重新进入 WSL,再用id命令确认 docker 组已出现。注意执行完wsl --shutdown后所有后台进程都会被终止,如果之前有挂着跑的脚本,先保存好数据。
5.3 现象:执行 cgroupfs-mount 提示 command not found
原因:apt 源里没有安装这个工具。它是单独的软件包,不会随 docker.io 自动装入。
解决:先sudo apt update,再sudo apt install -y cgroupfs-mount。如果安装后执行sudo cgroupfs-mount仍然报错,检查/sys/fs/cgroup目录是否已存在,若存在则先sudo umount /sys/fs/cgroup再挂载。
5.4 现象:Docker 能被启动,但容器内部网络不通,ping 外网失败
原因:WSL 的 NAT 网络模式与 Linux 原生的 iptables 规则不兼容,Docker 创建网桥时设置的 iptables 规则没有真正生效。
解决:在 WSL2 环境下优先用 Docker Desktop 的 WSL backend,避免自行管理网络栈;在 WSL1 环境下,如果只是本地开发,可以把容器端口映射到localhost使用,跨主机访问不要依赖容器网络,直接用宿主机端口转发。
6. 从修好到固化:写一个一键启动脚本,让 Docker 在 WSL 里不再“重启就消失”
折腾到这里,你的 Docker 应该能跑起来了。但 WSL 和普通 Linux 服务器有一个本质区别:WSL 每次重新进入都会重建部分内核态环境,也就是说你今天手动执行的挂载、启动步骤,明天关机后再打开又得重来。把修复流程固化成脚本,才是一劳永逸的做法。
在~/.bashrc末尾追加一段逻辑,让每次打开 WSL 终端时自动检查 Docker 状态:
# 自动修复 Docker daemon 状态,追加到 ~/.bashrc 末尾 if ! docker info >/dev/null 2>&1; then echo "Docker daemon 未运行,尝试自动修复..." sudo cgroupfs-mount sudo service docker start sleep 2 docker info >/dev/null 2>&1 && echo "Docker 已就绪" || echo "Docker 修复失败,请手动排查" fi这段脚本的思路是:每次新开终端时先检查 Docker daemon 是否可连,不可连才执行修复动作。docker info是比docker images更完整的连通性检查,它会向 daemon 请求完整的系统信息,只要有任意一项异常就会非零退出。sleep 2是必要的等待时间,因为service docker start执行后守护进程需要几百毫秒到一两秒才能进入就绪状态,不等就直接检查容易误判失败。
脚本里故意用了sudo service docker start而不是restart,原因在于 WSL 会话是短生命的,绝大多数情况下不存在需要先停止的旧进程,直接start更快更稳。如果你在同一个 WSL 实例里切换过多个终端,可能遇到“另一个终端已启动了服务,这个终端再 start 会报 already running”的情况,这不会影响使用,无需处理。
执行source ~/.bashrc或者重开终端后测试两次:第一次可以故意先sudo service docker stop,再走一遍完整脚本流程;第二次直接重开终端,确认脚本能自动检测并跳过启动步骤。从那以后我每装一个 WSL 环境都强制走一遍这套方案:先确认 WSL 版本,再决定用 Docker Desktop 还是原生 docker.io,不论哪条路,最后一定固化一个自检脚本。这样就不会再被Cannot connect to the Docker daemon这种初级问题打断思路。希望这篇拆解能帮你少走几趟弯路,把精力留到真正该用的容器编排和图像构建上去。
本文还有配套的精品资源,点击获取