随着越来越多的酷炫特性增加,Kubernetes 也变得越来越复杂,用户的个性化定制特性需求也越来越难以合入。所幸 Kubernetes 提供了扩展自身的能力,这样当其原生的特性不满足用户需求时,用户可以在不改变 Kubernetes 代码的情况下,通过扩展机制来达到目的。

欢迎戳 “阅读原文” 观看讲师【现场视频】,对话框回复【资料下载】获取本文 PPT。

目前, Kubernetes 有着极多的扩展点,比如 CNI、CRI、CSI、Device Plugin、CRD 等。本文将对这些扩展点进行分类讲解,探讨 Kubernetes 扩展机制的道与术。通过这些扩展机制,用户可以将复杂事情简单化,从而获得一个全新的 Kubernetes,这就是你自己的 Kubernetes 发行版!

本文能帮助 Kubernetes 用户更好理解 Kubernetes 的扩展性机制及扩展点,也能让开发者了解如何利用这些扩展点来增强 Kubernetes 的原生功能,打造一个更加灵活完备的基于 Kubernetes 平台。

今天的主要内容有这四个部分:

第一部分:Kubernetes 扩展性概述;第二部分:多维度对 Kubernetes 扩展性分类;第三部分:逐一介绍 Kubernetes 中的扩展点;第四部分:总结。

K8S 扩展性概述

首先,此图是 2017 年秋季的一个统计,它统计了一些组织、公司用于管理容器的平台。我们可以看出 Kubernetes 已占据绝对的份额和优势。

在图中我们也可以看到一些熟悉的面孔,比如说 Docker Swarm,是这场 Kubernetes 与 Docker 编排的赛场上,为避免 Kubernetes 一家独大而衍生的容器编排管理平台产物。但它的影响力,却远没达到 Kubernetes 的程度。当然了,比如图上所示像 Mesos 此类平台,它们的份额跟 Kubernetes 所占的份额基本不在同一级别。

虽然这是 2017 年的统计,但时至今日我们都能发现 Kubernetes 的热度仍未减缩且充满生命力。从上图 Kubernetes 的份额不难看出,Kubernetes 几乎赢得了这场容器之战。但 Kubernetes 在发展中也存在一些问题,而这些问题将会影响后面的功能扩展。

我们先对问题进行盘点。第一点,Kubernetes 变得越来越臃肿。今早,Dan Kohn 在 Keynote 中也提到,现在 Kubernetes 代码已有三百多万行,这个量级的代码对诸位来说,绝对是一个噩梦。因为它的这种臃肿,无论是在学习还是使用中,一旦出现问题你将很难进行排查。

在它发展过程中,Kubernetes 中的 in-tree 东西越来越多,很多都直接放到 Kubernetes 的主代码库中,或编到它自己的二进制文件中。它的稳定性会受到影响,因为你每加一个新特性或新功能,在没经过充分测试的情况下,可能会存在很多 bug 或不稳定性。

在如此庞大的代码体量下,它的学习曲线也会变得越来越陡峭。而引出后面更进一步的问题,就是它 in-tree 的东西越来越多,如果出现需要支持的新特性或需要新支持的 vendor 时,它该怎么办?

第二点,Kubernetes 会时常出现波动,难以稳定。当你把一些东西做到它的版本中,但新增的东西可能会出现 Crash 或挂掉,随之导致整体组件挂掉。另一个问题,你需要将这个特性编到 Kubernetes 版本中,从属于它的版本节奏。当出现这些问题时,Kubernetes 应该如何应对?

在这种情况下,社区的大牛们可能将出现的问题做成可扩展的。这也是 Kubernetes 能保持茂盛生命力的根本原因。此图中间列就是它的可扩展性,上面是模块化。Kubernetes 的各个组件各司其职,分工比较明确。它的 Controllers,都是独立的模块,这些模块化做得十分不错。

这幅图和题目的选取互相印证,简单的说就是道家所说的三生万物。它的哲学思想能够使它的扩展性、扩展点很好的体现在它的发展过程中。所谓的道法自然,就是你要顺应自然的发展规律,顺应事物的发展规律,推及到 Kubernetes 就是要顺应用户呼声。

Kubernetes 中的很多扩展性,也是针对社区用户使用的不适应感:开发者为用户新加了东西,用户需要跟着开发者的节奏来,但是有时用户又不想被束缚。对于第三方 vendor 来说,我可能需要编到开发者版本中去,但我又不想把代码放到开发者库中。正是用户的这些呼声和它的发展规律,使得 Kubernetes 社区致力于提高 Kubernetes 的扩展性,以更平稳的一个方式向前演进。

多维度对 K8S 扩展性分类

下面我将从多个角度对 Kubernetes 的扩展性进行分类。首先,根据领域的划分。它分三大领域,也就是网络、存储、计算,Kubernetes 对这三块都有可扩展性支持。

网络领域:即CNI。Kubernetes 从最初就有扩展性的东西。对于 Kubernetes 来说,它只定义了一个基本的网络模型,至于具体网络解决方案,它是交给第三方插件来实现的。

存储领域:Kubernetes 也要对接很多 vendor,像 Ceph,GulusterFS 等,它在长期的发展过程中,存储这部分很难稳定。所以 Kubernetes 提出了一些规范或是存储插件接口。

计算领域:CRI(容器运行时接口),随着容器运时开始遍地开花,比如说 rkt、Containerd、frakti 等,Kubernetes 需要支持他们并把相应的适配都编到 Kubernetes 中,也使 Kubernetes 变得越来越不稳定。

根据组件来划分,我们知道 Kubernetes 有它的客户端、服务端组件。像上图的 kubectl 就是它的 CLI 客户端,现在它也提供了插件机制。像 API Server,它有Aggregator,还可以自定义 API Server。像 Controller,你可以实现自定义的Controller。

比如,之前对云供应商的支持,如今已抽象成 cloud-controller-manager。像调度器,也是支持自定义调度器。它原生的调度器有一些相应的调度策略、调度算法,但在业务场景中,这些调度策略可能并不能满足你的要求,这样你就可以自行写一个调度器。像 kubelet ,在社区有一个 Virtual-Kubelet 项目,它并不是 Kubernetes 原生的扩展性。

再根据进行层次划分,分为基础设施层和 API 层。基础设施就是我们前面提到的三个领域,还有 Device Plugin(在后面将进一步介绍)。API ,首先是 CRD(用户自定义资源)。还有 Webhook 会在好多地方挂一堆钩子。Config file 会对应之前 Kubernetes 的组件里的 flag,在启动组件时,根据实际需要去配置那些 flag 对应的启动参数。在以前,当你执行某个组件时,比如 kube-apiserver -h,就能看到有两到三屏的 flag,这是一个海量的 flag,现在已经利用配置文件的方式代替 flag。还有 Annotation,Annotation 就相当于 Kubernetes 新特性的试验场。

此图基本上把 Kubernetes 里面现有的所有扩展点都列举出来。后面我会逐一进行简要说明。

扩展点

这一部分我们将前面所列的 Kubernetes 扩展点给大家做进一步简要说明。

CNI

CNI(容器网络接口,Kubernetes 中的网络支持规范),Kubernetes 对接的第三方网络插件是按照这个规范来实现的。规范可以简单的理解为,它有一个 add 和 delete 接口,add 接口是增加网络,也就是将 Pod 挂到网络中去,而 delete 接口是销毁网络。

此图上面是容器运行时(CRI 层),下面是 CNI。再往下是 CNI 社区原生的一些插件,比如 LoopBack、Bridge 等。最后,就是第三方的插件。

这是支持 CNI 规范的一些平台,像 Kubernetes、CloudFoundry 都支持 CNI 接口规范。

上图依次是现在比较主流的一些插件,外围都是单网络插件。社区从 2016 年初,就已经有要支持多网络的呼声,但在 Kubernetes 官方库中,一直都没能推进。这样做可能会对 Kubernetes 的 API 有太多改动,从而导致系统的不稳定性。而从插件侧支持多网络的,一个是 Intel 的 Multus 和中兴通讯的 Knitter。

CRI

CRI(容器运行时接口),它能利用 kubelet 对接各种不同的容器运行时,同时你不需要重新编译 kubelet。这是它的简要示意图,kubelet 只要做一个简要的 gRPC client 来支持 CRI 接口,在特定的容器进行时和 kubelet 之间有一个 shim 层(垫片),利用这个垫片来做适配。在 kubelet 需要支持一个新的运行时时,只需你在运行时那一方做垫片,而 kubelet 不需要做改动。下面列是现在一些支持 CRI 的容器运行时:

CSI

CSI(容器存储接口),在 Kubernetes 中它最初支持最原始的 HostPath、EmptyDir,后来就是 NFS 以及像 Ceph,Gluster 这一类的存储系统。这些存储系统也要在 Kubernetes 的代码库中做相应支持。在这种情况下,社区觉得这种方式扩展性还是太差,希望能够把它独立出一个接口,这就产生了 CSI。以上就是出现容器存储接口的动机。

现在存储插件的开发和 Kubernetes 的发布紧紧地耦合在一起,它依赖于 Kubernetes 版本。Kubernetes 的开发者社区要对所有存储插件的测试和维护来负责。如果你的存储插件里有些 bug 崩溃掉了也会导致其他一些组件随之挂掉。

再就是存储插件还要获取 Kubernetes 组件的全部权限,这也是 Kubernetes 本身不希望看到的。还有一点需要强调,插件开发者可能并不希望看到插件代码公开到社区中。

此图是社区建议部署 CSI 驱动机制:

此图是目前支持 CSI 的一些平台:

此图是一些生产级驱动:

Kubectl plugin

用户可以直接通过它来做增删改查工作,或进行一些特定的管理工作。当用户的管理需求并不单一时,原生接口将不能满足你的需求。比如,你正在执行一个命令,平台却告知你没有这个命令时,你就需要一个支持的插件,这个接口就能让你的运维操作,集成到 kubectl 客户端的命令中。

这是它在即将发布的 1.12 里的一个实现与之前的实现有些差异。你可以把插件命名为 kubectl- 这种格式,然后放到环境变量中 $PATH 对应的目录里,与插件具体怎么实现毫无关系。比如说输入 kubectl foo,再输入 version,它就会打印 1.0.0 这个结果。

API Server

当用户觉得它现在的 API 接口不能满足需求时,用户就想自己开发一些 API 接口。Kubernetes 会提供这样一个机制,它能够使你的 API Server 遵循它的 API 规范,把你的 API Server 集成到 Kubernetes 中。

API Server 扩展带来的收益:

扩展性;用户的 API Server 对 Kubernetes 不会做改动(这样 Kubernetes 社区就不需要去 review 用户的 API 实现);提供实现实验性的 API 的平台;保证新的 API 遵循 Kubernetes 的 API 规范。

这个机制能做什么?首先它的 aggregator 能提供一个注册 API Server 的 API 接口以及提供一个发现机制,然后能够代理用户的请求,发送到你特定的 API Server。

此图是当你存在很多扩展组件时的一个部署流程。首先将 etcd 部署上,依次将 master 组件、node 组件进行部署,此时就可以把你的 aggregator 当成一个集成器部署,而后是你自定义的一些 Controller,最后将你自行开发的一些 API Server 以及它相应的 Controller 部署上。

Custom controllers

这是一个自定义的 Controller。比如它原生支持的是 ReplicaSet、Deployment、Service 等 Controller。当你定义一个用户自定义资源(CRD),但是这个 CRD 只是给你提供了 API 的增删改查功能,它并不能提供管理控制功能。此时你就可以去开发自定义的 Controller,来管理你的自定义资源。这个 Controller 也可以在 Kubernetes 中进行部署和升级。

Cloud Controller Manager

另一个就是 Cloud Controller Manager。Kubernetes 现在支持对接很多公有云。在没有这个云控制管理器之前,跟公有云的对接涉及到很多组件 ,比如涉及到 Controller Manager,涉及到 API Server,还涉及到 kubelet。

当新支持一种云时,你就要在它核心组件里去改动。因此将这块抽象成一个独立的组件,它跟其他的组件基本上没有耦合。在这种情况下,用户新增一个云的实现,或者是新增对于一个云的支持,用户只需在 Cloud Controller Manager 里做相应的改动。

Multiple Schedulers

这个是一个多调度器。在前面也提到过 Kubernetes 的调度器原生有很多策略,但这些策略可能并不能满足用户的业务需求,就需要用户开发一些调度策略。但是用户这些调度策略要在 Kubernetes 中支持比较困难。

比如,你想在区里推一个调度策略,但社区觉得不会带来很大收益而拒绝接受。此时用户就可以自行开发调度策略,以及相应的调度器。它会把相应的 workload 根据设计的调度策略来调度到相应的节点上去。

这就是在 pod.yaml 文件中做的配置,用户可以利用该配置字段指定使用的调度器。

Scheduler Extender

这是 Scheduler Extender,有些集群级的资源可以通过这个扩展器来管理,因为它的原生资源只能通过它原生的 scheduler 来管理,扩展资源是没有办法用原生的 scheduler 来管理。

Virtual Kubelet

这是 Virtual Kubelet,对于原生的 kubelet,它是调度 Pod 的容器。但对于 Serverless 平台,它所调度的东西可能不太一样,此时用户就需要一个 Virtual kubelet 来对接下面的 Serverless 平台。但是它暴露给 Kubernetes 还是像一个原生的 kubelet 节点。此图就是 Virtual Kubelet 与 Kubernetes 的 API Server 对接的一个过程。

Config file

Kubernetes 各个组件已存在很多 flag。如果要利用这些 flag 你可能需要在命令行里挂载上,但你无法管理这些 flag 版本。于是社区就提供了 Config file 的方式。

比如说 API Server,是 Kubernetes 启动的一个 flag 示例。这张是 kube-proxy 与前面的那个示例不太对应。对于 kube-proxy(现已支持配置文件),在这个示例中只要在命令行里使用一个 --config 参数,你具体的配置文件就可以通过一个带版本号的 yaml 文件来管理。

Device Plugin

这个是 Device plugin。Kubernetes 原生支持 CPU、内存、GPU 等资源。但如果我需要支持 FPGA 或支持其他的 InfiniBand 类设备,用户就需要再往 Kubernetes 里创建但可能存在稳定性影响。

对于 Device Plugin 机制,它简单来说是在 kubelet 里注册的节点上的 Device Plugin,并告知用户该节点上管理着一些特定的设备资源。当用户的 Pod 启动时,就可以找此节点来索要资源,节点会给用户做相应的分配。这就是它的管理阶段。

Extended Resources

这是一个扩展资源,它其实很简单,你定义一个 example.com/foo 格式资源。如果其他用户想用,就按照这个格式在 Pod 资源请求中进行填写。

如图是 Extended Resources 的一个资源等级:

这是 Extended Resources 的配置方式:

CPI

这是 CPI,它现在还处于一个概念阶段。Kubernetes 现有很多 Policy,但它们没有统一的管理方式,也没有统一的接口,所以社区提出了 CPI 概念(尚未实现)。

CRD

CRD(用户自定义资源),用户可以定义它的基本属性,像 Kubernetes 的原生 object 对它进行管理,通过 API 进行增删改查操作。

这是你创建 CRD 后,通过客户端来 get 相应的资源对象:

这是一个自定义的 Controller 加上自定义资源,最终实现一个 declarative API(Kubernetes 核心的 API 资源)。

这是 CRD 的一个实现实例,现包括 Operator Framework 对应的一些 operator:

External Metrics

External Metrics 是一个外部度量。原生的度量包括 CPU 和 memory,还包括 Pod 级的 metrics、对象级的 metrics。对于用户想实现的一些额外度量策略、度量维度,K8S 提供的一个扩展性。

Webhook

此图是 Webhook。在 API 请求的各个阶段,它都提供对外 Webhook,用于对接外部相应阶段的处理。

调度器的 Policy 也用到 Webhook。在 Container 生命周期中,像 PostStart、Prestop 也可用 Webhook 方式来实现。

client-go

client-go 在进行客户端身份认证时可能并没有用到它原生支持的认证方式,此图提供了一个插件式的方式来支持它的认证。

Initializers

Kubernetes 中的 object 在创建之前会有一个 pre-initialization tasks 列表,还有一个相应的自定义 Controller。这个 Controller 会执行相应的 task 列表,做一些相应的初始化处理,controller 执行完就删掉,再执行对象创建。

KMS

当我有数据要存到 etcd 中,以前都是利用明文的方式来存储。但比如像 secret 等数据,可能希望它以加密的形式存储。目前 Kubernetes 向 etcd 存储数据时,支持使用一些加密方式,或者对接外部的 Key Management 管理系统,但是需要在 API Server 中增加与 KMS 的对接支持。所以 Kubernetes 提供了 KMS 插件,把集成的对接支持独立出来。

Annotations

Annotations 很多用户觉得它其貌不扬,但它却曾经是很多特性的实验室。比如说亲和性策略、Storage Class、初始化容器、Critical Pod 等,这些特性都是通过先配置在 Annotations 中来实验其功能的。

总结

经过上面的这些扩展性后,用户就得到了一个全新的 Kubernetes 管理系统和周边组件。在用户部署的过程中,定制化的 Kubernetes 多种多样,但它至少能满足用户的业务需求,配合用户进行工作。

曾有人说 Kubernetes 是云时代的 Linux,其实现在的 Kubernetes 不仅仅是一个平台,同时它也在向内核化做发展。它只做一个内核,而你可以根据它的扩展机制,做自己的发行版。

Kubernetes 仍然在不断的演进,相信在它的发展过程中,还会有新的扩展机制出现。拥抱变化、适应变化、改变变化,Kubernetes 一定能够变得更加灵活,为云原生带来更多惊喜!

赵相鹏/ 中兴通讯软件工程师

中兴通讯软件工程师,自 2015 年下半年开始参与 Kubernetes 社区,现为 Kubernetes 和 Kubernetes-incubator 两个组织的成员。在 Kubernetes 项目主要关注网络、集群生命周期等领域,此外也在 test-infra 和 docs 有一定参与,以帮助社区提升贡献者体验。

查看原文 >>
相关文章