应用场景
平时我们会使用 Docker 运行一些任务,这时我们需要手动部署这些 docker 的容器。但在某些场景下,完成部署工作的服务也是容器中的服务,例如在一个 Jenkins 服务中使用 docker 完成测试,而这个 Jenkins 服务本身也是用 docker 启动的,这时就需要在 Docker 中运行 Docker了
Docker 主要由 Docker cli 、Docker daemon 等部分组成,Docker cli 是我们在命令行中调用 docker run
,docker rm
时使用的客户端程序,然后转发到运行程序的 Docker daemon。
在 Docker 中运行 Docker 需要两个条件:
1、 Docker 镜像中包含命令行工具 Docker CLI
2、 使用这个镜像启动的容器中能访问到 Docker Daemon
方法1:挂载 docker.sock
如果我们在运行容器时将 host 上的 /var/run/docker.sock 挂载到容器中 /var/run/docker.sock,就能保证容器中的 Docker CLI 能够访问 host 上的 Docker Daemon。
例如我们使用下面的命令在 host 上启动一个容器:
$ docker run -ti -v /var/run/docker.sock:/var/run/docker.sock docker
此时是这样:
这时在 host 和 Container1 中运行 docker images
、docker ps
等指令会得到相同的输出。如果再次使用 docker run
创建新的容器,那么这个容器不是在 Container1 中,而会在 host 上。
方法2:使用 dind
Docker 开发组为了简化 Docker 的开发流程增加了 -privileged flag , 并开发了 dind 使得在 Docker 容器中也能使用 Docker。
Docker 在开发过程中需要使用 Docker 进行 build,这意味着开发者开发完新功能后,需要使用旧版本的 Docker build 新版的 Docker,然后关掉旧版的 Docker,用新版的替换。而使用 dind 则可以直接在旧版的 Docker 中嵌套新版的 Docker
这种方式使用了一个 Ubuntu 镜像,并在其中安装了 Docker。然后将容器中的 cgruops 挂载到了父容器同级的位置,这个过程由 wrapdocker 完成,因此相当于是在 Docker 中嵌套了一个 Docker,像这样:
使用下面的命令可以启动 dind:
docker run --privileged -d docker:dind
问题
1、 对于不同的操作系统,外部 Docker 和内部 Docker 的 Linux 安全模块的配置可能会冲突,导致程序崩溃。例如,dind 的实现并没有兼容在 Fedora 下使用 SELinux 的情况,如果设置了 SELINUX=enforce
就会崩溃。
> SELinux 是 Linux 的安全模块,控制程序获取资源的权限,并非所有 Linux 发行版都支持 SELinux。设置为 enforce 时会对文件和程序的 domain/type 进行限制,使用 getenforce 命令可以查看 SELinux 的模式和策略。详细说明:[www.nsa.gov/research/se…][www.nsa.gov_research_se] 和 [cn.linux.vbird.org/linux\_basic…][cn.linux.vbird.org_linux_basic]
![83\_4.png][83_4.png]
SELinux 运行的各组件之相关性(来源:鸟哥的 Linux 私房菜 -- 程序管理与 SELinux 初探)
2、 内外 Docker 使用的文件系统不同会造成冲突
3、 Docker 多层嵌套会导致文件描述符泄露
参考
- dind
- Docker in Docker? Can you run Docker inside a Docker container?
- ~jpetazzo/Using Docker-in-Docker for your CI or testing environment? Think twice.
- 鸟哥的 Linux 私房菜 — 程序管理与 SELinux 初探