Docker In Docker 容器嵌套¶
Docker in Docker 自定义开源项目: https://gitee.com/cncfstack/docker-in-docker/tree/master/dind
容器相比于 VM 虚拟机是属于弱隔离,在某些场景下需要增强容器的隔离或扩展应用场景来解决新的问题,如下场景在云原生实践过程中也经常会遇见,本文收集并分析了目前主流的容器嵌套方法,可根据当前团队的现状来综合评估后选择。
- 使用容器来提供传统的操作系统运行环境。VMLC(类虚拟机容器) 是 VM Like Container 的缩写,其设计目标是为开发者在容器中提供类似虚拟机的环境,包括:systemd服务管理能力,sshd远程登录能力,docker/k8s嵌套能力等。
- 在容器中嵌套运行容器,提供沙盒环境。
- 在容器/POD 中运行 Kubernetes 集群。
- 在流水线执行时任务本身是封装在容器中,但是流水线的执行过程可能会包含镜像的拉取、构建、上传、运行等任务场景。
- 在 Jenkins 中,使用 Docker 容器作为构建的 Slave 节点。
- 在短期使用环境运行容器时,例如学习容器的基本使用,可以快速创建和回收环境,并减少资源的消耗。
- 在产品快速体验或 POC 场景下,将容器服务运行在容器中进行打包,快速部署运行。
在 Docker 中运行 Docker 目前可提供三类方法
- 方法1:共享宿主机 dockerd 服务
- 方法2:独立隔离的容器运行环境
- 方法3:使用 Sysbox 运行时
方法1:共享宿主机 dockerd 服务¶
dockerd 服务在启动时会提供基于 socket 的连接方式,docker client 客户端可以选择使用 Socket 文件或 TCP/IP 的方式连接 Socket 来访问 dockerd 进行使用和管理功能,默认会使用 /var/run/docker.sock
的 socket 文件接口方式连接。
当在宿主机上运行容器 Container0 后,需要在该容器内继续运行 docker 命令来使用容器功能,可以参考如下步骤: - 准备 Container0 的容器镜像,该镜像提前安装好 docker 客户端命令;或者也可以直接使用 docker 官方提供的镜像 docker:latest
- 基于上述镜像来运行 Container0 的容器,该步骤需要配置宿主机 dockerd 连接方式。 - 在 Container0 中使用 docker 客户端命令进行容器的使用和管理
容器的嵌套和管理示意图:
- 1、在宿主机上创建出 Container0 后,可以通过
docker exec
进入到该容器 - 2、在 Container0 容器内继续创建/访问新的容器 Container3 ,可以再次使用
docker exec
进入到 Container3 容器中 - 3、通过两次的
docker exec
进入到嵌套的内容容器 - 4、在共享 dockerd 方法下,Container3 虽然是在 Container0 中创建和使用管理,但实际上还是运行在宿主机上的 dockerd 系统中
- 5、通过 socket 或 TCP/IP 的方式将宿主机的 dockerd 挂载到 Container0 容器中。
该方法的容器嵌套命令参考
执行输出内容:
# sudo docker run \
> -v /var/run/docker.sock:/var/run/docker.sock \
> -ti docker
/ # docker ps |grep docker0
b326055722fb docker "docker-entrypoint.s…" 29 seconds ago Up 28 seconds docker0
/ #
这里可以看到进入到容器内后,使用 docker 命令还可以看到当前容器本身。也使用 docker version
可以看到客户端和服务端是两个不同版本的 docker服务。
实例中使用的 socket 文件的方式连接,如果运行的容器和 dockerd 不在同一台机器,或者本地文件没有权限挂载,也可以通过指定容器的 IP 地址和端口的方式连接到 dockerd ,客户端连接参数 -H, --host list Daemon socket(s) to connect to
。
方法2: 独立隔离的容器运行环境¶
一般情况下操作系统在运行时会启动很多系统管理的服务,如 systemd;但容器启动时只有一个业务服务进程,即使安装了一些软件也无法像在操作系统中那样正常启动。
如下,是在 centos7 的基础镜像中安装了 docker 服务,在没有进行特殊改造的情况下,是无法来启动 docker.service 服务的。
[root@9952f799dab1 /]# systemctl start docker
Failed to get D-Bus connection: Operation not permitted
本篇文章不会深入的介绍如何构建这样的镜像,因为社区已经提供了相对完整的解决方案。这里主要提供 - 基于 Docker 提供的 docker:dind
- 基于 minikbue 提供的 kicbase:v0.0.32
这种方式提供的容器中的容器运行环境和宿主机上的 dockerd 是相互独立的
- 1、在宿主机上创建出 Container0 后,可以通过
docker exec
进入到该容器 - 2、在 Container0 容器内继续创建/访问新的容器 Container3 ,可以再次使用
docker exec
进入到 Container3 容器中 - 3、通过两次的
docker exec
进入到嵌套的内容容器
基于 Docker 提供的 docker:dind
¶
该镜像的目标是提供 docker 运行环境,因此启动的容器中主要功能就是 docker 服务,其他 systemd 的相关服务是无法正常运行的。
运行示例:
# docker run -itd \
> --name dind-test \
> --privileged docker:dind
4e2b702710f742f34b59683542c64724bb36502daa23115c537d25223c4ba138
# docker exec -it dind-test sh
/ # docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
基于 minikbue 提供的 kicbase
¶
kicbase 提供了完整的隔离环境,也提供 systemd 的系统服务的管理,和 VM 虚拟机使用体验很相似,也可以安装启动其他 systed 服务。
minikube 社区使用 kicbase 就用来提供类似虚拟化 VM 的驱动,使用该驱动来运行 Kubernetes 集群的。
该容器启动时默认
docker run -itd \
--name virtualvm1 \
--privileged \
-p 20022:22 \
registry.cn-hangzhou.aliyuncs.com/google_containers/kicbase:v0.0.32
启动实例
[minikube@localhost ~]$ docker run -itd \
> --name virtualvm1 \
> --privileged \
> -p 20022:22 \
> registry.cn-hangzhou.aliyuncs.com/google_containers/kicbase:v0.0.32
a06cc6ee28ebcc3eb5acfe7ac3f0fe171316693d67e68438e3c161efb043f98e
[minikube@localhost ~]$ docker exec -it virtualvm1 bash
root@a06cc6ee28eb:/# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
root@a06cc6ee28eb:/# ps auxf
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 350 0.0 0.0 4248 3404 pts/1 Ss 04:26 0:00 bash
root 359 0.0 0.0 5896 2960 pts/1 R+ 04:26 0:00 \_ ps auxf
root 1 0.5 0.0 21252 10168 ? Ss 04:26 0:00 /sbin/init
root 201 0.1 0.0 27472 8420 ? S<s 04:26 0:00 /lib/systemd/systemd-journald
message+ 212 0.0 0.0 7000 3640 ? Ss 04:26 0:00 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation
root 215 0.4 0.1 2021320 55556 ? Ssl 04:26 0:00 /usr/bin/containerd
root 224 0.0 0.0 12176 7008 ? Ss 04:26 0:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
root 238 0.5 0.2 1752512 76360 ? Ssl 04:26 0:00 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
kicbase 的容器默认会启动 init、systemd、dbus、docker、sshd 等服务,在容器内的使用体验就是一个 Linux 操作系统环境。
使用这两种方式运行容器嵌套都需要提供给 --privileged
参数来使用特权权限,该权限在不安全的网络环境中提供服务时具有一定的风险,如若考虑安全可以使用 docker rootless 的方式运行
方法3: 使用 Sysbox 运行时¶
SysBox 是一种与 containerd、cri-o 类似的容器运行时。
与前两种容器嵌套方式不同的是,SysBox 从容器运行时的角度提供了新的解决方案,它可以在能够运行 systemd,docker,kubernetes 的容器内创建虚拟环境,而无需特权访问基础主机系统。
在使用前需要先安装 sysbox 运行时环境。请参阅此页面以获取有关安装sysbox运行时的最新官方说明[1]。
一旦拥有sysbox运行时可用,您要做的就是使用 sysbox 运行时标志启动 docker 容器,如下所示。
在这里,我们使用的是官方 docker dind 映像。
容器启动后,就可以登录到容器中
# docker exec -it sysbox-dind /bin/sh
/ # docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
总结¶
本文提供了三种从不同思路提供了容器嵌套的解决方案。
使用共享宿主机 dockerd 的方式是能够快速理解和使用的一种方式,不需要进行特殊的调整和改造,在使用上可以解决一些容器嵌套的需求,但是没有提供较好的隔离。
使用独立隔离的容器运行环境其实是目前相对较好的选择,他可以提供相对独立的隔离环境,特别是 minikbue 提供的 kicbase,基本实现了基于容器的 VM 虚拟机实现;但是这种方式需要 root 特权,在网络安全要求较高的情况下需要进行特殊的处理。
使用 Sysbox Runtime 运行时的方案解决了上面两种独立和安全的问题,但 Sysbox 目前并非主流的容器运行时,并没有大众广泛接受。