作者 | Jeff Carpenter & John Sanda
译者 | Rayden
策划 | 蔡芳芳
K8ssandra 是 Apache Cassandra®在 Kubernetes 上的一个发行版,由多个开源组件构建而成。从一开始直到最近的 K8ssandra 1.3 版本,K8ssandra 一直使用 Helm 图表进行安装和管理。虽然该项目的某些组件使用了 Kubernetes Operators——包括 Cassandra(cass-operator)和 Medusa(medusa-operator),但还没有一个 Operator 对所有组件进行整体管理。
K8ssandra 团队最近完成了一个我们讨论了几个月的决定:为 K8ssandra 项目创建一个 Operator。在本文中,我们介绍了我们使用 Helm 的经验,我们为 K8ssandra 创建 Operator 的决定,以及我们希望这将为项目带来的好处。
1背景
K8ssandra 的核心是 cass-operator,我们使用它来部署 Cassandra 节点。我们围绕它添加了一系列组件,组成一个生态系统,用于在 Kubernetes 中有效地运行 Cassandra。这些组件包括用于管理反熵修复(Reaper)和备份(Medusa)的操作工具。我们引入了用于指标收集和报告的 Prometheus/Grafana 技术栈。Stargate 则是一个数据网关,通过 REST、GraphQL 和 Document API 提供了对 Cassandra 更灵活的访问。
一开始,我们使用 Helm 来帮助管理这些组件的安装和配置。这使我们能够快速启动项目并开始组建社区。最初对该项目感兴趣的人主要是 Cassandra 社区的开发人员,他们不一定有很多 Kubernetes 的专业知识和经验。他们中的许多人发现掌握像 Helm 这样的包管理工具和安装程序比掌握 Operator 和 CRD(定制资源定义)更容易。这并不是说 Helm 是为“不太了解 Kubernetes 的人”准备的,因为 Kubernetes 生态的很大一部分都在使用 Helm。
2进展:Helm 的优缺点
随着项目的发展,我们开始在 Helm 上遇到一些限制。虽然正确安装 K8ssandra 集群非常简单,但我们在升级和管理集群时遇到了比较多的问题。
编写复杂的逻辑
Helm 通过循环和 if 语句很好地支持控制流。然而,当嵌套层次比较多时,整个代码就很难理解和阅读,而且缩进也成为一个问题。特别是,我们发现对修改后的 Helm 图表进行同行评审变得相当困难。
重用和可扩展性
Helm 变量的作用范围被限制在声明它们的模板内。例如,我们在 Cassandra 数据中心模板中定义了一个变量,在 Stargate 模板中不可能重用它,我们必须在 Stargate 模板中重新创建相同的变量。这使得我们的代码很难保持 DRY 原则,我们发现这是缺陷的来源。
类似地,Helm 有一个很好很大的帮助模板函数库,但是这个库并没有涵盖所有用例,并且没有接口来定义您自己的函数。您可以定义自己的模板,模板可以被大量重用,但它们不能代替函数。
项目结构和继承
伞形图设计模式是 Helm 的最佳实践,但我们在尝试实现该模式时也遇到了困难。我们能够创建一个顶级 K8ssandra Helm 图表,其中包含 Cassandra 和 Prometheus 的子图表,但当我们试图为 Reaper 和 Stargate 创建额外的子图表时,却遇到了变量作用范围的问题。我们的目的是仅仅在顶级图表定义身份验证设置,这样它们不仅可以应用于 Cassandra,还可以应用于 Stargate 和 Reaper。Helm 的继承模型不支持这种将变量向下推到子图表的概念。
定制资源定义(CRD)管理
Helm 可以创建 Kubernetes 的定制资源定义(CRD),但不能管理它们。我们知道这是 Helm 开发者为 Helm 3 做出的深思熟虑的设计选择。由于定制资源的定义是集群范围的,如果多个 Helm 安装过程试图在不同版本的 CRD 上工作可能会带来一些混乱。然而,这给我们带来了一些困难。为了管理资源的更新——比如 Helm 内部的 Cassandra 数据中心,我们必须实现一个变通方案。我们实现了定制的 Kubernetes job,并将它们标记为升级前的钩子(Hook),这样 Helm 就可以在升级时执行它们。每个 job 都用 Go 语言编写,并打包成一个镜像。这本质上就像编写迷你控制器,并且在某种程度上开始感觉像编写 Operator。
临界点:多集群部署
虽然我们已经能够通过 1.3 版本解决这些 Helm 的问题,但我们路线图上的下一个主要特性是实现多集群 K8ssandra 部署(跨越多个 Kubernetes 集群的 K8ssandra/Cassandra 集群)。我们意识到,即使没有复杂的网络配置,我们也无法使用 Helm 有效实现这一步。
3设定新方向
最后,我们意识到我们让 Helm 做得太多了。很容易陷入这样的情况:您学会了如何使用锤子,所有东西看起来都像钉子,但您真正需要的是螺丝刀。
结果,我们发现我们与 Operator 框架 的创建者有一些共同点,他们已经为 Operator 定义了一个 功能模型,我们将其展示在这里:
如图所示,Helm 最适合 Operator 前两个级别的功能,侧重于简单的安装和升级。执行更复杂的操作如故障处理和恢复、自动伸缩,以及更复杂的安装和升级应该用诸如 Ansible 或 Go 之类的编程语言来实现,而不是使用像 Helm 这样的模板语言。
构建一个 Operator:K8ssandra 2.0
基于这一分析,团队决定开始构建一个 Operator,我们将其称之为 K8ssandra 2.x 系列版本。2.0 版本的首要任务是移植我们在 Helm 图表中已有的功能,确保 Operator 具有相同的特性,并在其中增加多集群支持。我们仍然打算解决 1.x 版本中的 bug 或漏洞,但我们正试图将所有主要的新功能都集中在 Operator 上。
Helm 仍有一席之地
在工具方面,我们不认为 Helm 和 Operator 是相互排斥的。这两种方法是互补的,我们需要根据其优势来使用每一种方法。我们将继续使用 Helm 执行基本的安装操作,包括安装 Operator 以及设置 Cassandra 和其他组件使用的管理员服务帐号(Administrator Service Account)。这些都是 Helm 这样的包管理器最擅长的功能。
4Operator 设计和实现的选择
在 K8ssandra Operator 的设计和实现中,我们做出了几个关键的选择。
模块化设计
虽然 Reaper Operator、Medusa Operator 和 Stargate Operator 有单独的仓库,但我们计划将它们合并到 K8ssandra Operator 中。K8ssandra Operator 将在单个 pod 中运行,但将包含与每个 CRD 对应的多个控制器。我们将会有多个 CRD 和多个控制器。因为 cass operator 已经被独立使用,所以它仍将是独立的,并将成为 K8ssandra Operator 的一个依赖项。
虽然目前这不是微服务架构,但它是松耦合和模块化的,所以未来如果需要,我们可以将控制器重新打包为独立的微服务。
基于 Operator SDK 使用 Go 语言开发
我们决定基于 Operator SDK 使用 Go 语言编写 K8ssandra Operator。对于我们来说,这是一个简单的选择,因为我们已经从开发 cass-operator 中熟悉了它。我们相信使用像 Go 语言这样的全功能编程语言会比使用 YAML 模板更有吸引力,并有助于吸引新的贡献者加入项目。这还将使我们能够使用该语言的全部功能。例如,Go 可以很容易地创建易于重用的辅助函数。
K8ssandra 集群级状态
新的 K8ssandra 集群 CRD 有一个状态字段,可以让您大致了解集群的状态,包括是否已经就绪(ready)、尚未就绪(not ready)、正在初始化(initializing)等等。该状态将汇总组成集群的所有对象的健康状况,包括 Cassandra 集群、Stargate、Reaper 和其他任何部署在其中的对象,而这不是 Helm 可以做到的。
与 Kubernetes 的方式更加一致
我们为每个定制资源开发控制器的设计方法与 Kubernetes 中管理资源的标准方法更加一致。例如,我们有一个特定的启动顺序,我们想定义如下规则:在 Cassandra 初始化之前不启动 Stargate。开箱即用的 Helm 无法实现这一功能。我们必须在 Stargate pod 中添加一个初始化容器,以执行集群启动和运行的基本检查。有了新的 Operator,Stargate 可以检查 Cassandra 数据中心 CRD 的状态变化。当它被调协器(reconciliation)触发运行,它查询获得 Cassandra 数据中心的状态,一旦其状态变为就绪(ready),Operator 就将部署 Stargate。
测试覆盖率
这种方式也将改进测试。有很多可用的测试覆盖工具,例如我们正在使用的 SonarCloud。然而,我们不能将 SonarCloud 与 Helm 模板一起使用。所以我们现在没有一个好的方法来衡量测试中的覆盖水平,而且 IDE 的支持也不像对静态语言那么好。
5我们仍在研究的事情
在开发 Operator 的过程中,我们还在继续探索和学习一些领域。
加速迭代开发
Helm 模板非常适合快速迭代,但 Operator 的开发步骤更复杂。在修改 Operator 代码之后,我们必须重新构建 Operator 镜像并部署它,然后部署 Operator 管理的定制资源,以便它随后生成 Deployment 对象,然后我们就可以验证部署了。这个过程涉及更多步骤,所以我们正在寻求改进方法,使其更加自动化。
多集群集成测试
测试多集群 K8ssandra 部署存在一些挑战。到目前为止,我们已经能够使用 GitHub Actions 进行大多数持续集成测试(使用免费的 tier runner),但我们发现这在涉及多集群资源的时候是不够的。
我们正在调研的集成测试工具之一是 Kuttl。使用 Kuttl,测试用例和预期结果都在 YAML 文件中描述,这意味着您不必是 Go 语言或 Kubernetes API 的专家也可以贡献测试。我们相信这会让开发人员更容易参与测试并立即做出贡献,然后如果他们愿意,可以按照自己的节奏开始使用 Go。
6您应该使用 Operator 吗?您应该开发一个 Operator 吗?
如果您已经读到这里,您可能想知道这对您自己的项目的影响。如果您在 Kubernetes 中使用数据库或其他基础设施,那么使用 Operator 尽可能自动化的操作工作负载肯定是有意义的。
如果您为数据基础设施供应商工作,或者为开源数据基础设施项目做出贡献,您可能会好奇何时应该开始构建 Operator。我们在自己的过渡过程中进行了很多思考,特别是在时间安排和对用户的影响方面。最终我们的建议是:您在很多情况下发现您所使用的工具对您不利,而不是对您有利,此时可能是时候考虑不同的解决方案了。
7建立社区
我们现在看到对 K8ssandra 的贡献有所增加,尤其是在问题创建方面。现在我们已经开始在 Operator 的发展中获得动力,不断增长的用户社区帮助我们认识到为了加快产品成熟而需要做的事情,这是拥有这样一个社区的巨大好处。
我们还想继续加强代码贡献团队。如果您有兴趣在 Kubernetes 上运行 Cassandra 或构建 Operator,我们很乐意让您成为 K8ssandra 项目的一员。您可以查看我们的网站,也可以在 论坛 或我们的 Discord 服务上提出任何问题。
原文链接:https://thenewstack.io/