翻译|CNCF Operator白皮书

2022-11-22 10:17:53 浏览数 (1)

执行摘要

人们在大多时候需要维护一些基础应用程序很多没有价值的重复性的工作。计算机是执行精确任务的首选方法,可以验证对象的状态,从而使基础设施需求能够被编码。Operator提供了一种方法来封装应用程序所需的活动、检查和状态管理。

在Kubernetes中,Operator通过扩展API的功能来提供智能、动态的管理功能。

这些 operator组件允许公共进程的自动化以及响应式应用程序不断适应其环境。反之,这也允许更快速的开发,更少的错误,更低的平均恢复时间,并增加了工程自主权。

鉴于operator模式越来越受欢迎,有一份参考文件,帮助新手和专家学习社区认可的最佳实践,以实现他们的目标,已经成为当务之急。在本文档中,我们不仅概述了 Operator的分类,还介绍了operator应用程序管理系统的推荐配置、实现和用例。

前言


该白皮书在比Kubernetes更广泛的内容下定义了operator。它描述了它们的特征和组件,概述了目前正在使用的常见模式,并解释了它们与Kubernetes控制器的区别。

它还深入研究了它们的功能,如备份、恢复和自动配置调优,深入了解了当前正在使用的框架、生命周期管理、安全风险和用例。

本文包括最佳实践,包括可观察性和安全性、技术实现和CNCF维护的代码样本。

文档目标

本文的目标是在Kubernetes和其他容器协调器的上下文中为云本机应用程序提供operator的定义。

受众目标和最低经验水平

本文档面向应用程序开发人员、Kubernetes集群运维和服务操作者(内部或外部)——他们希望了解Operator及其可以解决的问题。它还可以帮助已经关注Operator的团队了解何时何地使用它们以达到最佳效果。它假设已经了解了基本的Kubernetes知识,如熟悉Pods和deployment

基础部分


Kubernetes和其它编排器的成功,是由于他们一直专注于容器的主要功能。当公司开始他们的云原生之旅时,使用更具体的用例(微服务、无状态应用程序)更有意义。随着Kubernetes和其他容器编排器的名气和可扩展性的增长,需求变得更加旺盛。使用协调器的完整生命周期功能的愿望也转移到了高度分布式的数据存储上。

默认情况下,原本Kubernetes不是用来管理状态的。单独依赖原始的Kubernetes会给管理有状态的应用程序需求带来困难,比如复制、故障转移自动化、备份/恢复和升级(这些需求可能会基于特定的事件发生)。

Operator Pattern 可以用来解决管理状态的问题。通过利用 Kubernetes 的内置功能,例如自我修复、协调和扩展这些功能以及特定于应用程序的复杂性;可以自动化任何应用程序生命周期、操作并将其转变为功能强大的产品。

Operator被认为是 Kubernetes 的代名词。但是,可以将管理完全自动化的应用程序的想法导出到其他平台。本文的目的是将这一概念提升到比 Kubernetes 本身更高的层次。

Operator 设计模式

本节描述具有高级概念的模式。下一节 Kubernetes Operator Definition 将根据 Kubernetes 对象和概念来描述该模式的实现。

Operator 设计模式定义了如何使用特定领域的知识和声明性状态来管理应用程序和基础设施资源。该模式的目标是通过在代码中捕获特定领域的知识来减少使应用程序保持健康和维护良好状态所需的手动操作(如何备份、扩展、升级......)并通过声明式 API 暴露出来

通过使用Operator 模式,关于如何调整和维护资源的知识被捕获在代码中,并且通常在单个服务(也称为控制器)中。

当使用Operator 设计模式时,用户应该只需要描述应用程序和资源的期望状态。Operator的实现应该对某个事物进行必要的更改,使其处于所需的状态。Operator还将持续监控真实状态并采取措施使其保持健康并处于相同状态(防止漂移)。

Operator的概览图将具有可以读取所需spec的软件,并可以创建和管理所描述的资源

Operator 模式由三个组件组成:

  • 我们要管理的应用程序或基础架构。
  • 一种领域特定语言,使用户能够以声明的方式指定应用程序的所需状态。
  • 连续运行的控制器:
    • 读取并了解状态。
    • 以自动方式对应用程序运行操作。
    • 以声明的方式报告应用程序的状态。

在接下来的部分中,这种设计模式将应用于 Kubernetes 及其Operator

Operator 的特征

任何Operator的核心目的都是用新的领域知识扩展其编排器的底层 API。例如,·Kubernetes 中的编排平台通过 Pod 和 Service 对象原生地理解容器和4 层负载均衡器之类的东西。Operator为更复杂的系统和应用程序添加了新功能。例如,prometheus-operator 引入了新的对象类型Prometheus,扩展了 Kubernetes,为部署和运行 Prometheus 服务器提供了高级支持。

Operator提供的能力可以分为三大类:动态配置、Operator自动化和领域知识。

动态配置

从一开始,就有两种主要的软件配置方式:配置文件和环境变量。在云原生世界中,有一些较新的选项,例如在启动时查询知名 API,但世界上大多数现有软件都使用其中一个或两个选项。Kubernetes 自然地提供了许多与这些交互的工具(例如 ConfigMaps 和 Secrets),但由于它们是通用的,它们不了解配置给定应用程序的任何细节。Operator可以定义新的自定义对象类型(自定义资源)以更好地表达 Kubernetes 上下文中特定应用程序的配置。

允许更好的验证和数据结构减少了配置错误的可能性,并提高了团队自助服务的能力。使他们能够避免像传统要求那样对底层编排器或目标应用程序有深入或完整的知识。这可以包括诸如渐进式默认值之类的东西,其中一些高级设置用于填充最佳实践驱动的配置文件或自适应配置,例如调整资源使用以匹配可用硬件或基于集群大小的预期负载。

Operator自动化

除了自定义资源之外,大多数Operator还包括至少一个自定义控制器。这些控制器是在编排器内部运行的守护进程,与其他任何控制器一样,连接到底层 API 并提供常见或重复任务的自动化。这与实现编排器(如 Kubernetes)的方式相同,到目前为止,你可能已经看到过 kube-controller-manager 或 cloud-controller-manager 。但正如配置所说明的那样,Operator可以通过更高级别的自动化来扩展和增强编排器,例如部署集群软件、提供自动备份和恢复,或基于负载进行动态扩展。

通过将这些常见的操作任务放入代码中,可以确保它们以标准化的方式可重复、可测试和可升级。让人们从繁杂的任务中脱离出来,并且可以确保整个过程不会被遗漏,每个任务的不同部分也会解耦。这样,团队就可以更多的关注在更重要的任务中。

领域知识

Operator自动化类似,可以将其写入Operator中,通过编码方式来实现有关特定软件或流程的专业领域知识。一个常见的例子是应用程序升级。虽然一个简单的无状态应用程序可能只需要部署的滚动升级;数据库和其他有状态的应用程序通常需要按顺序执行非常具体的步骤才能安全地执行升级。这可以由Operator自主处理,因为它知道您当前和请求的版本,并且可以在需要时运行专门的升级代码。一般地说,这可以适用于任何云原生环境之前会使用手动操作的东西(有效地将Operator用作可执行的运行手册)。利用自动化领域知识的另一种常见方法是错误修复。例如,Kubernetes 内置的修复行为大多以“重启容器直到它工作”开始和结束,这是一个强大的解决方案,但通常不是最好或最快的解决方案。Operator可以监控其应用程序并以特定行为对错误做出反应,以解决错误或在无法自动解决时将问题升级。这可以减少 MTTR(平均恢复时间),还可以减少Operator因反复出现的问题而感到的疲劳。

Kubernetes 中的Operator组件

Operator是 Kubernetes 控制器,它了解 2 个领域:Kubernetes 和其他领域。通过结合这两个领域的知识,它可以自动执行通常需要理解这两个领域的人工Operator的任务” (Jimmy Zelinskie,kubeflow/training-operator#300(评论))

Operator 通过操作知识启用 Kubernetes API 的扩展。这是通过组合 Kubernetes 控制器和描述所需状态的监视对象来实现的。控制器可以监视一个或多个对象,这些对象可以是 Kubernetes 原语,例如部署、服务,也可以是驻留在集群外部的事物,例如虚拟机或数据库。

控制器将使用协调循环不断地将所需状态与当前状态进行比较,以确保被监视的对象以定义的方式转换到所需状态。

所需状态封装在一个或多个 Kubernetes 自定义资源中,控制器包含将对象(例如部署、服务)获取到其目标状态所需的操作知识

Kubernetes 控制器

Kubernetes 控制器负责日常任务,以确保特定资源类型表达的所需状态与真实世界状态相匹配(当前状态,https://engineering.bitnami.com/articles/a-deep-dive-into-kubernetes -controllers.html,https: //fntlnz.wtf/post/what-i-learnt-about-kubernetes-controller/ )。例如,当一个 pod 被删除或失败时,Deployment 控制器会负责确保所需数量的 pod 副本正在运行并启动一个新的 pod。

从技术上讲,典型的控制器和Operator之间没有区别。通常提到的差异是Operator中包含的操作知识。因此,在创建自定义资源时启动 pod 并随后销毁 pod 的控制器可以描述为简单控制器。如果控制器具有额外的操作知识,例如如何升级或修复错误,那么它就是Operator

自定义资源和定义自定义资源(Custom resources and custom resource definitions: CR和CRD)

CR用于在 Kubernetes 中存储和检索结构化数据,作为默认 Kubernetes API ( https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ ) 的扩展。在Operator的情况下, CR包含资源(例如应用程序)的所需状态,但不包含实现逻辑。此类信息可以是应用程序组件的版本信息,也可以是应用程序的启用功能或应用程序备份可能是其中一部分的信息。看起来像自定义资源定义 (CRD) 定义了此类对象,例如,存在哪些字段以及 CRD 的命名方式。这样的 CRD 可以使用工具(如OperatorSDK)搭建,也可以手动编写。

以下示例说明了此类自定义资源实例定义的外观:

代码语言:javascript复制
apiVersion: example-app.appdelivery.cncf.io/v1alpha1
kind: ExampleApp
metadata:
name: appdelivery-example-app
spec:
appVersion: 0.0.1
features:
exampleFeature1: true
exampleFeature2: false
backup:
enabled: true
storageType: “s3”
host: “my-backup.example.com”
bucketName: “example-backup”
status:
currentVersion: 0.0.1
url: <https://myloadbalancer/exampleapp/>
authSecretName: appdelivery-example-app-auth
backup:
lastBackupTime: 12:00

此示例表示一个名为“appdelivery-example-app”的自定义资源,类型为“ExampleApp”。

“Spec”部分是用户可以声明所需状态的地方。此示例声明表示在部署 appVersion 0.0.1 时启用一项功能并禁用另一项功能。此外,应备份此应用程序,并应使用 s3 存储桶。

“status”部分是Operator可以将有用信息传达给用户的地方。在此示例中,状态显示当前部署的版本。如果它与规范中的“appVersion”不同,那么用户可以根据请求预期去部署Operator请求的版本。status部分中的其他常见信息包括如何连接到应用程序以及应用程序的运行状况

控制回路

Kubernetes 控制器中的控制(协调)循环确保用户使用 CRD 声明的状态与应用程序的状态相匹配,而且状态之间的转换按预期工作。一个常见的用例可能是在升级应用程序时迁移数据库方案。控制循环可以在特定事件上触发,如 crd 上的更改,但也可以基于时间,例如在定义的时间备份数据。

Operator的能力

Operator能够通过解决许多不同的任务来协助操作应用程序或其他托管组件。在谈到Operator时,第一个也是最广为人知的能力是安装和升级有状态应用程序的能力。但是,Operator可以管理应用程序的整个生命周期,而不必完全处理安装/升级。

以下部分概述Operator拥有的功能,以及Operator实现这些功能,用户可以做什么。

安装应用程序/获得应用程序的所有权

Operator应该能够提供和设置所有必需的资源,因此在安装过程中不需要手动工作。Operator必须检查并验证提供的资源是否按预期工作,并准备好使用。

Operator还应该能够识别在安装过程之前提供的资源,并且只获得它们的所有权以供以后使用。在这种情况下,所有过程应该是无缝的,不会导致停机。所有过程的目的是使资源能够轻松迁移到Operator

Operator 应在此过程中报告资源的版本及其健康状态。

升级应用程序

Operator应该能够升级应用程序/资源的版本。Operator应该知道如何更新所需的依赖关系并执行自定义命令,例如运行数据库迁移。

如果在此过程中出现问题,Operator应监控更新和回滚。

Operator应在此过程中报告资源的版本及其健康状态。如果有错误,报告的版本应该是当前使用的版本。

备份

此功能适用于管理数据并确保Operator能够创建一个的Operator backups。此备份应以Operator的用户可以确定在数据丢失或受损时可以恢复的方式进行。此外,所提供的状态信息应该能够深入了解备份上次运行的时间以及备份的位置。

上图显示了这样一个过程的样子。首先,备份由人或其他触发器(例如时间触发器)触发。Operator指示其监视的资源(应用程序)设置一致的状态(如一致的快照)。之后,应用程序的数据使用适当的工具备份到外部存储。这可以是一步过程(直接备份到外部存储)或多个步骤,例如先写入持久卷,然后再写入外部存储。外部存储可能是本地 NFS/CIFS 共享(或任何其他网络文件系统),也可能是云提供商基础架构上的对象存储/存储桶。无论备份失败还是成功,

从备份中恢复

Operator的恢复能力可以帮助用户从成功的备份中恢复应用程序状态。因此,应恢复应用程序状态(应用程序版本和数据)。

可能有很多方法可以实现这一点。一种可能的方式是当前应用程序状态也得到备份(包括配置),因此用户只需为应用程序创建自定义资源并指向备份。Operator将读取配置、恢复应用程序版本并恢复数据。另一种可能的解决方案可能是用户只备份数据并且可能必须指定使用的应用程序版本。尽管如此,在这两种方式中,Operator都可以确保应用程序在之后使用指定备份中的数据启动并运行。

自动修复

Operator的自动修复能力应确保它能够从更复杂的故障状态中恢复应用程序,这种状态可能无法通过运行状况检查(实时和就绪探测)机制进行处理或检测。因此,Operator需要对应用有深入的了解。这可以通过可能指示应用程序故障或错误的指标来实现,也可以通过处理 Kubernetes 机制(如健康检查)来实现。

一些例子可能是:

  • 如果在版本更改后启动定义的 pod 数量不成功,则回滚到最后一个已知配置。在某些情况下,重新启动应用程序可能是一种短期解决方案,也可以由Operator完成。
  • 还可以想象,Operator通知依赖服务的另一个Operator后端系统目前不可访问(以采取补救措施)。

在任何情况下,此功能都使Operator能够采取措施保持系统正常运行。

监控/指标 - 可观测性

虽然托管应用程序应为自己提供遥测(telemetry)数据,但Operator可以提供有关其自身行为的指标,并且仅提供有关应用程序状态的高级概述(因为它可以进行自动修复)。此外,Operator提供的典型遥测数据可以是补救措施的计数、备份的持续时间,还可以是有关上次错误或已处理的操作任务的信息。

伸缩(Operator支持伸缩)

伸缩是Operator可以管理的日常操作的一部分,以便保持应用程序/资源的功能。伸缩能力不需要自动伸缩,只要Operator知道如何在水平和垂直伸缩方面改变资源。

Operator应该能够增加或减少其拥有的任何资源,例如 CPU、内存、磁盘大小和实例数量。

理想情况下,扩展操作不会停机。当所有资源处于一致状态并准备好使用时,伸缩操作结束,因此Operator应验证所有资源的状态并报告。

自动伸缩

Operator应该能够根据其不断收集的指标并根据阈值执行扩展能力。Operator应该能够自动增加和减少它拥有的每个资源。

Operator应该遵从最小和最大的基本缩放配置。

自动配置调整

这种能力应该使Operator能够管理托管应用程序的配置。例如,Operator可以根据运行环境(例如kubernetes)或DNS名称的变化来采用应用程序的内存设置。此外,Operator应该能够以无缝方式处理配置更改,例如,如果配置更改需要重新启动,则应该触发。

这些功能对用户应该是透明的,如果用户愿意,他们应该有可能覆盖这种自动配置机制。此外,自动重新配置应该以用户可以理解基础设施上正在发生的事情的方式进行详细记录。

卸载/断开连接

删除声明性请求状态(大多数情况下是自定义资源)时,Operator应允许两种行为:

  • 卸载:Operator应该能够完全移除或删除每个托管资源。
  • 断开连接:Operator应停止管理供应的资源。

这两个过程都应该应用于Operator直接提供的每个资源。Operator应该以声明的方式报告过程中的任何失败(例如使用状态字段)。

安全

Operator 旨在使用自定义资源定义通过 Kubernetes API 服务器管理其状态和配置。他们管理的从属 API 资源(通常是运行有状态应用程序的 pod)也有他们的生命周期并通过 Kubernetes API 管理支持 RBAC、服务等。在某些情况下,Operator还将通过网络与应用程序的 API 进行交互。所有这些路线都有可能危及Operator及其资源,因此应根据下文列出的最佳实践加以保护。

Operator开发者

Operator开发人员应了解Operator引入的安全风险并记录其安全使用。在开发Operator时,重要的是要关注关键领域,例如透明度和文档、Operator范围和漏洞分析。

透明度和文档

在开发Operator的过程中,开发人员应该清楚地了解它在 Kubernetes 中的工作方式和接口。随着开发者从开发转向发布Operator,用户也应该清楚地了解Operator做什么以及如何做。你写了一些你引以为豪的东西,但从最终用户的角度来考虑:他们是否应该信任来自互联网的源代码,Operator是否可以在他们的集群上以管理访问权限运行,这可能是大型且昂贵的,或者也许处理敏感信息?开发人员可以做的任何事情来帮助用户加快使用他们的软件,它是如何工作的,它是如何保护的,它可能对他们的集群产生什么影响,这将使他们更容易采用该软件。

以下是一些可以帮助用户就是否应该使用Operator做出明智决定的项目:

  • Operator如何进行通信的描述性图表(威胁模型)以及帮助用户了解他们必须如何保护它并为Operator应用策略的良好开端。
  • 打算如何使用软件以保持在合规范围内的用例,或者您冒着超出该范围的漏洞风险。
  • 记录的 RBAC 范围、威胁模型、通信端口、可用的 API 调用、pod 安全策略要求(或其他策略引擎要求)或为 Kubernetes 开发的任何其他策略引擎要求,例如 OPA。
  • 安全报告、披露和事件响应流程:如果有人发现潜在的安全问题,他们应该联系谁以及他们应该期待什么类型的响应?
  • 通过暴露的端点、日志级别或日志聚合记录和监控附件。
  • Operator问题、功能、版本跟踪。
  • 如果项目过去曾有过安全披露,那么在网页上列出这些披露(及其 CVE ID)是建立用户信任的重要一步。每个人都会在某个时候遇到安全问题——它们的处理方式显示了项目的成熟度。

有关开发过程安全性的更多想法,读者不妨查看 CNCF 安全 SIG 的自我评估问卷。

Operator范围

Operator有很多用例,您可以设计它的范围几乎没有限制。为了清楚Operator的安全性,每个范围都应该有清晰的沟通。可以使用的一般范围是集群范围的Operator、命名空间Operator和外部Operator。为了最好地保护它们,需要了解通信、创建的任何 API、控制器及其职责以及任何应用程序Metric Endpoint。如果向Operator提供此信息,则它可用于在实施范围内进一步保护Operator应用程序。如果未提供信息,您可能会受到无数攻击。

集群范围的 Operator用于跨集群执行自定义资源,无论这些资源是否位于另一个命名空间中。命名空间运算符的存在是为了在命名空间中执行自定义资源。通常会应用策略引擎策略来监禁命名空间内的范围,并且只与命名空间内的 pod 通信。这在本质上被认为更安全,但同样的规则也适用。外部 Operator用于执行集群外部的自定义资源。同样的规则也适用,除了保护这个范围之外,我们必须知道从集群到外部组件的通信的性质。

虽然本文还从用户的角度讨论了范围界定,但Operator的设计方式将严重影响可在生产中对其应用的安全控制类型。通常从宽松的权限开始,并打算在发布之前应用安全概念;在开发人员开始工作时花一些时间考虑Operator的安全设计,这将使开发人员及其用户的这一过程变得更加容易。

漏洞分析

专注于Operator的开发和安全,作为Operator开发人员必须采取一些步骤,以确保已完成验证和适当的安全分析。遵循 CNCF 云原生安全白皮书中的指南,有一个明确的生命周期流程,定义了Operator开发人员的关注层。三个层次都应遵守,严格关注Operator开发者范围内的开发和分发层。开发和分发层中有许多详细的指导方针,这将有助于对供应链进行完善的漏洞分析,以确保正在开发的Operator得到签名和信任,以实现最佳完整性。CNCF云原生安全白皮书 可在此链接上找到。

除了supply chain之外,还需要专注于执行Operator的安全模型,以保持对开发人员的控制,并确保没有任何意外遗漏可能会为攻击敞开大门。用于检查威胁的基础模型可以在 CNCF Cloud Native Security Whitepaper on Threat Modeling中观察到。

应用程序开发人员(Operator“用户”)

Operator代表用户执行管理任务,例如volume 创建/附加、应用程序部署和证书管理。由于用户将控制权委托给Operator,因此必须提供机器授权以执行所需的操作,但还必须注意不要授予Operator执行其角色所需的权限。

Operator的部署授予第三方软件对 Kubernetes 命名空间或集群的某种级别的访问权限。虽然使用Operator不需要安全专业知识,但以下 Kubernetes 概念强调了使用Operator的安全准备:

命名空间是对一组资源进行分组和隔离的主要方式之一。关于Operator,用户应该考虑Operator需要使用哪些命名空间。虽然可能存在单个Operator需要访问整个集群的一些用例,但 2021 年的常见用例似乎是Operator使用 Kubernetes 中的特定应用程序,因此为该应用程序提供命名空间通常是有意义的应用程序和相关Resource/Operator。为了进一步减少Operator与从属资源名称空间中任何松散或被盗的 RBAC 的分离,Operator的专用名称空间提供了更多的分离。

基于角色的访问控制在 Kubernetes 的现代版本中可用。在授予Operator对资源的访问权限时,重点应该是授予Operator执行其任务所需的最有限的权限集。这意味着仅在绝对必要时授予 ClusterRoles,但授予特定资源/命名空间的特定权限。使用 RBAC 授权 用户指南的章节详细介绍了该主题。Operator 构建工具包(例如 Operator SDK)使用开发人员可能尚未针对其特定 Operator 优化的通用 RBAC 默认值。集群外的服务帐户身份提供的权限包括在其他 Kubernetes 集群中具有权限的联合和跨集群Operator。随着Operator越来越多地用于管理集群外和云资源,应配置云 IAM 集成权限,以防止云帐户被受污染的Operator接管

需要注意的一件事:特权的“土地掠夺”——例如请求重要/管理访问——并不总是有恶意的。开发人员可能不太了解,或者有时间将所需的权限调整为最低权限的概念。然而,即使在最无辜的情况下,它仍然是一个危险信号:也许Operator的使用还不够广泛,以至于其他人遇到了过度使用特权的情况,也许这是Operator内部存在其他安全漏洞的迹象。如果发现这样的“土地掠夺”,建议谨慎行事。

软件出处:在撰写本白皮书时,“软件供应链”开始受到更多关注。考虑Operator的来源、安装方式以及恶意用户可能希望访问 Kubernetes 集群的方式或原因。在运行之前花几分钟查看安装脚本。虽然 kubectl 命令支持直接从公共 Internet(例如kubectl create -f <https://publicwebsite.com/install/operator.yaml)应用> yaml 脚本的能力,但强烈建议您首先在本地下载该文件,查看它,然后运行kubectl create -f operator.yaml.

要查看脚本,请询问以下问题:

  • 这个脚本的目的是什么?
  • 脚本正在创建哪些资源?此脚本是否创建角色和角色绑定?
  • 脚本将尝试使用哪些第三方来源?(例如容器图像、其他 yaml 文件) git 和 docker 图像存储库有多受欢迎和维护良好?这些可能是新项目的迹象,不再接收安全更新的废弃软件,或带有恶意意图的非官方存储库的迹象。
  • 脚本试图获得什么特权?脚本是否尝试使用主机共享或“特权模式”运行容器 securityContexts?

集群策略可能会强制要求高级安全控制,例如 SELinux、AppArmor 或 seccomp。开源Operator不太可能拥有这些 Linux 安全模块的配置,但如果组织熟悉这些控制系统之一,为Operator编写适当的安全配置应该不需要大量开销。

Operator配置:理想情况下,项目将“默认安全”以增加安全Operator或应用程序部署的可能性。不安全的默认设置需要手动配置以保护环境。虽然学习新Operator的配置参数似乎是不必要的工作,但通常最好手动调整Operator本身的配置和/或源代码以达到所需的安全级别。

适用于 Kubernetes 的Operator-Freamworks


目前,存在许多框架来简化引导Operator/Controller项目和Operator编写的过程。本章描述了其中的一些,但没有任何全面性的要求。

CNCF Operator框架

Operator Framework是一个开源工具包,用于以有效、自动化和可扩展的方式管理称为 Operator 的 Kubernetes 原生应用程序。

它面向具有 SDK 的 Operator 开发人员,以使用脚手架工具(基于kubebuilder)简化 Operator 开发,用于单元测试和集成的测试工具以及功能测试和打包/分发机制,以与用户一起发布 Operator 的版本历史-可配置的更新图。支持的项目类型有 Golang、Helm 和 Ansible。Python 和 Java 目前正在开发中。

它还满足 Kubernetes 管理员的需求,他们需要一个中心点来在可能安装了数十个 Operator 的多租户环境中安装、配置和更新 Operator。它涵盖了 Operator 生命周期的以下方面:

  • 持续的无线更新和Operator目录 发布机制和更新来源
  • 依赖模型,因此 Operator 可以依赖集群功能或相互依赖
  • 对于通常无法列出 CRD 或查看安装在单独命名空间中的 Operator 的低权限租户的可发现性
  • 集群稳定性避免了多租户集群上 Operator 的运行时冲突,同时尊重 CRD 的全局特性,以及 CRD 版本控制和 CRD 转换的微妙之处
  • 声明式 UI 控件,允许控制台为与 Operator 服务交互的最终用户生成丰富的 UI 体验

Kopf

Kopf - K ubernetes O peratorPythonic F ramework - 是一个框架,可以更快、更轻松地创建 Kubernetes Operator,只需几行 Python。它消除了大部分低级 Kubernetes API 通信的麻烦,并将 Kubernetes 资源更改编组到 Python 函数并返回。就这么简单:

代码语言:javascript复制
import kopf

@kopf.on.create(kind='KopfExample')
def created(patch, spec, **_):
    patch.status['name'] = spec.get('name', 'world')

@kopf.on.event(kind='KopfExample', field='status.name', value=kopf.PRESENT)
def touched(memo, status, **_):
    memo.last_name = status['name']

@kopf.timer('KopfExample', interval=5, when=lambda memo, **_: 'last_name' in memo)
def greet_regularly(memo, **_):
    print(f"Hello, {memo['last_name']}!")

如果您想要或需要在 Python 3.7 中创建临时(此时此地一次性不可通用)运算符,则应考虑使用此框架,特别是如果您想将应用程序域作为自定义资源直接引入 Kubernetes . 有关更多功能,请参阅文档。

kubebuilder

kubebuilder 框架为开发人员提供了通过使用自定义资源定义来扩展 Kubernetes API 并创建处理这些自定义资源的控制器的可能性。

kubebuilder 框架提供的主要入口点是Manager。与将原生 Kubernetes 控制器分组到单个 Kubernetes 控制器管理器 ( kube-controller-manager) 中的方式相同,您将能够创建多个控制器并让它们由单个管理器管理。

由于 Kubernetes API 资源附加到域并按组、版本和种类排列,因此您将定义的 Kubernetes 自定义资源将附加到您自己的域,并按您自己的组、版本和种类排列。

使用 kubebuilder 的第一步是创建一个附加到您的域的项目,这将创建用于构建单个 Manager 的源代码。

使用特定域启动项目后,您可以将 API 添加到您的域,并使这些 API 由管理器管理。

将资源添加到项目将为您生成一些示例代码:您将适应以构建自己的自定义资源的示例自定义资源定义,以及将为处理此资源的Operator实现协调循环的示例Reconciler 。

kubebuilder 框架利用了controller-runtime提供 Manager 和 Reconciler 概念的库。

kubebuilder 框架提供了构建管理器二进制文件的所有必要条件、启动管理器的容器映像以及部署此管理器所需的 Kubernetes 资源,包括CustomResourceDefinition定义您的自定义资源的资源、Deployment部署管理器的资源和 RBAC 规则您的Operator能够访问 Kubernetes API。

Metacontroller - 轻量级 Kubernetes 控制器即服务

Metacontroller是一种操作符,可以轻松编写和部署自定义操作符。

它介绍了两个 CRD 本身(2021):

  • 复合控制器
    • 允许写入由 CRD 触发的运算符
  • Decorator Controller
    • 允许写入由任何 kubernetes 对象触发的操作符(也由其他操作符管理)

由其 CRD 之一配置的元控制器本身将负责观察集群状态并调用由用户(用户控制器)提供的控制器以采取行动。

用户控制器应该在给定资源作为输入的情况下,计算依赖对象的期望状态。

这也可以称为lambda controller模式(更多内容请参见此处),因为仅考虑输入来计算输出,并且元控制器使用的逻辑也可以驻留在功能即服务提供程序中。

元控制器的主要优点:

  • 只需要提供一个函数(通过 webhook 调用),没有任何与查看 kubernetes 资源相关的样板文件
  • 这样的函数可以用任何语言编写,并通过 http 公开

主要限制:

  • 上面提到的,只有某些模式可以实现
  • 当前架构依赖于集群中的单个元控制器
  • Metacontroller 不知道任何外部状态,它完全依赖于集群状态

示例元控制器配置(如下所示)用于在StatefulSet未明确定义Service清单的情况下添加额外的网络暴露。

代码语言:javascript复制
apiVersion: metacontroller.k8s.io/v1alpha1
kind: DecoratorController
metadata:
name: service-per-pod
spec:
resources:
- apiVersion: apps/v1
resource: statefulsets
annotationSelector:
matchExpressions:
- {key: service, operator: Exists}
- {key: port, operator: Exists}
attachments:
- apiVersion: v1
resource: services
hooks:
sync:
webhook:
url: <http://service-per-pod.metacontroller/sync-service-per-pod>
timeout: 10s
使用上述配置:
metacontroller,对于每个匹配spec.resources描述的对象(在这种情况下 -apps/v1/statefulsets带有service和port注释),将监视匹配对象的任何更改(创建/更新/删除)并hooks.sync在每个对象上调用
• 可以根据响应返回(在这种情况下 - )hooks.sync中描述的对象,这些对象将由,根据响应创建/更新/删除 例如,如果将部署以下内容:spec.attachementv1/servicesmetacontrollerhookStatefulset

apiVersion: apps/v1
kind: StatefulSet
metadata:
annotations:
service: "statefulset.kubernetes.io/pod-name"
ports: "80:8080"
...
给定Service对象将由元控制器创建:
apiVersion: "v1"
kind: "Service"
spec:
selector: "statefulset.kubernetes.io/pod-name"
ports:
- port: 80
targetPort: 8080

用户定义的端点(在本例中 - http://service-per-pod.metacontroller/sync-service-per-pod)只需要关心 的计算Service以及对于给定的StatefulSet.

可以在元控制器示例页面上找到可以使用元控制器实现的其他示例和想法!

如有任何问题,请访问我们的 slack 频道 ( #metacontroller ) 或在github 讨论中提问。

Operator生命周期管理


Operator是一个应用程序,本节将描述有关Operator本身生命周期的注意事项。

升级Operator

在升级Operator时,应特别注意托管资源。在Operator升级期间,托管资源应保持相同状态且健康。

升级声明式状态

声明状态是算子的API,可能需要升级。CRD 版本的使用表明 CRD 和Operator的稳定性 -阅读有关 CRD 版本控制的更多信息

Operator的使用用例


示例:Operator用于安装应用程序,或提供另一个对象,这是通过定义一组由Operator管理的对象以及它们如何相互工作来实现的。安装后,目标应用程序应该在没有人工交互的情况下运行。此外,控制器用于系统的重新配置。

为此,Operator会观察当前状态以及在自定义资源或外部事件中所做的定义。比较它们并开始协调应用程序以在需要时达到所需的状态。自定义资源中的更改可能是启用功能或更改版本,外部事件可能是 API 报告的应用程序更新的可用性。当Operator管理的对象被删除时,应用程序的当前状态也可能不同,因此它们也被重新创建以达到所需状态。

更新应用程序时,Operator包含获取新应用程序版本所需的逻辑以及如何转换。如上一章所述,这些可能是在更新和更新数据库模式之前备份数据的机制。因此,Operator中包含的逻辑知道构建一致备份需要哪些先决条件,如何备份数据以及如何恢复正常状态。

最后,Operator能够删除应用程序和生成的对象。

Prometheus Operator

Prometheus Operator 是与 etcd 一起编写的首批 Operator 之一

“Prometheus Operator 的作用是让在 Kubernetes 上运行 Prometheus 尽可能简单,同时保留 Kubernetes 原生配置选项。”

安装Prometheus Operator后,除了Operator控制器 pod/deployment 之外,还有多种 API 可用于配置 Prometheus 堆栈。API 表示为自定义资源定义 (CRD),它允许我们配置负责的对象以及其他任务,用于:

  • 描述一组要被 Prometheus (ServiceMonitor) 监控的目标。
  • 以声明方式描述 Prometheus 部署的所需状态。
  • 描述一个AlertManager 集群来处理客户端应用程序发送的警报。

好处是使用 Kubernetes 原生配置作为配置整个操作堆栈的一种方式,受益于 Kubernetes 资源验证和自我修复功能。

Operator 控制器随后将与 K8s API 服务器通信以添加服务指标端点,并为已配置的服务自动生成所需的 Prometheus 抓取配置。

GitOps Operator

通常,Operator与安装、升级和操作应用程序相关联。在 GitOps 世界中可以找到一个Operator也可以在不管理应用程序的情况下“operate”事物的示例。

可能存在这样一种情况,即主要是命令式管理的应用程序应该以更具声明性和 Git 驱动的方式进行编排。因此,Operator可以协助从 git-repository 获取配置,分析配置以找出是否需要更改某些内容以及应该采取哪些措施并采取相应的措施。

上面的例子说明了这种情况:

  1. 在 git 存储库中检查了一个配置。
  2. Operator通过使用自定义资源定义(存储库路径和有关机密信息的位置)来确认 git 存储库。
  3. Operator获取配置并对其进行分析。
  4. 它应用其操作知识从当前状态变为所需状态(通过向应用程序查询其当前状态并发送指令以达到所需状态)。

这使用户能够拥有可重现的配置,并在 git 存储库中进行版本控制。

最佳实践


随着时间的推移,各种来源发布了许多编写Operator的最佳实践。下面,将提到其中一些来源,并根据场景描述其中的一部分。

场景:一个微服务应用程序(“PodTato Head”,https://github.com/cncf/podtato-head)应该完全由Operator管理(即使另一种部署机制更有意义)。该应用程序由 4 个服务和 1 个数据库组成,如下所示:

应将最佳实践应用于此应用程序部署。

管理单一类型的应用程序

(来源:https ://github.com/operator-framework/community-operators/blob/master/docs/best-practices.md,https : //cloud.google.com/blog/products/containers-kubernetes/best -practices-for-building-kubernetes-operators-and-stateful-apps )

Operator提供的功能应该特定于单个应用程序。应用到我们的示例中,这意味着应该有 5 个Operator一次管理一个组件(podtato-server、arm-service、foot-service、hat-service 和数据库)。这为所有这些提供了良好的关注点分离(基于https://cloud.google.com/blog/products/containers-kubernetes/best-practices-for-building-kubernetes-operators-and-stateful-apps) .

编写表示全栈的Operator

(来源:https ://github.com/operator-framework/community-operators/blob/master/docs/best-practices.md )

即使应用程序的每个部分都有一个运算符,在一个 CRD 中管理整个堆栈可能会更容易。如果是这种情况,代表整个全栈的Operator应将工作委托给其他Operator以处理更具体的部分。

从技术上讲,将有一个由Operator管理的整个全栈的自定义资源定义。该Operator为Stack的每个组件创建自定义资源,这些组件再次由Operator管理并管理底层资源。

每个控制器一个 CRD

Operator管理的每个 CRD 都应在单个控制器中实现。这使代码更具可读性,并且应该有助于分离关注点。

在哪里发布和查找Operator

有诸如 operatorhub.io 和 artifacthub.io 之类的服务可以帮助最终用户找到Operators,包括如何安装它们的说明。该服务通常包括有关当前安全问题和Operator来源的信息。此外,还提供了有关Operator能力的信息。

进一步阅读

还有很多最佳实践,例如:

  • 一个Operator不应该安装其他Operator
  • Operator不应该对他们部署的命名空间做出假设,但也
  • 使用 SDK 编写Operator

和许多其他最佳实践可以在 Internet 上找到。其中更多可以在以下来源中找到:

  • https://github.com/operator-framework/community-operators/blob/master/docs/best-practices.md
  • https://cloud.google.com/blog/products/containers-kubernetes/best-practices-for-building-kubernetes-operators-and-stateful-apps

设计Operator

上一章描述了一个Operator的用例,它是有史以来最早的Operator之一。本章不要求完整性,而是根据我们自己的经验或社区描述的编写自己的 Operator 时的一些最佳实践。然而,如果没有对实际状态的清晰了解,也没有对我们想要实现的目标的清晰想法,我们还需要一些方法和技术来指定我们的 Operator 应该做什么。因此,我们还必须处理需求工程的某些方面。

需求分析

Kubernetes 的一个关键承诺是,它可以实现操作任务的自动化,以便在多个环境中部署、扩展和管理容器化应用程序,而无需(或最少)人工干预。在 Kubernetes 中,无状态的云原生应用程序非常适合水平扩展、自动自我修复重启或新容器的渐进式部署。但是,在集群或分布式环境中运行的具有复杂组件的有状态应用程序并不总是非常适合这种基于容器的基础设施。在持久性、升级或高可用性方面,它们仍然需要人工交互才能保持稳定状态。

诚然,Kubernetes 通过使用 Operators 创建和管理自定义应用程序,以一种新颖的方式解决了这些问题。但是,这是第一个问题:作为开发人员,你真的知道这种类型的应用程序是如何在内部和外部工作和交互的吗?日常 IT 运营如何运作?应用程序如何备份(包括恢复)?发生故障转移或中断时需要哪些步骤,软件组件之间是否存在依赖关系?

因此,强烈建议需要进行全面的需求分析来确定Operator的要求或条件。需求分析对于Operator的成败至关重要。所有需求都应形成文件、可测量、可测试、可追溯、与已识别的需求相关,并以足以进行系统设计的详细程度进行定义。

构建正确Operator的步骤:

  1. 如果不确定是否使用Operator,请尝试进行可行性评估。找到使用 Operator 的合理且可理解的理由。将Operator的好处与实施和运营它们所需的努力进行对比。
  2. 研究您的应用程序的现有文档,采访负责的系统管理员和其他利益相关者(如有必要),获取可能的系统检查活动列表、业务和 SLA 相关的 KPI,并将它们与现有的事件报告或错误跟踪列表进行比较。
  3. 按照“谁做什么、何时、如何以及为什么”来详细描述一个具体的场景(例如,应用程序故障转移)。
  4. 描述Operator需要知道什么才能独立运行前一个场景,使应用程序保持稳定和高效的状态。

自定义或第三方Operator

既然已经明确了使用 Operator 的情况,本文的下一部分将重点关注 Operator 实现的可用位置以及最能满足要求的位置。

找到合适的 Kubernetes Operator 可能是一项挑战。一方面,您需要找到符合您收集的要求的东西。另一方面,Operator 需要定期更新并得到厂商的积极支持。

简而言之,要获得 Operator,您有以下三种选择:

(1) 你有一个数据库并且需要一个 Operator?咨询供应商的网站。

(2) 您可以搜索提供可用 Kubernetes Operators 的公共(或私有)注册表。例如,[1] 提供了一个以简化分发的方式发布和共享 Operator 的平台。该平台使查找支持的服务和基本文档变得更加容易。它还确定了活跃的Operator社区和Operator支持的计划。

(3) 从头开始或使用合适的框架编写自己的 Operator。

Operator是特定于应用程序的,其功能范围从简单的安装脚本到处理升级、备份和故障的复杂逻辑。在公共注册表中找到合适的Operator需要时间和精力,代价是功能过大或缺失。相比之下,在编写自定义 Operator 时,开发人员想要或需要实现的功能没有限制,但代价是开发和维护

使用正确的工具

在完成并拥有完整的需求分析并决定编写自定义 Kubernetes Operator 之后,下一个问题是开发人员应该使用哪些工具。[2] 的文章讨论了编写Operator的不同方法,并列出了每种解决方案的优缺点。本文以一个 Operator 为例,并使用了各种技术和工具。作者详细介绍了以下工具:

(a) Operator SDK(Helm、Go、Ansible)。

(b) Operator-Freamworks KOPF (Python)

(c) 裸编程语言(Java)

如前所述,本文不仅描述了各个工具,还比较了它们的方法。作者证明了命令式编程方法在开发过程中需要更多的时间、工作和谨慎。作为回报,它们使开发人员可以灵活地编写所需的任何类型的逻辑。相比之下,声明性方法(Helm Chart、Ansible)允许以非常简单的形式实现 Operator,这种形式精确且易于阅读。

[2] 的最佳实践是:

  1. 如果您的软件已经有 Helm 图表并且不需要任何复杂的功能级别 => Operator SDK:Helm
  2. 如果您想快速创建 Operator并且不需要任何复杂的能力级别 => Operator SDK:Helm
  3. 如果您想要复杂的功能或/并对任何未来的实现保持灵活=> Operator SDK:Go
  4. 如果您想在组织中保留一种编程语言 一个。如果您的语言存在流行的 Operator Framework 或/并且您想为其做出贡献 => Operator Framework 湾。如果您的编程语言不存在流行的 Operator Framework => 裸编程语言
  5. 如果以上都不是=> Operator SDK:Go

使用正确的编程语言

Operator是可以用任何选择的语言编写的程序。这是因为 Kubernetes 提供了一个 REST API,它允许使用 HTTP 等轻量级协议与客户端进行通信。因此,只要遵循 REST API 规范,软件开发人员就可以用他们喜欢的编程语言编写 Operator。

但是,如果开发人员可以自由选择他们的编程语言,迟早会出现不同技术和语言的拼凑。这最终会增加维护、故障排除、错误修复和支持请求的成本。更好的策略是专注于单一编程语言并将其用于团队开发。这极大地支持了团队中的协作和相互支持。

然而,根据 [1],用 Go 语言编写的Operator是迄今为止最受欢迎的。这样做的原因有两个:首先,Kubernetes 环境本身是用 Go 编写的,因此客户端库得到了完美的优化。其次,Operator SDK(带有嵌入式 Kubebuilder)支持在 Go 中开箱即用地实现 Operator。这为开发人员节省了大量的代码脚手架,并免费为他们提供代码生成。

以正确的方式设计您的 Operator

最后一段总结了一个未分类的最佳实践列表,这些最佳实践是由各种来源发现和发布的。

  • 编写 Operator 涉及使用 Kubernetes API。使用像 Operator-SDK 这样的框架来节省时间,并获得一套工具来简化开发和测试。[3]
  • 以这样一种方式设计 Operator,即即使 Operator 停止或移除,应用程序实例仍可继续不受影响且有效地运行。
  • 每个应用程序开发一个 Operator [4]
  • Operator应该向后兼容,并且始终了解已经创建的资源的先前版本。
  • 使用异步同步循环 [4]
  • Operator应利用内置的 Kubernetes 原语,例如副本集和服务。尽可能使用易于理解和经过良好测试的代码。
  • 如果可能,请针对模拟 Pod、配置、存储和网络的潜在故障的测试套件测试 Operator。

参考

[1] https://operatorhub.io

[2] https://hazelcast.org/blog/build-your-kubernetes-operator-with-the-right-tool/

[3] https://github.com/operator-framework/community-operators/blob/master/docs/best-practices.md

[4] https://cloud.google.com/blog/products/containers-kubernetes/best-practices-for-building-kubernetes-operators-and-stateful-apps

结论


最初,Operator 是一种一流的解决方案,用于将有状态的应用程序加入到通常处理无状态工作负载操作的编排器中。他们增强了 API 并进一步增强了容器编排器的功能,但并没有解决应用程序配置和“Day 2”操作的所有问题。重要的是要记住,Operator是一种管理特定需求和促进运营的模式,但它们也带来了在实施之前应该权衡的复杂性。

相关工作

最初,Operator 是通过 CoreOS 博客上的一篇博文介绍的。本文提供了一个粗略的概述,什么是操作符、为什么要开发这个概念以及它们是如何构建的。本文的见解主要用于本文档中算子的定义。由于博客文章仅提供了简明概述,因此本文档中更深入地描述了其他术语,如功能、安全性和其他概念。

Operator模式作为一个概念在 Kubernetes 文档中进行了描述,因此提供了示例Operator如何做的概述,并提供了编写Operator的起点。(https://kubernetes.io/docs/concepts/extend-kubernetes/operator/)。

《Kubernetes Operators》一书(Dobies & Wood,2020 年)全面概述了 Operator、他们解决了哪些问题以及开发它们的不同方法。本书中的定义融入了本文档。这同样适用于《Kubernetes 模式》一书(Ibryam,2019 年),它为Operator提供了更多的技术和概念见解。这些书中所做的定义在本文档中进行了总结(以提供Operator的通用声明)。

参考:Ref: Dobies, J., & Wood, J. (2020). Kubernetes Operators. O'Reilly.

Michael Hausenblas 和 Stefan Schimanski 写了一本关于 Kubernetes 编程的书,它提供了对客户端访问、自定义资源以及编写操作符的更深入的见解。

参考:Michael Hausenblas 和 Stefan Schimanski,Kubernetes 编程:开发云原生应用程序,第一版。

Google 提供了一篇关于构建 Kubernetes Operators 和有状态应用程序的最佳实践的博文。这篇文章的一些建议出现在白皮书的最佳实践部分(https://cloud.google.com/blog/products/containers-kubernetes/best-practices-for-building-kubernetes-operators-and-有状态的应用程序)。

许多文档描述了Operator的能力级别(也称为成熟度级别)。由于可能存在Operator支持属于最高能力级别的所有功能但不支持某些较低级别功能的情况,因此本文档选择涵盖“能力”而不是“能力级别”。然而,每个能力级别所需的能力都被考虑在内。

参考:Operator框架。从https://operatorframework.io/operator-capabilities/、 https://github.com/cloud-ark/kubeplus/blob/master/Guidelines.md检索 2020 年 11 月 24 日

CNCF SIG Security 花费大量精力在本白皮书中添加与安全相关的主题。由于本白皮书的内容应该主要涵盖Operator相关的安全措施,他们编写了一份云原生安全白皮书,这是处理云原生安全时非常有用的来源(https://github.com/cncf/sig-security/blob /master/security-whitepaper/cloud-native-security-whitepaper.md)。

参考书目

  • Dobies, J., & Wood, J. (2020). Kubernetes Operators. O'Reilly.
  • Ibryam, B. (2019). Kubernetes Patterns. O'Reilly.
  • Operator Framework. (n.d.). Operator Capabilities. Operator Framework. Retrieved 11 2020, 24, from https://operatorframework.io/operator-capabilities/
  • Philips, B. (2016, 03 16). Introducing Operators: Putting Operational Knowledge into Software. CoreOS Blog. Retrieved 11 24, 2020, from https://coreos.com/blog/introducing-operators.html
  • Hausenblas, M & Schimanski, S. (2019). Programming Kubernetes. O'Reilly.

本文翻译自:https://github.com/cncf/tag-app-delivery/blob/eece8f7307f2970f46f100f51932db106db46968/operator-wg/whitepaper/Operator-WhitePaper_v1-0.md#executive-summary

英文版本请查看: CNCF_Operator_WhitePaper.pdf

0 人点赞