容器嵌套,降本增效(Docker In Docker)

容器嵌套,降本增效(Docker In Docker)_第1张图片

容器相比于 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 客户端命令进行容器的使用和管理

容器的嵌套和管理示意图:

容器嵌套,降本增效(Docker In Docker)_第2张图片

  • 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 \
    --name docker0 \
    -ti docker

执行输出内容:

# 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 是相互独立的

容器嵌套,降本增效(Docker In Docker)_第3张图片

  • 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

运行示例:

# 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

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 run -itd \
  --runtime=sysbox-runc \
  --name sysbox-dind 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 目前并非主流的容器运行时,并没有大众广泛接受。

参考

  • https://hub.docker.com/_/docker
  • https://devopscube.com/run-docker-in-docker/
  • https://github.com/nestybox/sysbox
  • sysbox运行时安装
  • 使用 Docker 作为 Jenkins Build 的 Slave

你可能感兴趣的:(cncfstack,docker,云原生,docker,kubernetes,容器)