本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。

GSoC 2020 - 为集群插件构建 Operator

引言

Google 编程之夏是一个全球性项目,旨在向学生介绍开源。学生们与开源组织配对,在夏季与他们一起工作三个月。

我叫 Somtochi Onyekwere,来自尼日利亚奥韦里联邦科技大学。今年,我有机会与 Kubernetes(隶属于 CNCF 组织)合作,这使我度过了一个令人惊叹的夏天,学习、贡献并与社区互动。

具体来说,我致力于“集群附加组件:打包所有内容!”项目。该项目专注于构建运算符以更好地管理各种集群附加组件,扩展构建这些运算符的工具,并使这些运算符的创建过程顺畅。

背景

Kubernetes 在过去几年中取得了巨大进步,拥有繁荣的社区和大量的贡献者。代码库正在逐渐从所有代码都位于kubernetes/kubernetes仓库的单体结构,转变为拆分为多个子项目。集群附加组件的重点之一是使其中一些子项目以易于组装、自监控、自修复和 Kubernetes 原生方式协同工作。它使它们能够无缝工作,无需人工干预。

社区正在探索使用 Operator 作为一种机制来监控集群中的各种资源并妥善管理这些资源。此外,它还提供了自我修复功能,并且是一种 Kubernetes 原生模式,可以编码这些附加组件的最佳工作方式并妥善管理它们。

什么是集群附加组件?集群附加组件是资源(如 Services 和 Deployment)的集合,用于为 Kubernetes 集群提供额外的功能。它们包括从简单的 Kubernetes Dashboard(用于可视化)到更复杂的 Calico(用于网络)等。这些附加组件对于集群中运行的不同应用程序和集群本身都是必不可少的。附加组件 Operator 提供了一种更好的方式来管理这些附加组件,并了解构成附加组件的各种资源的健康状况和状态。您可以在这篇文章中获得更深入的概述。

Operator 是具有自定义资源定义的自定义控制器,它们编码特定于应用程序的知识,并用于管理复杂的有状态应用程序。这是一种被广泛接受的模式。通过 Operator 管理附加组件,这些 Operator 编码了关于附加组件最佳工作方式的知识,引入了许多优势,同时设定了易于遵循和扩展的标准。这篇文章很好地解释了 Operator。

附加组件 Operator 可以解决很多问题,但它们也面临挑战。在 cluster-addons 项目下,它们缺少一些组件,并且仍处于概念验证阶段。为 Operator 生成 RBAC 配置很痛苦,有时 Operator 被赋予了过多的特权。Operator 的可扩展性不强,因为它只能从本地文件系统或 HTTP(s) 服务器拉取清单,而且许多简单的附加组件都在生成相同的代码。我花了一个夏天来解决这些问题,用全新的视角审视它们,并为已知和未知的问题提出了解决方案。

对 kubebuilder-declarative-pattern 的各种补充

kubebuilder-declarative-pattern(以下简称 KDP)仓库是 kubebuilder SDK 之上的一层附加组件特定工具,通过将实验性的 --pattern=addon 标志传递给 kubebuilder create 命令来启用。它们共同为附加组件 Operator 创建了基础代码。在实习期间,我致力于 KDP 和集群附加组件中的一些功能。

Operator 版本检查

即使 Operator 具有复杂的逻辑,为 Operator 启用版本检查也有助于使附加组件不同版本的升级/降级更安全。这是一种将附加组件的版本与知道如何很好地管理它的 Operator 版本匹配的方法。大多数附加组件都有不同的版本,这些版本可能需要以不同的方式管理。此功能检查自定义资源是否存在 addons.k8s.io/min-operator-version 注解,该注解声明了管理该版本所需的最低 Operator 版本,并与 Operator 的版本进行比较。如果 Operator 版本低于所需的最低版本,Operator 将暂停并显示错误,告知用户 Operator 版本过低。这有助于确保为附加组件使用正确的 Operator。

用于存储清单的 Git 仓库

以前,仅支持本地文件目录和 HTTPS 仓库来存储清单。让附加组件 Operator 的创建者能够将清单存储在 GitHub 仓库中,可以加快开发速度并进行版本控制。启动控制器时,您可以传递一个标志来指定您的通道目录的位置。通道目录包含不同版本的清单,控制器从该目录中拉取清单并将其应用于集群。在实习期间,我将其扩展为包含 Git 仓库。

用于暂时禁用协调的注解

确保所需状态与实际状态匹配的协调循环会阻止集群中对象的修改。这使得实验或调查集群中可能出现的问题变得困难,因为任何更改都会迅速恢复。我通过允许用户在他们不希望控制器协调的资源上放置 addons.k8s.io/ignore 注解来解决这个问题。控制器会检查此注解,并且不协调该对象。要恢复协调,可以从资源中移除该注解。

kubebuilder-declarative-pattern 中的非结构化支持

我所研究的其中一个 Operator 是一个通用控制器,它可以管理多个不需要额外配置的集群附加组件。为此,该 Operator 不能使用特定类型,并且需要 kubebuilder-declarative-repo 支持使用 unstructured.Unstructured 类型。kubebuilder-declarative-pattern 中有各种函数无法处理这种类型,如果传入的对象不是 addonsv1alpha1.CommonObject 类型,则会返回错误。这些函数被修改为可以处理 unstructured.Unstructuredaddonsv1alpha.CommonObject

工具和 CLI 程序

我还编写了一些命令行程序,可以使使用附加组件 Operator 更容易。它们中的大多数在附加组件 Operator 之外也有用处,因为它们试图解决在使用 Kubernetes 时可能出现的特定问题。我鼓励您在有机会时查看它们

RBAC 生成器

Operator 最大的担忧之一是 RBAC。您必须手动查看清单并为每个资源添加 RBAC 规则,因为它需要 RBAC 权限才能在集群中运行时创建、获取、更新和删除清单中的资源。构建 RBAC 生成器使编写 RBAC 角色和角色绑定过程自动化。RBAC 生成器的功能很简单。它接受清单的文件名作为标志。然后,它解析清单并获取资源的 API 组和资源名称,并将其添加到角色中。如果解析了 --out 标志,它将角色和角色绑定输出到标准输出或文件中。

此外,该工具允许您通过分离清单中的集群角色来拆分 RBAC。这减少了 Operator 权限过高的安全问题,因为它需要拥有集群角色所拥有的所有权限。如果您想自己应用集群角色而不授予 Operator 这些权限,您可以传入一个 --supervisory 布尔标志,以便生成器不会将这些权限添加到角色中。该 CLI 程序位于此处

Kubectl Ownerref

一眼很难看出哪些对象是由附加组件自定义资源创建的。这个 kubectl 插件通过显示集群中资源拥有者引用的所有对象来缓解这种痛苦。您只需将资源的类型和名称作为参数传递给程序,它就会检查集群中的对象,并提供该对象的类型、名称和命名空间。通过传入自定义资源的名称和类型,它可以帮助您全面了解控制器正在协调的所有对象。该 CLI 程序位于此处

附加组件 Operator

要完全理解附加组件 Operator 并更改其创建方式,您必须尝试创建和使用它们。夏季的一部分时间用于为一些流行的附加组件构建 Operator,例如 Kubernetes Dashboard、Flannel、NodeLocalDNS 等。请查看cluster-addons仓库以获取不同的附加组件 Operator。在本节中,我将重点介绍一个与其他附加组件 Operator 略有不同的。

通用控制器

通用控制器可以在不需要太多配置的附加组件之间共享。这最大限度地减少了集群上的资源消耗,因为它减少了需要运行的控制器数量。此外,您无需构建自己的 Operator,只需使用通用控制器;当您觉得您的需求增长,需要一个更复杂的 Operator 时,您可以使用 kubebuilder 搭建代码,并从通用 Operator 停止的地方继续。要使用通用控制器,您可以使用此工具(generic-addon)生成 CustomResourceDefinition (CRD)。您传入种类、组和通道目录的位置(也可以是 Git 仓库!)。该工具会为您生成 CRD、RBAC 清单和两个自定义资源。

过程如下:

此工具创建

  1. 您的附加组件的 CRD
  2. CustomResourceDefinitions 的 RBAC 规则
  3. 应用清单的 RBAC 规则
  4. 您的附加组件的自定义资源
  5. 一个通用自定义资源

通用自定义资源看起来像这样

apiVersion: addons.x-k8s.io/v1alpha1
kind: Generic
metadata:
 	name: generic-sample
spec:
  objectKind:
  kind: NodeLocalDNS
  version: "v1alpha1"
  group: addons.x-k8s.io
channel: "../nodelocaldns/channels"

应用这些清单,但请确保在 CR 之前应用 CRD。然后,在您的机器上或集群中运行通用控制器。

如果您对构建 Operator 感兴趣,请查看本指南

相关链接

后续工作

在 GSoC 期间,集群附加组件上确实做了很多工作。但我们需要更多的人构建 Operator 并在集群中使用它们。我们需要在社区中更广泛的采用。为您最喜欢的附加组件构建 Operator,并告诉我们进展如何以及您是否遇到任何问题。请查看此README.md开始。

致谢

我真心感谢我的导师 Justin Santa Barbara (Google) 和 Leigh Capili (Weaveworks)。我的实习非常棒,因为他们非常棒。他们为导师制度树立了黄金标准。他们平易近人,随时可以解答任何困惑。我想我最喜欢的是他们不只是分派任务,而是我们就哪里出了问题以及如何改进进行了开放式讨论。他们真的是最棒的,我希望将来能再次与他们合作!此外,我还要特别感谢 Lubomir I. Ivanov 审阅这篇博文!

结论

到目前为止,我学到了很多关于 Go、Kubernetes 内部结构和 Operator 的知识。最后,我想鼓励大家(尤其是 Kubernetes :))无论经验水平如何,都为开源做出贡献。对我来说,这是一次全面的经历,我爱上了这个社区。这是一个很棒的倡议,也是学习和结识优秀人才的好方法。特别感谢 Google 组织了这个项目。

如果您对集群附加组件以及了解更多关于附加组件 Operator 感兴趣,欢迎加入我们在 Kubernetes 上的 Slack 频道 #cluster-addons


Somtochi Onyekwere 是一位热爱为开源做贡献和探索云原生解决方案的软件工程师。