问题背景

许多人会有疑问, 为什么要在容器中运行Docker, 我认为主要有以下几个场景可能会需要在容器中运行Docker:

  1. Jekins或是其他CI工具, 这些工具可能本身就运行在Docker容器中, 但是执行测试工作时, 仍然需要启动容器. 理论上讲, 这种情况比较常见.
  2. 某些容器编排工具, 自身已经实现了Docker化. 举个例子来看: 比如你写了一个软件通过操纵Docker的API来构建Docker镜像, 而你又把这个软件使用Docker来运行, 此时, 一定是由容器内部的程序调用DockerAPI触发的构建操作.

两种运行方式

dnd

这是一种在容器中启动另一个docker进程的方法:

Using Docker-in-Docker for your CI or testing environment? Think twice.

github.com/jpetazzo/dind

README中也讲了是如何工作的:

1
2
3
4
5
6
7
8
9
The main trick is to have the --privileged flag. Then, there are a few things to care about:

1. cgroups pseudo-filesystems have to be mounted, and they have to be mounted with the same hierarchies than the parent environment; this is done by a wrapper script, which is setup to run by default;
2. /var/lib/docker cannot be on AUFS, so we make it a volume.

主要的技巧就是--privileged, 不过也有其他事情需要关心:
1. cgroups的pseudo文件系统需要被挂载, 并且他们也需要与父环境在相同的层级(cgroup我没有深入使用, 无法做解释了), 由脚本在初始化时实现.
2. /var/lib/docker 不能运行在AUFS文件系统上, 所以需要挂载, Docker镜像构建与运行都是在AUFS(新环境是overlay2居多), 个人建议, 如果一定要用dnd, 也不要挂载本机的`/var/lib/docker`,
而是单独找个空文件夹挂载为`/var/lib/docker`.

mount挂载docker.sock

这是我在工作中所采用的一种方式, 使用unix socket的好处就是, 你只要mount, 就可以与程序进行交互

比如, 我们直接在有Docker的机器中使用(查找所有容器):

1
2
3
4
5
6
7
8
9

~$ docker run -ti -v /var/run/docker.sock:/var/run/docker.sock alpine /bin/sh
> apk add curl

> curl --unix-socket /var/run/docker.sock http:/containers/json

# 如果不行, 请用下面的语句

> curl --unix-socket /var/run/docker.sock http:/1.40/containers/json # docker version 看一下API version即可得到

此种挂载方式最大的好处就是公用一套docker环境, 最大的不好也是共用一套环境, 我来讲讲是为什么吧:

  1. 调用API即可操纵宿主机容器, 很适合用于某些编排工具的Docker化
  2. 权限实在过大, 在容器中可以使用--previledge参数, 我们甚至可以启动一个容器用于关机

总结

在特定情况下, 我们不可避免的需要在容器中调用Docker API来进行操作. 这里是希望告诉大家, 有这么两种做法, 但是具体使用一定要结合使用场景来看.