podman官方网站
k8s版本1.20功能发布
Docker的第二次死亡
容器化到容器编排之旅
在这里插入图片描述

1 背景

Kubernetes团队近日宣布将在最新版本中弃用Docker支持的功能,后续版本会陆续删除这些功能。

近日,Kubernetes团队发布了最新的1.20版本,新版本更新了许多内容:

存储卷快照功能趋于稳定;
Kubectl Debug进入Beta;
Beta:API优先级和公平性;
IPV4/IPV6 Alpha功能更新;
GA:限制进程PID;
Dockershim弃用;
Exec探针超时处理等等

在这里插入图片描述

其中,有一项更新对于开发者社区来说无疑是一枚重磅炸弹:正式宣布弃用Docker支持的功能。那么,究竟Kubernetes为什么要这么做,以及这么做会有什么影响呢?

Docker是一种以容器化的方式打包、分发和部署应用程序的方式。自2013年3月13日初始版本发布以来,Docker已成为容器业界的事实标准。而Kubernetes是一款由Google开发的开源容器编排系统。
Kubernetes架构示意图
在这里插入图片描述

1.1 Docker与OpenShift

在2015年的峰会上,红帽发布了OpenShift V3.0,该新版本OpenShift底层采用Docker容器,同时开始使用Kubernetes来编排镜像。
OpenShift架构
在这里插入图片描述
OpenShift是红帽的云开发平台即服务(PaaS),底层以Docker作为容器引擎驱动,以k8s作为容器编排引擎组件,OpenShift提供了开发语言、中间件、自动化流程工具及界面等元素,提供了一套完整的基于容器的应用云平台。

然而,在2016年的红帽峰会期间,Docker对红帽的 OpenShift 展开了锋芒毕露的攻击。他们不仅发表了以下推文,还给与会者发放带有“我们不接受模仿”字样的T恤衫:
在这里插入图片描述
显然左边的仿制鲸鱼就是在嘲讽红帽的OpenShift。当时,OpenShift采用了基于Docker的容器。红帽发布的Docker一般会比原版落后一点点,而且为了提供所谓的“企业支持”,红帽采取了给旧版本Docker打补丁的行为。但相比之下,Docker总是在发布最新版。

当然,对于维护企业应用应该采用升级还是采用移植补丁的方式,到现在依然众说纷纭,所以对于这一点在此不做评论,但Docker在红帽自己的峰会上的这种行为确实有点出乎意料。不得不承认,在此之前Docker是一项伟大的技术,毕竟它是红帽OpenShift的重要组成部分,但从那天起,事情就开始变味了。

1.2 平台之争

早期的PaaS平台主要是OpenShift,以及两家竞争对手Docker和Pivotal

Pivotal是EMC和VMware于2013年创建的公司,专注于开源PaaS的解决方案。他们的企业解决方案非常成功,原因非常简单:用户体验非常好,尤其是结合Pivotal Labs使用的时候。

Docker是企业解决方案的后起之秀,他们的优势就是开发者们早已熟知Docker引擎了,而当时k8s还不知道在哪儿。

然而,Docker对OpenShift的攻击行为,使红帽不得不将资源投入到了Kubernetes上。后来的结果大家都看到了,Kubernetes大获成功,并且获得了整个行业的拥护。

此时Docker为了挽回败局而推出了Docker Swarm,但为时已晚。2016 年后半年,Kubernetes超过了 Docker Swarm,成了行业事实上的标准。最终,Docker Swarm并没有给 Kubernetes 带来任何冲击。可以认为这是Docker 的第一次死亡

从此以后,Docker不再是企业级的PaaS解决方案,只能作为云原生系统中的一部分存在,好在它一直是 Kubernetes中的一个重要组成部分。

1.3 k8s宣布弃用Docker

2020年12月8日,Kubernetes宣布弃用Docker。这无疑是第二次宣布了Docker的死亡。在此之前,Docker始终是Kubernetes不可或缺的一部分。
在这里插入图片描述
版本1.19版以前的Kubernetes需要通过一个名为 Dockershim的模块连接到Docker,然后由Docker连接到Containerd来创建容器。从技术上来看,实际的容器运行时是Containerd,而不是Docker。

Docker的作用只不过是在Containerd上创建容器而已。作为人类用户,只需运行一个Docker run就可以创建一个容器,这一点非常方便。然而在方便的同时,Docker也带来了许多无用的操作和技术债务,对于Kubernetes而言,这就是负担

按照Kubernetes自己的说法,Docker已不再是必须的技术,而是变成了技术债务。
在这里插入图片描述
Kubernetes完全可以绕过Docker,自己在Containerd 上创建容器,从而获得同样的效果。而Kubernetes 1.20中就采用了这种做法。

尽管Docker公司的商业模式失败了,但我们必须承认Docker为整个行业做出的巨大贡献。Docker公司带来的技术是业内最好的。时至今日,我们的CI/CD系统还极其依赖Docker。没有Docker,也不可能有Kubernetes的成功,而且Kubernetes依然有Docker的影子。

不过也不用担心,Kubernetes团队已经做了大量的努力,尽可能使升级过程平稳。即使你升级到1.20,也只会收到一个关于Docker已被弃用的警告。

目前Kubernetes的计划是在2021年末期发布的1.22中彻底移除Docker支持,所以开发者必须在那之前切换到其他的容器运行时,比如Containerd或CRI-O等

2 基本概念

2.1 容器运行时

在容器服务里,运行时这个词是有歧义的。每个项目、公司或社区对术语容器运行时都有自己的、通常是基于上下文的特定理解。大多数情况下,运行时的特征是由一组职责定义的,从最基本的职责(创建namespace、启动init进程)到复杂的容器管理,包括(但不限于)镜像操作。
在这里插入图片描述
专门介绍一下低阶容器运行时。在OCI运行时规范中,组成Open Container Initiative的一些重要参与者对底层运行时进行了标准化。低阶容器运行时是一个软件,作为一个包含rootfs和配置的目录输入,来描述容器参数(如资源限制、挂载点、流程开始等),并作为运行时启动一个独立进程,即容器。

到2019年,使用最广泛的容器运行时是runc。这个项目最初是Docker的一部分(因此它也是用Go编写的),但最终被提取并转换为一个独立的CLI工具。很难高估这个组件的重要性——基本上runc是OCI运行时规范的一个参考实现

一个值得注意的OCI运行时实现是crun。它用C语言编写,既可以作为可执行文件,也可以作为库使用。

2.2 容器管理

在命令行中可以使用runc启动任意数量的容器。但是如果我们需要让这个过程自动化呢?假设我们需要启动数十个容器来跟踪它们的状态,其中一些在失败时需要重启,在终止时需要释放资源,必须从注册中心提取镜像,需要配置容器间网络等等。

这是一个稍微高级的任务,并且是容器管理器的职责。老实说,不知道这个词是否常用,但发现用它来描述很合适。将以下项目归类为容器管理器:containerd,cri-o,dockerd和podman,有时也称为容器引擎

2.2.1 containerd

与runc一样,我们可以再次看到Docker的遗产,containerd曾经是Docker项目的一部分,现在它是一个独立的软件,自称为容器运行时。但显然,它与运行时runc不是同一种类型的运行时。不仅它们的职责不同,其组织形式也不同。runc只是一个命令行工具,containerd是一个长活的守护进程。

一个runc实例不能比底层容器进程活得更久,通常它在create调用时启动,然后在start时从容器的rootfs中exec特定文件。

containerd可以运行的比成千上万个容器更长久,它更像是一个服务器,侦听传入的请求来启动、停止或报告容器的状态。

在幕后,containerd使用runc。它不仅仅是一个容器生命周期管理器,还负责镜像管理(从注册表中拉取和提交镜像,本地存储镜像等等),跨容器联网管理和其他一些功能。
在这里插入图片描述

2.2.2 cri-o

另一个容器管理器是cri-o。containerd是Docker重构后的结果,但cri-o却源于Kubernetes领域。

在过去,Kubernetes使用Docker管理容器。然而,随着rkt的崛起,一些人增加了在Kubernetes中可互换容器运行时的支持,允许Docker和或rkt完成容器管理。这种变化导致Kubernetes中有大量的条件判断,没有人喜欢代码中有太多的if。

因此,容器运行时接口(CRI,Container Runtime Interface)被引入到Kubernetes中,这使得任何兼容CRI的高阶运行时(例如容器管理器)都可以在Kubernetes中使用,而无需任何代码的更改。

cri-o是RedHat实现的兼容CRI的运行时,与containerd一样,它也是一个守护进程,通过开放一个gRPC服务接口来创建、启动、停止(以及许多其他操作)容器。在底层,cri-o可以使用任何符合OCI标准的低阶运行时和容器工作,默认的运行时仍然是runc。cri-o的主要目标是作为Kubernetes的容器运行时,版本控制也与K8S一致,项目的范围界定的很好,其代码库也比期望的更小。

cri-o架构
在这里插入图片描述
规范的好处是所有符合规范的技术都可以互换使用。一旦CRI被引入,一个containerd的插件就可以在它的功能之上实现CRI的gRPC服务。这个想法是可行的,所以后来containerd获得了原生的CRI支持。因此,Kubernetes可以同时使用cri-o和containerd作为运行时。

2.2.3 dockerd

还有一个守护进程是dockerd,它是个多面手。一方面,它为Docker命令行开放了一个API,为我们提供了所有这些常用的Docker命令(Docker pull、Docker push、Docker run、Docker stats等)。

但是我们已经知道这部分功能被提取到了containerd中,所以在底层dockerd依赖于containerd就不足为奇了。

但这基本上意味着dockerd只是一个前端适配器,它将containerd的API转换为广泛使用的docker引擎的API。

2.2.4 podman

podman是另一个Red Hat项目,目的是提供一个名为libpod的库(而不是守护进程)来管理镜像、容器生命周期和pod(容器组)。podman是一个构建在这个库之上的命令行管理工具。作为一个低阶的容器运行时,这个项目也使用runc。

从代码角度来看,podman和cri-o(都是Red Hat项目)有很多共同点。例如,它们都在内部使用storage和image库。

既然我们已经有了dockerd、containerd和cri-o,为什么还要开发这样的项目呢?守护进程作为容器管理器的问题是,它们大多数时候必须使用root权限运行。尽管由于守护进程的整体性,系统中没有root权限也可以完成其90%的功能,但是剩下的10%需要以root启动守护进程。

3 Docker的替代品

弃用Docker之后,开发者们对其替代品的讨论逐渐热烈,其中Containerd和podman倍受期待。

3.1 Docker容器引擎

在这里插入图片描述
docker由docker client和 dockerd(docker daemon)组成,前者为客户端,后者为对容器相关操作的api的最上层封装。docker client通过rest api调用dockerd。

containerd是dockerd和runc之间的一个中间交流组件,通过grpc的方式与dockerd通讯,dockerd实际真实调用的还是containerd的api接口。理论上,不运行dockerd,也能够直接通过containerd来管理容器。

docker-shim是一个运行容器的真实垫片载体,每启动一个容器都会启动一个新docker-shim进程。docker最开始的时候是没有shim进程的。那时候所有的容器进程都挂在dockerd进程下面,也就是说,如果dockerd重启了,那么所有的容器都得跟着挂了。shim会调用runC启动容器,容器的进程的父ID就是shim进程。

最终docker-shim运行runc去运行容器里面的程序。

3.2 Containerd容器引擎

Containerd是容器虚拟化技术,从docker中剥离出来,形成开放容器接口(OCI)标准的一部分。
在这里插入图片描述

docker对容器的管理和操作基本都是通过Containerd完成的。Containerd是一个工业级标准的容器运行时,它强调简单性、健壮性和可移植性。Containerd可以在宿主机中管理完整的容器生命周期:容器镜像的传输和存储、容器的执行和管理、存储和网络等。

一、docker安装后containerd默认已安装,containerd包含如下命令组件

containerd:高性能容器运行时。
ctr:containerd的命令行客户端。
runc:运行容器的命令行工具。

二、docker、containerd、containerd-shim、runC关系
(1)docker:docker本身而言,包括了docker client和dockerd,dockerd实属是对容器相关操作的api的最上层封装,直接面向操作用户。

(2)containerd:dockerd实际真实调用的还是containerd的api接口(rpc方式实现),containerd是dockerd和runC之间的一个中间交流组件。

(3)containerd-shim:一个真实运行容器的载体,每启动一个容器都会起一个新的containerd-shim的进程。它通过指定三个参数:容器ID、boundle目录(containerd对应某个容器生成目录)、运行时二进制(默认是runC)来调用runC的api创建一个容器。

(4)runC:一个命令行工具端,根据OCI的标准来创建和运行容器。

Containerd是一个工业级标准的容器运行时,它强调简单性、健壮性和可移植性。它可以管理容器的生命周期,可以被Kubernets CRI等项目使用,并为广泛的行业合作打下基础等。

3.3 podman容器引擎

Podman原来是CRI-O项目的一部分,后来被分离成一个单独的项目叫libpod。Podman的使用体验和 Docker类似,不同的是Podman没有daemon。直接通过OCI runtime(默认也是 runc)来启动容器,所以容器的进程是Podman的子进程。这比较像Linux的fork/exec模型,而Docker采用的是C/S(客户端/服务器)模型。

Podman是一个无守护进程的开源Linux原生工具,旨在使用开放容器倡议(OCI)容器和容器镜像轻松查找、运行、构建、共享和部署应用程序。Podman提供了任何使用过Docker容器引擎的人都熟悉的命令行界面(CLI) 。大多数用户可以简单地将Docker别名为Podman(别名docker=podman),没有任何问题。与其他常见的容器引擎(Docker、CRI-O、containerd)类似,Podman依赖于符合OCI的容器运行时(runc、crun、runv等)与操作系统交互并创建正在运行的容器。这使得Podman创建的运行容器与任何其他常见容器引擎创建的容器几乎没有区别。

虽然目前容器市场 Docker 还是占用很大的比例,但被弃用的结局已定,在这个过渡期中,不妨去拥抱Containerd和Podman吧!

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐