Kubernetes Docker-in-Docker存储注意事项

2017-10-11 cjy0318

【译者的话】本文介绍了基于Kubernetes运行Docker-in-Docker在存储方面应该注意的事项,介绍了Argo这个插件,演示了具体的流程和测试方式。鉴于在功能和性能上都表现良好,为我们开拓了一个权限的玩转Docker容器的思路。

Docker容器是运行其他工具的非常有用的工具,因此将Docker容器作为另一个容器的一个工具来调用是非常自然的。Docker-in-Docker(也称DinD)有很多用例 - 1)可以将持续集成(CI)应用程序(例如Jenkins)容器化,并且要为要运行的每个CI作业提供构建/测试容器,2)在Kubernetes群集的pod中运行Docker Compose文件,3)在容器化CI作业内部构建Docker容器镜像等等。

之前的一系列博客 中我们展示了一个案例,在Kubernetes上运行Pod,而Pod本身运行Docker。

在这篇文章中,我们将讨论如何配置和管理Docker-in-Docker的存储,并且确保其功能完整、性能优良。乍一看,这似乎是一个微不足道的事情,但由于各种因素,精确和性能的极致总是鱼和熊掌不可兼得。

我们将采用主机Docker守护程序作为 外部守护程序 ,Docker守护程序作为 内部守护程序 在容器内运行。运行DinD的一个重要方面是处理Docker守护进程存储的配置和管理。

精确和性能 - 您需要二者兼得

精确

Docker要求没有两个Docker守护程序同时使用相同的graph存储。鉴于此,内部和外部Docker守护程序必须具有独立的graph存储。也就是说,内部Docker守护程序的graph存储不能与在主机上运行的外部Docker守护程序的graph存储共享,以确保正确性。

性能

如果多个DinD容器依次运行,即一个容器如果挂掉,一个容器立即创建,而不是同时运行,那么他们如何重用同一个graph存储,以便为Docker镜像获得缓存的优势?例如,考虑内部Docker实例构建一系列提交的情况。用于构建的基础镜像将从一个构建到下一个构建将保持不变。如果镜像被缓存,构建将运行得更快。

正如我们在 上一篇文章 中讨论过的,正确性要求可以使用Kubernetes'emptyDir'作为DinD graph存储卷提供程序来处理。EmptyDir保证在每个pod的主机文件系统上挂载唯一的目录。这样的目录不会被不同的pod共享,从而确保正确性。但是,一旦pod被删除,目录被删除,graph存储层将丢失,导致性能不佳。

DinD graph存储的可选项

任何支持DinD工作负载,在Kubernetes集群上的Pod中运行Docker命令的存储解决方案,都必须满足正确性和性能要求。下面我们将讨论在AWS上运行Kubernetes集群的两种不同的技术:

  1. 存储池
  2. 带有FlexVolumes的本地磁盘

存储池

使用这种方法,外部Docker守护程序在本地挂载的磁盘上获取一个目录,用于其graph存储。通常,这是根磁盘上的 /var/lib/docker 。内部Docker守护程序获取单独的弹性块存储(EBS)卷以用作其graph存储。此EBS卷来自专门为此而创建的EBS卷。因此命名为“Volume Pool”。

为卷池中的每个卷创建Persistent Volume Claim(PVC)。当运行内部Docker守护程序的pod创建时,池中的一个PVC与此pod相关联,并挂载在 /var/lib/docker 上。

优势:

  • 可扩展性。卷池可以基于负载增长或缩小。
  • 可重用性和性能。卷可以附加到不同的节点。由于高速缓存的graph存储层是持久性的,高速缓存随着磁盘移动到集群中的其他节点。

缺点:

  • 必须管理卷池中的EBS卷。“VolumeManager”必须跟踪闲置和正在使用的卷。
  • 当池中的卷被分配到在不同节点上运行的不同pod时,它们必须被相应地移动。EBS卷经过连接,装载,卸载和分离的周期。如果您经常进行此操作,则可以按照 此处 所述,从AWS开始查看错误。
  • AWS不允许跨多个可用区域访问EBS卷。如果集群有EC2实例分布在不同可用区域之间,则每个区域将需要单独的卷池。需要选择来自相应可用性区域的PVC,具体取决于pod运行的实例。

具有FlexVolume的本地磁盘

使用单独的本地连接的磁盘用于Docker graph存储,而不是依靠几个EBS卷。主要思想很简单:每个主机磁盘都有专用于graph存储缓存的空间。该空间根据磁盘空间的需求分为slab。如果请求大小为X的slab,请检查是否有可用的,没有则创建它,有则重用现有的。这些slab的创建和管理是通过内部Docker守护程序的FlexVolume插件实现的。

Kubernetes文档指出:“FlexVolume使用户能够编写自己的驱动程序,并在Kubernetes中添加对其卷的支持”。

这是如何在高层次上工作的:

  1. 集群中的每个节点都配置了一个额外挂载的EBS卷,该卷仅用作内部Docker守护程序的graph存储。
  2. 这个额外的磁盘与EC2实例一起创建与消亡。磁盘上的实际存储容量由FlexVolume驱动程序管理。
  3. 当运行内部Docker守护程序的pod运行时,pod规范包含一个“flexVolume”规范。这有一个驱动程序名称“ax/vol_plugin”和其他细节,如文件系统类型和所需的存储容量。
  4. FlexBolume插件由Kubernetes调用,用于以下操作“init,unmount,mount,version,getvolumename,waitforattach,attach,detach,isattached,mountdevice,unmountdevice”。鉴于磁盘已经附加,唯一的感兴趣的是调用和卸载。
  5. FlexVolume插件适用于操作在EBS卷上创建的逻辑卷(lvms)。
  6. 在挂载调用中,FlexVolume插件会搜索一个空闲的,与所需大小匹配的现有逻辑卷。如果是这样,它会使用它。否则,FlexVolume将创建一个新的逻辑卷并进行挂载。
  7. 在卸载调用时,FlexVolume插件只会将卷标记为“不可用”,以便它可用于下一个内部Docker守护程序。

优势

  • 由于用于内部Docker守护程序的EBS卷总是附加的,因此AWS速率限制或附加错误几率很小。
  • Pods调度更快,因为没有移动的EBS卷。

缺点

  • FlexVolume插件的安装不是直接的。
  • 如果运行多个DinD pod并且本地磁盘已被充分利用,则后续的pod不得不等待。使用卷池,可以提供新的EBS卷,以便取得进展。同样,我们可以动态扩展我们的本地EBS卷,但这是额外的工作。

Argo FlexVolume插件的源代码可以在 这里 找到。

自己试试FlexVolume插件

如果您想尝试运行DinD,并使用基于FlexVolume的方法,使用本地连接的存储作为内部docker守护程序的存储,请执行以下步骤。

  1. 确保集群中所有的slave节点有一个磁盘“/dev/xvdz”。
  2. 下载插件:
    wget https://s3-us-west-1.amazonaws.com/ax-public/ax_vol_plugin/bin/1.1.0/vol_plugin
  3. 在每个节点上创建以下路径:
    sudo mkdir -p /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ax~vol_plugin/
  4. 卷插件应该复制到每个节点的以下路径:
    cp vol_plugin /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ax~vol_plugin/vol_plugin
  5. 确保该插件可执行:
    sudo chmod u+x /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ax~vol_plugin/vol_plugin
  6. 在每个节点上重新启动kubelet:
    sudo service kubelet restart
  7. 确保kubelet能够正确启动,并且该卷插件已正确初始化。/var/log/syslog会有
    Aug 24 19:40:46 ip-172–20–1–118 kubelet[4282]: I0824 19:40:46.150515 4282 plugins.go:363] Loaded volume plugin “ax/vol_plugin”

一旦插件安装并可以使用,这里是运行此Docker镜像的示例pod规范:

apiVersion: v1

kind: Pod

metadata:

name: dind

spec:

containers:

  - name: docker-cmds

    image: docker:1.12.6

    command: ['docker', 'run', '-p', '80:80', 'httpd:latest']

    resources:

        requests:

            cpu: 10m

            memory: 256Mi

    env:

      - name: DOCKER_HOST

        value: tcp://localhost:2375

  - name: dind-daemon

    image: docker:1.12.6-dind

    resources:

        requests:

            cpu: 20m

            memory: 512Mi

    securityContext:

        privileged: true

    volumeMounts:

      - name: docker-graph-storage

        mountPath: /var/lib/docker

volumes:

- name: docker-graph-storage

  flexVolume:

    driver: "ax/vol_plugin"

    fsType: "ext4"

    options:

      size_mb: "10240"

      ax_vol_type: "ax-docker-graph-storage"

代码托管在GitHub,点击 这里 查看。

请注意,此pod规格与我们之前的DinD 博客 中提到的格式非常相似。唯一的区别是,这个使用flexVolume插件而不是emptyDir。

我们来运行上面的pod,并验证docker-graph-storage是否真的做了它所声明的事情。

$ kubectl create -f dind_sample.yaml

pod "dind" created

当上述pod第一次运行时,ssh进入'docker-cmds'容器,并确认“ubuntu:16.04”镜像不存在。

$ kubectl exec -it dind sh

Defaulting container name to docker-cmds.

Use 'kubectl describe pod/dind' to see all of the containers in this pod.

/ # docker images

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

httpd               latest              e74fcb59d25b        4 weeks ago         177.3 MB

现在拉取ubuntu:16.04镜像并验证它现在。

/ # docker pull ubuntu:16.04

16.04: Pulling from library/ubuntu

d5c6f90da05d: Pull complete

1300883d87d5: Pull complete

c220aa3cfc1b: Pull complete

2e9398f099dc: Pull complete

dc27a084064f: Pull complete

Digest: sha256:34471448724419596ca4e890496d375801de21b0e67b81a77fd6155ce001edad

Status: Downloaded newer image for ubuntu:16.04

/ # docker images

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

ubuntu              16.04               ccc7a11d65b1        2 weeks ago         120.1 MB

httpd               latest              e74fcb59d25b        4 weeks ago         177.3 MB

现在终止pod。

$ kubectl delete pod dind

pod "dind" deleted

再次运行pod,并将ssh进去。 检查“ubuntu:16.04”镜像是否已经存在。

$ kubectl create -f dind_sample.yaml

pod "dind" created

$ kubectl exec -it dind sh

Defaulting container name to docker-cmds.

Use 'kubectl describe pod/dind' to see all of the containers in this pod.

/ # docker images

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

ubuntu              16.04               ccc7a11d65b1        2 weeks ago         120.1 MB

httpd               latest              e74fcb59d25b        4 weeks ago         177.3 MB

请注意,ubuntu:16.04镜像已经存在于新创建的pod中。 这是因为新的pod与上一个Dock相同。

为Docker内部守护进程创建的slab可能会添加额外的层,因此可能会耗尽容量。当pod完成执行完成,使用容量超过50%时,当前的FlexVolume实现只会删除slab。鉴于此存储是缓存,不会引发错误。

您可以在您的Kubernetes集群上手动执行上述操作,也可以尝试使用Argo。一切都是预配置且开箱即用的。

Argo工作流程本身支持DinD

Argo支持需要在pod内运行Docker命令的DinD工作负载。Argo项目中有两个组成部分共同合作,以实现这一目标。

  1. AXMON - 与Kubernetes互动管理pod
  2. VolumeManager - 用于卷管理。

VolumeManager跟踪闲置、使用和预留的卷等。AXMON调用VolumeManager为每个pod专门保留PVC。然后创建一个Kubernetes pod规范,并将PVC细节放在其中。然后将该pod规范发送到Kubernetes以运行pod。当pod完成其执行时,AXMON调用VolumeManager将PVC标记为空闲,以便可以重新使用。

在Kubernetes上运行Docker-in-Docker pod需要仔细的存储考虑。 这个博客讨论了两种方法,而不牺牲性能,同时确保正确性。

如果您有其他方法,请告诉我们。 如果您尝试在您的Kubernetes群集上的DinD,请告诉我们。和是Applatix的技术人员的成员,Applatix是一家产品和服务提供商,旨在帮助开发商在容器和Kubernetes方面取得成功。


用户评论
开源开发学习小组列表