Docker in Docker 实战
在容器化部署中,经常需要在容器内再运行容器。本文详细介绍 Docker-in-Docker 的两种实现方式及其完整配置过程。
一、两种实现方案对比
| 实现方式 | 原理 | 特点 | 网络视角 |
|---|---|---|---|
| DooD (Docker-outside-of-Docker) | 将宿主机的 /var/run/docker.sock 挂载到主容器,主容器的 docker 命令实际操作宿主机守护进程 |
轻量、性能好,但隔离性弱,主容器拥有宿主机 Docker 的"生杀大权" | 子容器是宿主机上主容器的"兄弟",共享宿主机网络命名空间 |
| DinD (Docker-in-Docker) | 主容器内运行完整的独立 Docker 守护进程(通常使用 docker:dind 镜像) |
隔离性强、更安全,但需要 --privileged 特权模式,资源开销大 |
子容器是完全的"孙子辈",运行在独立网络命名空间中 |
方案选择 :DooD 方式与直接在宿主机创建容器区别不大,本文采用 DinD 方案 ,以 log_viewer_deployer 模块为例进行实战演示。
二、环境准备:在容器中安装 Docker
2.1 基础安装步骤
bash
# 更新软件源
sudo apt-get update
# 安装前置依赖
sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
# 添加 Docker 官方 GPG 密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# 添加 Docker 官方软件源
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 安装 Docker 全家桶
sudo apt update && sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 给当前用户添加 Docker 权限
sudo usermod -aG docker $USER
# 验证安装
docker --version
docker compose version
2.2 国内源加速(可选)
如果在国内,建议使用阿里云镜像源:
dockerfile
# Dockerfile 中使用
RUN curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null \
&& apt-get update \
&& apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin \
&& rm -rf /var/lib/apt/lists/*
三、核心问题:启动 Docker 守护进程
3.1 问题现象
安装完成后,在主容器中构建子容器时报错:
ERROR: failed to connect to the docker API at unix:///var/run/docker.sock;
check if the path is correct and if the daemon is running:
dial unix /var/run/docker.sock: connect: no such file or directory
3.2 问题分析
观察发现 /var/run 路径下没有 docker.sock 文件,原因是 Docker 守护进程(dockerd)未启动。
3.3 解决方案
在 Docker Compose 配置文件中通过 command 启动 dockerd 服务:
yaml
services:
log_viewer:
container_name: log_dind
image: log:v2.0.0
privileged: true
ports:
- "2400:2400"
- "2401:2401"
- "2402:2402"
restart: always
volumes:
- /etc/localtime:/etc/localtime:ro
- /tmp/.X11-unix:/tmp/.X11-unix
- ./code:/root/code
- /data/log:/root/log
- /home:/root/home
- docker_data:/var/lib/docker
environment:
- DISPLAY
- QT_X11_NO_MITSHM=1
- TZ=Asia/Shanghai
- CONTAINER_TIMEZONE=Asia/Shanghai
tty: true
command: >
sh -c "
echo 'Starting dockerd...';
dockerd > /var/log/docker.log 2>&1 &
DOCKERD_PID=$$!;
echo 'Waiting for Docker daemon to be ready...';
while ! docker info >/dev/null 2>&1; do sleep 1; done;
echo 'Docker is ready. Executing run.sh...';
sh /root/code/run.sh;
echo 'run.sh finished. Waiting for dockerd to exit...';
wait $$DOCKERD_PID;
"
volumes:
docker_data:
关键点:
privileged: true- 必须授予特权模式- 后台启动 dockerd 并等待其就绪
- 使用
docker_data卷持久化 Docker 数据
四、完整 Dockerfile 示例
dockerfile
FROM ubuntu:22.04
# 安装基础工具
RUN apt-get update && apt-get install -y \
git \
vim \
wget \
tzdata \
ntp \
net-tools \
sudo \
ssh \
python3.10 \
python3-pip \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common \
lsb-release \
&& rm -rf /var/lib/apt/lists/*
# 使用阿里云镜像源安装 Docker
RUN curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null \
&& apt-get update \
&& apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin \
&& rm -rf /var/lib/apt/lists/*
五、子容器网络配置
5.1 子容器 Dockerfile SSH 配置
dockerfile
# 修改 SSH 端口为 2401
RUN sed -i 's/#Port 22/Port 2401/' /etc/ssh/sshd_config
# 允许 root 登录
RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
5.2 网络模式选择
子容器必须使用 host 网络模式:
yaml
services:
sub_container:
image: sub_image:v1.0.0
network_mode: host # 关键配置
六、调试命令参考
bash
# 进入主容器
docker exec -it log_dind bash
# 构建子容器
cd /root/code/sub_docker/task_spliter_deploy
./build_container.sh
# 查看网络监听端口
netstat -tlnp
# SSH 连接报错时清理已知主机
ssh-keygen -f "/home/lx/.ssh/known_hosts" -R "[127.0.0.1]:2400"
# 查看 SSH 端口配置
cat /etc/ssh/sshd_config | grep Port
# 手动启动 SSH 服务(如服务意外停止)
/usr/sbin/sshd
七、踩坑记录:SSH 服务中断问题
问题现象
子容器构建后,主容器的 2400 端口停止监听,导致无法从宿主机访问主容器:
tcp 0 0 0.0.0.0:2401 0.0.0.0:* LISTEN 8282/sshd # 只有子容器端口
临时解决
在主容器中手动执行:
bash
/usr/sbin/sshd
执行后 2400端口恢复监听:
tcp 0 0 0.0.0.0:2400 0.0.0.0:* LISTEN 17027/sshd # 主容器 SSH 恢复
tcp 0 0 0.0.0.0:2401 0.0.0.0:* LISTEN 8282/sshd # 子容器 SSH
待解决问题
疑问:为什么子容器构建后主容器的 sshd 服务会中断?
可能的原因:
- 子容器使用 host 网络模式时,端口冲突导致服务重启
- 子容器内的某些操作影响了宿主进程
- 资源竞争导致服务异常
八、总结:DinD 配置要点
主容器配置
- ✅ 镜像构建时安装 Docker 和 Docker Compose
- ✅ 启动命令行中运行 dockerd 服务(否则
/var/run/docker.sock不存在) - ✅ 设置
privileged: true授予特权 - ✅ 配置端口映射:
"2400:2400","2401:2401" - ✅ 挂载 Docker 数据卷:
docker_data:/var/lib/docker
子容器配置
- ✅ 使用
network_mode: host共享网络命名空间 - ✅ 修改 SSH 端口避免冲突
- ✅ 配置 SSH 允许 root 登录
网络架构示意
宿主机
├── 主容器 (log_dind)
│ ├── dockerd (独立守护进程)
│ ├── SSH (2400 端口 → 映射到宿主机 2400)
│ └── 子容器
│ ├── SSH (2401 端口,host 模式)
│ └── 应用服务
└── 其他容器
参考资料
- Docker 官方文档:https://docs.docker.com/
- Docker-in-Docker 最佳实践:https://www.docker.com/blog/docker-can-now-run-within-docker/