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


    在这里插入图片描述

    容器相比于 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 \
        --name docker0 \
        -ti docker
    
    • 1
    • 2
    • 3
    • 4

    执行输出内容:

    # 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
    / # 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这里可以看到进入到容器内后,使用 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
    
    • 1
    • 2

    本篇文章不会深入的介绍如何构建这样的镜像,因为社区已经提供了相对完整的解决方案。这里主要提供

    • 基于 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
    
    • 1
    • 2
    • 3

    运行示例:

    # 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    基于 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
    
    • 1
    • 2
    • 3
    • 4
    • 5

    启动实例

    ​[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