Docker in Docker容器化版的“盗梦空间”

2024-08-30 17:08:44 浏览数 (2)

Docker in Docker(DinD) 是一种容器虚拟化嵌套技术,它允许用户在容器内部运行 Docker 容器,层层嵌套,从而实现更复杂的容器化操作和构建环境 。

01、背 景

Kubernetes集群计划迁移至1.24版本的集群,底层容器运行时换成了Containerd,然而当前的CI/CD流水线大多地方使用到了docker指令,要改成Containerd指令成本巨大,如何以最小的改动,最为高效地完成迁移便成了难题,为了攻克这个难题我们引入了DinD的方案进行实现,流水线都不用做任何改动,简直完美。

02、Containerd的由来

我们顺便了解下Containerd的由来,据说,在很早之前,Kubernetes就放风不再支持Docker,主要原因是Docker不支持其引入的CRI(容器运行时接口),还需要额外维护docker-shim组件,费时费力。

2015年,为了应对这一挑战,Docker 公司决定将容器运行时从 Docker 引擎中分离出来,并支持 CRI,将这个项目命名为 Containerd。

2017年,被捐赠给云原生计算基金会(CNCF),后来成为了 CNCF的顶级开源项目。

Containerd专注于容器的生命周期管理,包括容器的创建、启动、停止和删除等。这种专注使其更轻量、高效,并且与 Kubernetes 的设计理念更为契合,简化了集群管理的复杂性,提升了系统的性能和可靠性。

03、方案说明

为了使 Jenkins Slave 能够使用 Docker 命令,我们需要将 Docker 二进制文件和 docker.sock(套接字)映射到容器中。我们并不需要在容器内启动一个 Docker 进程,因为只要 Docker 的二进制文件能够通过 docker.sock 与守护进程通信即可。

如上图,我们大致会执行以下步骤:

1. 运行Docker daemonset,将K8S Node的/var/run/docker-ci映射到容器的/var/run/docker目录

2. 将K8S Node的/var/run/docker-ci中的docker.sock和docker映射到Jenkins Slave中

04、操作步骤

将下面内容保存为docker-ci-daemonset.yaml,然后通过kubectl apply进行部署

代码语言:javascript复制
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: docker-ci     # 因为是用来做docker镜像构建的,所以我把他命名为docker-ci
  namespace: default  # 命名空间,我是放在default命名空间下,可以自行修改
spec:
  selector:
    matchLabels:
      app: docker-ci
  template:
    metadata:
      labels:
        app: docker-ci
    spec:
      initContainers:
      - name: init-sysctl
        image: busybox
        command:
          - rm
          - '-f'
          - /var/run/docker.sock  # 启动前,删除一下docker.sock,防止因异常原因退出留有残留文件,影响docker启动
        resources: {} 
        volumeMounts:
          - name: host
            mountPath: /var/run  # 将宿主机卷挂载到init容器
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        imagePullPolicy: Always
        securityContext:
          privileged: true       # 赋予容器特权
      containers:
      - name: docker-ci
        image: 'docker:20.10.24-dind'  # Docker-in-Docker镜像
        command:
        - dockerd
        - --host=unix:///var/run/docker.sock
        - --host=tcp://0.0.0.0:8000
        securityContext:
          privileged: true   # 赋予容器特权
        resources:
          limits:
            cpu: '2'
            memory: 4Gi
          requests:
            cpu: 300m
            memory: 500Mi
        volumeMounts:
        - mountPath: /var/run
          name: host  # 将宿主机卷挂载到主容器
        lifecycle:  
          postStart:  # 添加postStart钩子
            exec:
              command:
                - sh
                - -c
                - |
                  if [ -e /var/run/docker/docker ]; then
                    rm -f /var/run/docker/docker
                  fi
                  cp -a /usr/local/bin/docker /var/run/docker/
      volumes:
      - name: host
        hostPath:
          path: /var/run/docker-ci  # 创建一个目录,避免将/var/run的所有文件挂载到容器中

好了,接下来就是修改Jenkins的配置,我们在需要用到docker指令的容器配置模版加上docker二进制和docker.sock的映射即可,如

代码语言:javascript复制
- name: "jnlp"
    image: "jenkins/jnlp-slave:3.27-1"
    args: "^${computer.jnlpmac} ^${computer.name}"
    resourceRequestCpu: "100m"
    resourceLimitCpu: "500m"
    resourceRequestMemory: "500Mi"
    resourceLimitMemory: "2048Mi"
  workspaceVolume:
    emptyDirWorkspaceVolume:
      memory: false
  volumes:
  - hostPathVolume:
      hostPath: "/var/run/docker-ci/docker.sock"
      mountPath: "/var/run/docker.sock"
  - hostPathVolume:
      hostPath: "/var/run/docker-ci/docker/docker"
      mountPath: "/usr/bin/docker"

完成上述步骤,就可以在发布流程中使用docker指令了。

05、总 结

虽然DinD可以完美解决我的难题,但是也存在弊端,如安全性问题、容器不稳定等,都是我们需要关注的,这期就分享到这里,谢谢!

0 人点赞