你不需要在容器里再跑一个 Docker Daemon——通过挂载宿主机的 Docker Socket,容器内的 Docker CLI 可以直接调用宿主机的 Docker 守护进程,轻量、高效、零冗余。
适用场景
- 在 CI/CD 容器(如 GitLab Runner、Jenkins Agent)中构建 Docker 镜像
- 在开发容器(Dev Container)中进行容器化开发
- 在沙箱容器中临时操作宿主机 Docker
- 任何需要"容器内跑 Docker 命令"的场景
核心原理
很多人直觉上会想:"那我再在容器里装一个 Docker Engine 吧?"
不需要。 Docker 采用客户端-服务端架构:
┌──────────────────────────────────────────────────────────┐
│ 宿主机 │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Docker Daemon (dockerd) │ │
│ │ 负责拉取镜像、创建容器、管理网络等 │ │
│ └────────────────────────┬───────────────────────────┘ │
│ │ Unix Socket │
│ /var/run/docker.sock │
└───────────────────────────┼──────────────────────────────┘
│ bind mount
┌───────────────────────────┼──────────────────────────────┐
│ 容器内 │ │
│ ┌─────────────────────────▼──────────────────────────┐ │
│ │ Docker CLI(docker 命令) │ │
│ │ 只是客户端,通过 socket 把请求发给宿主机 daemon │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ 容器内所有 docker 操作,本质上都是在宿主机上执行 │
└──────────────────────────────────────────────────────────┘
关键点:Docker CLI 只是一个几百 KB 的命令行工具,不需要后台进程。 它通过 /var/run/docker.sock 与 Docker Daemon 通信。你只需要把这个 socket 文件挂进容器,再把 CLI 装进去就够了。
环境准备
宿主机的 Docker Compose 配置
在你的 Compose 文件中,确保以下两个关键配置:
services:
dev:
image: your-image:latest
# 【关键1】使用 host 网络模式(让容器能访问宿主机网络)
network_mode: host
# 【关键2】挂载 Docker 套接字(让容器内能操作宿主机的 Docker)
volumes:
- /var/run/docker.sock:/var/run/docker.sock
network_mode: host的原因:宿主机 Docker 创建的容器默认走宿主机网络栈。如果不用 host 模式,你的容器里虽然能看到这些容器,但网络拓扑会不同,端口映射关系也会混乱。开启后,localhost的视角与宿主机一致。
第一步:确认挂载点
进入容器后,先确认 socket 文件已经存在:
ls -la /var/run/docker.sock
预期输出:
srw-rw---- 1 root root 0 Jul 16 03:50 /var/run/docker.sock
看到这个文件就说明挂载成功。srw- 开头的 Unix Socket 类型是正确的。
第二步:安装 Docker CLI
容器内有两种方式安装 Docker CLI:
方式 A:通过 Docker 官方 apt 源安装(推荐)
这个方式安装的 CLI 版本与宿主机高度一致,且同时获得 docker compose 和 docker buildx 插件。
# 1. 安装基础依赖
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
# 2. 添加 Docker 官方 GPG 密钥
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# 3. 添加 Docker 官方 apt 源
echo "deb [arch=$(dpkg --print-architecture) \
signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 4. 更新包索引并安装 docker-ce-cli(仅 CLI,不含 daemon)
sudo apt-get update
sudo apt-get install -y docker-ce-cli
安装完成后会同时得到三个包:
| 包 | 说明 |
|---|---|
docker-ce-cli | Docker 主客户端(docker 命令) |
docker-compose-plugin | 提供 docker compose 命令(V2 语法) |
docker-buildx-plugin | 提供 docker buildx 命令(多平台构建) |
方式 B:通过系统自带源安装(够用即可)
sudo apt-get update
sudo apt-get install -y docker.io
系统源的版本通常较旧,但 docker run、docker ps 等基础功能都能正常使用。如果不需要最新特性,这种方式更简单。
第三步:解决权限问题
3.1 当前状态
默认情况下,/var/run/docker.sock 的权限如下:
srw-rw---- 1 root root ... /var/run/docker.sock
只有 root 用户能直接访问。普通用户执行 docker ps 会报错:
Got permission denied while trying to connect to the Docker daemon socket
3.2 解决方案
方法一:修改 socket 所属组(推荐)
# 创建 docker 用户组(如果不存在)
sudo groupadd -f docker
# 将当前用户加入 docker 组
sudo usermod -aG docker $USER
# 修改 socket 文件所属组为 docker,权限设为 660
sudo chown root:docker /var/run/docker.sock
sudo chmod 660 /var/run/docker.sock
修改后确认:
ls -la /var/run/docker.sock
# 预期输出:srw-rw---- 1 root docker ... /var/run/docker.sock
660权限的含义:owner(root)可读写,group(docker)可读写,others 无权限。加入 docker 组的用户即可直接访问。
方法二:当前 shell 立即生效
加入新组后,需要重新登录才能在当前终端生效:
newgrp docker
如果 newgrp 不方便,也可以在单条命令中使用:
sg docker -c "docker ps"
方法三:临时使用 sudo(立即可用)
在权限还没配好之前,随时可以用:
sudo docker ps
sudo docker run --rm hello-world
第四步:验证安装
4.1 查看版本
docker version
预期输出(展示 Client 与 Server 分别对应什么):
Client: Docker Engine - Community
Version: 29.6.2 ← 容器内装的 CLI 版本
API version: 1.55
...
Server: Docker Engine - Community
Engine:
Version: 29.6.1 ← 宿主机的 Docker 版本
OS/Arch: linux/amd64
...
Client 和 Server 版本可以有微小差异(29.6.2 vs 29.6.1),只要 API 版本兼容即可正常使用。
4.2 查看宿主机 Docker 信息
docker info --format "OS: {{.OperatingSystem}}
Arch: {{.Architecture}}
Kernel: {{.KernelVersion}}
Containers: {{.Containers}} total
Images: {{.Images}} total
CPUs: {{.NCPU}}"
你会看到宿主机的真实信息,而不是容器内的信息。
4.3 运行一个测试镜像
docker run --rm hello-world
如果看到:
Hello from Docker!
This message shows that your installation appears to be working correctly.
说明一切正常。
4.4 查看宿主机上的容器
docker ps -a
你会看到宿主机上所有的容器,包括你当前所在的容器。
完整安装脚本
把以下脚本在容器内执行,即可一键完成全部配置:
#!/usr/bin/env bash
set -e
echo "=== 检查 Docker Socket ==="
if [ ! -S /var/run/docker.sock ]; then
echo "错误:/var/run/docker.sock 不存在,请检查挂载配置"
exit 1
fi
echo "Socket 存在: $(ls -la /var/run/docker.sock)"
echo ""
echo "=== 安装 Docker CLI ==="
sudo apt-get update -qq
sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update -qq
sudo apt-get install -y docker-ce-cli
echo ""
echo "=== 配置权限 ==="
sudo groupadd -f docker
sudo usermod -aG docker $USER
sudo chown root:docker /var/run/docker.sock
sudo chmod 660 /var/run/docker.sock
echo ""
echo "=== 验证安装 ==="
sudo docker version --format 'Client: {{.Client.Version}} | Server: {{.Server.Version}}'
echo ""
echo "=== 安装完成!==="
echo "执行以下命令使 docker 组生效:"
echo " newgrp docker"
echo ""
echo "然后就可以直接使用 docker 命令了:"
echo " docker ps"
echo " docker run --rm hello-world"
常见问题
Q1:为什么不能直接 apt install docker 而不加 -ce?
可以,但系统源里的 docker.io 版本较旧,且默认会附带 Docker Daemon(dockerd),体积更大、启动更慢。而我们只需要 CLI,用 docker-ce-cli 更精准。
Q2:容器内创建的容器,宿主机的 docker ps 能看到吗?
能。 因为容器内和宿主机操作的是同一个 Docker Daemon,创建的容器、网络、卷都是宿主机级的,完全共享。
Q3:容器内 localhost:8080 能访问到宿主机上的容器吗?
如果宿主机上的容器使用了端口映射(如 -p 8080:80),那么在容器内访问 localhost:8080 是可以的,因为 network_mode: host 让容器和宿主机共享网络栈。
Q4:docker compose 能用吗?
装好 docker-ce-cli 后,docker-compose-plugin 会一并安装,docker compose(V2 语法,带空格的版本)可以直接使用。
Q5:如果容器内没有 newgrp 命令怎么办?
用 sg 替代:
sg docker -c "docker ps"
Q6:Docker Socket 权限改回 root:root 了怎么办?
如果容器重启后 socket 权限被重置,可以在容器启动脚本中自动修复:
sudo chown root:docker /var/run/docker.sock 2>/dev/null || true
sudo chmod 660 /var/run/docker.sock 2>/dev/null || true
Q7:这种方法支持 Docker 桌面版(Mac/Windows)吗?
不支持。 Docker 桌面版使用虚拟机,没有直接暴露 /var/run/docker.sock。请使用 Docker Engine 的 Linux 发行版环境。
安全提示
持有 Docker Socket 的访问权限 ≈ 拥有宿主机的 root 权限:
- 可以通过
docker run -v /:/host ...挂载宿主机根目录 - 可以访问宿主机上的所有网络和进程
- 可以执行任意特权操作
因此:
| ✅ 建议 | ❌ 避免 |
|---|---|
| 在可信环境(个人开发机、内部 CI)中使用 | 不要在生产环境随意暴露 Docker Socket |
| 使用专用的 Docker 用户组管理权限 | 不要将 Socket 挂载给不受信任的用户 |
| 容器网络尽量使用 host 模式时评估安全风险 | 不要把 Docker Socket 挂载到公共镜像中 |
总结
| 步骤 | 操作 | 一句话 |
|---|---|---|
| 1 | 挂载 /var/run/docker.sock | 让容器能"看到"宿主机的 Docker |
| 2 | 安装 docker-ce-cli | 装一个 Docker 客户端,不需要 daemon |
| 3 | 将用户加入 docker 组 | 解决 socket 权限问题 |
| 4 | docker ps 验证 | 看到宿主机容器列表即为成功 |
容器内没有 Docker Daemon,容器内也不需要 Docker Daemon。宿主机上的那个才是真的,容器内的 CLI 只是它的远程控制面板。

评论