本文讲的是Kubernetes Docker-in-Docker存储注意事项【译者的话】本文介绍了基于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集群的两种不同的技术:
- 存储池
- 带有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中添加对其卷的支持”。
这是如何在高层次上工作的:
- 集群中的每个节点都配置了一个额外挂载的EBS卷,该卷仅用作内部Docker守护程序的graph存储。
- 这个额外的磁盘与EC2实例一起创建与消亡。磁盘上的实际存储容量由FlexVolume驱动程序管理。
- 当运行内部Docker守护程序的pod运行时,pod规范包含一个“flexVolume”规范。这有一个驱动程序名称“ax/vol_plugin”和其他细节,如文件系统类型和所需的存储容量。
- FlexBolume插件由Kubernetes调用,用于以下操作“init,unmount,mount,version,getvolumename,waitforattach,attach,detach,isattached,mountdevice,unmountdevice”。鉴于磁盘已经附加,唯一的感兴趣的是调用和卸载。
- FlexVolume插件适用于操作在EBS卷上创建的逻辑卷(lvms)。
- 在挂载调用中,FlexVolume插件会搜索一个空闲的,与所需大小匹配的现有逻辑卷。如果是这样,它会使用它。否则,FlexVolume将创建一个新的逻辑卷并进行挂载。
- 在卸载调用时,FlexVolume插件只会将卷标记为“不可用”,以便它可用于下一个内部Docker守护程序。
优势
- 由于用于内部Docker守护程序的EBS卷总是附加的,因此AWS速率限制或附加错误几率很小。
- Pods调度更快,因为没有移动的EBS卷。
缺点
- FlexVolume插件的安装不是直接的。
- 如果运行多个DinD pod并且本地磁盘已被充分利用,则后续的pod不得不等待。使用卷池,可以提供新的EBS卷,以便取得进展。同样,我们可以动态扩展我们的本地EBS卷,但这是额外的工作。
Argo FlexVolume插件的源代码可以在这里找到。
自己试试FlexVolume插件
如果您想尝试运行DinD,并使用基于FlexVolume的方法,使用本地连接的存储作为内部docker守护程序的存储,请执行以下步骤。
- 确保集群中所有的slave节点有一个磁盘“/dev/xvdz”。
- 下载插件:
wget https://s3-us-west-1.amazonaws.com/ax-public/ax_vol_plugin/bin/1.1.0/vol_plugin
- 在每个节点上创建以下路径:
sudo mkdir -p /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ax~vol_plugin/
- 卷插件应该复制到每个节点的以下路径:
cp vol_plugin /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ax~vol_plugin/vol_plugin
- 确保该插件可执行:
sudo chmod u+x /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ax~vol_plugin/vol_plugin
- 在每个节点上重新启动kubelet:
sudo service kubelet restart
- 确保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项目中有两个组成部分共同合作,以实现这一目标。
- AXMON - 与Kubernetes互动管理pod
- VolumeManager - 用于卷管理。
VolumeManager跟踪闲置、使用和预留的卷等。AXMON调用VolumeManager为每个pod专门保留PVC。然后创建一个Kubernetes pod规范,并将PVC细节放在其中。然后将该pod规范发送到Kubernetes以运行pod。当pod完成其执行时,AXMON调用VolumeManager将PVC标记为空闲,以便可以重新使用。
在Kubernetes上运行Docker-in-Docker pod需要仔细的存储考虑。 这个博客讨论了两种方法,而不牺牲性能,同时确保正确性。
如果您有其他方法,请告诉我们。 如果您尝试在您的Kubernetes群集上的DinD,请告诉我们@argoproj。
Shrinand Javadekar和Abhinav Das是Applatix的技术人员的成员,Applatix是一家产品和服务提供商,旨在帮助开发商在容器和Kubernetes方面取得成功。
原文链接:Kubernetes Docker-in-Docker存储注意事项 (翻译:付辉)
原文发布时间为:2017-10-04
本文作者:justinfu
本文来自合作伙伴Dockerone.io,了解相关信息可以关注Dockerone.io。
原文标题:Kubernetes Docker-in-Docker存储注意事项