国漫手机壁纸

Kubernetes决定弃用Docker,到底会影响到谁?

k8s:Kubernetes决定弃用Docker,到底会影响到谁?

NetSmell 出品

导读:Kubernetes 在其最新的 Changelog 中宣布,自 Kubernetes 1.20 之后将弃用 Docker 作为容器运行时。那么这到底是怎么回事?开发者和企业会受到什么样到影响?

近几年,Kubernetes 已经成为自有机房、云上广泛使用的容器编排方案,最广泛的使用方式是 Kubernetes+Docker。从 DevOps 人员的角度,一面用 kubctl 命令、k8s API 来操作集群,一面在单机用 Docker 命令来管理镜像、运行镜像。

单独用 Docker 的情况,在一些公司的场景里面也是有的。一种场景是“只分不合”,把一台机器用 Docker 做资源隔离,但是不需要将多容器“编排”。单独用 Kubernetes,下层不是 Docker 的情况,并不算很多。

Kubernetes 和 Docker 的关系,简单来说,有互补,也有竞争。在一般的认知中,Kubernetes 和 Docker 是互补关系:

  • Dockers属于下层——容器引擎;
  • Kubernetes属于上层——编排调度层。

 

Docker 源于 Linux Container,可以将一台机器的资源分成 N 份容器,做到资源的隔离,并将可运行的程序定义为标准的 docker image;Kubernetes 则可以把不同机器的每份容器进行编排、调度,组成分布式系统。

Kubernetes 和 Docker 并不完全是“泾渭分明”的互补关系,它之间有重叠部分,也可以说成是竞争,主要在于几个点:

  • 系统三大移植资源是计算、存储、网络,从Kubernetes角度Docker属于“Runtime(运行时)”,也就是计算资源;但是Docker技术体系里面,本身也包括存储层、网络层。上下层职责的重叠,也可以看作竞争。
  • Docker原本有个原生的调度引擎——Swarm,几年前在调度编排领域,还是Kubernetes、Mesos、Swarm三者并存,Kubernetes最终胜出,但Docker仍有“继续向上做一层的意愿”。

 

Kubernetes 在如何使用 Docker 方面,存在争议和变数。kubernetes 1.20 ChangeLog 中所谓要废弃 Docker 的传言,也是无风不起浪。换句话说,即便 Kubernetes 一直用 Docker,也不是用 Docker 的全部,多少是不一样的。

而且,“弃用 Docker”这个词本身有多重的含义,Docker 并非一个单层软件,Kubernetes 1.20 启用 dockershim 并不代表弃用了 Docker 的全部,仍有 containerd 可以对接 docker。

Kubernetes 有 CRI、OCI 两个容器标准

在目前广泛使用 kubernetes 与 Runtime 的桥接方案,CRI(Container Runtime Interface)与 OCI(Oracle Call Interface)是“套娃“关系。Kubernetes 的 kubelet 调用 CRI,OCI 的实现者然后再调用 OCI。

下图也说明了 CRI 与 OCI 的关系:

从 Kubernetes 的角度,CRI 是与 CNI(网络)、CSI(存储)相同层级的接口。

OCI 是个自下而上的标准,也就是从实现抽象出接口,它是 Linux Foundation 主导的。Docker 实现的核心 RunC,也就是 OCI 的典型实现、标准实现。

CRI 是个自上而下的标准,源于 Kubernetes 对移植层(运行时)的要求。

容器引擎层自下而上定义 OCI,容器编排层自上而下定义 CRI,这也让它们出现了“套娃“运行情况。

在 Kubernetes 的 dockershim、cri-containerd、cri-o 三种实现中,RedHat 推崇的 cri-o 已经比较主流,它虽然仍是“套娃“,但已经比较精简。

下面是从 kubernetes 集群运行的全景图看 cri-o 的位置:

Docker 本源于 Linux Container

Docker 作为容器引擎,其实现的基础是 Linux Container——从内核到用户空间的机制。

Linux Container 可以分成两个部分,内核里面的 cgroup,用户空间的 lxc。Docker 最初实现的时候,也是完全基于 Linux Container 的,基于 lxc 做更上层的东西。

这张很多人认为“与事实不符“的图,其实代表了过去:

在 Docker 的发展过程中,最终启用了 C 语言写成 lxc,换成了 go 语言写成的 libcontainer。

下面的图也不是很新,但它更能表示 Docker 后续典型的架构,这里面已经没有了 lxc。

然而,万变不离其宗,Docker 实现的本源,还是 Linux Container。即便不用 lxc,当仍要用内核的 cgroup,并且模式也是类似的。

Kubernetes 最终如何桥接容器

从纯技术的角度,与其讨 Kubernetes 与 Docker 关系,不如讨论 Kubernetes 与最终容器实现层的关系。因为 Docker 这个名词,在不同的时候,有着不同的内涵、外延。

下面是 Docker 的简图:

从软件模块的角度,图中的 docker Engine、cri-containd、containd-shim、runC 都属于 Docker 体系的软件。

下图中的紫、橙、红三种颜色,代表了 dockershim、cri-containerd、cri-o 三种 CRI 的典型方式——流程在逐渐缩短,这也是 CRI 实现的一个演进过程。

如果是 kubelet 的 dockershim 模式(紫色),流程是这样的:

  • kubelet从CRI的gRPC调用dockershim,二者在同一个进程
  • dockershim调用docker守护进程
  • docker守护进程调用containerd;containerd调用containerd-shim(有时名为docker-containerd-shim守护进程)完成创建容器等操作
  • containerd-shim访问OCI的实现runC(命令行可执行程序)

 

如果是 kubelet 的 cri-containerd 模式(橙色),流程是这样的:

  • kubelet从CRI的gRPC调用cri-containerd;
  • cri-containerd调用containerd;containerd调用containerd-shim(同上)
  • containerd-shim调用RucnC (同上);

 

在很多人的印象中,如果不用 docker 守护进程,就相当于“弃用 docker“,这其实也就是从 dockershim 到 containerd 的变化。从另一个角度来说,containerd 这个守护进程,也是 docker 组织做的。

如果是 kubelet 的 cri-o 模式(红色),则更加简练:

  • kubelet从CRI的gRPC调用cri-o;
  • cri-o调用OCI的实现runC

 

如果以 kubelet 调用 CRI 为起点,OCI 的 runC 调用为终点,三种模式经历的可执行程序分别是:

  • dockershim模式:dockershim(*)->dockd->containerd->containerd-shim
  • cri-containerd模式:cri-containerd(*)-> containerd->containerd-shim
  • cri-o模式:cri-o

dockershim 模式有 3 个可执行程序,dockershim 一般与 kubelet 同进程;cri-containerd 模式有 2-3 个可执行程序,cri-containerd 可与 containerd 同进程;cri-o 模式只有 1 个可执行程序。

显然在这种 Red Hat 推崇的 cri-o 模式下,Docker 体系的 containerd 也用不着了,只剩 runC 这个命令行的程序。runC 也是用 go 写成的,里面有调用 libcontainer。

当 Docker 萎缩到这个地步,其实也只剩 Linux 内核里面 cgroup、namespace 功能的封装了。

总结来说,由于老技术实现的惯性,在生产环境大量使用的经典 Kubernetes+ Docker 方案依然运行,且运维已经成熟,不会很快升级。对于开发人员、企业,对于 K8S API 的使用频率、变数,远远大于 Docker API,至于 Kubernetes 和 Docker 的桥接,更不用关心。因此,即便“彻底弃用 Docker”,对开发者与企业的影响也非常有限。

显示余下内容
相关文章:
 

发表回复

您的电子邮箱地址不会被公开。