在 CentOS 或 Ubuntu 上隔离部署两套独立的 Docker 环境,核心思路其实不是“装两个 Docker 守护进程”(那样太折腾且容易冲突),而是利用 Docker 的命名空间(Namespaces)和 cgroups 资源控制,结合 不同的 Socket 路径、数据目录和端口映射 来实现逻辑上的完全隔离。
简单来说,我们要跑两个 dockerd 实例,或者更优雅地——使用 Docker-in-Docker (DinD) 配合不同的上下文,但最稳妥、生产级常用的方案是:通过自定义 daemon.json 启动第二个 Docker 守护进程,绑定不同的 Unix Socket 和数据存储路径。
以下是具体操作步骤,以 Ubuntu 22.04 / CentOS Stream 9 为例:
一、 核心原理
默认情况下,Docker 使用 /var/run/docker.sock 作为通信接口,镜像和容器数据存放在 /var/lib/docker。
要隔离两套环境,我们需要:
- 第二套 Docker 守护进程:监听不同的 socket 文件(如
/var/run/docker-test.sock)。 - 独立的数据目录:避免镜像、容器、卷互相污染。
- 独立的网络命名空间:防止 IP 冲突。
- CLI 客户端切换:通过
DOCKER_HOST环境变量或 Docker Context 来切换使用哪一套环境。
二、 详细部署步骤
第一步:安装基础 Docker(用于开发环境)
假设你已经安装了标准的 Docker CE,这将成为你的 Dev 环境。
# Ubuntu/Debian
sudo apt update
sudo apt install -y docker.io
# CentOS/RHEL
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install -y docker-ce docker-ce-cli containerd.io
# 启动并设为开机自启
sudo systemctl enable --now docker
此时,docker ps 看到的是 Dev 环境的容器。
第二步:创建测试环境(Test Environment)
我们不再安装另一个 Docker 软件包,而是手动启动一个额外的 dockerd 实例。
1. 创建独立的数据目录和配置
# 创建测试环境的专用目录
sudo mkdir -p /opt/docker-test/data
sudo mkdir -p /etc/docker-test
# 生成测试环境的 daemon.json
cat > /etc/docker-test/daemon.json <<EOF
{
"data-root": "/opt/docker-test/data",
"exec-root": "/opt/docker-test/exec",
"log-driver": "json-file",
"storage-driver": "overlay2"
}
EOF
⚠️ 注意:
data-root是关键,它决定了镜像、容器层、卷都存哪里,彻底隔离文件系统。
2. 编写 systemd 服务文件(推荐方式,便于管理)
创建 /etc/systemd/system/docker-test.service:
[Unit]
Description=Docker Test Environment Daemon
Requires=docker.socket
After=network.target
[Service]
Type=notify
ExecStart=/usr/bin/dockerd
--config-file=/etc/docker-test/daemon.json
--host=unix:///var/run/docker-test.sock
--containerd=/run/containerd-test/containerd.sock
ExecReload=/bin/kill -s HUP $MAINPID
Restart=always
RestartSec=5
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TimeoutStopSec=0
KillMode=process
HasDirectories=/opt/docker-test/data
HasDirectories=/opt/docker-test/exec
[Install]
WantedBy=multi-user.target
💡 说明:
--host=unix:///var/run/docker-test.sock:指定新的 socket 路径,避免与主 Docker 冲突。--containerd=...:为 test 环境单独运行一个 containerd 实例,避免共享状态导致的问题。
3. 启动测试环境
# 重新加载 systemd
sudo systemctl daemon-reload
# 启动测试环境的 Docker
sudo systemctl enable --now docker-test
# 检查是否正常运行
sudo systemctl status docker-test
验证新 socket 是否存在:
ls -l /var/run/docker-test.sock
第三步:切换和使用两套环境
现在你有两套 Docker 环境:
- Dev 环境:默认,使用
/var/run/docker.sock - Test 环境:使用
/var/run/docker-test.sock
方法 1:使用 DOCKER_HOST 环境变量(临时切换)
# 查看 dev 环境容器
docker ps
# 查看 test 环境容器
DOCKER_HOST=unix:///var/run/docker-test.sock docker ps
# 在 test 环境中拉取镜像
DOCKER_HOST=unix:///var/run/docker-test.sock docker pull nginx
方法 2:使用 Docker Context(推荐,更优雅)
Docker CLI 支持 Context,可以像切换数据库连接一样切换环境。
# 创建 test context
docker context create test-env
--description "Test Environment"
--docker-host unix:///var/run/docker-test.sock
# 查看所有 context
docker context ls
# 切换到 test 环境
docker context use test-env
# 现在直接运行 docker 命令,默认就是 test 环境
docker ps
# 切回 dev 环境
docker context use default
第四步:网络隔离(可选但重要)
如果两个环境中的容器使用了相同的内部 IP 段(如 172.17.0.0/16),虽然它们在不同 network namespace 中不会冲突,但如果你希望完全隔离网络策略,可以为 test 环境指定不同的 bridge 网段。
在 /etc/docker-test/daemon.json 中添加:
{
"bip": "172.20.0.1/16",
"default-address-pools": [
{
"base": "172.20.0.0/16",
"size": 24
}
]
}
然后重启 docker-test 服务:
sudo systemctl restart docker-test
这样,test 环境的容器将使用 172.20.x.x 网段,而 dev 环境保持默认的 172.17.x.x,从网络层面也实现了隔离。
三、 常见问题与注意事项
1. 权限问题
确保运行 docker-test 服务的用户有权限访问 /opt/docker-test/data 和 socket 文件。通常 systemd 会以 root 运行,所以没问题。但如果用非 root 用户操作,需确保该用户属于 docker 组,并且对 test socket 有读权限。
sudo chmod 660 /var/run/docker-test.sock
sudo chown root:docker /var/run/docker-test.sock
2. 资源限制
两个 Docker 守护进程会竞争 CPU、内存和磁盘 I/O。建议在 systemd 服务文件中添加资源限制:
[Service]
MemoryMax=4G
CPUQuota=80%
3. 为什么不用 Portainer 或其他 UI?
Portainer 等工具可以管理多个 Docker 主机,但它们本质上是远程 API 调用。对于本地双环境隔离,上述方案更轻量、更安全,且不依赖第三方 Web 服务。
4. 性能影响
由于是两个独立的 dockerd + containerd 实例,系统开销会比单实例略高。但在开发和测试场景下,这种开销完全可以接受。
四、 替代方案对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| 双 dockerd 实例(本文方案) | 完全隔离,灵活,可分别配置网络、存储、插件 | 配置稍复杂,需手动管理服务 |
| Docker-in-Docker (DinD) | 适合 CI/CD 流水线,嵌套容器 | 性能损耗大,特权模式要求,不适合日常开发 |
| Podman / Rootless Docker | 无需守护进程,更安全 | 生态兼容性略差,部分企业级功能缺失 |
| 虚拟机/容器化整个 Docker | 最强隔离 | 资源消耗极大,运维复杂度高 |
总结
在 Linux 上隔离两套 Docker 环境,最务实的做法是:
- 保留默认 Docker 作为 Dev 环境。
- 通过 systemd 启动第二个
dockerd实例,绑定不同 socket 和数据目录。 - 使用
docker context或DOCKER_HOST变量切换环境。
这种方式既保证了开发和测试环境的完全独立(镜像、容器、网络、数据互不干扰),又避免了重装系统或虚拟机的沉重开销,是工程师日常工作中非常实用的技巧。
云技术博客