docker查看正在运行的容器_干货 | 容器运行时从docker到containerd的迁移

11730023a348c305e618e055ac880a27.png

供稿 | eBay Infrastructure Engineering 苏菲

翻译&编辑 | 顾欣怡

本文2634字,预计阅读时间8分钟

更多干货请关注“eBay技术荟”公众号

104ae22c1a4a8aa7561d070efd6526e0.gif

导读

目前,docker是kubernetes默认的容器运行时(Container Runtime)。由于docker过于复杂,操作不便,eBay将容器运行时从docker迁移到containerd,并将存储驱动程序Device Mapper换成Overlayfs。尽管在迁移过程中,我们遇到了不少挑战,但都一一克服并最终完成了此次迁移。

d6cc46e6ceefceaa912b8ae6fa5240ab.gif

容器运行时(Container Runtime),运行于kubernetes(k8s)集群的每个节点中,负责容器的整个生命周期。其中docker是目前应用最广的。随着容器云的发展,越来越多的容器运行时涌现。为了解决这些容器运行时和k8s的集成问题,在k8s 1.5版本中,社区推出了CRI(Container Runtime Interface, 容器运行时接口)(如图1所示),以支持更多的容器运行时。

Kubelet通过CRI和容器运行时进行通信,使得容器运行时能够像插件一样单独运行。可以说每个容器运行时都有自己的优势,这就允许用户更容易选择和替换自己的容器运行时。

025e654bd572bac54d8265199a5f6fbe.png

图1 CRI在kubernetes中的位置

一、CRI & OCI

d6cc46e6ceefceaa912b8ae6fa5240ab.gif

CRI是kubernetes定义的一组gRPC服务。Kubelet作为客户端,基于gRPC框架,通过Socket和容器运行时通信。它包括两类服务:镜像服务(Image Service)和运行时服务(Runtime Service)。镜像服务提供下载、检查和删除镜像的远程程序调用。运行时服务包含用于管理容器生命周期,以及与容器交互的调用(exec / attach / port-forward)的远程程序调用。

如图2所示,dockershim, containerd 和cri-o都是遵循CRI的容器运行时,我们称他们为高层级运行时(High-level Runtime)

2535525daf8dc53d3bfe9f9b5c76e27d.png

图2 常用的运行时举例

OCI(Open Container Initiative,开放容器计划)定义了创建容器的格式和运行时的开源行业标准,包括镜像规范(Image Specification)和运行时规范(Runtime Specification)。

镜像规范定义了OCI 镜像的标准。如图2所示,高层级运行时将会下载一个OCI 镜像,并把它解压成OCI 运行时文件系统包(filesystem bundle)。

运行时规范则描述了如何从OCI 运行时文件系统包运行容器程序,并且定义它的配置、运行环境和生命周期。如何为新容器设置命名空间(namepsaces)控制组(cgroups),以及挂载根文件系统等等操作,都是在这里定义的。它的一个参考实现是runC。我们称其为低层级运行时(Low-level Runtime)。除runC以外,也有很多其他的运行时遵循OCI标准,例如kata-runtime。

104ae22c1a4a8aa7561d070efd6526e0.gif

二、Containerd vs Cri-o

目前docker仍是kubernetes默认的容器运行时。那为什么会选择换掉docker呢?主要的原因是它的复杂性。

如图3所示,我们总结了docker, containerd以及cri-o的详细调用层级。Docker的多层封装和调用,导致其在可维护性上略逊一筹,增加了线上问题的定位难度(貌似除了重启docker,我们就毫无他法了)。Containerd和cri-o的方案比起docker简洁很多。因此我们更偏向于选用更加简单和纯粹的containerd和cri-o作为我们的容器运行时。

0812d4baa9165582c4080d49aa223b32.png

图3 容器运行时调用层级

我们对containerd和cri-o进行了一组性能测试,包括创建、启动、停止和删除容器,以比较它们所耗的时间。如图4所示,containerd在各个方面都表现良好,除了启动容器这项。从总用时来看,containerd的用时还是要比cri-o要短的。

faaa2a7054fb29fa9915ca9fd58bbf05.png

图4 containerd和crio的性能比较

如图5所示,从功能性来讲,containerd和cri-o都符合CRI和OCI的标准。从稳定性来说,单独使用containerd和cri-o都没有足够的生产环境经验。但庆幸的是,containerd一直在docker里使用,而docker的生产环境经验可以说比较充足。可见在稳定性上containerd略胜一筹。所以我们最终选用了containerd。

a91109cf61e01d821580e13aebe70fb1.png

图5 containerd和cri-o的综合比较

104ae22c1a4a8aa7561d070efd6526e0.gif

三、Device Mapper vs. Overlayfs

容器运行时使用存储驱动程序(storage driver)来管理镜像和容器的数据。目前我们生产环境选用的是Device Mapper。然而目前Device Mapper在新版本的docker中已经被弃用,containerd也放弃对Device Mapper的支持。

当初选用Device Mapper,也是有历史原因的。我们大概是在2014年开始k8s这个项目的。那时候Overlayfs都还没合进kernel。当时我们评估了docker支持的存储驱动程序,发现Device Mapper是最稳定的。所以我们选用了Device Mapper。但是实际使用下来,由Device Mapper引起的docker问题也不少。所以我们也借这个契机把Device Mapper给换掉,换成现在containerd和docker都默认的Overlayfs。

从图6的测试结果来看,Overlayfs的IO性能比Device Mapper好很多。Overlayfs的IOPS大体上能比Device Mapper高20%,和直接操作主机路径差不多。

310ed8ea344eb29584503587270df586.png

图6 后端存储文件系统性能比较

四、迁移方案

4255fb08df72c2bfd9e019584c58cfbf.gif

最终,我们选用了containerd,并以Overlayfs作为存储后端的文件系统,替换了原有的docker加Device Mapper的搭配。那迁移前后的性能是否得到提升呢?我们在同一个节点上同时起10,30,50和80的pod,然后再同时删除,去比较迁移前后创建和删除的用时。从图7和图8可知,containerd用时明显优于docker。

0df68ce9bb685a3b39384815a77ac199.png

图7 创建pod的用时比较

d960afd6a2600ecb69f4cf6bd4af94cc.png

图8 删除pod的用时比较

五、迁移挑战

d6cc46e6ceefceaa912b8ae6fa5240ab.gif

从docker+Device Mapper到containerd+ Overlayfs,容器运行时的迁移并非易事。这个过程中需要删除Device Mapper的thin_pool,全部重新下载用户的容器镜像,全新重建用户的容器。

如图9所示,迁移过程看似简单,但是这对于已运行了5年且拥有100K+光怪陆离的应用程序的集群而言,如何将用户的影响降到最低才是最难的。Containerd在我们生产环境中是否会出现“重大”问题也未可知。

39d78d53081868eb81b2cd53362e2c87.png

图9 具体的迁移步骤

针对这些挑战,我们也从下面几个方面做出了优化,来保证我们迁移过程的顺利进行。

01

多样的迁移策略

最基本的是以容错域(Fault Domain, fd)为单元迁移。针对我们集群,是以rack(机架)为单元(rack by rack)迁移。针对云原生(cloud-native)且跨容错域部署的应用程序,此升级策略最为安全有效。针对非云原生的应用程序,我们根据其特性和部署拓扑,定制了专属他们的升级策略,例如针对Cassini的集群,我们采用了jenga(层层叠)的升级策略,保证应用程序0宕机。

02

自动化的迁移过程

以rack by rack的策略为例,需要等到一个rack迁移完成以后且客户应用程序恢复到迁移前的状态,才能进行下一个rack的迁移。因此我们对迁移控制器(Controller)进行了加强,利用控制平面(Control Plane)监控指标(Metrics)数据平面(Data Plane, 即应用程序)告警(Alerts),实现典型问题的自动干预和修复功能,详见图10。如果问题不能被修复,错误率达到阈值,迁移才会被暂停。对于大集群,实现了人为的0干预。

dc6334edf364e79482383d48d4e71ff7.png

图10 自动化迁移流程

03

高可用的镜像仓库

一个rack共有76台机器。假设每个机器上只有50个pod,就可能最多有3800个镜像需要下载。这对镜像仓库的压力是非常大的。除了使用本地仓库,这次迁移过程中还使用了基于gossip协议的镜像本地缓存的功能,来减少远端服务端的压力,具体参见图11。

757f92707c2b064ab41cd2013177ce12.png

图11 镜像仓库架构

04

可逆的迁移过程

虽然我们对containerd的问题修复是有信心的,但是毕竟缺少生产环境经验,得做好随时回退的准备。一旦发现迁移后,存在极大程度影响集群的可靠性和可用性的问题,我们就要换回docker。虽然迁移后,在线上的确发现了镜像不能成功下载,容器不能启动和删除等问题,但是我们都找到了根本原因,并修复。所以令人庆幸的是,这个回退方法并未发挥其作用。

104ae22c1a4a8aa7561d070efd6526e0.gif

六、用户体验

容器运行时是kubernetes的后端服务。容器运行时的迁移不会改变任何的用户体验。但是有一个Overlayfs的问题需要特别说明一下。如果容器的基础镜像(Base Image)centos6,利用Dockerfile去创建镜像时,如果用yum去安装包,或者在运行的centos6容器中用yum安装包的,会报以下错误:

e6c104d30da8a7af06c9de0158ad2ab7.png

因为yum在安装包的过程中,会先以只读模,然后再以写模式去打开rmpdb文件。

c67feece5bb5b5856693428747f7a22c.png

如图12所示,对于Overlayfs来说,以只读模式打开一个文件的话,文件直接在下层(lower layer)被打开,我们得到一个fd1。当我们再以写模式打开,就会触发一个copy_up。rmpdb就会拷贝到上层(upper layer)。文件就会在上层打开得到fd2。这两个fd本来是想打开同一个文件,事实却并非如此。

f38b13e6a370fb9860ce2646f4ae7289.png

图12

ea0cd39e357c0ac9ea2fde8119c64d96.png

图13

解决方案就是在执行yum命令之前先装一个yum-plugin-ovl插件。这个插件就是去做一个初始的copy_up,如图13所示。将rpmdb先拷贝到上层,参考Dockerfile如下:

6361c26a096c9dd741fb5b91f07fb084.png

如果基础镜像是centos7,则没有这个问题,因为centos7的基础镜像已经有这个规避方法了。

七、总结

d6cc46e6ceefceaa912b8ae6fa5240ab.gif

目前我们50个集群,20K+的节点已经全部迁到containerd,历时2个月(非执行时间)。从目前情况来看,还比较稳定。虽然迁移过程中也出了不少问题,但经过各个小组的不懈努力,此次迁移终于顺利完成了。

93cb67c6ac289af03615e634de8ad704.png

↓点击

 eBay大量优质职位,等的就是你!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值