Kubernetes-Crossplatform-端到端自动化指南-全-

Kubernetes Crossplatform 端到端自动化指南(全)

原文:annas-archive.org/md5/bc9c42360d0e85f617cc8ea1fe16e87e

译者:飞龙

协议:CC BY-NC-SA 4.0

序言

在过去的几年里,无数组织已经利用 Kubernetes 提供的颠覆性应用部署操作模型。随着 Crossplane 的推出,相同的好处也进入了基础设施供应和管理领域。基础设施即代码在偏差管理、基于角色的访问控制、团队协作和薄弱契约等方面的局限性,促使人们转向基于控制平面的基础设施自动化,但设置它需要大量的专业知识和努力。

本书将详细介绍如何利用 Kubernetes 和 Crossplane 构建基于控制平面的基础设施自动化平台。云原生领域拥有大量的配置管理工具,选择合适的工具可能会很困难。本书将引导云原生从业人员选择最适合用例的 Kubernetes 配置管理工具。你将通过一些基于流行配置管理工具(如 Helm、Kustomize、Argo 和 KubeVela)的动手模块,学习配置管理。这些动手示例将是你在日常工作中可以直接使用的指南。

本书的 DevOps 部分将在结束时使你熟练掌握构建现代化基础设施自动化平台的技能,统一应用与基础设施自动化。

本书适合的人群

本书面向云架构师、平台工程师、基础设施或应用运维人员以及对简化基础设施和应用自动化感兴趣的 Kubernetes 爱好者。在开始本书之前,需对 Kubernetes 及其构建模块(如 Pod、Deployment、Service 和 namespace)有基本的了解。

本书涵盖的内容

第一章介绍新的操作模型,讨论了许多人认为 Kubernetes 只是容器编排的问题,但 Kubernetes 远不止如此。理解 Kubernetes 为何颠覆了第一天和第二天的 IT 操作,是成功采用和优化使用的关键。

第二章基础设施自动化的现状,揭示了基础设施即代码的局限性,并提出基于控制平面的基础设施自动化作为新时代的自动化概念,利用 Crossplane 和 Kubernetes 来实现。

第三章利用 Crossplane 自动化基础设施,帮助我们理解如何设置 Crossplane 集群,讨论其架构,并解释如何将其用作基础设施自动化的基础版本。

第四章使用 Crossplane 构建基础设施,帮助我们理解 Crossplane 的构建功能,这是一个强大的概念,有助于我们创建新的基础设施抽象。这些抽象可以是我们自定义的基于 Kubernetes 的云 API,带有组织政策、合规要求和内置的配置。

第五章探索基础设施平台模式,分析了在组织内部成功运行基础设施平台产品所需的关键模式,这些模式可以与 Crossplane 一起使用。本章将详细探讨这些模式。

第六章更多 Crossplane 模式,探讨了更多有助于日常工作的 Crossplane 模式。我们将在实践中学习大部分这些模式。

第七章扩展和扩展 Crossplane,介绍了使 Crossplane 具有可扩展性和可扩展性的两个独特方面。第一部分将深入探讨 Crossplane 提供者,第二部分将讨论 Crossplane 如何在多租户生态系统中工作。

第八章了解权衡取舍,讨论了配置管理的许多细微差别。理解配置时钟将帮助我们对工具进行分类,并了解适用于每个类别的权衡取舍。

第九章使用 Helm、Kustomize 和 KubeVela,重点讲解如何使用今天流行的不同配置管理工具,如 Helm、Kustomize 和 KubeVela。

第十章使用 Crossplane 进行应用程序入驻,分析了基础设施配置和应用程序入驻如何涉及一些跨领域的关注点,如设置源代码仓库、持续集成工作流和持续部署。本章将探讨如何以统一的方式使用 Crossplane 处理应用程序、服务和基础设施自动化。

第十一章推动平台采用,解释了许多组织在技术平台项目中失败的原因,因为它们没有应用必要的产品开发实践和团队结构。本章旨在帮助理解成功构建和采用基础设施平台所需的各个方面。

为了最大限度地利用本书

请浏览 kubernetes.io/docs/concepts/overview/ 了解基本概念。所有代码示例均使用 Kind Kubernetes 集群(kind.sigs.k8s.io/ - v1.21.1)和 Crossplane 版本 1.5.0 作为控制平面进行测试。不过,它们也应适用于未来的版本发布。

请注意,安装 Crossplane 时,Kubernetes 版本应至少为 v1.16.0。

如果您使用的是本书的数字版本,我们建议您自己输入代码,或者从本书的 GitHub 仓库访问代码(链接将在下一部分提供)。这样可以帮助您避免与复制粘贴代码相关的潜在错误。

下载示例代码文件

您可以从 GitHub 下载本书的示例代码文件,网址是github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane。如果代码有更新,GitHub 仓库会进行更新。

我们还提供了其他代码包,来自我们丰富的书籍和视频目录,网址是github.com/PacktPublishing/。欢迎查阅!

下载彩色图像

我们还提供了一份包含本书中使用的截图和图表的彩色图像 PDF 文件。您可以在此处下载:packt.link/1j9JK

使用的约定

本书中使用了多种文本约定。

文本中的代码:表示文本中的代码词汇、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 用户名。这里有一个例子:“像 Pods、Deployments、Jobs 和 StatefulSets 这样的资源属于workload类别。”

代码块设置如下:

# List all resources 
kubectl api-resources
# List resources in the "apps" API group 
kubectl api-resources --api-group=apps
# List resources in the "networking.k8s.io" API group
kubectl api-resources --api-group=networking.k8s.io

当我们希望引起您对代码块中特定部分的注意时,相关的行或项目会以粗体显示:

apiVersion: "book.imarunrk.com/v1"
kind: "CloudDB"
metadata:
 name: "aws_RDS"
spec:
  type: "sql"
  cloud : "aws"

任何命令行输入或输出均如下所示:

% kubectl get all -n crossplane-system
helm delete crossplane --namespace crossplane-system

粗体:表示新术语、重要词汇或您在屏幕上看到的词汇。例如,菜单或对话框中的词汇会以粗体显示。这里有一个例子:“在 AWS Web 控制台中进入IAM部分,并点击添加用户。”

提示或重要注意事项

看起来是这样的。

联系我们

我们始终欢迎读者的反馈。

一般反馈:如果您对本书的任何方面有疑问,请通过电子邮件联系我们,邮箱地址是 customercare@packtpub.com,并在邮件主题中提到书名。

勘误:尽管我们已尽最大努力确保内容的准确性,但错误难免。如果您在本书中发现错误,我们将非常感激您向我们报告。请访问www.packtpub.com/support/errata并填写表格。

盗版:如果您在互联网上发现我们的作品以任何形式的非法复制品,我们将非常感谢您提供位置地址或网站名称。请通过版权@packt.com 联系我们,并附上材料的链接。

如果你有兴趣成为作者:如果你在某个领域具有专长,并且有兴趣撰写或为书籍贡献内容,请访问authors.packtpub.com

分享你的想法

一旦你阅读了《Kubernetes 与 Crossplane 的端到端自动化》,我们很想听听你的想法!请点击这里,直接前往本书的 Amazon 评论页面,分享你的反馈。

你的评论对我们和技术社区都非常重要,并将帮助我们确保提供优质的内容。

第一部分:Kubernetes 的颠覆

本书的这一部分将涵盖为什么 Kubernetes 在应用部署自动化的竞争中获胜,以及它如何演变为基础设施自动化的新趋势。

本部分包括以下章节:

  • 第一章介绍新操作模型

  • 第二章考察基础设施自动化的现状

第一章:第一章:介绍新操作模型

许多人认为 Kubernetes 赢得了容器编排战,因为它出色的容器管理能力。但 Kubernetes 不仅仅如此。除了在大规模下处理容器编排外,Kubernetes 还引入了全新的 IT 操作模型。任何新事物都有陷阱。由于我们的惯性,我们往往以旧有的方式使用新工具。理解 Kubernetes 如何打破 IT 操作的传统模式,对于避免这些陷阱并实现成功的采纳至关重要。本章将深入探讨新操作模型的关键方面。

本章将涵盖以下主题:

  • Kubernetes 的历程

  • 新操作模型的特点

  • 下一个 Kubernetes 用例

Kubernetes 的历程

Kubernetes 成为领先容器编排平台的历程充满了许多引人入胜的时刻。Kubernetes 是由几位 Google 工程师基于一个名为 Borg 的内部项目发起的开源项目。从一开始,Kubernetes 就拥有 Google 重度生产使用的优势,以及 Borg 超过十年的活跃开发历史。很快,它不再是一个小规模的 Google 工程师团队,而是得到了强大的社区支持。容器编排战争是 Docker、Mesosphere DC/OS、Kubernetes、Cloud Foundry 和 AWS 弹性容器服务ECS)之间的激烈竞争,始于 2015 年。Kubernetes 逐渐并稳步超越了其他竞争者。

最初,Docker、Mesosphere 和 Cloud Foundry 宣布原生支持 Kubernetes。最终,在 2017 年,AWS 宣布推出针对 Kubernetes 的 ECS。最终,所有云服务提供商都推出了托管的 Kubernetes 服务。由于 Kubernetes 的高效性和广泛采用,竞争对手别无选择,只能提供原生支持。这些是 Kubernetes 在容器编排战中的胜利时刻。此外,它继续成长,成为云原生生态系统的核心,围绕它发展出了许多工具和模式。以下图表展示了容器编排战:

图 1.1 – 容器编排战争

图 1.1 – 容器编排战争

接下来,让我们了解新操作模型的特点。

新操作模型的特点

理解 Kubernetes 如何积极影响 IT 操作,将为应用程序和基础设施自动化中 DevOps 的高效采纳提供坚实基础。以下是 Kubernetes 操作模型的一些重要特点:

  • 团队协作和工作流程

  • 控制理论

  • 互操作性

  • 可扩展性

  • 新架构的焦点

  • 开源、社区和治理

让我们在接下来的章节中详细了解这些特点。

重要提示

在深入探讨之前,首先需要了解的是,你应该对 Kubernetes 的架构和其构建块资源(如 Pods、Deployments、Services 和 namespaces)有基本的先前理解。如果你是 Kubernetes 新手,或者需要理解基础概念的指南,请查阅 kubernetes.io/docs/concepts/overview/ 文档。

团队协作和工作流

所有 Kubernetes 资源(如 Pods、卷、Services、Deployments 和 Secrets)都是持久化实体,存储在 etcd 中。Kubernetes 有良好建模的 RESTful API 用于执行这些资源的 CRUD 操作。对 etcd 持久化存储的创建、更新和删除操作是状态变化请求。状态变化是与 Kubernetes 控制平面异步实现的。这些 Kubernetes API 有几个特性,非常有利于高效的团队协作和工作流:

  • 声明式配置管理

  • 多角色协作

声明式配置管理

我们将自动化意图作为数据点传递给 Kubernetes API,这些数据点被称为 Git,而非代码。同时,作为数据表达自动化意图比代码更少出错,且更易于阅读和维护。只要我们有清晰的 Git 历史记录、简洁的意图表达和发布管理,配置的协作就会变得轻松。以下是一个简单的 NGINX Pod 部署意图记录:

apiVersion: v1_
kind: Pod
metadata:
  name: proxy
spec:
  containers:
    - name: proxy-image
      image: Nginx
      ports:
        - name: proxy-port
          containerPort: 80
          protocol: TCP

尽管许多现代自动化工具本质上是声明式的,但由于缺乏良好建模的 RESTful API,它们在协作方面较弱。以下的多角色协作部分将进一步讨论这一点。声明式配置与多角色协作的结合使得 Kubernetes 成为一个独特的方案。

多角色协作

通过 Kubernetes 或其他自动化工具,我们将数据中心完全抽象为一个单一窗口。与其他自动化工具不同,Kubernetes 为每个基础设施问题提供了单独的 API 映射。Kubernetes 将这些问题归纳为一个称为 API 组的构造,约有 20 个 API 组。API 组将单一基础设施资源拆分为多个小的责任区块,为不同角色的人员提供了操作基础设施的权限分离。为了简化,我们可以将这些 API 逻辑性地划分为五个部分:

  • workload 类别。这些资源主要属于 appscore API 组。

  • corenetworking.k8s.io API 组。

  • corestorage.k8s.io API 组。应用操作员可以访问这些 API。

  • RoleBindingCertificateSigningCertificateServiceAccount 和 namespaces 属于此类别,集群操作员应访问这些 API。这些资源分属于多个 API 组,如 corerbacrbac.authorization.k8s.iocertificates.k8s.io

  • coreautoscalingpolicy API 组。负责应用程序策略或自动化架构特性的人员可以访问这些 API。

请注意,core API 组包含前述所有类别的资源。可以通过 kubectl 命令来探索所有 Kubernetes 资源。以下是一些命令示例:

# List all resources 
kubectl api-resources
# List resources in the "apps" API group 
kubectl api-resources --api-group=apps
# List resources in the "networking.k8s.io" API group
kubectl api-resources --api-group=networking.k8s.io

以下截图让你快速浏览 appsnetworking.k8s.io API 组下的资源,但我强烈建议你自己动手探索所有资源及其 API 组:

图 1.2 – 应用 API 组下的资源

图 1.2 – 应用 API 组下的资源

以下是 network.k8s.io API 组下的资源:

图 1.3 – 网络下的资源.k8s.io API 组

图 1.3 – 网络.k8s.io API 组下的资源

我们可以根据单独的资源或 API 组为团队分配 RBAC。以下图表展示了开发人员、应用程序操作员和集群操作员在不同关注点上进行协作:

图 1.4 – 团队协作

图 1.4 – 团队协作

这种表示可能因组织结构、角色和职责的不同而有所变化。传统的自动化工具基于模板,团队之间的协作较为困难。这导致了由两个不同团队分别决定和实施政策的情况。Kubernetes 改变了这一操作模式,通过减少协作中的摩擦,使得不同角色能够直接协作。

控制理论

控制理论是来自工程学和数学的一个概念,它通过维持动态系统中的期望状态来工作。动态系统的状态随着环境变化而不断变化。控制理论通过执行持续的反馈循环来观察输出状态,计算偏差,然后控制输入以保持系统的期望状态。我们周围的许多工程系统都使用控制理论。例如,空调系统通过持续的反馈循环来维持温度,就是一个典型的例子。下图简化地展示了控制理论的流程:

图 1.5 – 控制理论流程

图 1.5 – 控制理论流程

Kubernetes 实现了最先进的控制理论。我们将应用程序的期望状态意图提交给 API。剩下的自动化流程由 Kubernetes 处理,一旦 API 被提交,标志着人类工作流程的结束。Kubernetes 控制器异步运行持续的协调循环,以确保期望的状态在所有 Kubernetes 资源中得以保持,如 Pods、Nodes、Services、Deployments 和 Jobs。控制器是 Kubernetes 的核心大脑,负责管理不同 Kubernetes 资源的控制器集合。观察、分析和反应是单个控制器的三大主要功能:

  • createdeleteupdate 事件。

  • 分析:一旦观察者接收到事件,分析器会介入,比较当前状态和期望状态,以找到差异。

  • 反应:执行所需的操作,以使资源恢复到期望状态。

Kubernetes 中的控制理论实现改变了 IT 在第一天和第二天操作中的方式。一旦我们将意图表示为数据点,人类的工作流程就结束了。机器接管了异步模式下的操作。漂移管理不再是人类工作流程的一部分。除了现有的控制器外,我们还可以通过新的控制器扩展 Kubernetes。我们可以轻松地将管理工作负载所需的任何操作知识编码到自定义控制器(操作器)中,并将自定义的第二天操作交给机器:

图 1.6 – Kubernetes 控制器流程

图 1.6 – Kubernetes 控制器流程

互操作性

Kubernetes API 不仅仅是我们与集群交互的接口。它是将所有组件粘合在一起的纽带。kubectl、调度器、kubelet 和控制器通过 kube-apiserver 来创建和维护资源。kube-apiserver 是唯一一个与 etcd 状态存储进行交互的组件。kube-apiserver 实现了一个明确的 API 接口,提供从任何 Kubernetes 组件以及集群外部的状态可观测性。kube-apiserver 的这一架构使其能够与生态系统互操作。其他基础设施自动化工具,如 Terraform、Ansible 和 Puppet,并没有明确的 API 来观察状态。

以可观察性为例。许多可观察性工具围绕 Kubernetes 演变,因为kube-apiserver具有互操作性特征。对于现代数字化组织来说,基于状态的持续可观察性和反馈循环至关重要。从不同利益相关者的角度,基础设施和应用的端到端可视性为实现卓越运营提供了一种方式。另一个互操作性的例子是使用各种配置管理工具,如 Helm 作为kubectl的替代品。由于意图记录是纯粹的 YAML 或 JSON 数据点,我们可以轻松地将一个工具替换为另一个工具。下图展示了kube-apiserver与其他 Kubernetes 组件的交互:

图 1.7 – Kubernetes API 交互

图 1.7 – Kubernetes API 交互

互操作性对 IT 运维有很多意义。其一些好处如下:

  • 与组织生态系统的轻松共存。

  • Kubernetes 本身将不断发展,并且会持续存在更长时间。

  • 通过选择已知的生态系统工具来利用现有的技能集。例如,我们可以使用 Terraform 进行 Kubernetes 配置管理,以便利用团队在 Terraform 方面的知识。

  • 假设未来保持从 Kubernetes 迁移的选项开放。(Kubernetes API 高度模块化,我们可以轻松替换底层组件。此外,纯声明性配置如果需要可以轻松从 Kubernetes 迁移。)

可扩展性

Kubernetes 添加新功能的能力令人印象深刻。我们可以从三个不同的角度来看 Kubernetes 的可扩展性:

  • 扩展 Kubernetes 核心组件

  • 组件的可互换性

  • 添加新的资源类型

扩展的 Kubernetes 核心组件

这种扩展模型将为核心组件添加额外的功能,或改变核心组件的功能。我们将看几个扩展的例子:

  • kubectl CLI。它们是以特定格式添加到操作员计算机上的可执行文件,不改变kubectl源代码的任何形式。这些扩展可以将需要几个步骤的过程合并成一个子命令,以提高生产力。

  • 自定义调度器是一个概念,允许我们修改 Kubernetes 的资源调度行为。我们甚至可以注册多个调度器并行运行,并为不同的工作负载配置它们。默认调度器可以覆盖大多数常见用例。如果我们的工作负载具有默认调度器无法提供的独特调度行为,那么就需要自定义调度器。

  • 基础设施插件是有助于扩展底层硬件的概念。设备、存储和网络是三种不同的基础设施插件。假设一个设备支持 GPU 处理——我们需要一种机制来宣传 GPU 使用详情,以便基于 GPU 调度工作负载。

组件的可互换性

Kubernetes 的互操作性特性提供了将一个核心组件与另一个组件互换的能力。这些类型的扩展为 Kubernetes 带来了新的功能。例如,我们来看看虚拟kubelet项目(github.com/virtual-kubelet/virtual-kubelet)。Kubelet是 Kubernetes 控制平面与虚拟机节点之间的接口,工作负载会在这些节点上调度。Virtual kubelet模拟 Kubernetes 集群中的一个节点,以便在除虚拟机节点外的基础设施(如 Azure 容器实例或 AWS Fargate)上进行资源管理。将 Docker 运行时替换为其他容器运行时环境(如 Rocket)是另一个互换性的例子。

添加新的资源类型

我们可以扩展 Kubernetes API 和控制器的范围,创建一个新的自定义资源,也称为CustomResourceDefinitionCRD)。它是用于扩展 Kubernetes 以管理容器以外资源的强大构造之一。Crossplane,一个云资源管理平台,属于这一类别,我们将在接下来的章节中深入探讨。另一个用例是自动化我们的自定义 IT 日常流程,也称为操作员模式。例如,像部署、升级和响应故障等任务可以被编码到一个新的 Kubernetes 操作员中。

人们称 Kubernetes 为构建平台的平台,因为它具有广泛的扩展性。它们通常支持新的用例或使 Kubernetes 适配到特定的生态系统中。Kubernetes 通过扩展和支持每种复杂的部署环境,向 IT 运营提供了一个通用的抽象。

架构重点

架构工作重点之一是使应用程序部署架构具备应对虚拟机故障、数据中心故障和多样化流量条件等各种情况的鲁棒性。同时,资源利用率应当做到最佳,避免在过度配置的基础设施中浪费成本。Kubernetes 使这一切变得简单,并统一了如何实现可靠性、可扩展性、可用性、效率和弹性等架构特性。它让架构师不再需要专注于基础设施。架构师们现在可以专注于将所需的特性融入到应用程序中,因为在基础设施层面实现这些特性已经不再复杂。这是传统 IT 操作方式的一个重大转变。在容器世界中,设计容错性、可观察性和混沌工程实践正变得越来越受欢迎,成为架构师们关注的重点领域。

可移植性是 Kubernetes 为工作负载提供的另一个架构特性。容器工作负载通常是可移植的,但依赖项则不是。我们倾向于引入其他云组件的依赖。近年来,将可移植性融入应用程序依赖成为一种架构趋势。这一点在 2021 年 InfoQ 的架构趋势中有所体现(www.infoq.com/articles/architecture-trends-2021/)。在趋势图中,设计可移植性、Dapar、开放应用程序模型以及可持续性设计是与工作负载可移植性相关的趋势。我们正慢慢朝着可移植的云服务提供商方向发展。

随着工作负载部署到 Kubernetes 中,我们对新 IT 组织架构的关注发生了根本性的变化。

开源、社区和治理

Kubernetes 几乎让人们不再需要直接与机器打交道。投资这种高层次的抽象需要谨慎,我们将看看这一变化是否能够持久。任何高层次的抽象要成为一种有意义且持久的变化,都需要具备一些特征。由于几乎所有主要的云服务提供商都在背后支持,Kubernetes 具备了这些特征。以下是使 Kubernetes 被广泛接受和采纳的几个特征。

项目所有权

项目所有权对于开源项目的成功和推动普遍采纳至关重要。开源项目应该由一个广泛接受的基金会来管理,而不是被单一公司主导,推动未来方向的工作组应由来自多个公司的代表组成。这样能够反映项目的中立性,每个利益相关者都能参与并从中受益。Kubernetes 非常符合这一定义。尽管 Kubernetes 起源于几位 Google 工程师的项目,但它很快成为了云原生计算基金会CNCF)的一部分。如果我们查看 CNCF 的管理委员会和成员,可以看到所有顶尖技术公司都有代表(www.cncf.io/people/governing-board/www.cncf.io/about/members/)。Kubernetes 还拥有特殊兴趣小组和工作小组,并且得到了许多技术公司的支持,包括所有云服务提供商。

贡献

Kubernetes 是 GitHub 上发展速度最快的项目之一,拥有超过 3000 名贡献者。由于社区提交的速度很快,Kubernetes 看起来非常可持续。此外,相关文档、书籍和教程也非常丰富。最重要的是,我们围绕 Kubernetes 发展了太多生态系统工具和平台,这使得在 Kubernetes 上开发和部署工作负载变得更加容易。

开放标准

由于 Kubernetes 抽象的范围并不小,它并没有试图单独解决所有问题。相反,它依赖于几个开放标准来整合现有的广泛接受的工具。它还鼓励生态系统开发符合开放标准的新工具。例如,Kubernetes 可以与符合标准的任何容器运行时(如 Docker 和 Rocker)一起工作,这就是容器运行时接口CRI)。同样,符合容器网络接口CNI)的任何网络解决方案都可以作为 Kubernetes 的网络解决方案。

Kubernetes 的开源治理方法为 IT 运营提供了几个优势:

  • Kubernetes 是可持续的,组织可以放心投资。

  • 更广泛的采用将维持一个强大的人才库。

  • 强大的社区支持。

前一节总结了新的 Kubernetes IT 运营模式的关键方面。虽然我们已经看到了每个特性的好处,但当我们将它们结合起来时,也有了优势。例如,像 Crossplane 这样的平台正在通过利用之前讨论的多个方面的优势而发展。

下一个 Kubernetes 使用案例

在过去几年中,许多组织已经利用 Kubernetes 提供的颠覆性应用部署运营模式。通过将意图表达与数据点分离,然后控制平面接管其余的自动化过程的模式被称为基础设施即数据IaD),这个术语是由 Kelsey Hightower 创造的。许多 Kubernetes 社区的人士认为容器只是这种模式的第一个用例,未来还会有更多用例出现。随着 2018 年底 Crossplane 的推出,一个新的用例正在发展,被视为 Kubernetes 的下一个重大用例。Crossplane 将 Kubernetes 运营模式的优势带入云基础设施的供应和管理领域。这一趋势将使人们摆脱传统的基础设施即代码IaC),转而使用 Crossplane 和 Kubernetes 的 IaD。这一转变解决了当前 IaC 模型的局限性,并统一了自动化应用和基础设施的方法。

总结

Kubernetes 提供了许多与现代数字化组织期望一致的 IT 运营模式方面的新特性。了解 Kubernetes 如何颠覆日常的 IT 运维操作对其成功采用至关重要。本章节详细介绍了 Kubernetes 提供的新运营模式的细节。在下一章中,我们将探讨 IaC 在云基础设施管理中的局限性,并介绍基于 Kubernetes 控制平面的基础设施管理作为新时代的替代方案。

第二章:第二章:审视基础设施自动化的现状

本章将回顾基础设施自动化的历史、发展以及现状。我们将探讨云原生生态系统和敏捷工程实践的发展,如何揭示 基础设施即代码 (IaC) 的局限性。我们还将研究基于控制平面的基础设施自动化作为一种前沿技术,如何解决 IaC 的局限性,并能够改变 DevOps 操作模型,推动软件工程向积极方向发展。

本章将深入探讨以下主题:

  • 基础设施自动化的历史

  • IaC 的局限性

  • 端到端自动化的需求

  • 多云自动化需求

  • Crossplane 作为云控制平面

  • 其他类似项目

基础设施自动化的历史

在 1990 年代,硬件采购周期是影响组织基础设施变更的重要因素。那时,基础设施自动化并未受到太多重视。从接到订单到物理基础设施可用所花费的时间远远超过基础设施设置所花费的精力。个别基础设施工程师和小团队会自动化重复的脚本任务,但行业内并未广泛适应。诸如 CFEngine 这样的工具(于 1993 年推出,用于自动化基础设施配置)在那个十年里并未得到广泛采用。由于自动化的效益较小、投资回报率低,行业内并没有投资自动化的趋势。到了 2000 年代,基础设施自动化的理念逐渐获得关注,原因如下:

  • 虚拟化技术

  • 云计算

虚拟化技术带来了资源的软件表示能力,例如内存、CPU、存储和网络,这些资源通过安装在物理硬件上的虚拟机监控器(Hypervisor)来实现。它将我们带入了虚拟机时代,在这个时代,机器被从底层物理硬件中抽象出来。我们可以在单一硬件上运行多个虚拟机。它带来了许多优势,如降低成本、最小化停机时间以及有效利用资源。但最关键的优势是基础设施工程的敏捷性,打破了传统的硬件采购周期。虽然虚拟化技术在 2000 年前就已存在,但由于云计算的普及,它在后期才得到了广泛应用。

不同的云平台在 2000 年代末推出,增加了更多的灵活性。我们进入了基础设施即服务IaaS)时代。随着我们增加创建新虚拟机的速度,新的问题也逐渐出现。需要管理的服务器数量迅速增长。此外,虚拟机是短暂的,我们需要快速迁移、修改和重建它们。保持配置与之前场景的一致性是一个挑战。由于人为管理虚拟机的错误多且繁琐,我们最终得到了“雪花服务器”。这些限制促使我们转向广泛采用基础设施自动化。像 Puppet、Ansible、Chef 和 Terraform 这样的新工具迅速发展,引入了 IaC,用代码的方式管理配置和基础设施的供应。我们在基础设施生命周期管理中保持敏捷,并将相关代码存储在 Git 中,这为现代基础设施工程奠定了基础。IaC 和 IaaS 是一个强强联手的组合,为基础设施工程提供了独特的特性。我们实现了可持续、可重复、可互换和弹性的基础设施供应与配置管理。

以下图表总结了从脚本到 IaC 的进化过程:

图 2.1 – 基础设施自动化的进化

图 2.1 – 基础设施自动化的进化

下一次进化的需求

随着我们不断前进,云计算成为了基础设施的“圣杯”。像 Terraform、Pulumi、AWS CloudFormation、Google Cloud Deployment Manager 和 Azure Resource Manager 这样的工具成为了基础设施即代码(IaC)的核心。虽然这些工具很好地履行了它们的承诺,但我们可以看到基础设施自动化的下一次进化已经开始显现。在审视基础设施自动化的下一个阶段之前,了解为什么我们需要围绕基础设施自动化进化我们的工具和实践至关重要。软件行业的一些最新趋势正在推动下一阶段的进化。这些趋势如下:

  • IaC 的局限性

  • Kubernetes 的自动化操作模型

  • 多云自动化需求

让我们来看一下这些趋势,以证明我们需要向基础设施自动化的下一个阶段进化。

IaC 的局限性

大多数广泛使用的基础设施自动化工具都是基于模板的,如 Terraform、Ansible 和 Azure 资源管理器。从多个角度来看,它们的扩展性较差。这并不意味着 IaC 工具在自动化中不合适,恕我直言,IaC 工具已经积极地推动了软件工程的发展超过十年。我们将尝试解释如何演变的情况暴露了基于模板的 IaC 工具的弱点,以及基于控制平面的工具如何作为替代方案并成为下一个演化步骤。让我们以 Terraform 为例,分析它的局限性。以下是 Terraform 的不同局限性问题:

  • 缺乏自助服务

  • 缺乏访问控制

  • 参数化陷阱

    术语

    执行任务所需拥有和处理的知识量称为认知负荷。你将在接下来的部分中遇到高团队认知负荷这一术语,这意味着一个团队必须扩展其处理知识的能力,以便执行日常职能,超出其通常所能承载的知识量。

缺乏自助服务

使用 Terraform 时,我们有太多模板抽象了成千上万的云 API。记住每个参数在成千上万模板中的使用方式并不是一件容易的事。此外,基础设施使用策略来自组织中的不同团队,例如安全、合规、产品和架构。实现 Terraform 自动化涉及显著的团队认知负荷和集中式政策要求。因此,许多组织倾向于通过集中式平台团队来实现基础设施自动化,以避免增加产品团队的认知负荷,并实现集中式政策管理。但基于模板的自动化工具不支持 API,这是提供平台自助服务的最佳方式。因此,我们必须构建 Terraform 模块/库来创造人工团队边界,并实现自助服务。模块/库是 API 的一个较弱替代方案。它们在实现平台自助服务时存在一些问题:

  • 平台团队在认知负荷抽象方面存在泄漏,因为产品团队使用 Terraform 模块/库意味着至少要学习 Terraform 的基本知识。

  • 团队间作为模块和库的依赖关系需要产品团队和平台团队之间的协作互动模式,而非自助式模式。这与现代平台拓扑结构相悖,阻碍了平台和产品团队的敏捷性。

另外,一些组织将基础设施的配置完全外包给平台团队。完全的集中化会妨碍产品团队的敏捷性,同时导致基础设施配置的外部耦合。很少有组织尝试将基础设施管理下放到产品团队中。完全的去中心化会增加团队的认知负担,并且增加跨团队对齐集中化政策的难度。新的发展需要在正确抽象的自助服务 API 中找到折衷点。

缺乏访问控制

正如我们在上一节中看到的,构建和使用 Terraform 模块需要多个团队之间的协作。通过与产品团队共享 Terraform 模块进行基础设施配置和管理,我们会遇到访问控制问题。我们无法对产品团队所需的单个资源实现精确的基于角色的访问控制RBAC),并且我们将泄露所有必要权限的底层云凭证,这些权限是模块所需要的。例如,一个用于配置 Cosmos DB 的 Terraform 模块需要 Azure 凭证来配置数据库和虚拟私有云VPC)。但产品团队所需的访问权限仅限于创建数据库,他们不需要直接修改 VPC。除此之外,我们还面临模块/库的版本管理问题。它需要所有产品团队之间的协调,这在模块/库版本升级时会产生摩擦。一个高度可互操作的基于 API 的基础设施自动化抽象可以解决协作和访问控制问题。

参数化的陷阱

参数化的陷阱是任何基于模板的解决方案中的常见问题,无论是基础设施自动化工具还是其他工具。我们为值变化的变量创建参数占位符,在任何基于模板的解决方案中都是如此。这些解决方案易于实现、理解和维护。如果我们在小规模操作时,它们表现得很好。然而,当我们尝试扩展基于模板的解决方案时,我们最终会遇到以下问题之一:

  • 随着时间的推移,我们将需要对新变量进行参数化,并且逐步地,我们将在某个时刻暴露所有变量。这将侵蚀我们通过模板创建的抽象。查看任何 Helm 图表都能清楚地看到这一点,几乎所有内容都是一个参数。

  • 我们可能决定分叉主模板,以实现特定用例的定制。分叉很难保持更新,随着分叉数量的增加,保持模板之间的政策一致性会变得非常困难。

参数化通常不是在大规模操作时完美的抽象。

重要提示

参数化的陷阱是 DevOps 工程师需要详细了解的关键话题。在后续章节中,我们将探讨配置时钟的概念,即随着时间推移模板抽象的侵蚀。

基于 Kubernetes 的自动化操作模型

正如我们在前一章中看到的,Kubernetes 的控制理论实现完全改变了应用程序自动化的 IT 运维方式。基础设施自动化也同样值得享受这些好处。但传统的基础设施自动化工具缺乏这些属性,因为它们没有完整的控制理论实现。以下是一些缺失的特性:

  • 同步配置是 Terraform 或类似自动化工具中的一个重要可扩展性问题。资源按照常规自动化工具中的依赖关系依次配置。如果基础设施A依赖于基础设施B,我们在定义执行顺序时必须尊重这一点,并且如果其中一个执行失败,整个自动化将失败。基础设施的单一表示是此问题的关键所在。在 Terraform 中,单一的状态文件是表示基础设施资源的模型。基于 Kubernetes 的自动化可以改变这个等式。将会有一个持续的调节过程,将当前状态朝着预期状态推进。因此,我们可以高效地管理依赖关系,而无需执行顺序。基础设施A的配置可能会失败,但一旦基础设施B可用,持续的调节最终会修复状态。

  • 建模团队边界是传统工具中另一个缺失的环节。单一的 Terraform 状态文件无法灵活地建模不同的团队边界。在基于 Kubernetes 的自动化模型中,资源以独立的 API 形式呈现,可以根据任何团队结构的需求进行分组和组合。我们无需将所有自动化内容集成到一个单一的庞大模型中。

  • 漂移管理是通过保护基础设施免受任何非预期或未经授权的更改,保持基础设施在预期状态的过程。在云控制台中直接更改身份与访问管理IAM)策略,而不更改相关的自动化代码,就是一种漂移的例子。漂移管理的核心是将其恢复到授权的状态。如果没有控制平面持续监控基础设施状态并执行与最后执行代码的调节,漂移管理是不可能实现的。通过额外的外部工具实现漂移管理会增加复杂性,并且不能解决所有问题。

  • 以标准化方式自动化第二天问题是传统工具中的另一个缺失环节。基于 Kubernetes 的自动化模型可以提供配置模型,以支持第二天问题,如扩展、监控和日志记录。此外,我们还可以使用标准扩展点(操作器)来自动化任何自定义的第二天问题。

这些是基于 Kubernetes 的基础设施自动化所能带来的几个关键视角。

多云自动化需求

几乎所有大规模的组织都会在多个云提供商上运行其工作负载。组织决定建立由多个云提供商支持的基础设施可能有多种原因。我们不会详细讨论这些因素,但我们必须理解多云对基础设施管理的影响。通常,云提供商提供托管服务,无论是像 Amazon EC2 这样的基础 IaaS,还是像 AWS Lambda 这样的更抽象的平台。从云基础设施消费者的角度来看,基础设施自动化就是在应用所有内部策略之后,以自动化方式进行这些托管服务的配置和生命周期管理。组织使用基础设施自动化工具在云基础设施 API 上构建抽象,以编码所有内部策略。

支持多云能力需要大量工作,因为它带来了新的需求。想一想多云环境吧。将策略嵌入到每个云提供商的自动化脚本中是一项艰巨的工作。即使我们在付出了重大努力之后完成了这项工作,保持这些策略在自动化脚本中的同步也涉及摩擦,且容易出错。跨云提供商的身份验证、授权、计费、监控和日志记录的集中式体验,将为组织提供统一的体验,这将是一个额外的优势。通过传统的自动化工具实现这些跨领域的关注点需要大量的定制化工程,使得我们的平台团队规模庞大。我们需要的是一个集中式控制平面,用于抽象跨领域的关注点和策略。

以下图展示了一个基于 API 的集中式控制平面如何为产品团队提供统一的体验:

图 2.2 – 多云控制平面

图 2.2 – 多云控制平面

Terraform 或 Pulumi 等工具在一定程度上帮助解决了这些问题,但它们并非端到端的自动化,存在可扩展性问题,并且需要定制化工程来构建。此外,这些工具也不是完全公正的开源项目。最初创建这些开源项目并提供企业级解决方案的公司主导了前者的控制。现在,我们都确信基础设施自动化的下一个演进是必需的,是时候定义此类工具所需的属性了。后续的基础设施自动化发展应该是基于控制平面的、完全由社区驱动的解决方案,依赖于 API。下图总结了从基础设施即代码集中控制平面基础自动化的演进:

图 2.3 – 下一阶段的演进

图 2.3 – 下一阶段的演进

Crossplane 作为云控制平面

Crossplane 是一个基于 Kubernetes 的现代控制平面基础设施自动化平台,具备了基础设施工程下一阶段所需的所有特性。通过 Crossplane,我们可以从多个云提供商那里组装基础设施,并将其作为高级 API 暴露。这些 API 可以为不同团队提供通用的体验,而不受底层云供应商的影响。在为产品团队组合 API 时,平台团队可以根据组织的结构使用不同的资源粒度。这样精心设计的基础设施自动化 API 将促进自服务、多角色协作、精确的 RBAC、更低的认知负担、持续的漂移管理以及异步调和的依赖管理。最重要的是,平台团队可以通过配置以无代码的方式组合这些 API。最后,我们可以拥有一个精简的平台团队,这也是现代团队拓扑学所强烈推荐的做法。

Crossplane 仅仅是一组自定义控制器,它扩展了 Kubernetes 用于管理来自不同供应商的基础设施。作为基于 Kubernetes 构建的新 API 扩展,Crossplane 继承了 Kubernetes 操作模型的所有优点,并能利用丰富的云原生工具生态系统。此外,这也可以统一我们自动化应用程序和基础设施的方式。Crossplane 可以覆盖端到端的自动化,包括第 1 天和第 2 天的关注点。基础设施的提供、编码策略、治理和安全约束是我们可以自动化的第 1 天关注点。第 2 天我们可以自动化漂移管理、升级、监控和扩展等任务。最重要的是,它遵循 Kubernetes 的开源治理模型,通过云原生计算基金会CNCF)进行管理。下图展示了 Crossplane 如何与 Kubernetes 一起工作:

图 2.4 – Crossplane 控制平面

图 2.4 – Crossplane 控制平面

要将平台作为通用控制平面进行采用,需要更深入地审视开源治理和生态系统的接受度。接下来的章节将深入探讨这些方面。

一个通用控制平面

Crossplane 作为一个开源项目于 2018 年启动,迈出了成为通用控制平面的步伐。2020 年捐赠给 CNCF 是其下一步重大进展。这帮助 Crossplane 成为一个由基金会推动的开源项目,吸引了更广泛的参与,而不仅仅是成为另一个开源项目。最初,它是一个沙箱项目,但并没有止步于此。2021 年,它被接受为孵化项目。最重要的是,Crossplane 只是 Kubernetes 的另一个扩展,而 Kubernetes 已经是一个被广泛接受的应用 DevOps 平台。这也意味着 Kubernetes 上可用的整个工具生态系统也与 Crossplane 兼容。团队可以在不增加太多认知负担的情况下,使用现有的工具集:

图 2.5 – 旅程

图 2.5 – 旅程

Crossplane 还有一些独特的特性,促使它被接受为一个通用的控制平面。这些特性包括:

  • 基础设施供应商的开放标准

  • 更广泛的参与

  • 云服务提供商合作伙伴关系

基础设施供应商的开放标准

Crossplane 使用 Crossplane 资源模型XRM),它是 Kubernetes 资源模型KRM)的扩展,作为基础设施提供商的开放标准。它解决了当来自不同供应商的基础设施服务整合到单一控制平面时,诸如命名身份、包管理和跨资源引用等问题。Crossplane 社区已经制定了这些标准,以规范基础设施提供商如何集成到集中式 Crossplane 控制平面中。在统一且无需编码的方式下组合不同基础设施的能力,正是建立在这种标准化基础上的。

更广泛的参与

Upbound 是最初创建 Crossplane 的公司。他们为需要支持和额外服务的组织提供企业版服务。但要成为一个通用的控制平面,Upbound 不能是唯一的企业 Crossplane 提供商。任何供应商都应该能够提供企业版服务。随着 Crossplane 获得 CNCF 孵化状态,许多工作正在这一领域进行。CNCF 和 Crossplane 社区推出了一项名为 Crossplane 合规性计划 的举措。这是由 CNCF 运营的一个项目(github.com/cncf/crossplane-conformance)。其目标是为任何供应商提供基础治理,使他们能够采用 Crossplane 开源项目,构建附加功能,并提供 CNCF 认证 版本。这与 Kubernetes 认证分发版 类似,Kubernetes 认证分发版是 CNCF 运行的一个项目,所有供应商基于基础 Kubernetes 版本进行扩展并提供认证版本。Crossplane 合规性计划在两个层面上运行:

  • 提供者:在一个层面上,基础设施提供者将有兴趣构建相应的 Crossplane 控制器,以便客户通过 Crossplane 使用他们的产品。这要求遵循 XRM 所设定的标准。CNCF 将通过认证基础设施供应商构建的提供者来确保这一点。

  • 分发:在另一个层面上,许多供应商将有兴趣提供 Crossplane 企业版服务。Crossplane 合规性计划使这一支持成为可能。

github.com/cncf/crossplane-conformance/blob/main/instructions.md 阅读更多关于 Crossplane 合规性计划的信息。

云服务提供商合作伙伴关系

Crossplane 与所有主要云服务提供商建立了出色的合作伙伴关系。现在,各大云服务提供商的生产就绪 Crossplane 提供者已经存在相当一段时间了。最初,IBM 加入了 Crossplane 社区,并在 2020 年发布了其第一个提供者版本。同样,AWS 和 Azure 将 Crossplane 提供者纳入了它们的代码生成流水线,确保最前沿的最新提供者可以用于所有云资源。阿里巴巴在其多个内部项目中实验使用 Crossplane,也有一个生产就绪的提供者。同样,社区管理的Google Cloud PlatformGCP)提供者也在其中。这些合作伙伴关系和社区努力使得 Crossplane 成为一个有吸引力且广泛接受的通用控制平面方案。

其他类似项目

其他一些基于 Kubernetes 的基础设施自动化项目与 Crossplane 有着共同的兴趣,并支持类似的用例。这些项目通过 API 和自定义控制器扩展了 Kubernetes,架构与 Crossplane 相同。本节将介绍这些工具,以便与 Crossplane 进行全面的比较。以下是一些此类项目的列表:

  • Kubernetes 服务目录是由开放服务代理(open service broker)提供的,旨在从 Kubernetes 中进行云资源的生命周期管理。像 Crossplane 一样,它作为 Kubernetes 控制器扩展工作。但它没有一个完整的框架来组合带有策略约束的基础设施配方。此外,我们无法为不同团队的边界建模 API。开放服务代理的 Kubernetes 服务目录并不适合平台团队构建带有编码策略的可重用配方。通常,这意味着我们需要在政策执行方面面临困难,且团队需要花费较多的认知精力来详细理解云服务的内容。

  • AWS Kubernetes 控制器(ACK)是 AWS 开发的基于 Kubernetes 的扩展,旨在通过控制器从 Kubernetes 集群中管理其资源。同样,它没有一个框架来组合基础设施配方和建模 API。此外,它不支持跨云使用,仅适用于 AWS。

  • GCP 配置连接器是由 Google 开发的,用来替代 GCP 服务目录。它的工作方式类似于 ACK,并继承了相同的局限性。需要注意的一点是,GCP 配置连接器并不是一个开源项目。

这些工具都无法覆盖端到端的自动化用例,也没有提供将资源组合成配方的能力。我们已经详细了解了 Terraform、AWS CloudFormation、Azure Resource Manager 以及类似的 IaC 工具的局限性。这些正是 Crossplane 开发者在开发这种通用抽象时的动机。

总结

本章讨论了 IaC 的局限性细节。我们还探讨了在软件工程不断发展的世界中,为什么向控制平面自动化过渡是不可避免的。这也标志着本书第一部分的结束。总的来说,第一部分介绍了 Kubernetes 如何赢得应用部署自动化的战斗,以及如何通过相同的模式推动基础设施自动化的新趋势。接下来的章节将带领我们通过动手实践学习 Crossplane、Kubernetes 配置管理以及生态系统工具。我们还将讨论使用 Crossplane 开发先进云基础设施自动化平台的不同细微差别和构建块。

在下一章,我们将学习如何使用 Crossplane 自动化基础设施。

第二部分:构建现代化基础设施平台

本节将介绍使用 Kubernetes 和 Crossplane 构建先进基础设施自动化平台的不同细节。这将是一个逐步的学习过程,帮助你了解 Crossplane 并利用 Kubernetes API 创建自服务基础设施自动化平台。

本部分包含以下章节:

  • 第三章使用 Crossplane 自动化基础设施

  • 第四章使用 Crossplane 组合基础设施

  • 第五章探索基础设施平台模式

  • 第六章更多 Crossplane 模式

  • 第七章扩展和扩展 Crossplane

第三章:第三章:使用 Crossplane 自动化基础设施

现在是时候不再仅仅抽象地讨论理念,而是深入细节了。从这一章开始,我们将进行一场实践之旅,实施我们所学的内容,并同时探索不同的 Crossplane 概念。在本书中给出的示例将确保我们掌握了现代基础设施工程的理念和经验,并能在日常工作中加以实践。本章将特别详细介绍 Crossplane 的架构及其开箱即用的功能。

本章涵盖的主题如下:

  • 理解自定义资源定义和自定义控制器

  • 理解 Crossplane 架构

  • 安装 Crossplane

  • 安装和配置提供者

  • 多提供者配置

  • POSTGRES 配置示例

理解自定义资源定义和自定义控制器

理解 Kubernetes 中自定义资源定义CRD)和自定义控制器的概念,对于了解 Crossplane 的工作原理至关重要。在深入 Crossplane 架构之前,我们将快速回顾一下 CRD 和自定义控制器。

术语

在 Kubernetes 中,资源一词代表一组相似类型的对象。Pods、Services、Deployments、命名空间等是内建的对象类型。每个资源在 kube-apiserver 上都有相应的 API 端点。

CRD 是扩展内建资源列表的方式。它添加了一种新的资源类型,包括在 kube-apiserver 上的一组 API 端点,用以操作该新资源。CRD 这一术语准确地表明了它的功能。通过 CRD 添加到 Kubernetes 的新资源称为自定义资源CR)。除非有自定义控制器的支持,否则存储和检索通过 CRD 定义的结构化对象是没有意义的。自定义控制器是我们对内建控制器的扩展。它们通常在控制循环中对 CR 进行操作,以便对给定资源的每个 API 事件执行特定操作。第一章介绍新操作模型,已经讲解了控制循环的概念。如果你想复习这一理念,请参阅控制理论部分。请注意,自定义控制器不一定总是与 CR 一起工作。它们可以与任何现有资源一起工作,以扩展其功能,这不在我们讨论的范围内。Prometheus 操作符是将少量新 CR 和控制器添加到 Kubernetes 集群中,用于监控工作负载的最著名和广泛使用的示例之一。

术语

当“操作符”一词应用于 Prometheus 时,它是 CoreOS 提出的术语。操作符不过是CRD + 自定义控制器 + 应用聚焦的组合。

添加新 CRD

虽然有几种方法可以将 CRD 添加到 Kubernetes 中,但我们通过创建一个 yaml 文件并将其应用于集群来添加 CRD。yaml 文件为 CR 提供了一个结构。以下是 CRD YAML 的主要属性:

  • apiextensions.k8s.io/v1beta1

  • CustomResourceDefinition

  • clouddbs.book.imarunrk.com,其中 clouddbs 是复数名称,而 book.imarunrk.com 是新定义的 API 组。复数名称仅仅是资源名称的复数表示。例如,现有资源 pod 的复数格式是 pods。我们总是可以使用它来执行 kubectl 命令(kubectl get pods)。

  • Spec 是定义 CR 详细信息的部分。该部分的属性包括 API 组、版本、作用域和名称。此部分还描述了 API 本身的验证要求,例如参数列表、数据类型以及它们是否是必填参数。

以下是一个示例 CRD YAML,表示一个云数据库资源。它包含几个必需的参数:数据库类型和云提供商名称:

apiVersion: "apiextensions.k8s.io/v1beta1"
kind: "CustomResourceDefinition"
metadata:
  name: "clouddbs.book.imarunrk.com"
spec:
  group: "book.imarunrk.com"
  version: "v1"
  scope: "Namespaced"
  names:
    plural: "clouddbs"
    singular: "clouddb"
    kind: "CloudDB"
  validation:
    openAPIV3Schema:
      required: ["spec"]
      properties:
        spec:
         required: ["type","cloud"]
         properties:
           type:
              type: "string"
              minimum: 1
           cloud:
              type: "string"
              minimum: 1 

执行以下 YAML 代码将在 kube-apiserver 上启用新的 REST 端点,用于创建、修改和删除 CloudDB 资源:

# Apply the CRD yaml to the Kubernetes cluster
kubectl create -f crd.yaml

下一步是编写自定义控制器来管理 CR API 事件。我们可以使用不同的开源框架,用许多不同的编程语言来编写控制器。编写控制器是一个高级话题,会在稍后的第七章中详细讲解,扩展与扩展 Crossplane。Crossplane 提供程序其实就是用来管理外部基础设施资源的控制器——通常是云提供商的托管服务。目前,我们必须记住,控制器实现了三个操作的功能——观察、分析和响应控制循环。在前面的示例中,控制循环将根据资源的创建、更新和删除 API 事件来创建、更新和删除云数据库。

使用 CRD

一旦我们有了 CRD 和控制器,就可以开始使用 kubectl 创建和管理云数据库。它的工作方式与其他内置资源(如 Pod)非常相似。以下 YAML 是创建 Amazon RDS 的示例:

apiVersion: "book.imarunrk.com/v1"
kind: "CloudDB"
metadata:
  name: "aws_RDS"
spec:
  type: "sql"
  cloud : "aws"

应用以下命令将创建一个新的 CloudDB 资源:

# Apply the CloudDB yaml to the Kubernetes cluster
kubectl create -f aws_rds.yaml

请注意,前面的 YAML 不会创建 RDS,因为我们还没有开发和部署控制器。该示例是为了说明 CRD 和自定义控制器是如何工作的。现在我们已经了解了 CRD 和自定义控制器,是时候详细查看 Crossplane 架构了。

理解 Crossplane 架构

根据我们目前所知,Crossplane 仅仅是一组表示外部基础设施资源的 Kubernetes 自定义控制器和 CRD。如果你仔细观察,Crossplane 远不止是 CRD 和自定义控制器的组合。Crossplane 有四个关键组件。组件如下:

  • 托管资源

  • 提供者

  • 组合资源

  • Crossplane 核心

托管资源

托管资源MR)将一个 CRD 和相应的自定义控制器连接起来,以表示一个单独的外部基础设施资源。MR 与基础设施资源是一一对应的。例如,CloudSQLInstance 是一个表示 Google Cloud SQL 的 MR。下图展示了 Amazon RDS 和 Google Cloud Storage 的 MR 映射:

图 3.1 – MR 映射

图 3.1 – MR 映射

Crossplane 资源模型XRM)是开发 MR 时使用的开放标准。XRM 是 Kubernetes 资源模型KRM)的一个有观点的扩展。XRM 设定了外部资源命名、依赖管理、包管理等方面的标准。MR 是构建 Crossplane 中一切的基本构件。我们可以直接使用 MR 来配置外部基础设施,但这很少发生。最佳实践是在 MR 之上组合一个更高层次的 API 来进行使用。我们将在本章后续部分以及专门的章节中深入探讨 如何为什么 要这样做。以下 YAML 表示直接使用 MR 来配置 CloudSQLInstance:

apiVersion: database.gcp.crossplane.io/v1beta1
kind: CloudSQLInstance
metadata:
  name: my-GCP-DB
spec:
  forProvider:
    databaseVersion: POSTGRES_9_6
    region: asia-south2
    settings:
      tier: db-n1-standard-1
      dataDiskSizeGb: 10
  writeConnectionSecretToRef:
    namespace: DB
    name: my-GCP-DB-credentials

该 YAML 将根据配置中提到的资源约束来创建一个 GCP Cloud SQL 实例。由于 MR 是映射到基础设施提供程序 API 的低级 Crossplane 构件,它将直接支持基础设施 API 中所有可用的属性。Crossplane 控制器的调和控制循环将为配置 YAML 中未提供的功能填充由基础设施 API 分配的默认值。MR 的这一概念被称为延迟初始化。我们在 forProvider: 部分提供的内容将代表基础设施 API 的属性。如果有人或并行进程对基础设施资源进行了未经授权的更改,Crossplane 将回滚这些更改,并恢复 YAML 中提到的事实来源。配置的其余部分将有助于确定 Crossplane 的其他行为。例如,前面的 YAML 中有 writeConnectionSecretToRef: 用于决定如何保存数据库凭证。可能还会有更多此类行为控制,我们将在后面的内容中进一步学习。以下命令可以帮助查看已创建的 GCP 资源的详细信息,并在测试后清理资源:

# View the resources created with
kubectl get cloudsqlinstance my-GCP-DB
kubectl describe cloudsqlinstance my-GCP-DB
# Delete the resources created with
kubectl delete cloudsqlinstance my-GCP-DB

我们还可以将现有的已配置基础设施导入到 Crossplane 生态系统中。MR 会在进行新资源配置之前检查配置 YAML 中指定的资源是否已经存在。当我们为现有基础设施构建 YAML 文件时,可以提供需要由对接循环在 forProvider 下维护的授权属性。在下一章中,我们将查看如何将现有基础设施导入到 Crossplane 的示例。

提示

Crossplane 行为控制的一个可能选项是删除策略,通过一个名为 deletionPolicy 的属性指定。它有两个可能的值——Delete(默认值)和 Orphan。Delete 会从基础设施提供者中移除资源,而 Orphan 仅仅移除 Crossplane 的引用。

提供者

提供者是一组相关的 MRs,打包成一个捆绑包。每个云服务供应商、其他基础设施服务(例如 Rook——Kubernetes 的云原生存储,rook.io/)以及像 Helm 这样的软件工具都有各自的提供者包。这些包的格式就是一个容器镜像。安装一个提供者可以通过配置 YAML 文件或使用 Helm 包来完成。在我们开始使用这些包之前,必须先配置一个 ProviderConfig 配置。ProviderConfig 资源帮助我们设置基础设施供应商的凭证。我们将在本章的下一节中讨论提供者的安装和 ProviderConfig 的设置。

下图表示 AWS 和 GCP 提供者扩展 Crossplane,用于相应的云资源配置:

图 3.2 – 提供者视图

图 3.2 – 提供者视图

组合资源

每个组织在其结构、规模、运营模式、技术成熟度、合规性和安全要求方面都有所不同。所有这些方面将直接创造一套用于基础设施供应和使用的政策规则。此外,组装基础设施堆栈的多个组件也存在复杂性。组合是一种方法,它可以在一个或多个 MR API 之上构建我们定制的基础设施 API。这些自定义 API 可以编码所有的政策指导原则,并将多个基础设施组件组装成简单、易于使用的基础设施配方。这些 API 被称为复合资源XRs)。关键点是,Crossplane 允许我们以无代码的方式定义这些资源,仅通过配置即可实现。在没有 Crossplane 复合的情况下,我们最终将不得不构建复杂的 Kubernetes 自定义控制器。Composition 和复合资源定义XRD)是我们用来组合更高层次 XR API 的配置。XRD 定义了我们正在构建的新自定义 API 的架构,它是一个新的 CRD 定义。Composition 是提供新 CRD 架构和现有 MR 之间映射关系的配置。一旦新的 XR 资源可用,我们就可以使用资源声明对象开始供应基础设施配方。Claim API 是当我们添加新的 XR API 时创建的,前提是我们在 XRD 配置中请求了它。Claim 和 XR API 几乎相同,只有一些细微的差别,我们将在后续章节中探讨。下图展示了我们如何使用 GCP 提供商的 CloudSQLInstance、防火墙和网络 MR 来构建一个 XPOSTGRES 数据库复合资源:

图 3.3 – 复合资源

图 3.3 – 复合资源

我们有成千上万的云管理服务,具有许多需要详细配置的属性。这给筛选选项和实施资源供应带来了显著的认知负担,需要设置适合我们组织需求的良好护栏。每个产品团队都无法承担这种程度的认知负担。通常,组织会使用平台团队来抽象这种认知负担。组合层是供平台团队构建这种抽象的。Crossplane 使我们能够将这些抽象作为 Kubernetes API 暴露,从而为产品团队提供自助服务。下图展示了平台团队和产品团队的互动模型:

图 3.4 – 产品团队和平台团队互动

图 3.4 – 产品团队和平台团队互动

Crossplane 核心

在设置我们感兴趣的提供者并在其上构建 XR 之前,我们需要在集群中安装 Crossplane 核心组件。该组件提供了管理多个提供者、构建新 XR 和新包所需的原语。再次强调,核心组件是 CRD 和自定义控制器的集合。它是将 Crossplane 其他所有部分联系在一起的粘合剂。下图展示了所有组件如何在 Kubernetes 生态系统中配合工作:

图 3.5 – Crossplane 的所有组件

图 3.5 – Crossplane 的所有组件

让我们回顾一下迄今为止使用过的一些缩写,今后我们将继续使用它们:

  • CRD 代表 自定义资源定义,它是一种扩展 Kubernetes API 以支持新资源类型的方法。

  • CR 是通过 CRD 定义的资源。CR 代表 自定义资源

  • XRM 代表 跨平面资源模型,是 Kubernetes 资源模型 的扩展。它是 Crossplane 制定的一套规则,用于构建 XR 或 MR。

  • MRs 仅仅是 托管资源,它是 CRD 和自定义控制器与外部基础设施资源一对一映射的组合。

  • XRD 代表 组合资源定义,它是构建自定义基础设施 API 的定义,位于 Crossplane 之上。

  • XR 代表 组合资源,它表示使用 Crossplane 构建的自定义基础设施 API。

安装 Crossplane

我已经在我的 Macintosh 电脑上设置了一个本地 Kubernetes 集群。我们将在教程中将此集群作为 Crossplane 控制平面,从 Google Cloud Platform 中提供资源。为了跟随教程,我们假设你已经能够访问一个 Kubernetes 集群。如果你需要帮助设置本地 Kubernetes 集群,请参考 kind.sigs.k8s.io/。kind 是设置本地 Kubernetes 集群的最简单方式之一,但你可以使用任何 Kubernetes 集群设置进行教程操作。以下截图快速展示了集群信息、版本和节点详情:

图 3.6 – 集群信息、版本和节点详情

图 3.6 – 集群信息、版本和节点详情

虽然有几种方法可以在 Kubernetes 中安装 Crossplane,我们将通过 Helm 图表来安装它。确保你已经安装了 Helm。在 Macintosh 和 Windows 上,通过 brewchoco 包管理器安装 Helm 非常简单。以下命令可以在你的环境中安装 helm

# Install helm in mac
brew install helm
# Install helm in windows
choco install kubernetes-helm

有关更多安装选项,请访问 helm.sh/docs/intro/install/。对于 Crossplane 安装,我们需要至少 v1.16.0 的 Kubernetes 版本和至少 v3.0.0 的 Helm 版本。Crossplane 有 Master 和 Stable 两个版本。Master 版本包含最新的提交,但可能会有稳定性问题。Stable 版本是为社区使用准备的,建议用于生产环境。在本教程中,我们将使用 Crossplane 的 Stable 版本。Crossplane 的安装分为三步,如下所示:

  1. 创建一个新的目标命名空间 crossplane-system 用于安装。

  2. 将 Crossplane 仓库添加并更新到 helm 仓库列表中。

  3. 在目标命名空间中使用 helm 安装 Crossplane。

代码如下:

# Step 1: Create target namespace 
kubectl create namespace crossplane-system
# Step 2: Add crossplane stable repo to helm and update 
helm repo add crossplane-stable \ https://charts.crossplane.io/stable
helm repo update
# Step 3: Install Crossplane
helm install crossplane --namespace crossplane-system crossplane-stable/crossplane

Crossplane Helm 图表支持许多模板值,用于自定义配置选项。副本数就是其中一个例子,它指定在高可用性设置中要运行多少个 Crossplane Pod。可以在 Artifact Hub 上查看 Crossplane Helm 模板的所有可能配置选项。安装截图如下:

图 3.7 – Crossplane 安装

图 3.7 – Crossplane 安装

我们可以使用标准的 helm delete 命令来删除 Crossplane 安装:

# To remove Crossplane
helm delete crossplane --namespace crossplane-system

结果截图如下:

图 3.8 – Crossplane 安装健康状况

图 3.8 – Crossplane 安装健康状况

现在我们已经安装了 Crossplane,接下来的部分将教你如何安装和配置提供程序。

安装和配置提供程序

一旦我们在 Kubernetes 集群中安装了 Crossplane 核心组件,接下来的步骤是安装和配置 Crossplane 提供程序。在本教程中,我们将安装和配置 GCP 提供程序。我们可以通过三步过程来完成:

  1. 设置云账户

  2. 安装提供程序

  3. 配置提供程序

我们将在接下来的部分中通过逐步指南详细讲解这些方面。

设置云账户

我们需要拥有一个启用了项目和计费设置的 Google Cloud 账户。Google Cloud 为新用户提供 300 美元的信用额度,供用户学习和实验,最多 3 个月,前提是你拥有信用卡。这对于我们学习 Crossplane 和其他基础设施自动化概念来说绰绰有余。我们需要做的就是登录 Google Cloud 账户,填写表单并开始免费试用。接下来的步骤是为我们实验 Crossplane 创建一个单独的项目空间。你可以通过点击 GCP 控制台顶部栏的项目下拉菜单,然后点击 NEW PROJECT 来创建一个新项目,如下图所示:

图 3.9 – 创建一个新的 GCP 项目

图 3.9 – 创建一个新的 GCP 项目

一旦我们拥有 GCP 帐户、免费额度和项目启动,下一步就是启用所有所需的云 API 访问。我们将使用gcloud CLI 来完成此操作。请从cloud.google.com/sdk/docs/install下载 CLI 并按照以下步骤操作:

  1. 下载后安装gcloud CLI:

    ./google-cloud-sdk/install.sh
    
  2. 使用云凭证和项目初始化 SDK:

    gcloud init
    

gcloud init将引导我们通过浏览器进行身份验证。一旦我们成功提供必要的访问权限,CLI 会要求我们选择项目。现在,我们应该能够使用以下命令启用所需的云服务:

# Enable Kubernetes APIs , CloudSQL APIs, Network APIs and Compute APIs
gcloud services enable container.googleapis.com
gcloud services enable sqladmin.googleapis.com
gcloud services enable compute.googleapis.com
gcloud services enable servicenetworking.googleapis.com

结果截图如下:

图 3.10 – 启用云 API

图 3.10 – 启用云 API

完成启用项目的 API 访问后,下一步是创建服务帐户并提取设置 GCP Crossplane 提供程序所需的凭证。它涉及几个步骤:

  1. 查找项目 ID,以便在接下来的必要命令中使用:

    gcloud projects list --format='value(project_id)'
    
  2. 创建服务帐户,获取名称,并在服务帐户上启用所需的角色。请注意,以下所有命令中的crossplane-330620是我的 Google 项目 ID,您需要将其替换为您的 Google 项目 ID。使用之前的命令来查看您的项目 ID 列表。类似地,crossplane-service-account@crossplane-330620.iam.gserviceaccount.com是我在 GCP 环境中创建的服务帐户名称。将其替换为您的服务帐户名称。您可以通过执行以下代码块中的第二个命令来列出服务帐户,以获取您的服务帐户名称:

    # Create service account
    gcloud iam service-accounts create crossplane-service-account --display-name "crossplane service account" --project=crossplane-330620
    # Get the name of the service account
    gcloud iam service-accounts list --filter="email ~ ^crossplane-service-account" --format='value(email)'
    # Add required IAM role to the service account
    gcloud projects add-iam-policy-binding crossplane-330620 --member "serviceAccount:crossplane-service-account@crossplane-330620.iam.gserviceaccount.com" --role="roles/iam.serviceAccountUser"
    gcloud projects add-iam-policy-binding crossplane-330620 --member "serviceAccount:crossplane-service-account@crossplane-330620.iam.gserviceaccount.com" --role="roles/cloudsql.admin"
    gcloud projects add-iam-policy-binding crossplane-330620 --member "serviceAccount:crossplane-service-account@crossplane-330620.iam.gserviceaccount.com" --role="roles/container.admin"
    gcloud projects add-iam-policy-binding crossplane-330620 --member "serviceAccount:crossplane-service-account@crossplane-330620.iam.gserviceaccount.com" --role="roles/redis.admin"
    gcloud projects add-iam-policy-binding crossplane-330620 --member "serviceAccount:crossplane-service-account@crossplane-330620.iam.gserviceaccount.com" --role="roles/compute.networkAdmin"
    gcloud projects add-iam-policy-binding crossplane-330620 --member "serviceAccount:crossplane-service-account@crossplane-330620.iam.gserviceaccount.com" --role="roles/storage.admin"
    
  3. 使用以下第一个命令提取服务帐户文件:

    gcloud iam service-accounts keys create crossplane-service-account.json --iam-account crossplane-service-account@crossplane-330620.iam.gserviceaccount.com
    
  4. 最后,使用服务帐户文件创建一个 Kubernetes Secret。该 Secret 将在我们进行提供程序配置时被引用,您将在下一部分看到:

    kubectl create secret generic gcp-account -n crossplane-system --from-file=creds=./crossplane-service-account.json 
    

安装提供程序

现在让我们安装 GCP Crossplane 提供程序。我们需要运行以下提供程序资源 YAML,并使用最新的 GCP 提供程序容器镜像版本。当前可用的稳定版本是 v0.18.0。执行时请根据需要更新版本:

apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-gcp
spec:
  package: crossplane/provider-gcp:v0.18.0

应用 YAML 并验证提供程序容器是否正在运行:

kubectl apply -f Provider.yaml
kubectl get all -n crossplane-system

结果截图如下:

图 3.11 – 运行 GCP 提供程序

图 3.11 – 运行 GCP 提供程序

配置提供程序

最后一步是使用ProviderConfig Crossplane API 设置提供程序配置以进行身份验证。准备凭证对于每个提供程序可能略有不同,具体取决于基础设施供应商的身份验证构造。对于 GCP,它将是服务帐户;对于 Azure,它是服务主体;对于 AWS,它是 IAM,依此类推。以下 YAML 将为 GCP 提供程序配置凭证:

apiVersion: gcp.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
  name: gcp-credentials-project-1 
spec:
  projectID: crossplane-330620
  credentials:
    source: Secret
    secretRef:
      namespace: crossplane-system
      name: gcp-account
      key: service-account

GCP 提供者配置的几个关键方面包括项目 ID 引用和服务帐户凭据。我们需要在projectID:下提供我们的 GCP 项目 ID。对于其他基础设施提供者配置(如 AWS),我们没有这个配置元素。请注意,前面提到的提供者配置引用了 Kubernetes Secrets。应用 YAML 文件并通过以下命令验证提供者配置:

kubectl apply -f providerconfig.yaml
kubectl get providerconfig

结果截图如下:

图 3.12 – GCP 提供者配置

图 3.12 – GCP 提供者配置

通过这些步骤,我们完成了 Crossplane 安装、GCP 提供者设置和配置。其他提供者设置也大致与 GCP 相似。现在,环境已准备好通过 MR 配置 GCP 资源,或者我们可以开始在 MR 之上编写 XR API。

多提供者配置

我们可以配置多个 ProviderConfig 对应一个提供者。就像拥有多个凭据或云账户来访问云平台,并根据给定的上下文选择合适的凭据一样。在使用 MR 或 XR 配置基础设施资源时,我们通过 providerConfigRef: 指定在给定上下文中使用哪个 ProviderConfig。如果在 MR 或 XR 中没有提供 providerConfigRef:,Crossplane 将引用名为 default 的 ProviderConfig。通过这种方式将基础设施资源组织在不同凭据下,可以帮助我们管理基础设施账单,并根据组织结构以组的方式维护资源。以下 YAML 文件将使用前面章节中创建的名为 gcp-credentials-project-1 的提供者配置,从 GCP 配置 POSTGRES:

apiVersion: database.gcp.crossplane.io/v1beta1
kind: CloudSQLInstance
metadata:
  name: my-GCP-DB
spec:
  # Reference to use a specific provider config 
  providerConfigRef:
    name: gcp-credentials-project-1
  forProvider:
    databaseVersion: POSTGRES_9_6
    region: asia-south2
    settings:
      tier: db-n1-standard-1
      dataDiskSizeGb: 10

以下图示表示多个团队使用不同的提供者配置:

图 3.13 – 多提供者配置

图 3.13 – 多提供者配置

POSTGRES 配置示例

现在是时候进行实际的配置体验了。我们将使用 GCP 中的 CloudSQLInstance,它是 GCP Crossplane 提供者中可用的 MR,来创建一个 POSTGRES 实例。通过 MR 或 XR 直接配置基础设施资源并不是一个好的策略。相反,我们应该使用通过 XR 创建的 claim 对象来进行配置。下一章将专门讨论这些主题。目前,我们使用 MR 进行资源配置,以便理解 Crossplane 的基本构建模块。在配置资源时,我们将使用以下属性:

  • 配置的资源名称应为 db-gcp

  • 配置的区域将是 us-central

  • 我们将请求 POSTGRES 版本 9.6 – POSTGRES_9_6

  • 数据磁盘的大小应该是 20 GB。

  • 数据库的 GCP 等级应该是 db-g1-small

  • gcp-credentials-project-1 提供者配置下创建资源。

  • 数据库凭证应放入 crossplane-system 命名空间,并使用名为 db-conn 的 Secret。

这些只是一些可能的参数。CloudSQLInstance 的完整 API 文档可以在 doc.crds.dev/github.com/crossplane/provider-gcp/database.gcp.crossplane.io/CloudSQLInstance/v1beta1@v0.18.0 查阅。请注意,有一些 API 参数是必填的,这些在 YAML 中是强制要求的。以下 YAML 代码将根据指定的属性配置 GCP 的 POSTGRES:

apiVersion: database.gcp.crossplane.io/v1beta1
kind: CloudSQLInstance
metadata:
  name: db-gcp
spec:
  providerConfigRef:
    name: gcp-credentials-project-1
  writeConnectionSecretToRef:
    namespace: crossplane-system
    name: db-conn
  forProvider:
    databaseVersion: POSTGRES_9_6
    region: us-central
    settings:
      tier: db-g1-small
      dataDiskSizeGb: 20

一旦应用 YAML 代码,你可以在 GCP 控制台中看到配置的资源。使用以下命令查看资源的状态。注意,初始时,我们会看到状态为待创建(PENDING_CREATE),随后会变为可运行(RUNNABLE)。另外,我们还可以看到数据库凭证已经出现在 Secrets 中:

图 3.14 – 数据库配置

图 3.14 – 数据库配置

假设我们在 GCP 控制台查看数据库详细信息,并更改数据库机器类型,Crossplane 会将其恢复到 YAML 代码中提到的原始级别。尝试更改并查看状态变化很有趣。初始时,当我们更改控制台设置时,系统会进入维护模式,然后 Crossplane 会发现有更改,使资源处于不同步状态。接着,Crossplane 会将资源恢复到原始状态。请参考以下截图并查看每列中的值(STATESYNCEDREADY):

图 3.15 – 协调循环

图 3.15 – 协调循环

最后,资源将同步。我们可以根据删除策略清理配置的资源:

图 3.16 – 最终状态

图 3.16 – 最终状态

提示

如果你想在供应商基础设施中创建一个与 Crossplane 资源声明不同名称的资源,可以在元数据部分使用 crossplane.io/external-name:my-special-name 注解。

本书中讨论的所有示例都可以参考 github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane

总结

我们从理解 CRD 和自定义控制器开始,然后了解 Kubernetes 资源扩展点的概念,这些都是 Crossplane 的构建块。接着,我们讲解了各种 Crossplane 组件、它们的架构以及这些组件如何协同工作。最后,我们通过安装 Crossplane 及其 GCP 提供程序,并进行 Postgres 数据库配置的实验,进行了动手操作。现在,我们已经了解了 Crossplane 的完整工作流程,这也标志着本章的结束。在下一章中,我们将学习更高级的 Crossplane 概念。

第四章:第四章: 使用 Crossplane 组合基础设施

组合是 Crossplane 的一个强大构造,使其在同类产品中独树一帜,如 Open Service Broker API 或 AWS Kubernetes 控制器。以无代码的方式组织基础设施方案的能力,完美契合了组织在构建精益平台团队时对敏捷的期望。本章将带我们从头到尾学习组合。我们将从详细了解 Crossplane 复合资源XR)如何工作开始,然后一步步深入了解如何构建一个 XR。

本章将覆盖以下主题:

  • 感觉像一个 API 开发者

  • XR 如何工作?

  • XR 后配置

  • 预配置资源

  • 构建 XR

  • 故障排除

感觉像一个 API 开发者

传统上,基础设施工程师了解最深刻的基础设施配置选项和不同的基础设施设置模式。但他们可能没有构建 API 的经验。使用 Crossplane 构建基础设施平台将是这些传统方式的一种转变。现代基础设施平台开发者应该具备两方面的知识,即基础设施和 API 工程。作为平台开发者,构建基础设施 API 意味着实现以下方面:

  • 随着时间的推移,API 会不断演变。这包括引入新的 API、更新现有的 API 版本以及废弃旧的 API。

  • 为消费产品团队应用 API 跨领域关注点,如认证、授权、缓存和审计。

  • 在 API 中封装不同的基础设施策略。

  • 构建跨团队共享的可复用基础设施方案。例如,一些产品团队可能使用 MEAN 技术栈(MongoDB、Express.js、AngularJS 和 Node.js)开发应用程序。我们可能有兴趣为这个技术栈开发作为模板 API 的基础设施配置。

  • 构建跨团队共享的基础设施。例如,我们可能希望提供一个虚拟私人网络,供不同的基础设施资源共享。

  • 考虑到不同的基础设施方案和共享基础设施,达成并不断演进正确的 API 边界。我们必须进行权衡分析,以处理基础设施方案和共享基础设施之间的冲突。

基础设施方案和共享基础设施是 API 边界上下文权衡中的重要因素。我们将在接下来的章节中详细探讨这一点。下图展示了 API 基础设施工程的细节:

图 4.1 – API 基础设施工程

图 4.1 – API 基础设施工程

我们正在从这些方面入手,以便尽可能最佳地理解 XR 架构。Crossplane 复合体的每个元素都是从 API 角度设计的,以覆盖基础设施工程实践。

提示

我们可以利用微服务架构模式的经验来定义基础设施 API 边界。没有完美的边界,每种设计方案都会有优缺点。在第六章《更多 Crossplane 模式》中,我们可以寻找将微服务与 Crossplane 基础设施平台结合的方法。

XR 是如何工作的?

XR 可以在后台做两件事。第一个目的是将相关的托管资源MRs)组合成一个堆栈,并构建可重用的基础设施模板 API。当我们这样做时,可能会应用不同的模式,例如应用之间共享资源或用于更快速提供的缓存基础设施。第二个目的是在抽象了所有组织策略后,只向应用团队公开有限的基础设施 API 属性。随着本章的深入,我们将详细探讨实现这些方面的细节。以下是 XR 中的关键组件:

  • 复合资源定义XRD

  • 组成

  • 声明

让我们开始查看每个组件的目的以及它们如何相互作用。

XRD

CompositeResourceDefinition 是用于定义 XRD 的 Crossplane 配置元素。创建此配置就像编写 OpenAPI 规范或 Swagger API 定义。以下是 CompositeResourceDefinition 配置 YAML 的关键方面:

  • 新 API 的 kind 属性。请注意,使用 X 作为 XR 名称的前缀是标准做法。

  • API 组: 这将帮助我们逻辑地分组 API,避免命名冲突,并管理授权。

  • <resource plural name>.<API group>

  • versions 元素是一个数组,可以容纳同一 XR API 的多个版本配置。通常,在我们开始时,只会有一个版本。随着时间的推移,我们将随着变化增加 API 版本。旧版本可能会变成技术负担,需要在之后弃用。

  • served 元素将指示给定版本的 XR API 是否已经提供服务。referenceable 标志将决定我们是否可以为给定的 API 版本定义实现。我们可以在第五章《探索基础设施平台模式》中更深入地研究版本管理和这些属性。现在,当我们在 XRD 中定义了唯一版本时,两个标志都将为 true

  • Schema: 这是每个版本下的一个部分,涵盖了实际的 OpenAPI 规范。它包括诸如参数列表、数据类型和必需参数等详细信息。

  • 连接密钥: 这将包含需要在资源提供后创建并填充到 Kubernetes Secrets 中的密钥列表。

  • DefaultCompositionRefEnforcedCompositionRef 是提供这种灵活性的几个属性。

  • Claim 名称:这些是可选参数,用于为指定名称的 XR API 创建一个代理 API。应用 claim 对象的创建、删除和更新操作将创建、删除和更新底层 XR。Claims 是 Crossplane 中的关键组件,我们将在本章稍后的专门主题中讨论它。

XRD 仅仅是一个有特定意见的 自定义资源定义CRD),许多配置部分看起来像 CRD。这些只是一些可能的参数。随着我们继续阅读本书,我们还会看到更多参数。完整的 API 文档可以在 doc.crds.dev/github.com/crossplane/crossplane 上找到。

提示

我们正在查看 Crossplane 文档的 v1.5.1 版本,这是本章撰写时的最新版本。请参考阅读时的最新版本以获取更准确的详细信息。

请注意,之前讨论的一些配置并不包含在下面的 YAML 中,例如 DefaultCompositionRefConnectionSecretKeys。如果未指定,这些配置会由 Crossplane 注入并采用默认行为。请参考以下 YAML 示例:

apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  #'<plural>.<group>'
  name: xclouddbs.book.imarunrk.com
spec:
  # API group
  group: book.imarunrk.com
  # Singular name and plural name.
  names:
    kind: xclouddb
    plural: xclouddbs
  # Optional parameter to create namespace proxy claim API
  claimNames:
    kind: Clouddb
    plural: Clouddbs
  # Start from alpha to beta to production to deprecated.
  versions:
  - name: v1
    # Is the specific version actively served
    served: true
    # Can the version be referenced from an API implementation
    referenceable: true
    # OpenAPI schema 
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              parameters:
                type: object
                properties:
                  storageSize:
                    type: integer
                required:
                - storageSize
            required:
            - parameters

在完成 API 规范之后,下一步是构建 API 实现。Composition 是 Crossplane 用于提供 API 实现的构造。

Composition

Composition 将一个或多个 MRs 与 XR API 关联。当我们创建、更新和删除 XR 时,相同的操作将应用于所有关联的 MRs。我们可以将 XRD 看作是 CRD,而 composition 则是自定义控制器实现。下面的图示展示了 XR、XRD、composition 和 MRs 之间的关系:

图 4.2 – XRM、composition 和 XR

图 4.2 – XRM、composition 和 XR

提示

本书中我们提到 XR 有两种上下文。我们可以使用 XR 来指代我们正在构建的新基础设施 API。并且,composition 资源列表可以同时包含一个 MR 和一个现有的 XR。我们也会在那个上下文中提到 XR。请看 图 4.2,在其中 XR 被提及于两个维度。

让我们看一下 composition 配置中的一些关键元素:

  • kindapiVersion 是在 CompositeTypeRef 下定义的两个配置元素。kind 用于指定 XR 名称,apiVersion 将引用在 XRD 中定义的特定版本。映射的版本应配置为在 XRD 中可以引用。

  • WriteConnectionSecretsToNamespace:此项将指定用于存储连接密钥的命名空间。

  • 资源:这一部分是一个数组,包含创建、更新和删除 XR 时需要创建、更新和删除的 MR 列表。我们甚至可以在这一部分定义另一个 XR。这是一个必填部分,我们必须定义至少一个资源,可以是 MR 或 XR。Base 是每个资源下的关键对象,保存 XR/MR 配置模板。

  • FromCompositeFieldPath 是默认类型,使用最为频繁。它有助于将 XR 中的属性补丁到组合资源基础模板中,即将用户输入传入组合资源。FromFieldPathToFieldPath 是执行实际补丁操作的子属性。有一个补丁类型叫做 ToCompositeFieldPath,它的作用是执行与 FromCompositeFieldPath 相反的操作。我们可以使用这个补丁类型将字段从资源复制回 XR。CombineFromComposite 补丁类型在组合多个属性时最为适用。

  • convert用于类型转换,math用于数学运算,map用于键值操作。我们可以在给定的补丁中列出转换函数,它们按配置中指定的顺序执行。补丁和转换都是关键模式。在本书中,我们将查看不同的补丁和转换配置示例。

  • Policy:这些配置在每个补丁下,决定补丁行为。我们可以强制要求补丁路径的存在,因为默认行为是在字段缺失时跳过补丁。同时,我们可以配置在对对象执行补丁时的合并行为。

  • ConnectionDetails:这些是在每个资源下指定的,将保存我们希望回传到 XR 的密钥列表。

  • ReadinessChecks:这些允许我们定义任何自定义的就绪检查逻辑。如果没有提供该部分,则默认行为是在所有组合资源就绪时将 XR 状态标记为已就绪。

  • spec.parameters.storageSizespec.versions[0].name

我们已覆盖了组合配置中大部分可用的选项。请查看 Crossplane 文档,了解完整的选项列表。下图展示了组合配置选项及其相互关系:

图 4.3 – 组合配置

](https://github.com/OpenDocCN/freelearn-devops-pt6-zh/raw/master/docs/e2e-auto-k8s-xpln/img/B17830_04_03.jpg)

图 4.3 – 组合配置

以下是一个示例的组合配置 YAML:

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: xclouddb-composition
spec:
  # Link Composition to a specific XR and version
  compositeTypeRef:
    apiVersion: xclouddb.book.imarunrk.com/v1
    kind: Xclouddb
  # Connection secrets namespace
  writeConnectionSecretsToNamespace: crossplane-system
  # List of composed MRs or XRs.
  resources:
  - name: clouddbInstance
    # Resource base template
    base:
      apiVersion: database.gcp.crossplane.io/v1beta1
      kind: CloudSQLInstance
      spec:
        forProvider:
          databaseVersion: POSTGRES_9_6
          region: us-central
          settings:
            tier: db-g1-small
            dataDiskSizeGb: 20
    # Resource patches 
    patches:
    - type: FromCompositeFieldPath
      fromFieldPath: spec.parameters.storageSize
      toFieldPath: spec.forProvider.settings.dataDiskSizeGb
    # Resource secrets
    connectionDetails:
    - name: hostname
      fromConnectionSecretKey: hostname

我们将在构建 XR一节中介绍一个包含更多配置元素的示例。一个 XRD 版本可以有多个组合,也就是 XRD 版本与组合之间的一对多关系。它为我们的基础设施 API 提供了多态行为,以便根据上下文进行工作。例如,我们可以为生产和暂存环境定义不同的组合。XR 中定义的 CompositionRef 属性可以引用特定的组合。除了 CompositionRef,我们还可以使用 CompositionSelector 基于标签匹配组合。

图 4.4 – XR 和组合关系

图 4.4 – XR 和组合关系

在下一节中,我们将查看XR 声明,也称为声明

声明

声明是对 XR 的代理 API,通过在 XRD 配置中提供声明名称属性来创建。一般做法是提供 XR 的精确名称,并去掉最前面的 X。在上述示例中,xclouddb 是 XR 名称,Clouddb 是声明名称,但遵循这种命名约定并非强制要求。声明与 XR 非常相似,可能会让我们认为它是一个不必要的代理层。然而,声明在许多方面都是有帮助的,例如:

  • XRs 是集群级别的资源,而声明是命名空间级别的。这使我们能够创建命名空间级别的授权。例如,我们可以根据不同产品团队的命名空间所有权,分配不同的权限。

  • 我们可以将某些 XR 仅作为集群级别的平台团队专用私有 API。例如,平台团队可能不希望公开创建虚拟专用网络的 XR API。

  • 在命名空间级别管理某些资源并不理想,因为这些资源是多个团队共享的,无法适应上下文。

  • 我们还可以使用这种模式来支持基础设施的预配置。声明可以通过引用已经预配置的 XR 基础设施,保持较低的配置时间。这与缓存非常相似。

下图表示声明、XR、XRD、组合和 MR 之间的关系,提供了一个端到端的视图,展示了整个概念的工作方式:

图 4.5 – 组合是如何工作的?

图 4.5 – 组合是如何工作的?

以下是示例声明和 XR 的 YAML。声明 YAML 如下:

apiVersion: book.imarunrk.com/v1
# Kind name matches the singular claim name in the XRD
kind: Clouddb
metadata:
  name: cloud-db
spec:
  # Parameters to be mapped and patched in the composition 
  parameters:
    storageSize: 20
  # Name of the composition to be used
  compositionRef:
    name: xclouddb-composition
  writeConnectionSecretToRef:
    namespace: crossplane-system
    name: db-conn

声明 YAML 中不包含命名空间部分。因此,它将在默认命名空间中创建资源,这是 Kubernetes 的标准。与上述声明 YAML 等效的 XR YAML 如下:

apiVersion: book.imarunrk.com/v1
kind: XClouddb
metadata:
  name: cloud-db
spec:
  parameters:
    storageSize: 20
  compositionRef:
    name: xclouddb-composition
  writeConnectionSecretToRef:
    namespace: crossplane-system
    name: db-conn

请注意,XR 总是以集群级别创建,元数据下的命名空间配置不适用。我们可以在构建 XR一节中查看更详细的声明和 XR 配置。让我们从后置配置的角度,探索更多 XR、XRD、组合和声明配置。

XR 的后置配置

在对声明或 XR 资源执行 CRUD 操作后,以下是完成 API 请求的一些关键方面:

  • 就绪检查

  • 修补状态

  • 将凭据传播回来

让我们从学习就绪检查开始。

就绪检查

当所有底层资源都准备就绪时,XR 状态将默认就绪。组合中的每个资源元素都可以定义其自定义的就绪逻辑。让我们看一些自定义就绪检查配置。如果你想将某个组合资源的状态字段与预定义的字符串匹配,使用 MatchStringMatchString 的示例配置如下:

- type: MatchString
  fieldPath: status.atProvider.state
  matchString: "Online"

MatchInteger 在匹配两个整数时将执行类似的功能。以下示例配置将检查 state 属性与整数 1

 - type: MatchInteger
  fieldPath: status.atProvider.state
  matchInteger: 1

使用 None 类型,将资源一旦可用就认为就绪:

- type: None

使用 NonEmpty 使资源在指定字段中存在某个值时立即就绪。以下示例将使就绪状态为真,只要在指定的字段路径下存在某个值:

- type: NonEmpty
  fieldPath: status.atProvider.state

在下一节中,我们将查看在资源配置后修补状态属性的示例。请注意,fieldPath 属于 status 属性。这些是由 MR 在资源配置过程中根据从云提供商返回的值填写的属性。

修补状态

ToCompositeFieldPath 是一种补丁类型,用于将特定组合资源中的任何属性复制回 XR。通常,我们用它来复制状态字段。我们可以将这些视为定义 API 响应的一种方式。虽然已有一组默认的状态字段,修补的字段是自定义定义的,用于增强我们的调试、监控和审计活动。首先,我们需要在 XRD 中将状态字段定义为 openAPIV3Schema 的一部分,以便在 XR 中使新状态字段可用。下一步是在特定的组合资源下定义补丁。以下补丁将把 CloudSQLInstance 当前的磁盘大小复制到 XR:

- type: ToCompositeFieldPath
  fromFieldPath: status.atProvider.currentDiskSize
  toFieldPath: status.dbDiskSize

如果我们需要复制多个字段的组合,可以使用 CombineToComposite 补丁类型。

将凭据传播回来

我们可以看到与连接密钥相关的配置是 XRD、XR、声明和组合的一部分。我们必须理解这些配置之间的关系,以便正确配置并使其正常工作:

  • 使用 ConnectionSecretKeys 配置在 XRD 中定义连接密钥列表。

  • 配置组合资源以定义如何填充 XRD 中定义的连接密钥。连接详情的配置可以有不同的类型。当从现有的密钥复制秘密时,FromConnectionSecretKey 类型是正确的。我们有 FromFieldPath 类型用于从某个组合资源字段中复制连接详情。

  • 声明或 XR 应该使用WriteConnectionSecretToRef配置来保存机密。

以下图示有助于创建这些配置的思维导图:

图 4.6 – 传播机密

](https://github.com/OpenDocCN/freelearn-devops-pt6-zh/raw/master/docs/e2e-auto-k8s-xpln/img/B17830_04_06.jpg)

图 4.6 – 传播机密

本节介绍了在资源配置后我们可以与组合一起使用的不同模式。这就像定制 API 响应。现在我们可以看看重用现有资源的实用性。

预配置资源

在某些使用场景下,我们可能不会创建新的外部资源,而是会重用现有的已配置资源。本节将介绍两个此类使用场景。第一个使用场景是当我们决定缓存已组合的资源时,因为新的资源配置可能需要较长时间才能完成。平台团队可以配置一个 XR 并将资源保持在资源池中。然后,产品团队可以通过在声明 YAML 的 spec 下添加ResourceRef配置来申请这些资源。在这种模式下,我们应确保新声明的属性与现有预配置 XR 中的属性匹配。如果某些属性不同,Crossplane 将尝试更新 XR 规范,以匹配声明中提到的内容。

第二个使用场景是将现有资源从外部提供者导入到 Crossplane 中。crossplane.io/external-name注释可以帮助实现这一点。Crossplane 将查找配置中提到的名称的现有资源。在声明中提到的外部名称配置将自动传播到 XR 中。然而,我们有责任将此配置补丁到组合资源中。以下是一个示例 MR YAML,其中我们将现有的名为alpha-beta-vpc的 VPC 引入:

apiVersion: compute.gcp.crossplane.io/v1beta1
kind: Network
metadata:
  name: alpha-beta-vpc-crossplane-ref
  annotations:
    # Annotation to provide existing resource named
    crossplane.io/external-name: alpha-beta-vpc
spec:
  providerConfigRef:
    name: gcp-credentials-project-1
  # Provide the required parameters same as external resource.
  forProvider:
    autoCreateSubnetworks: true

一旦应用了 YAML 文件,您将看到它已准备好在 Crossplane 中使用。以下截图可以看到这一点:

图 4.7 – VPC 引用状态

](https://github.com/OpenDocCN/freelearn-devops-pt6-zh/raw/master/docs/e2e-auto-k8s-xpln/img/B17830_04_07.jpg)

图 4.7 – VPC 引用状态

请注意,alpha-beta-vpc VPC 是我们在 GCP 中手动创建的现有 VPC。我们在这里的目标是将手动资源映射到一个 Claim。本节介绍了我们如何使用预配置资源与 XR/声明结合的不同方式。接下来的部分将是从零开始构建 XR 的动手实践。

构建 XR

现在是时候开始动手构建一个从零开始的 XR 了。我们将从高层次编写基础设施 API 需求开始,然后提供 XRD 的 API 规范,最后通过组合提供实现。我们将在本章中讨论的大部分配置都将涵盖在 API 需求中。

基础设施 API 需求

我们将开发一个 API,从 Google Cloud 中配置一个数据库。以下是合规性、架构和产品团队的需求:

  • us-central区域,以遵守政府的数据存储规定。

  • 架构政策:我们应该有两个数据库层级。对于小型配置,磁盘大小应为 20 GB,对于大型配置,应为 40 GB。

  • db-g1-small,和db-n1-standard-1用于大型层级。

  • 产品团队:我们应该有选择 Postgres 和 MySQL 的选项。

  • SMALLBIG)。

  • 平台团队:将数据库创建的区域补丁回 XR/声明状态字段中,以满足监控要求。

下一步是编写 XRD 配置 YAML。

创建 XRD

在定义 XRD 的 API 规范时,以下配置应编码到 YAML 中:

  • 使用alpha-beta.imarunrk.com作为 API 组,组织所有 alpha 和 beta 团队的 API。

  • 我们将提供 XR 名称为XGCPdb,声明名称为GCPdb

  • 我们将从新的 API 版本 v1 开始。

  • 创建size作为输入参数,并将zone作为响应状态属性。

由于示例 XRD 过大,这里仅介绍模式定义。请参考完整的 XRD 文件:github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane/blob/main/Chapter04/Hand-on-examples/Build-an-XR/xrd.yaml。节省时间,我们来看看模式:

    schema:
      openAPIV3Schema:
        type: object
        properties:
          # Spec – defines the API input
          spec:
            type: object
            properties:
              parameters:
                type: object
                properties:
                  # Size will be a user input
                  size:
                    type: string
                required:
                - size
            required:
            - parameters
          # status – the additional API output parameter
          status:
            type: object
            # Recourse zone - status patch parameter.
            properties:
              zone:
                description: DB zone.
                type: string

从 GitHub 保存 YAML 并使用kubectl apply -f xrd.yaml应用到集群中。请参阅以下截图,显示了成功创建 XRD:

图 4.8 – XRD 创建

图 4.8 – XRD 创建

请注意截图中的ESTABLISHEDOFFERED标志为True。这意味着 XRD 已正确创建。如果这些状态不是True,请使用 kubectl 描述 XRD 的详细信息并查找错误。

提供实施方案

下一步是提供 API 实现。作为实现的一部分,我们应该提供一个组合配置。我们将创建两个组合,一个用于 Postgres,另一个用于 MySQL。这将是多态行为实现的示例。以下是在构建组合 YAML 时需要记住的步骤:

  1. 使用CompositeTypeRef配置引用 v1 XRD API 版本。

  2. 在资源基础下定义CloudSQLInstance配置。

  3. 硬编码区域为us-central1以满足合规性要求。

  4. 数据库层级和磁盘大小将保持默认值,但补丁配置将使用FromCompositeFieldPath补丁类型覆盖它们。

  5. 使用Map转换将SMALL层级大小转换为db-g1-small机器层级。使用MapConvert转换将SMALL层级大小映射到 20 GB 磁盘大小。

  6. 类似的映射将应用于BIG配置。

  7. 从 MR 状态将 GceZone 属性补丁到 XR/声明以进行监控。我们可以使用 ToCompositeFieldPath 补丁类型来实现这一点。

  8. 提供 MR 连接密钥与 XR/声明密钥之间的映射,使用 ConnectionDetails 配置。

我们将分四个部分查看 Postgres 组成示例。组成的 XRD 和资源定义部分将类似于以下配置:

spec:
  # Refer to an XRD API version
  compositeTypeRef:
    apiVersion: alpha-beta.imarunrk.com/v1
    kind: XGCPdb
  writeConnectionSecretsToNamespace: crossplane-system
  resources:
    # Provide configuration for Postgres resource
  - name: cloudsqlinstance
    base:
      apiVersion: database.gcp.crossplane.io/v1beta1
      kind: CloudSQLInstance
      spec:
        # reference to GCP credentials
        providerConfigRef:
          name: gcp-credentials-project-1
        forProvider:
          databaseVersion: POSTGRES_9_6
          # Compliance Policy
          region: us-central1
          settings:
            # These are default values 
            # Architecture policies will be a patch
            tier: db-g1-small
            dataDiskSizeGb: 20

阅读代码片段之间的注释,以详细理解概念。以下配置使用映射转换来补丁虚拟机层级:

- type: FromCompositeFieldPath
  fromFieldPath: spec.parameters.size
  toFieldPath: spec.forProvider.settings.tier
      # Use map transform
       # If the from-field value is BIG, then 
       # the mapped to-field value is db-n1-standard-1
       transforms:
       - type: map
         map:
           BIG: db-n1-standard-1
           SMALL: db-g1-small
       policy:
         # return error if there is no field.
         fromFieldPath: Required

接下来,我们可以查看配置以补丁磁盘大小。补丁将包含两个转换操作。第一个操作是映射磁盘大小,第二个操作是将映射的字符串转换为整数:

- type: FromCompositeFieldPath
  fromFieldPath: spec.parameters.size
  toFieldPath: spec.forProvider.settings.dataDiskSizeGb
  # If the from-field value is BIG, then 
  # the mapped to-field value is '40;
  # Apply the second transform to convert '40' to int
  transforms:
  - type: map
    map:
      BIG: "40"
      SMALL: "20"
  - type: convert
    convert:
      toType: int
  policy:
    # return error if there is no field.
    fromFieldPath: Required

最后,以下补丁将资源区域添加到 API 响应中:

# Patch zone information back to the XR status
# No transformation or policy required
- type: ToCompositeFieldPath
  fromFieldPath: status.atProvider.gceZone
  toFieldPath: status.zone

MySQL 的组成配置与前面的配置相同,除了两个变化。我们应该在元数据中更改组成的名称,在资源定义中,我们应将数据库版本更改为 MYSQL_5_7。我们也可以通过在 XR 中添加一个额外的参数来实现这一点。当差异如此之小时,构建两个不同的组成并不合理。我们可以将差异作为 XR 中的一个参数来捕捉。我们正在构建两个组成作为示例。所有组成示例和接下来的声明示例可以参考 github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane/tree/main/Chapter04/Hand-on-examples/Build-an-XR

请参考以下截图,显示了成功创建两个组成:

图 4.9 – 组成创建完成

图 4.9 – 组成创建完成

最后的步骤是使用声明 API 创建数据库资源。

使用声明配置资源

最后,我们可以开始使用 XR 或声明来配置 GCP 数据库。CompositionRef 配置将指定使用哪个组成实现。请注意,声明是命名空间资源,我们在这里的 alpha 命名空间中进行配置。以下是 MySQL 数据库的一个示例声明 YAML:

apiVersion: alpha-beta.imarunrk.com/v1
kind: GCPdb
metadata:
  # Claims in alpha namespace
  namespace: alpha
  name: mysql-db
spec:
  # Refer to the mysql composition
  compositionRef:
    name: mysql
  # save connection details as secret - db-conn2
  writeConnectionSecretToRef:
    name: db-conn2
  parameters:
    size: SMALL

Postgres YAML 也将类似,只是有一些小的变化。请参考以下截图,显示成功创建数据库:

图 4.10 – 声明状态

图 4.10 – 声明状态

请注意,区域信息作为声明状态的一部分提供:

图 4.11 – 区域信息

图 4.11 – 区域信息

这标志着构建 XR 的旅程结束。接下来,我们将查看一些故障排除技巧。

故障排除

如果我们遇到基础设施 API 的问题,以下提示可能帮助我们以最佳方式调试问题:

  • 状态属性和事件是调试问题的重要元素。可以通过在给定资源上运行kubectl describe命令查看这些细节。

  • 当我们开始寻找问题时,我们采取自上而下的方法。这是因为 Crossplane 遵循与 Kubernetes 相同的约定,将错误紧密绑定到发生错误的资源上。

  • 调试顺序将是声明,然后是XR,接着是每个组成资源。我们应从声明的对象开始。如果我们无法定位问题,就深入查看 XR,然后是组成资源。

  • 来自声明描述的spec.resourceRef可以帮助我们识别 XR 名称。同样,使用该属性也可以从 XR 中找到组成资源。

在组成的资源配置中故意犯一个错误,以体验调试过程。调试问题时你会学到更多。这部分内容就是我们的故障排除部分。接下来,在进入下一章之前,我们将回顾本章的总结。

总结

在本章中,我们涵盖了 Crossplane 的一个关键方面——XR。我们从理解 XR 的工作原理和配置 XR 开始。最重要的是,我们通过动手实践,完整构建了一个全新的基础设施 API。本章还介绍了一些高级 XR 配置模式以及在遇到问题时的调试方法。这将成为我们下一章学习的基础知识。

下一章将涵盖不同的高级基础设施平台模式。

第五章:第五章:探索基础设施平台模式

使用 Crossplane 运行基础设施平台产品的成功,依赖于在需要时遵循一些原则和模式。本章将探讨一些关键的实践,同时在探索概念的过程中,我们还将学习一些调试技巧。在过去几章学习了 Crossplane 的基础知识之后,本章将是学习构建最先进的基础设施平台的关键模式的地方。你将学习构建健壮的 XR API 和轻松调试问题的几个关键方面。

本章涉及的主题如下:

  • 发展 API

  • 嵌套和多资源 XR

  • XRD 详细信息

  • 管理外部软件资源

发展 API

Crossplane 主要是一个基于 API 的基础设施自动化平台。随着业务需求和技术环境的发展,API 的变更是不可避免的。我们可以将这些变更分为三类:

  • API 实现变更

  • 非破坏性 API 合同变更

  • 破坏性 API 合同变更

让我们从 API 实现变更开始。

API 实现变更

这些变更仅限于 API 实现的细节,而不涉及合同的更改。换句话说,这些是对组合 YAML 的更改,这是一种由 XR 用于 API 实现的构造。CompositionRevision 是 Crossplane 的概念,它将与组合一起工作,以支持这些更改。如果在安装 Crossplane 时设置了 --enable-composition-revisions 标志,那么会为所有组合更新创建一个 CompositionRevision 对象。CompositionRevision 对象的名称会在每次递增时自动生成。组合是可变对象,可以永远变化,但单个 CompositionRevision 是不可变的。CompositionCompositionRevision 之间存在一对多关系。每次只能有一个 CompositionRevision 处于活动状态。最新的版本号将始终处于活动状态,排除以下场景。

提示

组合的每个配置状态都映射到一个单一的 CompositionRevision。假设我们在第 2 个版本,并且将组合配置更改为与第 1 个版本相同。这时不会创建新版本,而是第 1 个版本变为活动版本,第 2 个版本变为非活动版本。

在启用组合修订标志的 Crossplane 环境中,Crossplane 会自动向每个 XR/Claim 对象添加两个属性。以下是属性名称及其用途:

  • spec.compositionRevisionRef:这将包含与资源创建相关的 CompositionRevision 的名称。

  • spec.compositionUpdatePolicy:此属性将指示 XR/Claim 是否会自动迁移到新的可用 CompositionRevision。手动和自动是两种可能的值,默认值为自动。如果您希望覆盖默认行为,请在 XR/Claim 配置中添加此属性,并指明为手动。

以下图表表示 CompositionCompositionRevision 如何协同工作,持续推动基础设施 API 实现的演变:

图 5.1 – 演变中的组成

图 5.1 – 演变中的组成

要手动迁移组成,请在 XR/Claim 中更新 spec.compositionRevisionRef 配置,并使用最新的修订名称。这个特定设计使平台 API 创建者和消费者之间的职责分离。基础设施 API 创建者将更新组成,API 消费者可以选择其修订升级策略。如果在创建 XR/Claim 时想使用特定的组成修订,请在 spec.compositionRevisionRef 下明确提及修订名称。

让我们来看一些这样的更改示例:

  • 错误修复:假设我们将一个错误的属性映射到 XRD 状态字段。正确映射的范围仅限于相应组成修补部分的更改。

  • 政策更改而不更改合同:添加一个新的基础设施合规政策,以便在特定区域提供所有新资源。

  • 共享基础设施:朝着共享虚拟私有云VPC)的方向发展,而不是为所有新的 XR/Claim 请求动态创建新的 VPC。

组成修订标志默认情况下未启用。使用 --enable-composition-revisions 参数与 Crossplane pod 一起启用组成修订。以下 Helm 命令将设置/更新带有组成修订的 Crossplane 环境:

#Enable Composition revision in an existing environment
helm upgrade crossplane –namespace crossplane-system crossplane-stable/crossplane –set args='{--enable-composition-revisions}' 
#Enable Composition revision in a new Crossplane setup
helm install crossplane –namespace crossplane-system crossplane-stable/crossplane –set args='{--enable-composition-revisions}'

以下部分将通过一个示例查看组成修订。

动手体验组成修订

让我们通过一个动手实践来体验组成修订。此练习的目标如下:

  • 在启用组成修订的 Crossplane 环境中为 GCP MySQL 提供一个 XR API

  • 创建两个 MySQL 实例,采用自动化和手动组成修订策略

  • 更新组成,以更改数据库磁盘大小的计算方式

  • 验证采用自动修订策略的 MySQL 实例是否会自动迁移到最新的组成修订版本

  • 发现采用手动修订策略的 MySQL 实例无法迁移到最新的组成修订版本

  • 最后,将第二个 MySQL 实例手动迁移到最新的组成修订版本

让我们使用一个简单的 XRD 和组成来探索组成修订。以下是 XRD,其中仅包含一个参数,该参数用于设置 MySQL 磁盘大小:

apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: xmysqls.composition-revision.imarunrk.com
spec:
  group: composition-revision.imarunrk.com
  names:
    kind: XMySql
    plural: xmysqls
  claimNames:
    kind: MySql
    plural: mysqls
  versions:
  - name: v1
    served: true
    referenceable: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              parameters:
                type: object
                properties:
                  size:
                    type: integer
                required:
                - size
            required:
            - parameters

前述 XRD 的组成如下所示,它将size属性从 XR 补丁到 GCP CloudSQLInstance MR 中:

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: gcp-mysql
spec:
  compositeTypeRef:
    apiVersion: composition-revision.imarunrk.com/v1
    kind: XMySql
  resources:
  - name: cloudsqlinstance
    base:
      apiVersion: database.gcp.crossplane.io/v1beta1
      kind: CloudSQLInstance
      spec:
        providerConfigRef:
          name: gcp-credentials-project-1
        forProvider:
          region: us-central1
          databaseVersion: MYSQL_5_7
          settings:
            tier: db-g1-small
            dataDiskSizeGb: 40
    patches:
    - type: FromCompositeFieldPath
      fromFieldPath: spec.parameters.size
      toFieldPath: spec.forProvider.settings.dataDiskSizeGb

将这两个 YAML 应用到启用了组成修订的目标 Crossplane 集群。你将看到为该组成创建了CompositionRevision。执行以下命令查看给定组成的所有CompositionRevision

# List of revisions for Composition named gcp-mysql
kubectl get compositionrevision -l crossplane.io/composition-name=gcp-mysql

参考以下屏幕截图,其中为gcp-mysql组成创建了一个修订对象。请注意,修订 1 的current属性为 true。如果我们更新组成,这将发生变化:

图 5.2 – 组成修订列表

图 5.2 – 组成修订列表

现在,让我们通过 Claim API 配置两个 MySQL 实例。手动修订更新策略配置的示例如下所示。YAML 的自动修订版本与没有compositionUpdatePolicy参数的版本相同,后者默认进行自动修订更新:

apiVersion: composition-revision.imarunrk.com/v1
kind: MySql
metadata:
  namespace: alpha
  name: mysql-db-manual
spec:
  compositionUpdatePolicy: Manual
  compositionRef:
    name: gcp-mysql
  parameters:
    size: 10

你可以参考以下屏幕截图,其中包含了两个已引导的 MySQL 实例:

图 5.3 – MySQL 声明

图 5.3 – MySQL 声明

现在,更新组成补丁,使用转换函数在补丁之前将磁盘大小乘以四。更新组成的补丁部分将如下所示:

patches:
    - type: FromCompositeFieldPath
      fromFieldPath: spec.parameters.size
      toFieldPath: spec.forProvider.settings.dataDiskSizeGb
      transforms:
      - type: math
        math:
          multiply: 4

更新组成后,你将看到两个修订版本。只有最新的修订版本会将current标志设为true。另外,我们可以注意到,通过自动修订更新策略配置的 MySQL 会增加存储空间。以下屏幕截图总结了应用更新组成后的输出:

图 5.4 – 新的组成修订

图 5.4 – 新的组成修订

最后,我们可以通过在 XR/Claim 配置中添加spec.compositionRevisionRef属性来手动升级第二个 MySQL 实例。该字段将保存自动生成的组成修订名称。组成修订的实践示例可以在github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane/tree/main/Chapter05/Hand-on-examples/Composition-Revision找到。在接下来的部分中,我们将探索更改 XR API 契约的方法。

API 契约更改

API 实现细节只是 XR 更改可能演变的一个方向。XR 创建团队与消费团队之间高度互操作的 API 契约也需要随着时间的推移进行更改。契约的更改可以分为两类:

  • 非破坏性更改:XR API 将向后兼容,这意味着消费者要么不受更改影响,要么可以选择在其阶段采纳新的更改。

  • 破坏性变更:XR API 将不兼容旧版本。必须引入新 API 版本,并在适当的时候弃用旧 API 版本。所有旧的 API 用户应安全地迁移到新 API 版本。

让我们深入探讨非破坏性变更。

非破坏性变更

向 XRD 合同中添加一个或多个可选参数可以视为非破坏性变更。这是非破坏性的,因为旧的外部资源可以与新模式共存,因为新参数是可选的。请注意,移除 XRD 中现有的可选参数是破坏性变更,因为 Crossplane 在前端无法知道如何调和现有的已配置资源。一个简单的思考方式是,如果 Composition/CompositionRevision 能够处理旧资源和新配置资源的共存,那么 XRD 合同变更就是非破坏性的。选择磁盘大小的 MySQL XR 中的新可选参数就是一个非破坏性变更的例子。此变更将涉及合同变更和组合修订。让我们通过一个动手实践过程来完成前面的 XR 示例。此过程所需的所有配置 YAML 文件可以在github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane/tree/main/Chapter05/Hand-on-examples/XRD-Contract-Change-Non-Breaking查看。参考以下动手过程的截图:

图 5.5 – 非破坏性合同变更

图 5.5 – 非破坏性合同变更

以下是整个动手实践过程中实验非破坏性合同变更时需要执行的步骤:

  1. 在目标集群中创建 XRD 的第一个版本(xrd-v1.yaml)。该模式将vm作为必需参数。

  2. 创建组合的第一个修订版(Composition-V1.yaml)。它将把vm值补丁回MR-CloudSQLInstance层属性。

  3. 现在,MySQL 资源可以通过db-n1-standard-1作为 GCP 中的配置选择来配置(Claim-v1.yaml)。

  4. 更新并应用 XRD,增加一个可选参数size来指定数据库磁盘大小(xrd-v2.yaml)。

  5. 更新并应用新的组合(Composition-V2.yaml)。它将把额外的大小参数补丁到 MR 中。

  6. 最后,创建第二个具有特定磁盘大小和配置的 MySQL 实例(Claim-v2.yaml)。

  7. 为了验证第一个 MySQL 实例是否仍然可以更新,请使用更新的 YAML(Claim-v1-validate.yaml)更改配置。

在更新合同时,我们没有升级 API 版本。我们将在接下来的部分讨论这个问题。

版本升级

在前一节中,我们并未将 XRD 的版本号从 v1 更改。Crossplane 当前不支持在合同变更后进行 XR 版本升级。没有合同变更的 API 版本化有助于指示 API 的稳定性(如 alpha、beta、v1 等)。我们可以在不更改合同的情况下从 alpha 升级到 beta,再到更稳定的版本。当前,版本升级是通过在 XRD 中列出旧版本和新版本定义来实现的。versions 数组用于列出多个版本。每个版本下的两个关键布尔属性是 servedreferenceablereferenceable 标志将决定是否可以为给定版本定义组合实现。只有一个版本可以将 referenceable 标志设置为 true,该版本将用于任何新的 XR 创建/更新事件。由旧 API 版本触发的创建/更新事件仍将使用最新版本的组合,并将其标记为可引用。served 标志表示给定的 XR API 版本是否正在使用。一些团队可能仍会使用旧版本来访问 API。关闭 served 标志意味着该版本不再对客户端可用。这将是从 XR 中移除旧版本之前的最后一步。

请查看一个包含三个版本(alpha、beta 和 v1)的 XRD 示例,网址为 github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane/blob/main/Chapter05/Samples/XRD-Versions/xrd-multiple-version.yaml。这个 XRD 包含三个版本。alpha 版本将不再提供服务,beta 版本将提供服务,但不能用于资源的创建或更新。最新版本 v1 将是进行资源创建或更新时的首选版本。

Kubernetes CRD 支持多个 API 版本,包括有和没有 API 合同更改的版本。当存在 API 合同更改时,CRD 作者会配置一个转换 webhook 来支持版本间的转换。由于 CR 对象将以旧合同和新合同的形式存储在 etcd 中,因此需要进行转换。Crossplane 中与 CRD 等效的 XRD 不采用这种方法。转换 webhook 涉及编程,选择这种方法会违反 Crossplane 在构建 API 时不编写代码的目标。需要注意的是,Crossplane 社区正在积极努力构建一个基于配置的解决方案,以支持不同版本之间的转换和迁移。

版本升级与破坏性变更

另一种方法通过引入新的 XR API 并行支持破坏性契约。这种方法使用外部命名技术和删除策略来处理破坏性变化。采用这种模式时,我们将把资源迁移到新的 XR API,并在迁移完全完成后删除旧的 API。完成版本升级的步骤如下:

  1. 创建 v1 版本的 XRD。在组成中,定义一个标准的命名法来命名外部资源(MRs)。我们应该能够在新的 API 中重新构建名称。通常,我们可以将 XR 和组成名称连接起来(<XR>+'-'+<Composition>)。你可以根据你的环境制定适合的资源命名策略。也许我们可以使用命名空间名称来表示资源的产品所有者。

  2. 确保所有组成中的 MRs 的spec.deletionPolicy定义为Orphan

  3. 假设我们有几个 XR 的消费者,并且他们已经创建了一些外部资源。假设我们有一个策略要求,必须对 API 契约进行破坏性修改。

  4. 为了支持破坏性修改,删除所有 v1 版本的 XR。这将仅删除 Crossplane 引用。由于孤立删除策略,外部资源不会被删除。

  5. 然后,删除 v1 版本的 XRD 和 Composition。

  6. 最后,创建新的 v2 版本的 XRD,使用相同的 XR 名称。更新组成以处理最近的破坏性变化。确保新的组成遵循相同的外部资源名称创建逻辑,并映射到新的 XRD 版本。

  7. 重新创建已删除的 XR 对象,指向 v2 版本的 API。新的 XR 对象将引用旧的孤立外部资源。Crossplane 控制器将协调任何属性值的变化。

请注意,这种迁移到新 API 版本的操作必须与所有 XR 消费者团队协调。一旦迁移完成,旧的 API 版本将不再可用。下图表示迁移过程:

图 5.6 – 版本迁移

图 5.6 – 版本迁移

提示

总是采用一种标准化的方式来生成外部资源名称是很好的。除了版本迁移,具有可重复的命名模式还可以带来其他几个优势。使用预配置的资源来支持共享或缓存的基础设施就是使用标准外部资源命名模式的一个例子。将资源迁移到新的 Crossplane 环境也是一个例子。

推荐通过实践操作了解 API 合同破坏,使用在 github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane/tree/main/Chapter05/Hand-on-examples/XRD-Contract-Change-Breaking 提供的示例配置。执行以下步骤进行实践操作以处理合同变更:

  1. 首先,执行 xrd-v1.yamlComposition-V1.yamlClaim.yaml

  2. 它将创建一个 XRD 和一个 Composition,其中数据库大小为可选参数,虚拟机为必选参数。Claim 将根据指定的大小和虚拟机配置数据库。配置的资源将获得标准的外部资源名称。

  3. 请注意,Claim.yaml 中的 claim.name 标签用于在 Composition 部分构造外部资源名称。它应该对于每个 XR/Claim 对象唯一,以生成唯一的外部资源名称。

  4. 现在让我们删除 v1 版本的 Claim、Composition 和 XRD。当我们删除 v1 claim 时,外部资源不会被删除,因为删除策略被配置为孤立资源。

  5. 最后,应用 v2 版本的 Claim、Composition 和 XRD。在 v2 XRD 中,我们通过移除必选参数 vm 来破坏合同。新的 v2 claim(Claim-migrate.yaml)将不包含 vm 参数。请注意,Composition 和 Claim 都将指向 v2 版本的 XRD。

  6. 注意,Crossplane 将回收孤立的资源,并使用 Composition 中提供的新默认值来协调虚拟机。我们可以通过查看 GCP 控制台或 Claim 资源描述来验证这一点。

请参阅以下截图,其中测试了前面的示例:

图 5.7 – XRD 破坏性变更

图 5.7 – XRD 破坏性变更

以下是与前述 Composition 示例中外部资源名称修补相关的代码片段。它必须出现在两个 Composition 版本中,且生成的名称在两个版本中应相同:

- type: FromCompositeFieldPath
      fromFieldPath: metadata.name
      toFieldPath: metadata.annotations[crossplane.io/external-name]
      transforms:
      - type: string
        string:
          fmt: "%s-gcp-mysql-cloudsqlinstance"

请注意,我们使用了新的转换类型来格式化字符串,然后再进行修补。通过这种方式,我们总结了不同的 XR API 演变方法。接下来我们将深入探讨一个有趣的案例,构建一个 XR 组合另一个 XR。

嵌套和多资源 XR

每个软件产品都依赖于多个基础设施资源。为了让产品团队能够以统一的体验使用这些资源,构建单一的基础设施配方是至关重要的。基础设施依赖的编排应该保持抽象。这些配方要求多个资源被组合成一个单一的 XR。在此前所有的示例中,我们始终将单一的 GCP 资源组合进一个 XR。现在让我们看一个示例,其中多个 GCP 资源被组合成一个单一的 XR API。下图展示了我们将在示例中使用的资源和 XR API:

图 5.8 – 多资源嵌套 XR

图 5.8 – 多资源嵌套 XR

除了在单个 XR 中提供多个资源外,我们在图 5.8中还展示了一个嵌套的 XR 模式。我们在两个 XR 中组合了三种资源。第一个 XR 组合了两种资源,第二个 XR 组合了第一个 XR 和一个数据库资源。让我们看一下每个 XR 的细节:

  • XR 1:我们将通过这个 XR 组合一个 Google Kubernetes Engine 集群和一个 Google Cloud 存储资源。目的是提供云存储来保存应用日志,以供未来分析。请注意,这个 XR 在 XRD 定义中不会包含声明名称。它将是集群范围的,并且是平台团队的私有 API。只有拥有命名空间访问权限的产品团队才不会直接使用这个 API。这个 XR 将把区域和自动驾驶配置作为参数暴露出来。区域将传递回两个资源,而自动驾驶配置用于 Kubernetes 的资源配置。

  • XR/声明 2:第二个 XR 将组合 MySQL 数据库、一个 MR 和第一个 XR,以创建一个嵌套的 API。我们将把区域参数补丁应用到 MySQL MR,并将其传递到内部的 XR。

本次实践旅程中的所有示例都可以在github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane/tree/main/Chapter05/Hand-on-examples/Nested-Multi-Resource-XR找到。

首先创建这两个 XR 的 XRD 和 Composition。将xrd k8s.yamlComposition k8s.yamlxrd Application.yamlComposition Application.yaml应用到 Crossplane 集群中。你会看到两个 XRD 的ESTABLISHED标志都是True,这表示 Crossplane 已启动新的控制器来协调已建立的 XR。OFFERED标志在应用 XR 中为True,在 Kubernetes XR 中为False,这表示 Crossplane 仅为应用 XR 启动了新的控制器来协调已建立的声明。而对于 Kubernetes XR,由于我们没有相应的声明,OFFERED标志为False。请参阅以下截图,了解 XRD 的创建过程:

图 5.9 – 嵌套 XR-XRD 与组合

图 5.9 – 嵌套 XR-XRD 和组合

提示

类似于使用来自单一云提供商的多个资源创建 XR API,我们还可以混合使用来自多个云的资源。我们只需添加与相应 ProviderConfig 云相关的资源。

现在是时候创建应用程序声明资源了。将声明Application.yaml应用到 Crossplane 集群中。你将看到CloudSQLInstance实例、集群和存储桶资源已经被配置。请参考下面的截图,查看资源是否已成功配置:

图 5.10 – 资源配置

图 5.10 – 资源配置

如果你想详细查看每个资源,可以使用资源引用。执行kubectl describe application my-application -n alpha来查看声明的详细信息。它将引用XApplication XR 对象。如果我们查看XApplication对象的详细信息,它将持有对CloudSQLInstance MR 和XGCPCluster XR 的引用。类似地,我们可以继续查看,直到到达最后的 MR。这对于调试活动非常有用。有时你可能会发现资源没有准备好。在这种情况下,检查每个嵌套资源,并查看事件部分以确认发生了什么。以下是从资源描述中引用嵌套资源的示例:

图 5.11 – 嵌套资源引用示例 1

图 5.11 – 嵌套资源引用示例 1

上面的截图展示了应用程序声明描述,它引用了XApplication XR 资源。下面的截图展示了XApplication XR 描述,它引用了XGCPCluster XR 实例和CloudSQLInstance MR:

图 5.12 – 嵌套资源引用示例 2

图 5.12 – 嵌套资源引用示例 2

以下是一个事件示例,告诉我们提供了错误的区域参数:

图 5.13 – 资源描述存在错误

图 5.13 – 资源描述存在错误

重要

当我们组合多个资源以为产品团队提供统一体验时,需要遵循更多的模式。前面的示例是一个简单的示例,用来引入这一话题。在接下来的章节中,我们将看到更多内容。

PatchSets

如果你查看前面的示例中的组成部分,你会看到我们使用了一种新模式,称为patchSets函数定义,用于修补一个区域:

patchSets:
  - name: region
    patches:
    - type: FromCompositeFieldPath
      fromFieldPath: spec.parameters.region
      toFieldPath: spec.forProvider.region

我们可以定义多个patchSet函数。要在特定资源中包含某个补丁集函数,请使用以下代码片段:

patches:
    - type: PatchSet
      patchSetName: region

在接下来的章节中,我们将看到更多嵌套和多资源 XR 的示例。在接下来的部分中,我们将详细查看定义 XRD 架构的配置选项。

XRD 详细

在前一章节查看 复合资源定义XRD)时,我们探讨了学习 XR 基础所需的有限配置选项。现在是时候查看更详细的配置选项,以构建干净且稳健的 XR API。我们将深入探讨的一个重要部分是 openAPIV3Schema,它用于定义 XR API 的输入和输出。以下是我们将在本节中覆盖的主题:

  • 命名版本

  • openAPIV3Schema 结构

  • 属性的附加参数

  • 打印列

让我们从 命名版本部分 开始。

命名版本

我们的 XRD 的版本名称不能包含任何随机字符串。它必须遵循从 CRD 和标准 Kubernetes API 继承的特定验证规则。该字符串只能包含小写字母数字字符和 -。此外,版本名称必须始终以字母字符开头,以字母数字字符结尾,这意味着 - 不能是起始或结尾字符。同时,数字不能作为起始字符。一些有效的版本名称有 my-versionversion-1abc-version1v1。虽然我们可以为版本命名提供许多排列组合,但在 CRD 中遵循一些标准做法。遵循这些做法来命名 XRD 可以帮助 API 使用者了解 API 的稳定性。版本字符串以 v 开头,后跟一个数字(例如 v1、v2)。然后可以选择性地跟随 alphabeta,以表示 API 的稳定性。通常,alpha 表示最低稳定性(v5alpha),而 beta 是下一个稳定性级别(v3beta)。如果这两者都缺失,表示 XR 已准备好投入生产使用。可选的数字可以跟随在 alpha/beta 后面,表示增量发布(例如 v2alpha1v2alpha2 等)。

如果提供的版本字符串无效,您会发现 XRD 无法正确配置。ESTABLISHED 标志不会被设置为 True。您可以使用来自 samples 文件夹的 – xrd\ invalid\ version\ test.yaml 文件,查看在版本号不正确时会发生什么。请参阅以下截图:

图 5.14 – 无效版本 XRD

图 5.14 – 无效版本 XRD

此外,您还可以在 crossplane-system 命名空间中的 Crossplane pod 中看到以下错误日志:

2022-01-15T20:06:46.217Z     ERROR     crossplane.controller-runtime.manager.controller.defined/compositeresourcedefinition.apiextensions.crossplane.io     Reconciler error     {"reconciler group": "apiextensions.crossplane.io", "reconciler kind": "CompositeResourceDefinition", "name": "xbuckets.version-test.imarunrk.com", "namespace": "", "error": "cannot apply rendered composite resource CustomResourceDefinition: cannot create object: CustomResourceDefinition.apiextensions.k8s.io \"xbuckets.version-test.imarunrk.com\" is invalid: [spec.versions[0].name: Invalid value: \"v1.0\": a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-name', or 'abc-123', regex used for validation is 'a-z?'), spec.version: Invalid value: \"v1.0\": a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-name', or 'abc-123', regex used for validation is 'a-z?')]"}

提示

当我们排查 Crossplane 问题时,来自 Crossplane pod 的日志可以提供帮助。通过向 Crossplane pod 添加参数--debug,可以启用调试模式。同样,我们也可以查看提供者容器的日志。

openAPIV3Schema 结构

XR API 的规范是通过 openAPIV3Schema 定义的。XRD 中该部分的每个配置元素都表示 XR API 的输入和输出:

versions:
  - name: v1alpha
    schema:
      openAPIV3Schema:
      # Input and output definition for the XR

通常,我们将openAPIV3Schema部分配置为两个对象,specstatusspec对象代表 API 输入,而status对象代表响应。如果没有任何自定义要求,我们可以跳过在 XRD 中定义status部分。Crossplane 会将标准的状态字段注入到 XR/Claim 中。参考以下代码片段,表示 XR API 输入输出的openAPIV3Schema配置模板:

openAPIV3Schema:
  type: object
  properties:
    # spec – the API input configuration
    spec:
      type: object
      properties:
        ............. configuration continues 
     # status – the API output configuration 
     status:
       type: object
       properties:
         ............. configuration continues

模式配置完全是由-属性、它们的类型和属性组成。object类型的attribute将包含一个属性列表。例如,根属性openAPIV3Schema:就是object类型,后面跟着一个属性列表(specstatus)。属性列表其实就是属性的列表。假设属性类型是原始类型,例如stringinteger,那么这个属性就是终结节点。对象-属性递归可以根据需要继续深入。参考以下代码片段:

# The root attribute openAPIV3Schema of type object
openAPIV3Schema:
  type: object
  # spec/status - attributes (properties) of openAPIV3Schema
  properties:
    # spec – the XR input
    spec:
      type: object
      properties:
        # parameters - again an object with attributes list
        parameters:
          type: object
          properties:
            # region – string primitive  - node ends
            region:
            type: string
    # status – API output configuration
    # The exact structure of configuration as before
    # Attributes, their types, and properties
    status:
      type: object
            properties:
              zone:
                description: DB zone.
                type: string   

在接下来的部分,我们将查看一些额外的有价值的配置选项,以及基本的openAPIV3Schema配置。

属性的附加参数

属性节点可以配置一些 API 开发人员日常使用的其他关键配置。以下是一些常用的配置:

  • 描述是一个字符串,可以帮助我们为 API 消费者提供有关该属性的有价值的信息。它可以包含关于参数使用的说明、我们可以配置的可能值以及验证要求。

  • 必需是一个属性,表示用户在 API 中需要提供的必填输入列表。

  • 默认值是一个属性,如果用户未输入值,则提供默认值。

  • 枚举可以配置给定属性的可能值列表。

除了这些字段外,还有一系列与验证相关的配置,包括minimummaximumpatternmaxLengthminLength。参考以下示例配置:

spec:
  type: object
  description: API input specification 
  properties:
    parameters:
      type: object
      description: Parameter's to configure the resource
      properties:
        size:
          type: integer
          description: Disk size of the database
          default: 20
          minimum: 10
          maximum: 100
        vm:
          type: string
          description: Size of the virtual machine.
        enum:
        - small
        - medium
        - large
      required:
      - size
  required:
  - parameters

要探索更多详细的可能性,请访问github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#schemaObject

提示

我们可以使用描述字段来宣布参数的废弃信息。此技术有助于通过将必填字段变为可选字段并附上废弃消息,延迟对契约的破坏性更改。

打印机列

我们可以使用打印机列来添加kubectl在获取资源列表时显示的内容。我们应为每一列提供名称、数据类型和映射到我们希望显示的属性的 JSON 路径。可选地,我们还可以提供描述。参考以下示例配置:

additionalPrinterColumns:
- name: Zone
  type: string
  description: 
  jsonPath: .spec.zone
- name: Age
  type: date
  jsonPath: .metadata.creationTimestamp

打印机列配置与模式配置保持平行。

本节讨论了详细的 XRD 配置,涵盖了日常工作所需的大部分配置,但仍有无限的可能性。通过阅读 kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/ 上的 CRD 相关内容,您将能进一步获得价值。

管理外部软件资源

从本书一开始,我们就讨论了使用 Crossplane 管理外部基础设施资源。然而,这不一定仅仅是基础设施资源。我们甚至可以从 Crossplane 管理外部软件应用程序。为了让一个软件应用与 Crossplane 生态系统兼容,它必须具备以下特点:

  • 我们应该有明确定义且稳定的 API 来执行 CRUD 操作。

  • API 应该具有高保真度的设计,并且需要过滤器来控制细粒度的应用配置。

现在是时候来看一个例子了。想象一下,在 Kubernetes 中使用 Helm 部署一个应用。Helm 可以打包任何应用,并提供一个定义明确的 CRUD API 用于部署、读取、更新和卸载。最重要的是,我们可以通过参数来实现对应用配置的细粒度控制。我们已经有一个 Helm Crossplane 提供程序,并且被社区广泛使用。通过 Crossplane 控制平面管理外部应用程序的概念,可以开启一个统一应用和基础设施自动化的新世界。接下来的部分将详细介绍这一统一性方面。

统一自动化

使用 Crossplane 管理外部软件资源是统一基础设施和应用 DevOps 的关键。我们可以将软件和基础设施依赖打包成一个单一的 XR。这样完整的应用和基础设施包带来了许多优势,部分优势列举如下:

  • 该方法将统一应用和基础设施自动化所需的工具和技能。

  • 更重要的是,整个堆栈将享有 Kubernetes 操作模型的所有优势。

  • 将供应商软件集成到企业生态系统中将变得更加迅速和标准化。软件供应商可以快速构建适配不同生态系统的包。目前,软件供应商必须为各个云服务提供商市场进行定制开发。这种方法可以帮助构建一个通用的供应商软件市场。

  • 我们可以轻松地应用审计过程以遵守任何合规标准。之前,由于软件及其基础设施依赖分散,遵守合规要求会更加复杂。

下图展示了一个统一的 XR API:

图 5.15 – 统一的 XR

图 5.15 – 统一的 XR

重要

在后续章节中,我们可以通过实践体验,构建一个涵盖应用和基础设施依赖的 XR API。

总结

我希望你在阅读这一章并完成实践旅程时感到有趣。它介绍了在采用 Crossplane 时,我们日常工作中有用的不同模式。我们讨论了如何演化我们的 XR API、详细的 XR 配置、如何管理应用资源以及嵌套和多资源 XR。还有更多的模式需要涵盖。

下一章将讨论更高级的 Crossplane 方法及其相应的实践旅程。

第六章:第六章: 更多 Crossplane 模式

紧接着上一章,我们将继续探索更多关键的 Crossplane 模式,这些模式对于构建先进的基础设施自动化平台至关重要。我们将涵盖不同的主题,如管理资源之间的依赖关系、传播秘密、使用 Crossplane Helm 提供商、定义 XR API 边界时的权衡点,以及使用 Prometheus 监控 Crossplane 控制平面。在本章中,我们将通过示例和实践过程来理解这些概念。在之前的所有章节中,我们使用的是 GCP,本章中我们将同时使用 GCP 和 AWS 来学习 Crossplane。最后,我们将学习更多调试技能,这对于日常平台开发和运维至关重要。

本章涵盖的主题如下:

  • AWS 提供商设置

  • 管理依赖关系

  • 秘密传播实践

  • Helm 提供商实践

  • 定义 API 边界

  • 警报与监控

  • 更多故障排除模式

AWS 提供商设置

本章中的一些示例将使用 AWS 作为云提供商。除了 GCP,我们还将介绍 AWS,以帮助我们了解如何与新云提供商合作。它将帮助我们认识到,与一个云提供商合作能使我们具备足够的能力来处理 Crossplane 中的任何云提供商。我们可以通过以下三个步骤来设置 AWS 提供商:

  1. 创建 AWS 账户和 IAM 用户

  2. 创建 Kubernetes 秘密

  3. 提供商和 ProviderConfig 设置

创建 AWS 账户和 IAM 用户

你可以注册 AWS 并免费使用其部分服务,前提是你有信用卡。你可以在aws.amazon.com/free/free-tier-faqs/阅读有关 AWS 免费套餐的更多信息。一旦你创建了免费账户,下一步就是创建一个新的 IAM 用户。以下截图将展示 IAM 用户创建的不同阶段。进入 AWS 网络控制台中的IAM部分,然后点击添加用户。选择凭证类型为访问密钥,如下截图所示:

图 6.1 – 创建用户

](https://github.com/OpenDocCN/freelearn-devops-pt6-zh/raw/master/docs/e2e-auto-k8s-xpln/img/B17830_06_01.jpg)

图 6.1 – 创建用户

下一步是将用户添加到访问组中。如果你还没有用户组,可以使用创建组按钮,创建一个具有适当访问控制的新组。或者,我们可以将现有的访问策略附加到用户,或从当前用户复制权限。确保你已为通过 Crossplane 配置的资源添加了所需的权限。我已提供AdministratorAccess角色,以便为所有 AWS 资源提供完全访问权限。

图 6.2 – 将用户添加到组中

](https://github.com/OpenDocCN/freelearn-devops-pt6-zh/raw/master/docs/e2e-auto-k8s-xpln/img/B17830_06_02.jpg)

图 6.2 – 将用户添加到组中

现在你将在 AWS 控制台中获得新 IAM 用户的访问 ID 和密钥,这对 Crossplane AWS 提供商配置非常有帮助:

图 6.3 – 新的 IAM 用户

](https://github.com/OpenDocCN/freelearn-devops-pt6-zh/raw/master/docs/e2e-auto-k8s-xpln/img/B17830_06_03.jpg)

图 6.3 – 新的 IAM 用户

下一步是使用 IAM 用户的访问密钥 ID 和秘密密钥来配置 Kubernetes 秘密。

创建 Kubernetes 秘密

创建 Kubernetes 秘密从在本地环境中设置 AWS CLI 开始。这将是一个简单的步骤,只需下载安装包并执行安装即可。按照docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html上的安装说明进行操作。接下来,我们可以使用 aws configure --profile default 命令创建 AWS 登录配置文件。它会要求输入访问密钥 ID、秘密访问密钥、默认区域和输出格式。访问密钥 ID 和秘密密钥是在创建 IAM 用户时获得的。你可以忽略默认区域和输出格式。

图 6.4 – 登录配置文件

图 6.4 – 登录配置文件

下一步是使用前面创建的配置文件创建 Kubernetes 秘密。执行以下命令:

# Set a variable with the profile name
AWS_PROFILE=default
# Create a configuration file with profile data
echo -e "[$AWS_PROFILE]\naws_access_key_id = $(aws configure get aws_access_key_id --profile $AWS_PROFILE)\naws_secret_access_key = $(aws configure get aws_secret_access_key --profile $AWS_PROFILE)" > aws-credentials.conf
# Create kubernetes secret from the configuration file
kubectl create secret generic aws-credentials -n crossplane-system --from-file=creds=./aws-credentials.conf

参见以下截图,其中 Kubernetes 秘密被创建:

图 6.5 – Kubernetes 秘密创建

图 6.5 – Kubernetes 秘密创建

我们现在已经完成了 Kubernetes 秘密的创建。接下来的章节将讨论 Crossplane 环境中 AWS 提供者的安装和设置。

AWS 提供者和 ProviderConfig 设置

通过将以下 YAML 应用到集群,安装 Crossplane AWS 提供者。该配置分为两部分。提供者配置将安装 AWS 提供者,ControllerConfig 启用提供者 pod 日志的调试模式。ControllerConfig 配置不是必须的。这里的示例在你需要调试问题时会很有帮助。请注意,ControllerConfig 名称指代的是提供者配置:

apiVersion: pkg.crossplane.io/v1alpha1
kind: ControllerConfig
metadata:
  name: debug-config
spec:
  args:
    - --debug
---
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-aws
spec:
  package: "crossplane/provider-aws:v0.23.0"
  controllerConfigRef:
    name: debug-config

最后,应用以下提供者配置 YAML,参照该机密:

apiVersion: aws.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
  name: aws-credentials
spec:
  credentials:
    source: Secret
    secretRef:
      namespace: crossplane-system
      name: aws-credentials
      key: creds

我们已经准备好从 AWS 免费套餐中创建资源并进行实验。所有设置说明可以在github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane/tree/main/Chapter06/Hand-on-examples/aws-setup找到。执行 rds.yaml 文件以验证 AWS 提供者设置是否正确。以下截图显示了成功从 AWS 配置 RDS 资源:

图 6.6 – RDS 配置

图 6.6 – RDS 配置

这完成了 AWS 设置活动。接下来的章节将讨论资源引用,以管理资源之间的依赖关系。

管理依赖关系

在基础设施中,一个外部资源引用另一个资源是一个常见的模式。例如,我们可能希望在特定的虚拟私人网络VPN)中配置我们的 Kubernetes 集群。引用 S3 桶的 S3 桶策略定义也是一个例子。我们可以列举许多这样的例子。从构建 XR API 的角度来看,可能需要在给定的 XR 或嵌套 XR 场景中,或者在独立 XR 之间建立外部资源之间的依赖关系。Crossplane 提供了三种不同的方式来引用一个资源。每种方式都有其特定的使用场景:

  • UserPolicyAttachment。它可以将 IAM 用户附加到 Policy 对象。在这里,可以通过名为 PolicyARN(ARN 引用)的属性引用 Policy 对象。类似地,也可以使用 UserName 属性(名称引用)来执行 User 对象的引用。

  • selector 属性。selector 是一个属性,指示 Crossplane 根据其子属性中指定的条件查找引用的资源。MatchControllerRefMatchLabelsselector 属性的子属性。MatchControllerRef 的值将为 true,指引 Crossplane 在 XR 内查找引用的资源。第二个属性,MatchLabels,则驱使 Crossplane 查找具有指定标签的引用资源。如果 selector 识别到多个资源,则会随机选择其中一个。如果配置中存在前面提到的直接引用属性,则会忽略selector属性。

  • MatchControllerRef 值。它指引 Crossplane 在 XR 外查找匹配的资源。

    提示

    我们可以使用两种策略来确定直接引用配置的值。我们可以创建具有可预测名称的资源,以便在引用点重新构建它们。这类似于我们在上一章讨论的外部资源名称。如果唯一标识符是云生成的 ID,例如 ARN,则将标识符复制到自定义定义的状态属性(XR API 响应)中,以供后续使用。

如果感到困惑,不必担心。让我们通过几个实际的例子来看一下资源引用。第一个例子将涵盖在 XR 内和嵌套 XR 中的直接和选择器配置。

在 XR 内部和嵌套 XR 中的资源引用

这个例子将是一个实际场景。我们将创建一个具有特定 IAM 策略的 S3 桶,并创建一个可以访问该桶的 IAM 用户。以下是示例中涉及的托管资源:

  • Bucket:这是一个 MR,用于创建一个 S3 桶。我们将使用它在特定区域中配置桶。

  • Policy:这是 AWS IAM 资源列表中的 MR 部分。它有助于为给定资源定义使用指南。在这里的示例中,我们将创建一个具有完全访问权限的策略,以便读取和编辑桶中的所有对象。

  • User:MR 代表 AWS IAM 用户。我们将创建一个新用户以访问已创建的桶。

  • UserPolicyAttachment:这也是 AWS IAM 资源列表中的一部分。该 MR 将把一个策略附加到用户上。我们将把创建的桶策略链接到该用户。

你可以看到,某些资源需要相互引用。例如,Policy 资源必须引用桶名称以构建策略配置。另一个例子是 UserPolicyAttachment,它引用 PolicyUser 资源以将它们附加起来。下图将表示资源之间的关系、它们的引用选项以及 XR 边界:

图 6.7 – 资源引用与嵌套 XR

图 6.7 – 资源引用与嵌套 XR

带有 XRD、组合和声明的完整示例可在 github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane/tree/main/Chapter06/Hand-on-examples/same-nested-xr-reference 上查看。让我们先看一些关键的代码片段,以便熟悉这个示例和概念。桶名称在策略文档中的引用是我们将看到的第一个片段。PolicyBucket MR 都在同一组合中。要求是在策略文档 JSON 中引用桶的 ARN 名称。幸运的是,ARN 标识符有一个可预测的格式,我们可以根据桶的名称构造 ARN。桶的名称已经可用,因为这两个 MR 位于同一组合中,并且桶名称是 XR API 输入。以下是展示所讨论资源引用的代码片段。它使用 CombineFromComposite 补丁类型修补策略文档属性。在这里,桶名称通过 fmt 字符串操作直接嵌入:

- type: CombineFromComposite
  toFieldPath: spec.forProvider.document
  combine:
    variables:
    - fromFieldPath: spec.parameters.bucketName
    - fromFieldPath: spec.parameters.bucketName
    strategy: string
    string:
      fmt: |
        {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Action": [ "s3:*" ],
              "Resource": [
                "arn:aws:s3:::%s",
                "arn:aws:s3:::%s/*"
              ]
            }
          ]
        }

接下来,我们将查看如何提取 Policy 资源的 ARN,并将其传递给内部嵌套的 XR - XIAMResourceUser。这个过程分为两步:

  1. Policy 对象的 ARN 标识符重新插入到 API 响应属性中。

  2. 将提取的 ARN 标识符作为 API 输入,传递给嵌套 XR(XIAMResourceUser)。

请注意,初始情况下,XIAMResourceUser 将失败,直到 Policy 对象完全创建并且 ARN 可用。这是典型的控制平面行为,确保资源最终一致性。以下是来自两个资源 PolicyXIAMResourceUser 的 ARN 补丁代码片段:

# Policy - Patch API response with ARN
- type: ToCompositeFieldPath
  fromFieldPath: status.atProvider.arn
  toFieldPath: status.policyARN
# XIAMResourceUser - patch the policy arn as API input
- fromFieldPath: status.policyARN
  toFieldPath: spec.parameters.policyARN

最后,我们将查看UserPolicyAttachment资源的代码片段,其中包含了两个外部资源(UserPolicy),使用了不同的引用方法。策略引用将直接使用 ARN 标识符,而用户引用则使用选择器。请参考以下代码:

- base:
  apiVersion: iam.aws.crossplane.io/v1beta1
  kind: UserPolicyAttachment
  spec:
    providerConfigRef:
      name: aws-credentials
    forProvider:
    # Selectors refer to the User from the same composition 
      userNameSelector:
        matchControllerRef: true
  patches:
  # Patch the resource name
  # <Type>-<Parent Type>-<Parent Resource Name>
  - type: CombineFromComposite
    toFieldPath: metadata.name
    combine:
      variables:
      - fromFieldPath: spec.parameters.resourceType
      - fromFieldPath: spec.parameters.resourceName
      strategy: string
      string:
        fmt: "policy-attachement-%s-%s"
  # Patch the policy ARN reference
  - toFieldPath: spec.forProvider.policyArn
    fromFieldPath: spec.parameters.policyARN

若要自己执行示例并验证引用,请按照以下步骤操作:

  1. 将 XRD 和组合应用于目标 Crossplane。

  2. 接下来,应用 Claim 配置。它将创建所有所需资源并建立必要的依赖关系。

以下截图显示了在 AWS 中成功创建桶:

图 6.8 – S3 桶已配置

图 6.8 – S3 桶已配置

以下截图展示了该示例的所有执行步骤:

图 6.9 – 示例执行

图 6.9 – 示例执行

此外,请注意,在 AWS 控制台中,User 对象是与附加的 Policy 资源一起创建的:

图 6.10 – 创建的用户执行

图 6.10 – 创建的用户执行

最后,查看以下截图,展示了由于策略 ARN 不可用导致 XIAMResourceUser 失败的事件。一旦策略 ARN 可用,它将自动生效:

图 6.11 – XIAMResourceUser 失败事件

图 6.11 – XIAMResourceUser 失败事件

信息

请注意,在我们的选择器引用中并没有使用 MatchLabels。只有 MatchControllerRef 被设置为 true 作为值。在这种情况下,同一组合中只有一个 User 对象,可以无问题地引用。如果组合中有多个 User 对象,或者我们想要引用当前组合外的资源,我们将使用 MatchLabels

我们已经完成了对内部和嵌套 XR 中资源引用的探索。接下来我们将在下一节中引用组合外部的资源。

引用外部资源

要引用组合外的资源,我们将使用 MatchLabelsMatchControllerRefMatchControllerRef 应该被设置为 false。这将引用外部资源、MR 或另一个位于 Claim/XR 内部的资源。我们将把最后一个示例修改为两个独立的 XR,并确保 UserPolicyAttachment 对象能够使用标签选择器引用独立 XR 中的 Policy 对象。以下图示将表示资源之间的关系、它们的引用选项和 XR 边界:

图 6.12 – 引用外部资源

图 6.12 – 引用外部资源

请注意,这里 XRs 没有嵌套。XMyBucket XR 不会将XIAMResourceUser作为其中的资源。在这两个 XR 独立的情况下,Policy对象必须通过标签选择器引用 XR。让我们看几个重要的代码片段,这些片段通过选择器标签引用资源。以下是将几个标签添加到Policy资源的代码。第一个标签resourceType被直接添加到元数据中。第二个标签resourceName通过桶名称进行补丁,这是 XR 的输入参数:

- base:
  apiVersion: iam.aws.crossplane.io/v1beta1
  kind: Policy
  metadata:
    # Add labels one as the resource type
    labels:
      resourceType: bucket
  spec:
    providerConfigRef:
      name: aws-credentials
    forProvider:
      path: "/"
  patches:
  # patch labels two from the resource name
  - fromFieldPath: spec.parameters.bucketName
    toFieldPath: metadata.labels[resourceName]

代码的下一部分将把resourceNameresourceType标签应用到UserPolicyAttachment资源。它将在 policyArnSelector 的MatchLabels属性下进行补丁处理。这两个标签值是 XR API 输入的一部分。您可以根据自己的预测标签策略来标准化这一发现过程。请注意,XR 中的User对象引用的MatchControllerRef值为true,而Policy对象引用的MatchControllerRef值为false

- base:
  apiVersion: iam.aws.crossplane.io/v1beta1
  kind: UserPolicyAttachment
  spec:
    providerConfigRef:
      name: aws-credentials
    forProvider:
      # refer to the IAM user from the same composition 
      userNameSelector:
        matchControllerRef: true
      policyArnSelector:
        matchControllerRef: false
  patches:
  # Patch the policy ARN lable 1
  - toFieldPath: spec.forProvider.policyArnSelector.matchLabels.resourceName
    fromFieldPath: spec.parameters.resourceName
  # Patch the policy ARN lable 2
  - toFieldPath: spec.forProvider.policyArnSelector.matchLabels.resourceType
    fromFieldPath: spec.parameters.resourceType

所讨论的示例可以在github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane/tree/main/Chapter06/Hand-on-examples/different-xr-reference找到。要完全体验资源配置,应用composition-IAM.yamlcomposition-bucket.yamlxrd-IAM.yamlxrd-bucket.yaml到目标 Crossplane 集群。这将创建两个 XR 及其各自的组合。然后,应用claim-bucket.yamlclaim-iam.yaml来创建资源。以下截图展示了示例的完整执行过程:

图 6.13 – 引用外部资源 – 示例

图 6.13 – 引用外部资源 – 示例

图 6.10所示,User对象将在 AWS 控制台中创建,并附加上Policy资源。我们现在已经完成了对资源引用的探讨。接下来的部分将通过一个动手示例来讲解密钥传播。

密钥传播动手操作

密钥传播是一个关键的 Crossplane 模式,因为所有配置的资源通常都需要凭证来访问资源。我们在第四章中已经讨论过相同的话题,这是理论部分。现在,我们将通过一个实际案例进行动手操作。在深入示例之前,让我们快速回顾一下这个概念的几个要点:

  • 使用ConnectionSecretKeys属性在 XRD 中定义密钥列表。

  • 使用WriteConnectionSecretToRef配置定义各自资源下的命名空间和密钥名称。

  • 最后,使用ConnectionDetails配置填充在 XRD 中定义的密钥。

我们将扩展用于资源引用的实践示例,并使用嵌套的 XR 来学习存储秘密的配置。在这个特定的示例中,我们创建了一个 S3 存储桶、其策略以及一个用于访问该存储桶的 IAM 用户。直到我们将存储桶详情和 IAM 凭证提取到秘密中,示例才算完全完成。这正是我们在此示例中要做的。存储桶的详细信息已在 Bucket 资源中,但我们需要创建一个新的名为AccessKey的资源,附加到已创建的用户上,以获取 IAM 凭证。以下图表将表示两个 XR、它们的资源以及秘密密钥存储结构:

图 6.14 – 秘密传播

图 6.14 – 秘密传播

让我们来看看示例中的一些关键代码片段。以下是来自XIAMWithCredentialXBucketWithCredential的代码,用于列出秘密密钥:

# List of secrets defined in XRD - XBucketWithCredential
connectionSecretKeys:
  - bucket_url
# List of secrets defined in XRD - XIAMWithCredential
connectionSecretKeys:
  - iam_username
  - iam_password

它只是一个简单的在 XRD YAML 文件中列出ConnectionSecretKeys属性下的秘密密钥列表。秘密名称和存储命名空间必须推送到资源中,以复制秘密密钥。请注意,秘密的命名空间会自动从声明中提取出来。以下是来自AccessKeyBucket资源的代码,用于定义秘密名称和存储命名空间:

# Secret name and namespace patching for Bucket resource
# Namespace to save the secret same as the resource namespace
- fromFieldPath: spec.claimRef.namespace
  toFieldPath: spec.writeConnectionSecretToRef.namespace
# Generate and patch the kubernete secret name 
- fromFieldPath: spec.parameters.bucketName
  toFieldPath: spec.writeConnectionSecretToRef.name
  transforms:
  - type: string
    string:
      fmt: "details-bucket-%s"
# Secret name and namespace patching for AccessKey resource
# Namespace to save the secret is the same as the resource
- fromFieldPath: spec.parameters.secretNamespace
  toFieldPath: spec.writeConnectionSecretToRef.namespace
# Generate and patch the kubernete secret name 
- type: CombineFromComposite
  toFieldPath: spec.writeConnectionSecretToRef.name
  combine:
    variables:
    - fromFieldPath: spec.parameters.resourceType
    - fromFieldPath: spec.parameters.resourceName
    strategy: string
    string:
      fmt: "credentials-%s-%s"

我们将要查看的最终配置是将秘密复制到 XRD 中定义的密钥中。以下是来自AccessKeyBucket资源的代码,用于执行相同操作:

# Populate the connection secret keys from AccessKey secrets
connectionDetails:
- name: iam_username
  fromConnectionSecretKey: username
- name: iam_password
  fromConnectionSecretKey: password
# Copy the endpoint secret key to bucketURL for 
connectionDetails:
- name: bucketURL
  fromConnectionSecretKey: endpoint

讨论的示例可以在github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane/tree/main/Chapter06/Hand-on-examples/secret-propagation找到。要在 Kubernetes 集群中完整体验秘密创建,请从前述链接创建 XR、组合和声明。以下截图展示了完整的示例执行过程:

图 6.15 – 秘密传播

图 6.15 – 秘密传播

一旦资源完全创建,您将看到秘密已在 alpha 命名空间中可用:

图 6.16 – 创建的秘密

图 6.16 – 创建的秘密

信息

一些组织可能更愿意将秘密存储在密钥库中,而不是 Kubernetes 秘密。Crossplane 网站上有一个示例,可以执行此集成,网址为crossplane.io/docs/v1.6/guides/vault-injection.html。Crossplane 团队正在通过 MR 提供一种更简便的方式来实现这一点。MR 将代表特定的外部密钥库资源,并相应地推送秘密。请持续关注 Crossplane 的更新。

这就结束了我们对密钥的探索。本章的下一部分将使用 Crossplane Helm 提供者在远程 Kubernetes 集群中部署应用程序。它将继续我们在第五章中在管理外部软件资源部分中讨论的内容。

Helm 提供者实操

介绍 Crossplane 的这一方面令人兴奋。它恰恰是将基础设施自动化和应用程序自动化统一的交汇点。在创建基础设施资源后,我们会对进行其他操作感兴趣。例如,在部署完 Kubernetes 集群后,我们会想要设置 Prometheus 或在远程 Kubernetes 集群中部署应用程序。Helm Crossplane 提供者可以执行此操作。

类似地,在配置完数据库后,我们将有兴趣创建表。SQL 提供者可以从 Crossplane 执行这些操作。示例展示了一种方法,可以在 Crossplane 中定义所有应用程序依赖,并将它们与基础设施一起打包。此部分将通过实践演示来实验 Crossplane Helm 提供者。我们将使用 GCP 创建一个 Kubernetes 集群,它将完全符合免费套餐的限制。以下图示表示 Helm 提供者如何在 Crossplane 生态系统内部工作,以管理远程 Kubernetes 集群中的应用程序部署:

图 6.17 – Helm 提供者和 GKE

图 6.17 – Helm 提供者和 GKE

让我们看一下不同组件如何通过几个步骤协同工作来使用 Helm 管理应用程序的细节:

  1. 使用现有的 GCP 提供者和提供者配置,我们可以创建一个 Cluster.container.gcp.crossplane.io MR。

  2. 在 MR 中定义命名空间和密钥名称,以存储远程集群凭据。

  3. 使用相应的配置 YAML 在 Crossplane 控制平面中安装 Helm 提供者。

  4. 接下来,使用存储在 Kubernetes 密钥中的 Kubernetes 凭据和 GCP 凭据,为 Helm 提供者设置提供者配置。

  5. 现在我们可以使用 Release.helm.crossplane.io MR 在远程 GKE 集群中创建 Helm 发布。

请参考以下代码,了解 Helm 提供者配置的 YAML:

apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-helm
spec:
  package: crossplane/provider-helm:master

以下是 Helm 提供者 GKE 的配置。它需要来自 Kubernetes 集群和云提供商的凭据。在凭据部分下的密钥引用指向特定的 Kubernetes 集群。身份部分下的密钥引用指向 GCP 云凭据。凭据的身份部分对于其他云提供商可能不可用。请确保 Kubernetes API 已为 GCP 云凭据启用:

apiVersion: helm.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
  name: helm-provider
spec:
  # GKE credentials 
  credentials:
    source: Secret
    secretRef:
      name: secret-gke-for-helm-deployment
      namespace: crossplane-system
      key: kubeconfig
  # GCP credentials
  identity:
    type: GoogleApplicationCredentials
    source: Secret
    secretRef:
      name: gcp-account
      namespace: crossplane-system
      key: service-account

在应用提供程序配置之前,我们必须确保 GKE 集群已创建,并且其凭证已秘密存储。所有 Helm 提供程序实验的示例可在 github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane/tree/main/Chapter06/Hand-on-examples/helm-provider 中找到。应用 GKE.yaml 来创建集群。参见以下 GKE 集群创建、Helm 提供程序安装和提供程序配置设置的截图:

图 6.18 – GKE 和 Helm 提供程序设置

图 6.18 – GKE 和 Helm 提供程序设置

现在我们可以开始使用 Helm 在 GKE 集群中管理应用程序部署了。Release 是 Helm 提供程序中用于管理应用程序的 MR 构造。Release MR 具有以下关键配置:

  • spec.forProvider.chart 配置将保存图表的基本信息,例如仓库名称、图表名称和版本。

  • spec.forProvider.valuesFromspec.forProvider.valuesspec.forProvider.set 是为 Helm 模板变量提供值的三种不同方式。如果我们以多种方式为相同的变量设置值,则优先顺序将与前述顺序相同。

  • spec.forProvider.patchesFrom 在指定后渲染补丁以在部署前最后一公里覆盖值时将非常有用。

参见一个简单的 Release 配置:

apiVersion: helm.crossplane.io/v1beta1
kind: Release
metadata:
  name: redis-crossplane-example
spec:
  providerConfigRef:
    name: helm-provider
  forProvider:
    chart:
      name: hello
      repository: https://www.kleinloog.ch/hello-helm/
      version: 0.3.0
    namespace: default

应用前述配置将会在 GKE 集群中创建 hello world 示例。参见以下应用部署的截图:

图 6.19 – Crossplane Helm 部署

图 6.19 – Crossplane Helm 部署

来自 Crossplane Helm 提供程序的相同 Release 合并请求(MR)也处理了我们初始发布的 Helm 图表的升级。我们可以更改所需的图表信息、值或补丁,并重新应用 YAML 文件以升级我们的发布。应用 Helm-test-deploy-upgrade.yaml,该文件将更改容器版本以更新发布版本。在创建升级后的发布之前,Release controller 合并请求会检查配置的任何实际更改。它会确保没有不必要的发布。参见下面的截图,显示了升级后的发布:

图 6.20 – Crossplane Helm 发布升级

图 6.20 – Crossplane Helm 发布升级

这就是我们当前的 Helm 实验。接下来的部分将带我们脱离代码,帮助我们学习一些指导原则,以定义 XR API 边界。

提示

在我们所有的示例中,我们直接在声明/跨平台请求(Claim/XRs)中使用名称引用了组件。我们也可以在组件元数据中添加相应标签后,使用标签选择器引用组件。

定义 API 边界

我们期望平台工程师将所有基础设施和应用自动化的关注点组合到 XR API 中。如何定义 API 边界有点棘手。因为有许多冲突的权衡点影响着 API 边界。让我们从我们希望将应用程序及其基础设施所需的每个资源组合到一个单一的组合开始。以下是一些使我们无法做到这一点的考虑因素:

  • 会有许多需要集中管理的安全和架构策略。我们不能在多个组合中反复添加它们。

  • 一些资源可能有合规要求,必须单独组成以便审计。

  • 过于庞大的组合很难阅读、理解、调试和重构。

  • 测试大规模组合是困难的。

  • 每个应用程序都需要对基础设施配方进行一些定制,前提是我们有一个大规模组合。我们将分叉主代码以进行定制。随着规模增长,这将变得难以维护。

  • 特定的基础设施,如网络层,属于特定团队。它必须单独组成,并在需要的 XR 中引用。

根据你所在组织的实际情况,可能会有更多的原因。总的来说,我们必须构建小型的 XR API,并将它们与资源引用和嵌套 XR 一起组织。当我们谈论小型 XR API 时,API 边界的问题就会出现。哪些资源组合在一起是有意义的?这就像我们在微服务世界中所做的事情。合并与拆分的权衡是我们在微服务中总是要做的事情。

提示

在第一次尝试时,很难正确确定边界。我们应该进行初步的权衡分析,提供一个初步的边界,然后根据实际经验迭代演变。

在我们之前的 S3 桶示例中,我们将桶和其策略组合在一个单一的 XR 中。第二个嵌套 XR 持有 IAM 用户和策略附加资源。这个设计确保了 IAM XR 可以与其他资源一起使用。

信息

稍后在 第十章,《使用 Crossplane 完成应用程序入职》中,我们将进行一次权衡分析,结合实际操作示例来分析不同 API 边界的影响。

以下图表涵盖了影响权衡分析的不同因素:

图 6.21 – Crossplane Helm 发布升级

图 6.21 – Crossplane Helm 发布升级

这部分讨论结束了 API 边界。在本章接下来的部分,我们将探讨监控 Crossplane 控制平面。

警报和监控

Prometheus 和 Grafana 是 Kubernetes 世界中流行的工具,也可以用于 Crossplane 监控。在开始之前,我们应该确保 Crossplane pod 能够发出指标。只需在部署 Crossplane 时将 metrics 参数设置为 true--set metrics.enabled=true)即可。这可以在第一次发布 Crossplane 时完成,或者使用以下命令升级 Helm 发布:

# Fresh install with metrics enables
helm install crossplane --namespace crossplane-system crossplane-stable/crossplane --set args='{--debug}' --set metrics.enabled=true
# Helm upgrade with metrics enables
helm upgrade crossplane --namespace crossplane-system crossplane-stable/crossplane --set args='{--debug}' --set metrics.enabled=true

我们可以将监控和告警设置分为三个部分:

  • 启用 Prometheus 抓取指标。

  • 设置监控告警。

  • 启用 Grafana 仪表板。

我们可以从指标抓取开始。

启用 Prometheus 抓取指标

首先,我们必须在 Kubernetes 控制平面中设置 Prometheus。我们将通过 Prometheus 操作器进行此安装。您可以查看 prometheus-operator.dev/docs/prologue/quick-start/ 上的快速入门指南。以下是安装 Prometheus 操作器的简单步骤:

# Step 1: Clone the Prometheus operator repository and switch to the folder
git clone https://github.com/prometheus-operator/kube-prometheus.git
cd kube-prometheus
# Step 2: Execute the initial setup instructions
kubectl create -f manifests/setup
# Step 3: Install the operator
kubectl create -f manifests/
# Step 4: Once the pods are up and running, view the dashboard after the port forward 
kubectl --namespace monitoring port-forward svc/Prometheus-k8s 9090
http://localhost:9090/

监控示例所需的所有配置可以在 github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane/tree/main/Chapter06/Hand-on-examples/monitoring 中找到。一旦您安装了 Prometheus,下一步就是让 Prometheus 从 Crossplane 和 GCP 提供程序 pod 中抓取指标。我们需要向 GCP 提供程序添加 ControllerConfig,以定义指标端口。相应的配置可以在 GCP-Provider.yaml 中找到。然后,我们可以配置 PodMonitor,指示 Prometheus 在指定端口从特定 POD 中抓取指标。相应的配置可以在 monitor.yaml 中找到。完成这些步骤后,我们可以开始在 Prometheus 控制台中查看控制器调和指标。创建一个配置错误的 GCP CloudSQLInstance 实例,该实例将无法调和,并查看调和失败的指标。以下是从 CloudSQLInstance 获取调和失败指标的 Prometheus 查询:

sum_over_time(controller_runtime_reconcile_errors_total{namespace="crossplane-system", controller="managed/cloudsqlinstance.database.gcp.crossplane.io"}[5m])

请参考以下截图,我们正在查看 CloudSQLInstance 的调和失败指标:

图 6.22 – 指标

图 6.22 – 指标

下一步是为调和失败场景设置监控告警。

设置监控告警

我们还可以使用以下配置为此协调错误设置警报。为每个协调失败触发警报可能有些过多。此外,某些协调错误是预期的场景。例如,如果一个资源引用了另一个资源,那么在被引用的资源未被提供之前,引用资源的协调会失败。以下警报配置仅在协调错误超过 20 次,并且在 5 分钟窗口内发生时触发警报:

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: sql-alerts
  namespace: crossplane-system
  labels:
    app.kubernetes.io/part-of: crossplane
spec:
  groups:
  - name: Crossplane
    rules:
    - alert: ReconciliationFailure
      expr: sum_over_time(controller_runtime_reconcile_errors_total{namespace="crossplane-system", controller="managed/cloudsqlinstance.database.gcp.crossplane.io"}[5m]) > 20
      for: 5m
      labels:
        severity: page
      annotations:
summary: '{{ $labels.controller }} reconciliation has been failing for more than 20 time in the last 5 minutes.'

请参见以下截图,我们正在查看协调失败警报:

图 6.23 – 警报

图 6.23 – 警报

信息

Crossplane 发出的指标列表是一个需要改进的领域。我们应当获得关于组合和声明的详细指标。我们可以期待 Crossplane 社区很快会做出更多的改进。

最后一步是设置 Grafana 仪表板,以更好地可视化指标和错误。

启用 Grafana 仪表板

最后,我们可以设置一个 Grafana 仪表板来可视化指标、日志和警报。Grafana 仪表板已经作为 Prometheus Operator 的一部分安装在集群中。我们需要做的额外工作是为 Crossplane 控制平面设置一个仪表板。在 Git 仓库中的 grafana.json 文件里,我添加了来自 Crossplane 社区的示例仪表板配置。将 JSON 导入 Grafana 并查看指标。请参见以下 Grafana 截图,显示 CloudSQLInstance 是运行协调循环的活动控制器:

图 6.24 – Grafana 指标视图

图 6.24 – Grafana 指标视图

我们将在这里结束监控概念的讨论,并进入本章的最后部分,介绍几个故障排除模式。

更多故障排除模式

在上一章节和本章节的前面部分,我们探讨了不同的故障排除模式。它涵盖了查看资源描述中的资源引用,以从组合转移到 MR,使用资源描述中的事件日志,启用 Crossplane/provider pod 日志进行调试的方法。本节将增加一些调试技巧,以提升我们的平台开发技能。以下是我们将要查看的新模式:

  • ControllerConfig(我们之前用来启用调试和暴露指标端口的配置)。

  • 挂起的资源:有时,我们可能会注意到资源被挂起,并且无法删除它们。这通常是由于 finalizer 导致的。我们应当用空的 finalizer 修补该资源,然后再次删除它。这将确保仅在 Crossplane 控制平面中删除资源。不能保证资源从外部提供商处删除。我们必须访问外部提供商控制台,以验证资源是否已删除。以下代码将 finalizer 设为空:

    kubectl patch <resource-type> <resource-name> -p '{"metadata":{"finalizers": []}}' --type=merge
    

总结

本章介绍了构建最先进资源编排控制平面所需的许多新的 Crossplane 模式。我特别提到资源编排控制平面,而不是基础设施编排控制平面,因为我们不再仅仅编排外部云提供商的资源。在实际操作示例的帮助下,我们实验了资源引用、秘密传播和 Helm 部署。我们还通过另一个实践示例探讨了如何设置监控。除了这些动手实践,我们还学习了一些调试技巧以及定义 API 边界的方法。

下一章将介绍扩展和规模化 Crossplane 的不同方法。

第七章:第七章:扩展和扩展 Crossplane

本章将深入探讨一些使 Crossplane 可扩展和可扩展的特性。本章的初始部分将讨论为尚未支持的外部资源开发新的 Crossplane 提供者。我们将研究在设计提供者时需要考虑的标准以及可用的方法,使得提供者开发相对轻松。接下来的部分将涵盖配置、打包 XR/Claim API 的方法,以及如何测试我们的 XR。本章的最后部分将介绍 Crossplane 支持的不同模式,如何将控制平面扩展为多租户生态系统。

本章涵盖的主题如下:

  • 构建新的提供者

  • XRD 详细信息

  • 构建提供者的框架

  • XR/Claim 的打包和分发

  • 测试配置

  • 多租户控制平面模式

构建新的提供者

Crossplane 提供者仅仅是相关的托管资源MRs)的集合。MR 是有观点的自定义控制器和自定义资源的组合,能够与外部资源进行一对一映射,使我们能够从 Kubernetes 中管理这些资源。将新资源作为 MRs 纳入现有或新提供者是一个耗时的过程。在过去几年里,Crossplane 社区已努力将大多数重要资源与 Crossplane 原生控制器结合起来。随着 Crossplane 社区最近在自动生成 Terraform 提供者的 Crossplane 提供者方面的进展,实现了 100% 的云资源覆盖。除了所有主要云提供者的提供者外,我们还为其他外部资源提供了提供者,如 GitLab、Helm、SQL 和 Argo CD。访问 Crossplane 网站或 Upbound Registry 探索可用的提供者。

当我们尝试自动化所有应用程序和基础设施的问题时,我们可能会遇到某些外部资源没有提供者的情况。你可能决定自己添加一个新的提供者——例如,目前没有提供者来管理 Bitbucket 仓库资源。本章的这一部分将介绍新提供者开发者所需的Crossplane 资源模型XRM)的关键方面,并探讨可用的框架,以简化开发过程。首先,我们将了解 XRM,这是一个对Kubernetes 资源模型KRM)的有观点的子集。

提示

查看 XRM 定义的标准以支持新的提供者开发,也使我们能够更好地理解现有的 MRs。它将进一步帮助我们使用相同的 XRM 标准构建我们的 XR。遵循这些标准与 XR 一起使用将使消费团队对其有统一且容易理解的认识。

接下来的部分将深入探讨 XRM 规范的细节。

XRM 详细信息

作为 KRM 的扩展,XRM 继承了大多数标准。如第三章第四章所述,Crossplane 继承了许多来自 Kubernetes CRD 的标准。基于 Kubernetes 标准的 XRM 独特标准定义了 Kubernetes 与外部资源之间的统一桥梁。XRM 标准涵盖了以下特点:

  • 配置保真度

  • 规格与状态配置

  • 命名自定义和外部资源

  • 配置所有权

  • 敏感的输入和输出字段

我们将在接下来的章节中深入探讨这些特点的细节。

配置保真度

MR 应该包含外部资源 API 中所有可用的配置字段。它在控制平面级别提供所有配置组合。我们应该将抽象留给通过 XR 为平台开发人员编码策略和创建配方。外部资源 API 中每个可配置字段都应在 MR 中以相同名称呈现。属性使用相同名称将使用户更容易进行比较和故障排除。在开始新提供者的开发之前,必须确保资源具有明确定义的 CRUD API 和细粒度的控制。没有这样的 API 标准,控制理论实现将无法有效运行。接下来的章节将进一步讨论 API 输入和输出。

规格与状态配置

规格是作为 API 输入的配置部分,状态是作为 API 输出的属性。首先来看规格部分。我们可以在规格部分中设置三种类型的配置选项——初始初始化、延迟初始化和不可变配置。初始初始化是用户在资源配置时使用的配置,许多参数都属于这一类。例如,RDS 配置中的数据库版本就属于初始初始化。延迟初始化属性由提供者在资源创建时初始化为默认值,之后在调和循环中根据用户配置更新为实际值。这通常对那些在资源创建后才可用的外部资源字段非常有用。资源标签和标签是最常见的例子。

不可变配置是指在初始配置后其值无法更改的属性——例如,在创建资源后我们无法更改 RDS 区域。这些配置应在 MR 中标记为不可变。如果用户尝试更新相同的配置,将触发协调失败事件。从创建和更新 API 操作中要求的必填字段应在 MR 中标记为必填字段。可选字段应为pointer类型——这是从 Kubernetes 继承的标准。我们之所以这样做,是因为一些非指针类型在用户未指定值时可能会解析为零——例如,我们应该使用*bool而不是bool。这个问题不适用于必填字段,因为用户必须指定值。

信息

一些字段可以包含struct类型的值,因为底层资源 API 以这种方式支持它。我们在上一章中看到过一个例子,AWS IAM 中的Policy对象就是作为输入接受 JSON 格式的策略。

状态部分表示外部资源的当前状态。它包含每次协调循环后从外部资源返回的观察到的属性。如果某个配置及其子属性代表另一个外部资源,则这些字段可以添加到状态部分。如果一个配置及其子属性可以在删除后重新创建,那么它们是合适的候选项。例如,我们在创建 Azure 虚拟网络时,也可以同时创建子网。换句话说,Azure 虚拟网络的云 API 也有一个部分用于定义子网。在 Crossplane 控制面板中,我们应将其作为两个独立的资源进行管理,并进行引用。

自定义和外部资源的命名

每个 MR 代表一种独特的类型,属于特定的 API 组,并且可以存在于多个版本中。这是一种从 Kubernetes 标准继承的行为。在创建特定 MR 类型的对象时,我们有两个重要的名称:

  • 控制平面内资源的名称

  • 外部生态系统中资源的名称

控制平面中的资源名称也可以称为自定义资源CR)名称。归根结底,无论是 MR、Claim 还是 XR,它们都是一种有意见的 CR。当用户创建 MR/XR/Claim 时,他们会提供一个与类型、版本和 API 组并行的名称。这个名称将唯一标识集群中的一个资源。如果是 MR/XR,则该名称在整个集群中唯一;如果是 Claim,则它在每个命名空间内唯一。由于这些名称不是自动生成的,使用的团队可以选择一个随机值。这样的随机名称难以进行资源交叉引用。可预测的命名策略和标签将有助于资源引用变得直接明了。我们可以使用准入控制工具,如Open Policy AgentOPA),在配置中管理这些标准。

外部名称是资源在外部生态系统中的名称。资源提供者可能允许或不允许我们影响这个名称——例如,在 S3 存储桶中,我们可以在 AWS 中确定存储桶的名称。我们通过诸如 ID、名称或 UID 等属性来控制此名称。如果用户未配置这些属性,控制器可以使用<CR Name>-<Namespace>-<Kubernetes 分配的随机 5 个字符>模板自动生成名称。Amazon VPC 是一个典型的例子,我们无法影响其名称。AWS 会生成一个唯一的 ID,称为 vpcID,作为名称。如果我们希望将 MR/XR/Claim 链接到现有的外部资源,我们可以添加一个 crossplane.io/external-name 注释,将资源的名称与外部生态系统关联。如果指定名称的资源不存在,则会创建一个新资源,并使用提供的名称。如果是一个不允许命名的资源,则最终名称将复制回 crossplane.io/external-name 属性。在设计新 MR 时,我们需要牢记这些命名行为。

信息

KRM 还建议在外部环境中的资源上添加标签,包含资源类型、CR 名称和提供者名称。这将有助于诸如在外部环境中识别资源、基于标签的批量操作、监控和调试等用例。需要注意的是,并非所有资源都支持此类标签。

配置所有权

有些字段仅与控制平面相关,无论是在 Spec 部分还是在 Status 部分——例如,Spec 部分中的 ProviderConfigRef 指示使用哪个凭证。我们还有一些仅适用于外部资源的其他属性。这些字段之间可能会发生冲突——例如,CreationTimestamp 可以同时存在于集群和外部资源级别。KRM 提出了 spec.forProviderstatus.atProvider 部分作为外部资源相关字段的所有者。

敏感的输入和输出字段

MR 的输入和输出字段中可能包含敏感信息。配置数据库主密码就是 MR 输入中敏感数据的一个例子。IAM AccessKey 凭证是 MR 输出中敏感数据的一个例子。直接暴露这些字段值在 MR/XR/Claim 中是不明智的。对于 MR 输出中的敏感信息,Kubernetes Secret 应该存储这些数据。Crossplane 社区也在制定 vault 集成来发布凭证。类似地,包含机密信息的输入字段应为保密引用。控制器应从机密源获取这些字段的值。此类字段在 MR 中的命名约定应为 <字段名称>SecretRefRDSInstance MR 中的 MasterPasswordSecretRef 字段就是这种字段的一个例子。

KRM 是一个不断发展的领域,具有新的需求。正在创建 KRM 标准,以支持 Terraform 生成的 Crossplane 提供者中的资源引用。另一个正在进行中的标准示例是仅将 Crossplane 用作观察者,特别是对于由其他自动化工具(如 Helm 和 Terraform)管理的现有资源。定期查看 Crossplane Slack 频道或发布说明可以帮助我们保持对发展中的标准的了解。KRM 的讨论到此为止。在接下来的部分中,我们将了解几种提供者开发的可用方法。

构建提供者的框架

为了降低开发新提供者所需的认知负担,Crossplane 社区已确定了几种相对无痛的方式,如下所示:

  • ProviderConfig,可以从 Kubernetes Secrets 中读取凭证,以管理外部提供者的身份验证和授权。它还具有一个示例 MR 和一个控制器。我们可以使用外部资源的 CRUD API,将所有新的 MR 及其相应的控制器与适当的控制理论实现添加到其中。www.youtube.com/watch?v=dhuqH308Tc0上的视频带我们通过一个实践示例,介绍了使用提供者模板进行提供者开发。

  • 对外部资源的创建读取更新删除操作。MR 中的配置参数将转换为 JSON,并作为输入用于使用 Terraform CLI 进行的CRUD操作。有关如何开发此类提供者的逐步指南,请参见github.com/crossplane/terrajet/blob/main/docs/generating-a-provider.md

我们大多数人可能不需要自己开发一个提供者。现在,我们已经为所有主要云提供商的资源提供了 100%的资源覆盖。Crossplane 社区将很快为每个可用的 Terraform 提供商创建一个 Terrajet Crossplane 提供者,只要我们减少认知负担。如果你仍然有此需求,本节将引导你朝着正确的方向前进。此外,学习 KRM 将使你对 MR 的理解更上一层楼。接下来的章节将介绍如何打包和分发 XR/Claims。

XR/Claim 的打包与分发

Crossplane 配置是一种打包我们 XR 和 Claim API 的方式。这种打包将帮助我们可靠地将这些 API 建立到任何启用了 Crossplane 的 Kubernetes 集群中。Crossplane 配置主要是一种组合分发机制。随着分发,我们还可以管理版本和依赖关系。我们最终可能会使用 Crossplane 配置来处理三种不同的用例:

  • 当一个大型组织希望在不同的团队边界之间分发多个控制平面时,它非常有用。

  • 当有人有兴趣构建一个控制平面平台并将其作为产品出售时,这也很有用。

  • 希望与社区分享其 XR/Claim 配方的开源开发者也可以使用 Crossplane 配置。

本章的这一部分将带你进行一个实践之旅,让你学习 Crossplane 配置。首先,让我们来看一下打包和分发。

打包和分发

使用 Crossplane 配置时,我们将组合打包为符合 OCI 标准的镜像以进行分发。将组合打包为配置是一个简单的两三步过程:

  1. 第一步,创建一个 crossplane.yaml 文件。这是一个简单的 Configuration.meta.pkg.crossplane.io 类型的 YAML 文件。它定义了配置名称、最低支持的 Crossplane 版本以及依赖关系。提供者和其他配置都可以定义为依赖关系。以下是示例配置 YAML 文件:

    apiVersion: meta.pkg.crossplane.io/v1
    kind: Configuration
    metadata:
      name: aws-bucket
    spec:
      crossplane:
        version: ">=v1.6.0"
      dependsOn:
        - provider: crossplane/provider-aws
          version: ">=v0.23.0"
    

此示例可以在 github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane/tree/main/Chapter07/Hand-on-examples/configuration 中找到。我们将把最后一个 Amazon S3 桶示例打包为名为 aws-bucket 的配置,该配置设计为支持任何版本大于或等于 v1.6.0 的 Crossplane。AWS Crossplane 提供程序被添加为该包的依赖项。

  1. 接下来,我们将执行 Crossplane CLI 命令 build 来生成 configuration 包。我们将在包含 crossplane.yaml 文件和所有组合的文件夹中运行该命令。此步骤将输出一个具有 **.xpkg 扩展名的文件。一旦包生成,下一步是将包推送到任何符合 OCI 标准的镜像注册表中。Crossplane CLI 的 push 命令将创建一个符合 OCI 标准的镜像并将其移动到注册表中。以下是构建和推送配置的命令:

    # Build the configuration package
    kubectl crossplane build configuration
    # Push the image into an image registry
    kubectl crossplane push configuration arunramakani/aws-bucket:v1.0.0
    
  2. 如果你还没有设置 Crossplane CLI,请使用以下两个命令:

    # Download and run the install script
    curl -sL https://raw.githubusercontent.com/crossplane/crossplane/master/install.sh | sh
    # Move the plugin to the bin folder
    sudo mv kubectl-crossplane /usr/local/bin
    

请参考以下屏幕截图以了解 macOS 上的安装:

图 7.1 – Crossplane CLI 安装

图 7.1 – Crossplane CLI 安装

请注意,默认的镜像注册表是 Docker Hub。你应该使用 docker login 在 CLI 中登录到 Docker Hub。你可以看到该镜像可在 hub.docker.com/repository/docker/arunramakani/aws-bucket 上找到。你也可以配置其他你选择的镜像注册表。

信息

请注意,每个组合和 XRD 组合都保存在不同的文件夹中(BucketIAM)。这将帮助 build 命令映射和验证组合和 XRD 组合。如果所有组合和 XRD 都保存在同一文件夹中,你将看到构建错误。

请参考以下截图,我们在其中创建了配置和符合 OCI 标准的镜像:

图 7.2 – 打包并推送 OCI 配置镜像

图 7.2 – 打包并推送 OCI 配置镜像

一旦配置作为镜像存储在注册表中,我们可以将 configuration 包安装到 Crossplane 控制平面中,并开始使用组合。我们将在接下来的章节中详细讨论这一点。

安装和使用配置

我们有两种方式可以在 Crossplane 生态系统中安装配置。第一种方式是使用以下 Crossplane CLI:

# To install a new configuration package
kubectl crossplane install configuration arunramakani/aws-bucket:v1.0.0 aws-bucket
# To upgrade an existing configuration package
kubectl crossplane update configuration aws-bucket v1.1.0

CLI 将名称和镜像作为两个参数进行安装。它甚至会安装我们定义的依赖 AWS 提供者。一旦配置安装完成,可以使用以下命令查看配置和 ConfigurationRevision 的详细信息。每次配置更新都会创建一个新的 ConfigurationRevision,并且在任何给定时间只有一个修订版是活动的:

图 7.3 – 安装配置

图 7.3 – 安装配置

第二种安装配置的方法是使用 Configuration.pkg.crossplane.io 类型的 YAML。请注意,Crossplane 核心中有两种不同 API 组的配置类型。第一个 API 组 meta.pkg.crossplane.io 用于构建 Configuration 包。第二个 API 组 pkg.crossplane.io 用于安装 Configuration 包。以下是一个示例配置 YAML:

apiVersion: pkg.crossplane.io/v1
kind: Configuration
metadata:
  name: aws-bucket
spec:
  package: arunramakani/aws-bucket:v0.7.0
  packagePullPolicy: IfNotPresent
  revisionActivationPolicy: Manual
  revisionHistoryLimit: 3

在 CLI 中,我们没有使用 PackagePullPolicyRevisionActivationPolicyRevisionHistoryLimit 属性。PackagePullPolicy 的工作方式与其他 Kubernetes 类型的 ImagePullPolicy 类似。RevisionActivationPolicy 可以是 AutomaticManual,默认为 Automatic。当设置为自动时,包中的新 XRs 将被安装,旧包中的 XRs 将变为非活动状态,新的 XRs 将变为活动状态并负责资源的协调。当升级 Configuration 包时,你会看到两个 ConfigurationRevisions,每次版本递增都会创建一个新的 ConfigurationRevision。同时,也可以看到 CompositionRevision 伴随着组合的变化而被创建。请参考以下截图,其中有一个活动的和一个非活动的 ConfigurationRevision:

图 7.4 – 配置更新和修订版

图 7.4 – 配置更新和修订版

如果我们将 RevisionActivationPolicy 设置为 Manual 模式,我们必须手动编辑修订版并将其设为 ActiveRevisionHistoryLimit 字段是 Crossplane 跟踪的最大修订版数量。以下章节将探讨测试 Crossplane 配置的方法。

测试配置

平台开发者需要一种方法来测试他们开发的 XRs 和配置。此外,许多团队可能有兴趣实践测试驱动开发。本书的这一部分将探讨 KUbernetes Test TooLKUTTL)作为测试驱动开发和配置测试流水线的测试工具。KUTTL 是一个声明式测试工具,用于测试最佳的 Kubernetes 控制器状态和 CRDs。KUTTL 的关键特性是针对 CR 编写声明式测试用例。由于 KUTTL 能够很好地与 CR 和 CRDs 配合使用,它也能够与 XR、XRDs 和 Claim 良好配合。首先,我们将了解所需的基本安装和设置。

安装 KUTTL

KUTTL CLI 是 kubectl 的扩展。要安装 KUTTL,我们首先需要安装 Krew。这是一个 kubectl 插件管理器,帮助发现、安装和更新 kubectl 插件。要在 macOS/Linux 操作系统上安装,请运行以下脚本:

(
  set -x; cd "$(mktemp -d)" &&
  OS="$(uname | tr '[:upper:]' '[:lower:]')" &&
  ARCH="$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\(arm\)\(64\)\?.*/\1\2/' -e 's/aarch64$/arm64/')" &&
  KREW="krew-${OS}_${ARCH}" &&
  curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/latest/download/${KREW}.tar.gz" &&
  tar zxvf "${KREW}.tar.gz" &&
  ./"${KREW}" install krew
)

设置路径,确保我们可以在终端中访问 Krew,使用以下命令:

export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"

在其他操作系统上安装 Krew 的指令是不同的。要在不同操作系统上下载并安装 Krew,请参考 krew.sigs.k8s.io/docs/user-guide/setup/install/。安装 Krew 后,KUTTL 设置只需一个简单的步骤——执行以下指令:

kubectl krew install kuttl

执行上述命令后,您将在本地环境中成功安装 KUTTL。下一步是了解 KUTTL 项目的结构以及设置测试的基础。

KUTTL 测试设置

KUTTL 设置有三个关键组件。TestSuite.kuttl.dev 是第一个组件,也是核心配置。它包含整个测试套件的配置。以下是一个 TestSuite 配置示例:

apiVersion: kuttl.dev/v1beta1
kind: TestSuite
# Information on k8s cluster to use for testing
startKIND: false
kindContext: gke_crossplane-339717_us-central1_autopilot-cluster-1
skipClusterDelete: true
# Commands to be executed for any initial setup before testing
commands:
  - command: kubectl apply -f init/
# Directory where all our tests are kept 
testDirs:
- ./bucketwithcredential

请注意,TestSuite 文件的名称应该是 kuttl-test.yaml,以便 KUTTL CLI 查找测试配置。StartKINDKindContextSkipClusterDelete 是定义 Kubernetes 集群用于测试的配置项。在上面的示例中,我们使用了来自 GCP 的现有 Kubernetes 集群。通过 KindContext,我指定了 kubeconfig 中 Kubernetes 集群的名称。我们也可以创建一个新的 kind Kubernetes 集群进行测试,并在测试结束时销毁它。这对于测试流水线非常有用。参考 KUTTL 文档以了解完整的集群配置选项。TestSuite 文件的 Commands 部分将使我们能够执行所有初始化。在我们的实践示例中,我们正在初始化 ProviderConfig。完整示例请查看 github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane/tree/main/Chapter07/Hand-on-examples/test-configuration

请注意,我们的 ProviderConfig 并不包含任何实际的 AWS 凭证。ProviderConfig 位于 Git 仓库的 init 文件夹中。我们初始化 ProviderConfig 是为了确保 Crossplane 不会因为缺少配置而报错。请记住,测试的范围是验证我们的 XR 和 Claim 是否正确转换为 MR。换句话说,我们将测试我们的组合逻辑。如果 MR 按预期创建资源,那是供应商测试的范围。如果我们使用动态类型的 Kubernetes 集群,我们应该在初始化过程中安装 Crossplane、AWS 提供程序以及相关配置。由于我们使用的是现有集群,这不在我们的测试范围内。

提示

如果你希望进行端到端测试,或者你的测试用例依赖于我们从外部资源返回的一些状态字段,请在 ProviderConfig 中提供实际的云凭证。

第二个关键组件是测试文件夹和测试步骤。KUTTL 会扫描 TestSuite 文件中指定的 TestDirs 文件夹的所有子文件夹中的测试用例。每个文件夹就是一个测试用例。在我们的例子中,有两个测试文件夹。bucket-failure 文件夹包含一个会失败的测试用例,bucket-success 文件夹包含一个会成功的测试用例。每个测试用例可以有多个按指定顺序执行的步骤。KUTTL 可以从文件名的前缀识别步骤的编号。请注意,测试用例文件夹中的文件(在 Git 示例中)有一个数字前缀。

最后一部分是实现单个测试步骤。每个测试步骤可以包含我们在 Kubernetes 集群中应用的配置(XR/Claim)以及相应的断言配置(MR)。这些断言不必定义整个 MR,而是可以包含我们想要验证的字段。我们可以在一个断言步骤文件中定义多个断言配置。在前面的例子中,我们应用了 bucket Claim,并验证组合是否正确地修补了 bucket 名称(参考 Git 仓库中的 bucketwithcredential 文件夹)。我们甚至可以断言并验证 MR 中的状态字段,只要提供有效的 ProviderConfig。通过以下命令从根文件夹执行测试,该文件夹中有 kuttl-test.yaml 文件:

kubectl kuttl test

请注意,bucket-failure 文件夹的断言配置中的 bucket 名称与 bucket Claim 中的名称不同,因此测试用例会失败。请参见以下截图,其中一个测试用例失败,另一个测试用例成功:

图 7.5 – KUTTL 测试用例

图 7.5 – KUTTL 测试用例

信息

我们刚刚介绍了 KUTTL 的基本内容。要更详细地了解这个工具,请访问 kuttl.dev/docs/

接下来的部分将讨论将 KUTTL 用作 测试驱动开发TDD)工具进行 XR/Claim 开发的可能性。

TDD

TDD(测试驱动开发)是一种软件开发实践,开发者首先从需求中编写测试用例,然后编写通过测试用例的代码。这是一种迭代开发模型,我们从失败的测试用例开始,逐步演化代码使其通过测试。使用 TDD 有很多好处,包括干净的代码和全面的测试覆盖率。讨论所有的好处超出了本书的范围。本节将重点讨论在 XR 开发中使用 TDD。下图表示了 XR/Claim 的迭代 TDD 过程:

图 7.6 – XR/Claim 的 TDD

图 7.6 – XR/Claim 的 TDD

以下步骤描述了 XR/Claim 的 TDD 阶段:

  1. 使用上一章讨论的权衡分析来定义 API 范围和边界。

  2. 使用 XRD 配置创建 XR/Claim API 规范,采用范围和边界定义。这将是 API 消费者的一个要求。

  3. 在组合中定义每个资源的最终 MR 状态。这仅有 API 实现要求。组织政策和合规性要求将成为实现要求的一部分。

  4. 步骤 23 中定义的所有需求开发 KUTTL 测试用例。运行测试用例,查看所有实例均未通过。

  5. 开发组合并重新运行测试用例。迭代直到所有测试用例成功。

  6. 完善 API 范围和边界需求,继续循环。

我们可以将 KUTTL 与 Kubernetes 生态系统中的其他工具(如 Skaffold)结合使用,使 TDD 更加简便。本节讨论完了配置/组合的测试。在本章的最后一节,我们将讨论如何将 Crossplane 扩展为多租户生态系统。

多租户控制平面模式

通常,多个产品团队必须访问控制平面平台,以利用平台工程师构建的组合配方。本节将介绍 Crossplane 支持的不同模式,以启用多租户控制平面。以下是 Crossplane 用户可以选择的两个关键模式:

  • 使用单个集群的多租户

  • 多租户与多个集群

使用单个集群的多租户

使用单个集群的多租户是一种模式,在这种模式下,所有的产品团队使用一个单一的 Crossplane 控制平面。控制平面被配置为支持在同一集群内实现多租户。以下事实描述了这种设置的样子:

  • 产品团队通过命名空间 Kubernetes 构造进行隔离。每个产品团队应分配一个命名空间。

  • 如前所述,XR 和 Claim 之间的区别,Claim 是命名空间范围的,而 XR 是集群范围的。

  • 此外,我们之前提到过,XR 仅应由平台团队使用。

  • 精确的 RBAC 可以应用于给定命名空间中的 Claim API,基于团队成员角色和 API 组。

  • 组织可以实现 RBAC,使用默认的 Kubernetes RBAC API,或者使用通用的策略引擎,如 OPA(Gatekeeper)。

除了前述要点外,每个团队还应拥有不同的外部提供商凭证,以便跟踪使用情况、成本、监控、审计等。这可以通过使用以命名空间命名的 ProviderConfig 轻松实现。我们还应当在组合中补丁来自 Claims 命名空间引用的 ProviderConfigRef。下图展示了帮助你可视化单集群多租户架构:

图 7.7 – 单集群多租户

图 7.7 – 单集群多租户

以下是参考该模式的代码片段:

# ProviderConfig for each team 
# Match the ProviderConfig name to the namespace
# Example ProviderConfig for team-alpha
apiVersion: aws.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
  name: team-alpha
spec:
  credentials:
    source: Secret
    secretRef:
      namespace: crossplane-system
      name: team-alpha-creds
      key: creds

在组合中的每个资源下,动态地补丁ProviderConfigRef,指向 Claims 命名空间:

# Patch claims namespace to the ProviderConfigRef of the MR
patches:
  - fromFieldPath: spec.claimRef.namespace
    toFieldPath: spec.providerConfigRef.name

接下来的部分将探讨多个集群下的多租户设置。

多租户与多个集群

一些组织设置可能有多个独立的业务单元,具有不同的基础设施需求,如监控、成本管理和合规性。我们可能需要设置多个 Crossplane 控制平面。以下两种基本模式将支持这种环境:

  • 配置:我们可以利用本章前面讨论的 XR/Claim 打包机制来可靠地开发和分发 XR/Claim。

  • 嵌套 Crossplane:一个中央 Crossplane 控制平面可以管理每个业务单元的其他 Kubernetes 集群。我们可以从中央 Crossplane 集群使用 Helm 提供者,在其他集群中设置 Crossplane。

我们还可以尝试在单个集群中进行多个 Crossplane 设置,每个租户/团队一个。我们可以尝试使用vcluster等工具。它是一个高级模式。我们在这里尝试的是在一个 Kubernetes 内部运行另一个 Kubernetes。如果你有类似的用例,可以尝试使用 vcluster 进行设置。这也结束了多租户的讨论。

总结

本章讨论了扩展和扩展 Crossplane 的各个方面。在过去几章中,我们已经逐步学习了从 Crossplane 的基础知识到许多高级概念。我们涵盖了使用 Kubernetes 和 Crossplane 构建现代化控制平面的许多细节。这也标志着本书第二部分的结束。

书籍的第三部分将探讨一种配置管理方法以及一些配置管理工具和技巧。这将是一次将应用程序和基础设施自动化与 Crossplane 及其他配置管理工具统一的旅程。

第三部分:配置管理工具和示例

Kubernetes 配置管理生态系统中存在大量工具,我们缺乏对权衡的明确指导。本书的这一部分将指导云原生从业者选择最适合其用例的 Kubernetes 配置自动化工具。

本部分包括以下章节:

  • 第八章权衡取舍

  • 第九章使用 Helm、Kustomize 和 KubeVela

  • 第十章使用 Crossplane 上线应用

  • 第十一章推动平台采纳

第八章:第八章:了解权衡

在前几章中,我们学习了很多关于 Crossplane 的内容,从基础到许多高级模式。同时,我们介绍了统一的应用程序和基础设施自动化方法。本章将回顾并从整体上分析统一自动化的配置管理。本章深受 Kubernetes Steering Committee 荣誉成员 Brian Grant 的白皮书《Kubernetes 中的声明式应用程序管理》(goo.gl/T66ZcD)的影响。该白皮书主要讨论Kubernetes 资源模型 (KRM)。我们将从 KRM 和Crossplane 资源模型 (XRM) 的角度讨论本章的主题。本章将定义统一自动化方法的范围,并继续探讨许多其他概念,例如可用的工具、常见的陷阱以及使用不同模式时的权衡。从某种意义上说,这是对第六章 API 边界讨论的详细回顾。

本章涵盖的主题如下:

  • 统一自动化范围

  • 复杂度时钟、需求和模式

  • 开放应用模型

  • 专业化和可扩展的抽象

  • 变更频率的影响

统一自动化范围

我们中的大多数人对“统一的应用程序和基础设施自动化”有不同的理解。本节将帮助我们更加清晰地理解这一范围。我们部署的任何应用程序/产品,通常都是定制开发的专用应用程序和现成的 (COTS) 组件的组合。定制应用程序指的是为我们的需求编写的、具有特定用途的软件。定制应用程序通常是无状态的、容器化的工作负载。从 Kubernetes 的角度来看,它们是运行在 Pod 上的工作负载,Pod 是 Kubernetes 中的基本计算单元。COTS 组件通常是有状态的基础设施组件,例如数据库、缓存系统、存储系统和消息系统。在云原生时代,大多数 COTS 组件是黑箱式的、完全托管的软件即服务 (SaaS) 或 平台即服务 (PaaS)。COTS 供应商通过暴露 CRUD API 来与资源交互,并为安全性、扩展、监控和更新等问题提供精确的配置控制。这些特定的配置控制将支持不同消费者的使用案例。当我们谈论统一的应用程序和基础设施自动化时,这是指使用相同的工具、流程和模式来配置定制应用程序和 COTS 部署。换句话说,定制应用程序可以遵循 KRM 标准,COTS 依赖项可以遵守 XRM 标准,即 KRM 的扩展。这样统一的方法有许多优点:

  • 在现代软件工程领域,产品团队通过垂直拆分来实现最大的交付速度。这些垂直拆分的团队既负责定制应用程序,也负责它们依赖的 COTS 组件。统一的工具、流程和模式将显著减少认知负担。

  • 这将减少对专门团队管理 COTS 组件的需求,进一步加速交付速度。

  • 定制应用程序及其 COTS 依赖项的政策配置数据可以非常简单。我们可以在一个地方和格式中验证架构适应性函数、安全问题和合规标准等政策。

  • 所有 COTS 供应商都可以提供符合 KRM 规范的 API(MRs),作为通用的应用程序依赖和集成标准。这已经在所有主要云资源的 Crossplane 提供商中实现。这个列表正在不断扩展,涵盖其他外部资源,如 Git Lab、Argo CD、Rook 和 Cloudflare,以实现端到端自动化。

接下来的部分将涵盖一些统一自动化方法的要求、模式和工具。

复杂性时钟、要求和模式

配置复杂性时钟是一个概念,用来解释配置管理如何随着时间的推移变得复杂。它探讨了配置管理演变的不同阶段、使用场景及其陷阱。最初是从应用程序配置的角度在这篇博客文章中讨论的:mikehadlow.blogspot.com/2012/05/configuration-complexity-clock.html。我们将从 Kubernetes 配置管理的角度来审视这个相同的概念。

配置复杂性时钟

假设我们正在将我们的第一个应用工作负载部署到 Kubernetes 中。首先,Kubernetes 中的每个工件,例如 Deployment、Service 和 Ingress,都可以作为独立的配置工件来管理。在我们操作小规模时,这可能不会成为问题。但很快我们会意识到,当应用配置分散在多个 Kubernetes 资源上时,持续进行发布和回滚就会出现问题。我们将开始考虑使用 Helm 等打包和发布管理工具。Helm 是一个基于模板的配置管理工具。我们将通过变量来参数化值,以实现配置抽象,并仅暴露有限的属性。一个典型的例子是为每个部署目标(生产和预发布)设置副本数。很快,组织会意识到 Kubernetes 的强大,并决定在 Kubernetes 生态系统中进一步部署类似的工作负载。由于新应用的配置数据与初始 Helm 模板看起来相同,我们将选择参数化更多的变量,以支持多个应用使用相同的 Helm 模板。这将是推动重用目标并最小化需要维护的配置的一个好方法。这些新参数的典型例子包括命名空间、标签和应用名称。

组织将再次从 Kubernetes 中获得更多好处,并决定尝试一些新的工作负载。这些工作负载可能与初始的 Helm 模板有相似的配置数据,仅做了少量定制。我们会决定分叉主 Helm 模板来进行所需的定制。基于模板的抽象在需要定制时变得复杂,难以重用。随着配置时钟的持续运行,经过一段时间后,我们将看到太多的分叉,难以保持同步。另一方面,许多独立模板会引入新的参数,以支持新的本地使用案例。这是我们通过 Helm 模板创建的抽象的漏洞。随着时间的推移,我们可能会将所有值参数化,完全侵蚀掉抽象。以这个 WordPress 图表为例:github.com/helm/charts/tree/master/stable/wordpress。从最初的提交开始,参数数量缓慢增加,逐渐变多。完全侵蚀的模板难以阅读,最终用户会发现很难理解每个参数的用途。用户会发现,很难理解那些与他们无关的参数。例如,开发人员可能不知道如何在前述的 WordPress Helm 图表中定义 Ingress 参数。

随着配置时钟的不断推进,我们必须加强配置的安全性。基础设施运营者希望拥有配置的控制权,因为应用程序拥有者并不知道如何配置安全参数。Helm 后渲染功能可以帮助我们解决这个问题,并通过延迟绑定注入安全配置。但是,由于定制化分支过多和渲染输出不可预测,基础设施运营者仍然会面临在部署流水线中管理后渲染步骤的挑战。有些开发者可能决定使用如 Terraform 这样的 DSL 来管理配置,因为它容易理解。然而,DSL 继承了输入变量的相同参数化问题,后渲染定制化步骤在 DSL 中同样具有挑战性。此外,DSL 存在开发者学习曲线复杂且对测试等问题支持有限的问题。漏洞或合规性扫描是我们可能面临的另一个工具问题领域。随着我们规模的扩大,Crossplane 配置也可能面临类似的问题。讨论这些局限性并不是为了阻止使用任何特定工具。每个工具都有其使用场景和权衡。例如,Helm 打包在小规模操作和其他少数用例(如发布管理)时表现良好。某种程度上,我们会研究如何通过结合工具和模式来克服这些局限性。

配置管理要求

为了在不被复杂时钟问题困住的情况下管理配置,我们应该牢记一些指导原则。这些指导原则将成为在选择配置管理工具和模式时进行权衡分析的技术要求。将这些指导原则融入我们的配置管理实践中,将使我们能够轻松地进行演进和扩展:

  • 保持配置数据对人类和机器可读,以便在配置流水线中进行变更。诸如特定环境补丁、安全配置补丁和策略作为配置等要求,最好在流水线中作为自动化步骤实施。避免将配置作为代码进行维护,因为它们难以操作。代码渲染的输出可能并不总是容易被机器读取。

  • 配置扫描要求,如审计、合规性和安全性,与作为数据的干净配置非常契合。在整个流水线中尽可能将配置保持为数据,并将操作配置的代码分离,这对于满足不断变化的需求至关重要。许多工具在 Kubernetes 生态系统中得到了发展,因为配置数据与代码(控制器)分开存储(在 etcd 中),并且具有标准的数据格式 KRM。

  • 关注点分离是配置管理的另一个关键方面。并非所有自动化所需的配置项都应由单一角色来定义。我们应该管理配置,使不同的人可以轻松协作。

  • 构建专门的抽象层,以支持定制化并复用配置。例如,Pod 是一个基本的配置抽象,Deployment、ReplicationController、DaemonSet 等都是在其之上构建的。后续章节中,我们将看到来自 Crossplane 和完整应用管理视角的更具体的例子。

  • 对源配置和最终的变更配置进行版本控制,后者表示期望的状态。在应用到集群后,应避免修改配置。应避免使用如变更的准入控制器等工具。晚期配置绑定可能会以难以预测的方式失败(尽管准入控制器适用于策略验证)。

  • 使用组合将应用程序及其依赖资源绑定为一个单一的 API,同时保持关注点分离,并使用嵌套的子 API。为整个应用包的发布管理提供支持,如发现、生命周期管理和依赖管理等。

我们将在接下来的部分深入探讨所有这些指导原则。下一部分将研究可用于配置管理的不同模式及每种模式的权衡。

模式与权衡

随着规模的扩大,复用配置变得具有挑战性。大多数复用模式在配置复杂性时钟一节中已有讨论。本节将更详细地介绍这些模式及其优缺点。以下是配置复用的几种著名方法:

  • 分支:这是最常用的配置复用方法之一,主要因为它便于定制。重基是同步配置随事物发展而演变的策略。重基过程无法自动化,因为人类必须解决合并冲突。这种方法在短期内提供了高度的灵活性和维护的舒适性。随着时间的推移,一些挑战出现了。随着时间的推移,分支会大幅分歧,带来重基的挑战。随着独立的敏捷团队管理不同的分支,复用的优势可能被忽视,以便跟上发展速度。一年前,我有过类似的经历,当时我决定分支一个代码库,以满足定制化的部署需求。不同的敏捷团队分别管理两个分支。随着时间的推移,代码合并活动完全被忽视,最终屈服于交付压力。最后,我们陷入了一个状态,两个分支永远无法同步,且冲突成千上万。分支解决方案最适合快速尝试概念验证。我不建议在此之外使用它们。

  • 模板参数化:我们在第二章以及本章前面部分讨论了基于模板的参数化。在小规模时,它运作良好,但在规模扩大时会面临抽象泄漏和团队协作问题。此外,它还可能促使我们采用分支模式,因为它使得自定义变得复杂。像 Helm 这样的工具就属于这一类。作为一种基于模板的参数化工具,Helm 因其应用发现和发布管理等其他功能而极受欢迎。

  • 补丁/覆盖:这种方法将基础配置作为纯数据点,并使用补丁文件覆盖所需的自定义变量。我们可以替换现有配置项的值或向整体基础配置中添加新配置。我们还可以将这种技术用作基于模板的抽象工具(例如 Helm)的后渲染步骤。通过这种方式,额外的参数化可以作为后渲染补丁来管理,从而避免了信息泄露。它是一种目前广泛使用的方法,尤其是自从 Kustomize 工具问世以来。这些工具不需要人工干预,可以作为部署流水线中的一个自动化步骤。

  • 组合:这是一种技术,我们通过组合依赖项来构建更高级别的 API 和多个被不同用户角色使用的子 API。我们已经充分讨论了这种模式,因为 Crossplane 本身就是一个组合工具。拉取和推送是组合中的两个子模式。在拉取模式中,依赖项是直接引用的。嵌套 XR 是 Crossplane 世界中的典型示例。对于推送模式,依赖项通过运行时绑定间接跨 API 引用。推送模式适用于分离关注点并构建可扩展的抽象。通过标签在两个独立的 XR 之间进行资源引用是推送组合的一个示例。组合和补丁/覆盖配合使用能很好地实现自定义。例如,在 Crossplane 中,我们可以使用补丁来生成环境特定的组合(如生产/预生产环境)。

组合、补丁/覆盖和基于模板的工具可以相互补充,结合使用它们可以帮助你构建一个强大的部署流水线。我们将在接下来的两章中展示一些这样的实践示例。本章的下一节将介绍开放应用模型OAM),这是一种开源标准,用于定义面向应用和团队的配置管理。

开放应用模型

OAM 是由微软和阿里巴巴开发的开源标准,旨在指导我们创建更高级别的抽象 API 用于应用部署。换句话说,它是通过组合模式来创建应用配置管理模型。该标准关注解决以下三个问题:

  • 开发者聚焦:将开发者直接暴露于 Kubernetes 配置管理中来部署应用,可能会让他们花费更多时间去搞清楚基础设施细节,而不是专注于应用。因此,OAM 尝试让开发者专注于应用本身。

  • 供应商依赖:配置应用通常会依赖于底层基础设施。将应用配置与底层基础设施完全解耦,可以实现工作负载的可移植性。Kubernetes 在一定程度上做到了这一点,但在跨领域问题和 COTS 依赖方面仍需要更多的工作。

  • 可扩展性:大规模的配置管理可能会遇到很多问题,尤其是在平衡重用和定制化时。OAM 提出了一个适用于大规模重用和定制化的模型。

OAM 提出了基于角色的分层配置管理,并根据角色抽象来解决这些问题。OAM 定义了三种角色来管理不同的应用部署问题,如下图所示:

图 8.1 – OAM 角色

图 8.1 – OAM 角色

KubeVela,OAM 实现

OAM 社区开发了 KubeVela,一个实现该规范的项目。它是一个 CNCF 沙箱项目,并且是一个像 Crossplane 一样的组合工具。KubeVela 仅专注于组合定制应用配置,而 Crossplane 同时组合定制应用和 COTS 基础设施/外部依赖。KubeVela 和 Crossplane 可以通过以下两种不同的模式相互补充:

  • KubeVela 组合 Crossplane:我们可以使用 KubeVela 组合定制应用部署抽象,并可以依赖 Crossplane 作为 COTS 外部依赖提供者。此模式要求 Crossplane 控制平面必须在应用工作负载集群中存在。

  • Crossplane 组合 KubeVela:我们可以使用 Crossplane 为定制应用和 COTS 基础设施/外部依赖组合抽象。对于定制应用,它可以通过 Crossplane 提供者与 Kubernetes 结合使用 KubeVela。此模式可以与集中式的 Crossplane 控制平面或分布式的 Crossplane 控制平面一起使用。

以下图展示了 KubeVela 组合 Crossplane 模式,其中 Crossplane 控制平面和应用工作负载集群是相同的:

图 8.2 – KubeVela 组合 Crossplane

图 8.2 – KubeVela 组合 Crossplane

上面的图示展示了一个具有 RDS 数据库作为 COTS 依赖项的 Web 服务工作负载。KubeVela 的工作负载构造定义了应用程序的工作负载类型。Traits 定义了所有工作负载特性,如路由和流量。组件帮助定义 COTS 依赖项。首先,我们将通过 Crossplane 将 RDS 组合成 XR API。之后,我们可以将 XR 作为 KubeVela 中的组件进行组合。下图展示了第二种模式,即 Crossplane 将 KubeVela 进行组合:

图 8.3 – Crossplane 组合 KubeVela

图 8.3 – Crossplane 组合 KubeVela

该图展示了在同一集群和远程集群中的应用程序管理。此外,图中还展示了使用 Helm 提供程序来设置 KubeVela 和其他集群依赖项。我们将在第九章中介绍 KubeVela 的实践示例。

信息

OAM 和 KubeVela 是值得进一步探索的激动人心的话题。欲了解更多信息,请访问oam.dev/

接下来的部分将介绍两种令人兴奋的模式,这些模式可以帮助平台团队构建可扩展和可重用的平台 API。

专门化和可扩展抽象

随着我们在 Kubernetes 中部署的应用程序数量的增加,可能会出现配置管理的指数级增长。管理大量 KRM/XRM 配置文件容易出现人为错误,且难以保持同步,且需要高度熟练的团队。重用是保持配置量低的关键。但各个团队的定制需求将限制我们进行重用,除非我们有一种简单快捷的方法来做到这一点。此外,敏捷和产品工程实践将从最小外部依赖的角度带来额外压力。专门化和可扩展的抽象对于解决这些问题至关重要。接下来的部分将介绍专门化抽象。

专门化抽象

专门化抽象是一种技术,我们通过构建基本抽象并重用该基本抽象来创建处理定制需求的专门化抽象。这实际上就是面向对象编程中的继承模式。此外,这是 Kubernetes 核心中的一种广为人知的模式。以 Pod 资源类型为例,Deployment、DaemonSet 和 Job 是使用 Pod 作为基础的专门化抽象。让我们来看一些作为平台团队可以构建的专门化抽象:

  • 策略抽象层:作为一个组织,我们可能会针对每种云资源制定策略。这些策略可能来自多个领域,例如安全、架构和合规性。如果你的组织的云策略就是这样的方式,可以考虑在 Crossplane 提供程序之上创建一个抽象层,并为每个资源使用 XR。该层可以作为创建配方的坚实基础。

  • 数据库模板:不同的产品团队将有不同的数据库需求。有些产品团队可能要求地理分布式数据库,而其他团队则可能满足于单一地区的可用性区域容错。第三个团队可能要求一个符合 PCI-DSS 标准的数据库来存储支付交易。我们可以创建一个基础数据库 XR,并在其上创建一个专门的 XR 来满足每个需求。

  • 工作负载模板:并非每个工作负载都是相同的。Web 应用程序、RESTful API、定时任务和数据转换是一些工作负载的例子,每种工作负载都有不同的依赖要求。例如,定时任务工作负载不需要 Ingress。我们创建一个包含所有通用跨领域关注点的主工作负载,并在此基础上开发一个专门的抽象。我们可以使用 Crossplane、KubeVela 或两者的混合来实现这一点。

这些只是一些可能适用于您环境的通用示例。找到适合您环境的正确抽象需要更深入地了解组织结构、技术成熟度、治理模型、工作负载等。基础和专门的抽象通常由平台团队开发,供应用程序操作员和开发人员使用。我们可能会遇到一个特殊场景,即应用程序操作员在平台团队开发的基础抽象上构建一个专门的抽象。我们可以在接下来的章节中查看可扩展抽象。

可扩展抽象

可扩展抽象是一种技术,我们构建一个部分抽象,并允许其他团队将其完全实现。这正是面向对象编程中的多态模式。此外,这是 Kubernetes 核心中一个广为人知的模式。Ingress 就是 Kubernetes 核心中的一个例子。每个托管的 Kubernetes 提供商都会为 Ingress 提供自己的实现。可扩展抽象是面向平台团队和应用程序操作员的。我们来看一些这种模式使用的示例:

  • 共享资源:我们以 VPC 作为共享资源的例子。不同场景下,有时需要创建和使用资源的多个变种。我们可以为这些资源制定标准的标签名称/值策略,其他 XR 模板可以选择带有适当标签引用的 VPC。这就是多态行为,并通过依赖注入提供了可扩展性。

  • 嵌套 XR:平台团队可以创建一个带有缺失依赖项的 XR 模板。缺失的依赖项可以单独实现。这两个部分可以通过嵌套 XR 模式组合起来。当缺失的依赖项随每个产品团队的需求变化时,选择此模式。一个开放数据库选择的应用程序模板就是嵌套 XR 在多态方式下表现的一个例子。应用程序操作员可以根据具体产品团队的需求完成该模板。

再次强调,这些只是示例性说明。这一节的抽象讨论到此为止。在接下来的章节中,我们将详细探讨配置变更的频率。

变更频率的影响

从变更频率的角度审视配置参数,将有助于我们根据 OAM 中定义的人物角色进行分类。变更频率的详细描述将引入所有权的讨论,因为这些视角是相关联的。本节内容从定制应用程序(符合 KRM)和 COTS 外部依赖(符合 XRM)的角度,介绍了配置变更频率。我们可以将配置变更频率分为以下三类:

  • 变化非常频繁:通常,应用程序 KRM 配置参数,如镜像标签、发布版本、环境变量、密钥引用和应用程序配置,变化非常频繁。这些配置以应用程序为中心,主要由应用程序开发人员拥有。如果我们使用基于模板或 DSL 的抽象,它们是作为变量暴露的良好候选项。假设使用的是普通配置 YAML 文件而不是基于模板或 DSL 的抽象,开发人员可以拥有一个版本控制的补丁/叠加文件。当使用组合作为解决方案时,这些配置应通过高级 API 向开发人员暴露。

  • 不太频繁变化:诸如应用程序上下文相关信息(命名空间、标签、引用等)、资源限制(内存、卷等)以及与发布相关的参数(副本数量和发布方式)等配置,属于不太频繁变化的配置。上述配置主要会因环境不同而变化,或者当有新的操作要求时发生变化。例如,副本数量、命名空间或标签可以根据部署环境(生产环境、预生产环境等)发生变化。无论是使用基于模板、DSL,还是普通 YAML 或组合,最好使用补丁机制来管理不太频繁变化的配置值,并根据管道中的目标环境选择补丁文件。使用补丁机制是因为应用程序操作员独立拥有这些配置。将它们暴露给开发人员会增加认知负担。

  • 很少变化:很少定制的配置是核心应用程序的基本结构。平台开发人员应拥有这些配置。基本结构是核心抽象,旨在减少应用程序操作员和开发人员的认知负担。此外,它将总结配方中的策略要求。通常,我们使用叠加、专业化和可扩展的抽象来实现不同产品团队和工作负载所需的核心配置的多个变体。

XRM 变更频率

符合 XRM 标准的 COTS 外部基础设施依赖项通常不会频繁变化。例如,外部基础设施的变化,如区域扩展、自动扩展设置、安全配置调整、升级和迁移,通常会在初始设置之后的较低阶段发生。使用 XRs 构建这些基础设施抽象应由平台团队负责。应用程序操作员可以做一些 XR 组合练习,创建一个新的、专门的配方。换句话说,他们可以使用嵌套 XR 模式创建特定工作负载的抽象。如前所述,XRM 超越了基础设施依赖项,管理外部应用程序,使用如 Helm 和 SQL 等提供程序。这些配置可能会频繁变化。例如,每次应用程序发布可能会改变数据库的 SQL 架构。因此,应用程序操作员可以扩展现有配方,以满足产品团队的需求。

小结

本章讨论了大规模定制应用程序和 COTS 组件的统一配置管理的各个方面。我们介绍了配置管理中可用的不同工具、扩展过程中常见的陷阱,以及使用不同模式时的权衡。此外,我们还讨论了在不同情况下,不同工具和实践的组合如何互相补充。

接下来的两章将通过实际操作示例,尝试本章中讨论的一些配方。这些配方将包括 KRM 和 XRM 配置管理,随着我们朝着整个应用程序及其 COTS 依赖项的端到端自动化迈进。

第九章:第九章:使用 Helm、Kustomize 和 KubeVela

本章主要集中在定制应用程序的配置管理上。定制应用程序的 Kubernetes 配置包括部署、服务、Ingress、机密、ConfigMap、治理所需的标签、跨切面关注点、应用程序安全上下文和其他依赖项。管理这些配置需要仔细选择适合用例的模式和工具。此外,我们还必须考虑复用、团队协作和可扩展性。在上一章中,我们简单提到了一些工具,如 Helm、Kustomize 和 KubeVela,用于应用程序配置管理。本章将提供一个机会,深入探索这些工具。

本章涵盖的主题如下:

  • 应用程序配置管理能力

  • 使用 Helm 进行应用程序部署

  • 实践图表开发

  • 使用 Kustomize 定制配置

  • 使用 KubeVela 部署应用程序工作负载

应用程序配置管理能力

在 Kubernetes 环境中成功运营应用程序,从配置管理的角度来看,要求具备一些能力。以下是配置管理中的关键能力列表:

  • 打包:如前所述,将应用程序部署到 Kubernetes 中涉及配置多个资源。它要求具备一种能力,能够将所有这些资源打包成一个单一的包。

  • 生命周期管理:应用程序及其所有依赖项必须作为一个单一的部署执行到集群中,支持所需的发布管理构件,如滚动更新、回滚、版本管理和蓝绿部署。

  • 应用发现:这是日常操作所需的能力。它将使任何发现工具能够在仪表板上列出已部署的应用程序、其版本和依赖关系。

  • 应用描述定制:并非所有环境的配置都相同。例如,暂存环境中的复制数可能为 1,而在生产环境中,我们可能会设置水平 Pod 扩展。此能力在我们需要注入依赖项以实现关注点分离时也非常必要。

让我们在考虑这些能力的基础上,探索用于应用程序配置管理的工具。接下来的部分将深入探讨 Helm,这是我们要探索的第一个工具。

使用 Helm 进行应用程序部署

Helm 是 Kubernetes 生态系统中流行的配置管理工具之一。它早在 2015 年就已问世,并且在不断演进,解决了所有瓶颈。作为云原生计算基金会CNCF)毕业项目,表明其成熟性、生产准备性和价值。以下是 Helm 的三个关键概念:

  • Charts:Charts 是 Helm 中应用程序的基本单元。Chart 不过是将应用程序及其所有依赖项打包的包。

  • 仓库:一个捆绑的图表需要一种一致的存储方式,以便可靠地分发,仓库支持这一需求。虽然开源应用程序可以使用公共仓库,但私有仓库可以用于专有应用程序。从 Helm v3.8.0 开始,任何符合开放容器倡议OCI)标准的仓库都将支持 Helm。这意味着大多数容器注册表也支持 Helm 包。

  • 发布:这是图表在集群中运行的一个实例。当我们第一次安装图表时,它会创建一个新的发布版本。任何更新都会导致发布版本的递增。该结构使得发布管理具备如滚动更新、回滚和蓝绿部署等功能。

使用 Helm 需要客户端的 brew 来安装 CLI,而 choco 安装程序可用于 Windows。以下代码片段展示了如何在这两个操作系统上安装 CLI:

# Helm install macOS
brew install Helm
# Helm install Windows
choco install kubernetes-helm

更多安装选项,请访问 helm.sh/docs/intro/install/。我们将分两部分探索 Helm——第一部分将涵盖使用现有图表,第二部分将讲解新图表的开发。

使用现有图表

使用现有图表可以分为仓库管理、发布管理和集群发现。以下是一些仓库管理命令:

# Add a repo (Add bitnami repo to your local repo list)
helm repo add bitnami https://charts.bitnami.com/bitnami
# For private repository, use additional authentication flags
# To view the list of possible authentication flags
helm repo add --help
# Update the charts in all the added repo's
helm repo update
# Update the charts in a specific repo.
helm repo update bitnami
# Search for charts with the given name within the added repo
helm search repo wordpress
# Search charts in ArtifactHub, a famous open-source repo  
helm search hub wordpress
#List all the repositories added
helm repo list

前述的仓库管理命令足以满足我们日常的仓库操作。接下来,我们将看一些发布管理的 Helm 命令,如下所示:

# Install a chart
# Format
helm install <release-name> <chart-name>
# Example
helm install redis bitnami/redis
# Each chart will support a list of variables to be set
# Variables can be hierarchical. For example, the 'enabled' flag is under the 'auth' hierarchy in the bitnami/redis chart.
helm install redis bitnami/redis --set auth.enabled=false
# Install with a value set in the values file
helm install redis bitnami/redis -f values.yaml
# If the same variable is present in both the command line set and value file, the command line set takes precedence. 
helm install redis bitnami/redis -f values.yaml --set auth.enabled=false
# Update a release
# Format
helm upgrade <release-name> <chart-name>
# Example
helm upgrade redis bitnami/redis -f values.yaml --set auth.enabled=true
# Roll back a release 
# Format
helm rollback <release-name> <release>
# Example, roll back redis to the first release version 
helm rollback redis 1
# Uninstall format
helm uninstall <release-name>
# Uninstall redis release 
helm uninstall redis

前述命令是一些常用的发布管理命令。以下代码片段将涵盖几个集群发现命令:

# List all the Helm releases in the target cluster
helm list
# Find the status of a release named redis
helm status redis

现在是时候通过一个逐步的动手示例来探索新的图表开发了。

动手图表开发

Helm 图表只不过是一组配置模板,模板中包含变量占位符。当模板被渲染并用于安装时,这些占位符可以被具体的值替换。Helm 提供了一个强大的领域特定语言DSL),它提供了多种结构用于变量替换。在接下来的部分中,我们将学习一些常用的结构来掌握图表开发。

图表生成

一个 Helm 图表包包含一组组织好的文件和文件夹。我们要么需要理解其结构并从头开发,要么可以使用生成器。在本示例中,我们将使用生成器创建一个名为 hello-world 的图表(helm create <chart-name>),如以下屏幕截图所示:

图 9.1 – 创建图表

图 9.1 – 创建图表

让我们来看看每个文件的使用,如下所示:

  • Chart.yaml:这是一个包含图表描述的文件。它包含诸如支持的 Helm 版本、图表版本、应用程序版本、应用程序名称、描述、其他依赖图表、维护者等信息。它还包含一个名为 type 的属性,表示 applicationlibraryapplication 表示我们正在打包一个 Kubernetes 应用程序,而 library 表示该图表包含可重用的工具函数。

  • charts 文件夹:这是一个可以容纳依赖子图表列表的文件夹。我们可以出于多种原因使用子图表。将一个庞大的应用程序划分为多个小模块,并为每个模块创建一个子图表,是一种使用方法。另一种方式是将其用作应用程序依赖项(如数据库)的打包机制。我们也可以将其用作可重用函数的容器,作为共享库使用。需要注意的一点是,子图表可以独立部署,这意味着它们不能显式引用父图表,但父图表可以在需要时覆盖子图表模板的值。

  • values.yaml:这是一个文件,您可以通过 helm upgrade redis-install-1 bitnami/redis -f values.yaml 来使用它。此外,我们还可以使用 CLI 中的 set 标志来覆盖特定的值。

  • template 文件夹:NOTES.txt_helpers.tpl 和 Kubernetes 资源 YAML 模板是 template 文件夹的一部分。NOTES.txt 文件是一个模板文件,当我们运行 helm installhelm upgrade 时,它会被渲染并打印在控制台上。_helpers.tpl 文件将包含可在整个图表中使用的可重用函数。其余文件是与应用程序相关的标准 Kubernetes 资源模板。使用 Helm create 命令生成基础图表时,会将部署应用程序所需的 Kubernetes 资源列表添加到 template 文件夹中。我们可以删除不必要的资源。

  • tests 文件夹:这里可以存放单元测试,用于测试我们在资源模板中编写的逻辑。

Helm 使用来自 Go 语言的 template 包,并提供许多强大的模板构造来渲染复杂场景。接下来的章节将使用 hello-world 示例来解释每个概念。

变量访问

在模板渲染时,我们可以通过简单地指定变量层级来替换占位符变量,语法如下:

# Variable reference syntax - {{ variable-hierarchy }}
# Examples  
# Refer deployment.yaml line no 9
{{ .Values.replicaCount }}
# Refer _helpers.tpl line no 50
{{ .Release.Name }}

在前面的示例中,请注意 . 作为分隔符,表示变量层级。我们从 . 开始,表示根节点,然后引用根节点中的一个对象。请注意,我们可以有多个层级的变量,例如 {{ .Values.image.repository }}。以下是一些可以使用的重要内建根对象:

  • Release:此对象包含与发布相关的信息,如发布名称、发布命名空间、修订号等。

  • Values:由值文件或命令行设置标志组成的对象。

  • Chart:在 chart.yaml 文件中定义的值将可在此对象下使用。

还有更多可用对象,例如 FilesCapabilitiesTemplate 等。完整列表请参考 helm.sh/docs/chart_template_guide/builtin_objects/

提示

要从模板中删除一个对象或特定属性,请使用带有 null 值的 --set 命令行。例如,--set livenessProbe.httpGet=null

函数和管道

我们可能需要在进行一些转换后替换变量,内置函数和管道可以帮助我们完成这些操作。例如,参考以下代码片段:

# Refer _helpers.tpl line no 40
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}

在上面的例子中,我们引用了应用程序版本,并将一个字符串包含在引号内。这里有两点需要注意:引号是我们可以使用的内置函数,| 符号将帮助我们将一个指令的输出传递到另一个指令。以下是一些常用函数的列表:

  • indent:用于格式化配置 YAML。它接受一个数值输入,并根据指定的索引缩进行。

  • nindent:该函数的作用类似于 indent 函数,唯一的区别是它会在行首添加一个换行符。

  • trunc:截断字符串至指定的索引数。

  • trimSuffix:该方法以字符串后缀作为输入,如果操作字符串中存在该后缀,则将其截断。

  • replacereplace 方法可以在操作字符串中将一个子字符串替换为另一个子字符串。

  • semverCompare:此函数可用于比较两个语义版本。

这些是在 deployment.yaml_helpers.tpl 中使用的一些函数。请参考 helm.sh/docs/chart_template_guide/function_list/ 查看内置函数的详细列表。

信息

请注意,许多模板占位符中都有 - 符号——例如,deployment.yaml 第六行有 {{-。这指示模板引擎去除左侧的空白。同样,-}} 可以去除右侧的空白。

流程控制

流程控制使任何编程语言都变得强大,并使我们能够编码复杂的逻辑。Helm 的模板语言提供了三种流程控制。第一个可用的流程控制是标准的 if/else 语句。它有助于在特定条件下包含一个块。以下是来自 ingress.yaml 文件的代码片段,它检查 Kubernetes 版本以决定使用的 ingress 应用程序编程接口 (API) 版本:

# Refer ingress.yaml line no 9
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}

注意,Capabilities 是一个内置对象,提供目标 Kubernetes 集群的功能。第二个流程控制,with,允许我们创建一个具有特定变量作用域的块。参考以下来自 serviceaccount.yaml 的代码片段:

# Refer serviceaccount.yaml line no 8
# We create a scope block with variable Values.serviceAccount.annotations
{{- with .Values.serviceAccount.annotations }}
annotations:
  {{- toYaml . | nindent 4 }}
{{- end }}

还记得我们之前讨论的变量引用吗?我们提到过,初始的 . 代表所有对象的根。在 with 块内,定义会发生变化。在该块内的初始 . 将指代定义的作用域变量。第三个流程控制是用于循环的 range。请参阅以下来自 NOTES.txt 的代码片段:

# Refer NOTES.txt line no 2
# Loops over the hosts with an inner loop on path
{{- range $host := .Values.ingress.hosts }}
  {{- range .paths }}
  http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
  {{- end }}
{{- end }}

在前面的示例中,我们声明了一个新变量 host,并在循环中引用它。类似地,我们也可以在其他地方使用变量声明。

提示

我们可以在 helm installhelm upgrade 命令中使用 dry-rundisable-openapi-validation 标志来调试或验证 YAML 输出。

命名模板

命名模板经常被使用,它们充当静态自定义定义的函数。我们通过定义一个有名称的模板,然后将其导入到需要的地方。通常,这些命名模板在帮助文件中进行描述,并在整个图表中重复使用。请参阅以下代码片段中的两段代码:

# Template named hello-world.selectorLabels is defined in the _helpers.tps line no 45
# hello-world is a release name prefix added to avoid name conflicts when we have sub-charts and dependent charts.
{{- define "hello-world.selectorLabels" -}}
app.kubernetes.io/name: {{ include "hello-world.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
# deployment.yaml line number 12
# Template included with the name
matchLabels:
  {{- include "hello-world.selectorLabels" . | nindent 6 }}

请注意,模板输出可以与其他内置函数一起使用。我们已经涵盖了创建新 Helm 图表所需的大部分技能。更改 value.yaml 文件中的镜像名称为 hello-world 后,我们可以部署该图表。请参阅以下截图以了解图表安装过程:

图 9.2 – 安装图表

图 9.2 – 安装图表

在接下来的部分中,我们可以使用 Kustomize 来定制配置。

使用 Kustomize 自定义配置

无论是由 Helm 还是其他配置管理工具管理的配置,Kustomize 都是自定义配置的最佳工具之一。让我们来看一下 Kustomize 的一些用例,如下所示:

  • 将特定环境的自定义与基础配置分开是一个用例。例如,复制计数可以在暂存环境中进行,而生产环境可以启用自动扩展。

  • 管理跨领域的配置,避免将其包含在基础配置中,是另一个用例。例如,应用程序操作员在所有部署中处理特定于治理的标签时,可以将该配置与基础配置分开。这样可以实现关注点分离SoC),促进多角色协作而不产生摩擦。将服务网格配置作为跨领域关注点注入,亦是另一个示例。

  • 第三个用例是修复漏洞,作为配置管道中的一步。假设 nginx 镜像存在安全漏洞。安全团队可以在管道中添加一个自定义步骤,以确保所有部署都更新受影响的 nginx 版本。

  • 经典的用例是避免抽象泄漏,就像我们在前面的章节中多次讨论过的那样。当我们想要在几个相似的工作负载中重用基础配置模板时,可以将 Kustomize 视为新的参数。

以下截图展示了如何在多角色协作环境中使用 Kustomize 补丁:

图 9.3 – 配置自定义

图 9.3 – 配置自定义

我们来看一个使用 Kustomize 的简单示例。我们应该把基础配置放在一边,而 kustomization.yaml 放在另一边。kustomization.yaml 定义了如何自定义基础配置。以下是一个 kustomization.yaml 文件示例:

resources:
- deployment.yaml
commonLabels:
    team-name: alpha
namespace: test

上述配置是参考基础配置并定义如何进行自定义。deployment.yaml 文件是 resources 部分下的基础配置参考。commonLabels 配置将团队名称作为标签添加到部署中,而 namespace 配置将覆盖部署资源的命名空间。我们可以运行 kubectl kustomize . 命令来执行自定义操作。请参考以下截图:

图 9.4 – 配置自定义(续)

图 9.4 – 配置自定义(续)

Kustomize 也可以与 Crossplane 配置一起使用。添加标签到组合的示例可以在本章的实践示例仓库中找到。

提示

helm install 可以将 Kustomize 作为后处理步骤,方法是指定 kustomization.yaml 的路径。语法为 helm install <release-name> <chart-name> --post-renderer ./path/to/executable

使用 Helm 和 Kustomize 的示例可以参见 github.com/thomastaylor312/advanced-helm-demos/tree/master/post-render

除了标签和命名空间,Kustomize 还可以实现更多自定义功能。有关所有可能自定义的深入探讨,请参考 kubectl.docs.kubernetes.io/references/kustomize/kustomization/。这也是我们关于 Kustomize 讨论的结尾,接下来的部分将讨论 KubeVela 在应用工作负载部署中的使用。

使用 KubeVela 部署应用工作负载

如前所述,KubeVela 是一个类似于 Crossplane 的项目,但它主要专注于定制化应用工作负载。它还可以通过附加组件支持现成的组件。在详细讨论之前,让我们看看如何安装 KubeVela。我们将分两步完成 KubeVela 的安装。第一步是安装 KubeVela CLI。如果您使用 macOS 操作系统,可以通过 Homebrew 或脚本安装。在 Windows 系统下,我们可以使用 PowerShell。以下是 CLI 安装说明:

# Installing in macOS with Homebrew
brew update
brew install kubevela
# Installing in macOS with script 
curl -fsSl https://kubevela.io/script/install.sh | bash -s 1.3.0
# Installing in windows with a script
powershell -Command "iwr -useb https://kubevela.io/script/install.ps1 | iex"

下一步,我们应该将 KubeVela 安装到 Kubernetes 集群中,KubeVela 实际上是一组 自定义资源定义CRD)。以下是 KubeVela CRD 的安装说明。我们可以使用 CLI 或 Helm chart 进行安装:

# Using the CLI
vela install
# Using a Helm chart
helm repo add kubevela https://charts.kubevela.net/core
helm repo update
helm install --create-namespace -n vela-system kubevela kubevela/vela-core --version 1.3.0 --wait

此外,我们可以启用附加组件。附加组件增强了 KubeVela 的功能。例如,我们可以使用 velaux 附加组件作为应用程序管理仪表板。terraform-gcp 是另一个附加组件,有助于构建谷歌云平台GCP)资源的依赖关系:

# View list of Add-ons available
vela add-on list
# Install the application management dashboard add-on
vela add-on enable velaux

我们已经准备好开始使用 KubeVela。KubeVela 配置的核心是应用程序 API,应用程序 API 的结构将在以下部分中描述。

KubeVela 应用程序定义的结构

应用程序规范包含以下四个关键部分:

  • 部署Job。外部现成的依赖组件可以是 Terraform 模块、CloudFormation 模板,甚至是 Crossplane 组合资源XR)/声明。

  • 特性:特性只是声明性的操作行为。应用程序发布行为、自动扩展规则和路由规则是特性的一些示例。特性附加到组件上,我们可以附加多个特性。

  • 策略:策略是一组需要强制执行的规则。例如,Pod 安全策略和健康检查配置。

  • 工作流:工作流是一个最终部分,允许我们控制组件交付过程。审批步骤和特定环境的特性是其中的示例。

使用以下命令查看集群支持的完整组件、特性、策略和工作流列表:

# List of Components
kubectl get ComponentDefinition -n vela-system
# List of Traits
kubectl get TraitDefinition -n vela-system
# List of Policies
kubectl get PolicyDefinition -n vela-system
# List of Workflows
kubectl get WorkflowStepDefinition -n vela-system

以下截图描述了一个带有实践示例的 KubeVela 应用程序结构:

图 9.5 – 应用程序 API

图 9.5 – 应用程序 API

应用application YAML,你会发现我们的hello-world应用程序成功运行,如下所示:

图 9.6 – 已部署应用程序

图 9.6 – 已部署应用程序

KubeVela 社区基于开放应用模型OAM)规范开发了许多组件、特性、策略和工作流,并在使用示例中进行了文档化。该列表涵盖了大多数需求。深入了解请参考kubevela.io/docs/end-user/components/references。如果我们有自定义需求,KubeVela 提供了构建自定义组件、特性、策略和工作流的所有必要工具。这实际上是创建并注册新的 CRD(自定义资源定义)。与 Crossplane 一样,KubeVela 也提供了开发这些 CRD 的框架。至此,本章关于 KubeVela 的讨论就结束了。

概要

本章从定制化应用的角度讨论了一些流行的配置管理工具。虽然我们没有涵盖工具的每一个方面,但我们介绍了基本概念、使用模式和一个实际操作示例。这里讨论的每个工具都需要一本书才能深入学习,这超出了本书的范围。我们讨论的概念、模式和工具将引导我们使用 Crossplane 实现整个应用程序的端到端E2E)自动化。

下一章将通过实践操作,使用 Crossplane、Helm 以及其他一些工具,带领我们完成一个完整应用程序及其依赖的引导过程。

第十章:第十章:使用 Crossplane 进行应用程序引导

这一章将是一个完全动手实践的章节,我们将探讨一个应用程序及其所有依赖项的端到端自动化。依赖项将涉及项目仓库的设置、创建持续集成与持续部署CI/CD)流水线、相关基础设施资源等。你将看到 Crossplane 如何在从初始仓库设置开始的每一个可能步骤中,展现出它的真正力量。我们将从三个不同角色的视角出发,进行这段动手实践的旅程。三种角色分别是:创建所需 XR/claim API 的平台开发者,使用 XR/claim 配置应用程序部署的应用程序运维人员,以及参与应用程序开发的开发者。平台开发者角色是整个过程的关键,因此本章的大部分内容将从他们的视角展开。每当需要时,我们会明确提到其他角色。整个动手实践旅程将涵盖应用程序、服务和基础设施,三方面的 Crossplane 自动化内容。

本章将涉及以下主题:

  • 自动化需求

  • 解决方案

  • 准备控制平面

  • 自动化应用程序部署环境

  • 仓库和 CI 设置

  • 部署依赖关系

  • API 边界分析

我们将从产品团队的需求开始,探索自动化的方式。

自动化需求

我们将从一个假想组织X的角度,开始讲述我们的高层需求故事。他们计划开发一个名为product-a的新电子商务网站。该网站包含多个模块,每个模块在客户旅程中的不同时间点发挥作用,例如购物车、支付和客户支持。每个模块都需要独立发布和扩展的能力,同时共享一个标准的网站主题和统一的用户体验。产品架构小组推荐了微前端架构,并为每个模块在 Kubernetes 中进行独立部署。他们还建议由一个独立的团队开发网站框架、共享 UI 组件以及跨领域的关切点,形式为库。独立的模块团队可以使用这些依赖库来构建他们的功能。产品团队最近听说了 Crossplane 及其能够实现端到端自动化应用程序的能力。他们希望借着开发一个全新产品的机会,尝试使用 Crossplane 来设置一个高效、可靠的产品开发实践。他们已联系平台团队,请求帮助开发概念验证POC)。POC 项目将是我们本章动手实践旅程的范围。下图表示了产品开发团队希望实现的目标:

图 10.1 – 产品团队需求

图 10.1 – 产品团队需求

信息

请注意,本章讨论的要求和解决方案并不全面。我们的尝试是寻找一种端到端自动化的方式,涵盖整个应用生命周期及其依赖项。

以下部分从平台工程师使用 Crossplane 的角度探讨一种可能的解决方案选项。

解决方案

我们将分三步进行解决方案:

  1. 首先,我们将完全自动化 product-a 部署环境的配置(Kubernetes)和跨切关注设置,以支持所有微前端的部署。

  2. 接下来是应用程序的引导过程,包括创建新仓库和为特定微前端设置 CI 流水线等步骤。

  3. 最后的步骤是设置 CD 流水线并为微前端配置相关的基础设施(数据库)。我们将使用一组提供者,例如 Helm、GitLab、GCP 和 Kubernetes 来完成此操作。

    信息

    我们将创建一个模板 GitLab 项目,包含相关依赖库,并使用从基础模板库克隆的仓库启动微前端开发。

以下图示表示完整的解决方案:

图 10.2 – 高级解决方案视图

图 10.2 – 高级解决方案视图

以下阶段将更加详细地描述前面图示中的高级解决方案:

  • 阶段 1:第一阶段将准备好 Crossplane 控制平面,配置所需的提供者(GCP、Helm、Kubernetes 和 GitLab)。

  • product-a 集群。Helm 提供者配置有助于在 product-a 集群中设置 Argo CD。Kubernetes 提供者配置将有助于将微前端应用部署到 product-a 集群中。

  • 阶段 3:第三阶段适用于产品中的每个微前端应用程序。此步骤将从模板仓库创建微前端的新仓库。在创建新仓库时,我们还将克隆 CI 流水线。

  • 阶段 4:最后阶段将使用 Kubernetes 提供者设置已创建仓库的 CD。第二阶段创建的 Kubernetes 提供者配置将在此处使用。此阶段还将创建子模块/微前端所需的云数据库实例。

本章的其余部分将探讨如何配置 Crossplane 并实施所讨论的解决方案。以下部分将深入分析实现用例所需的控制平面设置。

信息

完整示例可以在 github.com/PacktPublishing/End-to-End-Automation-with-Kubernetes-and-Crossplane/tree/main/Chapter10/Hands-on-example 查看。

准备控制平面

这是将所需组件安装到 Crossplane 集群中的阶段。我们将建立必要的提供者及其配置。第一步是安装 GCP 提供者。

GCP 提供者

这是我们在第三章通过 Crossplane 自动化基础设施》中采取的相同步骤,但有所偏差。我们将在创建和使用 GCP 提供者配置的方式上有所不同。为每个产品团队创建独立的提供者配置有助于增强安全性、审计、政策合规性、治理等方面,特别是在使用 XR/claim API 时。每个产品团队和平台团队都应该创建一个不同的提供者配置,引用一个单独的 GCP 服务账户 Secret。这些提供者配置将以产品名(product-a)命名,并且会创建一个同名的新命名空间。组合将以动态方式参考基于声明命名空间的提供者配置。这是我们在第七章扩展和扩展 Crossplane》中讨论的多租户模式之一。要完成 GCP 设置,执行以下操作:

  1. 执行 GCP-Provider.yaml 来安装提供者,等待提供者 Pods 启动并运行。

  2. 同时,确保集群中有 product-a GCP 服务账户的 Kubernetes Secret。该 Secret 将在提供者配置中引用。提醒自己如何创建该 Secret,请参阅第三章中的《配置提供者》部分。

  3. 一旦你拥有 Secret,执行 Provider-Config.yaml 来创建产品特定的提供者配置。注意,提供者配置的名称为 product-a

  4. 最后,应用 namespace.yaml 来创建 product-a 命名空间。这是一个额外步骤,用于保存 Claim 对象。

上述步骤将确保 GCP 提供者已经完全设置。接下来的部分将介绍 GitLab 提供者。

GitLab 提供者

我们将使用 GitLab 提供者来管理微前端仓库和 CI 流水线。GitLab 提供的免费账户足以继续我们的实验。提供者设置分为三步:

  1. GitLab 凭证:我们需要将 GitLab 访问令牌创建为 Kubernetes Secret,并在 GitLab 提供者配置中引用它。你可以在 GitLab UI 用户设置中生成 GitLab 访问令牌。使用以下命令来创建 Secret:

    # Create Kubernetes secret with the access token
    kubectl create secret generic gitlab-credentials -n crossplane-system --from-literal=gitlab-credentials=<YOUR_ACCESS_TOKEN>
    
  2. 使用 provider-gitlab.yaml 安装 GitLab 提供者,并等待 Pods 启动并运行。

  3. 使用 provider-config.yaml 创建提供者配置。再次强调,它将是特定于产品的,名称为 product-a

我们已经完成了 GitLab 提供者的设置。接下来将介绍 Helm 和 Kubernetes 提供者的设置。

Helm 和 Kubernetes 提供者设置

Helm 和 Kubernetes 提供程序有助于配置远程或相同的 Kubernetes 集群。对于我们的案例,它是为 product-a 创建的远程 Kubernetes 集群。这两个提供程序都需要凭证来访问远程集群。当我们使用 XR API 配置集群时,产品特定的提供程序配置将自动为远程集群创建。我们将在下一节中查看更多细节。现在我们只需要安装提供程序。执行 Helm-Provider.yamlk8s-Provider.yaml 来安装提供程序。请参阅以下截图,展示了所有提供程序及其相应的配置设置:

图 10.3 – 提供程序设置

图 10.3 – 提供程序设置

要自己运行设置,请使用以下命令:

# GCP Provider
kubectl apply -f Step-1-ProviderSetup/Platform-OPS/GCP
kubectl apply -f Step-1-ProviderSetup/Platform-OPS/GCP/product-a
# Helm Provider
kubectl apply -f Step-1-ProviderSetup/Platform-OPS/Helm
# GitLab Provider
kubectl apply -f Step-1-ProviderSetup/Platform-OPS/Gitlab
kubectl apply -f Step-1-ProviderSetup/Platform-OPS/Gitlab/product-a
# Kubernetes Provider
kubectl apply -f Step-1-ProviderSetup/Platform-OPS/k8s

这一步骤完成了 Crossplane 控制平面的配置。所有这些活动都将由平台团队完成。在接下来的部分,我们将深入探讨如何将远程 Kubernetes 集群设置为 product-a 的部署环境。

自动化应用程序部署环境

完整的 Kubernetes 集群创建以及跨领域问题的配置将通过此步骤实现自动化。我们将开发一个 XR/claim API,它执行以下操作:

  1. 配置一个远程 GKE 集群

  2. 设置 Helm 和 Kubernetes 提供程序配置以配置 GKE 集群

  3. 使用 Helm 提供程序将 Argo CD 安装到 product-a GKE 集群

让我们看看 XRD 和组合,详细了解 API(请参阅书籍 GitHub 仓库中的 XRD 和组合)。我们将捕获两个必需的参数(节点数量和机器大小)。size 参数的枚举值为 BIGSMALL。在组合内部,我们编排了五个资源。以下是资源列表及其用途:

  • 集群和节点池:集群和节点池是负责 GKE 集群配置的两个相关资源。它们与我们在第五章中配置 GKE 的方式非常相似,扩展提供程序。节点数量和机器类型将被打补丁到节点池中。节点池再次在集群内被引用。两个资源都将动态引用 GCP 提供程序配置,使用声明命名空间。同时,连接到 GKE 集群所需的密钥存储在声明命名空间中。请参阅以下代码片段,了解集群资源中的打补丁操作:

    patches:
    - fromFieldPath: spec.claimRef.namespace
      toFieldPath: spec.providerConfigRef.name
    - fromFieldPath: spec.claimRef.name
      toFieldPath: metadata.name
    - fromFieldPath: spec.claimRef.namespace
      toFieldPath: spec.writeConnectionSecretToRef.namespace
    - fromFieldPath: spec.claimRef.name
      toFieldPath: spec.writeConnectionSecretToRef.name
      transforms:
         - type: string
            string:
               fmt: "%s-secret"
    
  • Helm 和 Kubernetes ProviderConfig:集群准备好后,是时候创建 Helm 和 Kubernetes 提供程序配置了。提供程序配置将引用新创建的集群密钥。另一个关键点是将就绪检查定义为 none,因为 ProviderConfig 不是外部资源。如果未执行此操作,XR/claim 将无法变为就绪状态。请参阅以下代码片段:

    # Patches and reediness check from the Helm Provider config
    patches:
    - fromFieldPath: spec.claimRef.namespace
      toFieldPath: spec.credentials.secretRef.namespace
    - fromFieldPath: spec.claimRef.name
      toFieldPath: spec.credentials.secretRef.name
      transforms:
      - type: string
        string:
          fmt: "%s-secret"
    - fromFieldPath: spec.claimRef.name
      toFieldPath: metadata.name
      transforms:
      - type: string
        string:
          fmt: "%s-helm-provider-config"
    readinessChecks:
    - type: None 
    
  • 安装 Argo CD:我们将使用 Helm 提供者将 Argo CD 安装到集群中。同样,提供者配置将通过可预测的命名策略动态引用。Argo CD 旨在为微前端仓库提供持续交付(CD)支持。

    信息

    请注意,这里讨论的集群创建 XR/声明 API 示例并非生产就绪。你应该使用 Helm 或 Kubernetes 提供者安装其他横向关注点。此外,我们错过了许多细粒度的集群配置。欲了解更详细的集群配置,请参考 github.com/upbound/platform-ref-gcp

要将我们的集群 API 集成并验证到控制平面中,请执行以下命令:

# Install GCP Cluster XR/Claim API 
kubectl apply -f Step-2-CreateProductTeamsKubernetesCluster/Platform-OPS
# Validate the health of installed API
kubectl get xrd
kubectl get composition

管理控制平面的平台团队将执行上述操作。请参考以下截图,查看 API 的建立情况:

图 10.4 – 集群 API

图 10.4 – 集群 API

下一步,接近产品团队的应用操作员可以使用声明配置创建集群。应用操作员将使用以下命令创建名为 product-a 的 GKE 集群:

# Create the GCP Cluster using a Claim object 
kubectl apply -f Step-2-CreateProductTeamsKubernetesCluster/Application-OPS
# Validate the health of the GKE cluster and the Argo CD
kubectl get GCPCluster -n product-a
kubectl get release

请参考以下截图,其中展示了 GKE 集群和 Helm 发布的建立情况:

图 10.5 – 集群声明

图 10.5 – 集群声明

集群创建一切正常。我们将在接下来的章节中讨论将微前端仓库接入的下一阶段。

仓库和 CI 设置

在这个阶段,我们开发 XR/声明 API 来克隆模板仓库,创建新的微前端仓库和 CI 管道。我们可以分两步来完成。首先,我们将配置 GitLab,然后开发 XR/声明 API。

GitLab 配置

在开始 XR/声明 API 开发之前,我们需要在 GitLab 中进行以下一次性配置:

  • 创建模板项目:我们需要一个模板仓库,从中创建一个新的微前端仓库。你可以访问我创建的模板仓库,链接为gitlab.com/unified.devops/react-template。该仓库已设置 GitLab 管道,用于构建并推送 Docker 镜像到 Docker Hub 注册表。你也可以在此处设置私有注册表。我们在克隆模板仓库创建微前端时,会自动获得模板项目结构和 CI 设置。Docker 镜像名称将根据微前端仓库的名称来选择。

  • product-a 的组:我们将在一个 GitLab 组中管理所有微前端仓库,以保持组织结构,管理用户权限,并为 CI 管道维护环境变量。你可以访问我创建的该组,链接为 gitlab.com/unified-devops-project-x

  • 使用你的 Docker Hub 凭证填写 REG_USERREG_PASSWORD,如以下截图所示:

图 10.6 – CI Docker Hub 变量

图 10.6 – CI Docker Hub 变量

提示

请注意,组创建和用户引导到组的过程可以自动化。考虑使用 Crossplane 实现这一点。此操作的示例可在 github.com/crossplane-contrib/provider-gitlab/tree/master/examples/groups 中找到。

我们拥有开发项目引导 XR/声明 API 所需的所有组件。接下来的部分将详细介绍引导 API 的内容。

引导 XR/声明 API

如果我们查看 XRD(gitproject-xrd.yaml),我们需要输入两个参数。模板的名称指的是我们应该克隆的模板仓库,组 ID 将决定在 GitLab 中创建仓库的组。你可以从 GitLab 组详情页或组设置页面获取组 ID。这两个参数使得 API 更具通用性,因此可以在整个组织中使用。新创建的微前端仓库 URL 和用于与仓库交互的访问令牌将作为连接机密存储。我们可以使用这些信息与 Argo CD 一起读取仓库。我们的示例不需要访问令牌,因为该仓库是公开的。这将是一个简单的组合:将模板名称与模板 URL 映射,克隆仓库到指定的组中,并将仓库详情复制回机密。仓库的名称将通过声明对象的名称来引用。要在控制平面中建立并验证引导 API,请执行以下命令:

# Install the onboarding API 
kubectl apply -f Step-3-GitProjectOnboarding/Platform-OPS
# Validate the health of installed API
kubectl get xrd
kubectl get composition

请参阅以下截图,其中已建立 API:

图 10.7 – 引导 API

图 10.7 – 引导 API

作为引导阶段的最后一步,应用操作员可以使用 Claim 配置引导仓库和 CI 管道。应用操作员将使用以下命令创建名为 micro-frontend-one 的仓库:

# Create claim and validate
kubectl apply -f Step-3-GitProjectOnboarding/Application-OPS
kubectl get gitproject -n product-akubectl get xrd

请参阅以下截图,其中在 GitLab 中创建了声明:

图 10.8 – 引导仓库

图 10.8 – 引导仓库

你可以进入新仓库的 CI/CD 部分,运行 CI 管道,查看 Docker 镜像是否已创建并推送到 Docker Hub。开发者现在可以对仓库进行更改,任何新的提交将自动触发 GitLab CI 管道。在接下来的部分中,我们可以研究最终阶段,设置 CD 并提供其他基础设施依赖。

部署依赖

最终阶段是自动化微前端的部署依赖。自动化部署依赖意味着需要处理两个方面:

  • 基础设施依赖:此步骤涉及为微前端配置所需的基础设施依赖。在我们的例子中,我们将创建一个 GCP MySQL 数据库。一个应用程序可能会有更多的依赖,我们将以数据库为例,保持示例的简洁。

  • template-helm 文件夹位于我们的模板仓库内 (gitlab.com/unified.devops/react-template/-/tree/main/template-helm),它包含一个 Helm chart,用于将应用程序部署到 Kubernetes 中。为了以 GitOps 的方式部署该 Helm chart,我们必须在 product-a Kubernetes 集群中添加一个 Argo CD 配置,以同步该 chart。我们将构建一个 Object 类型的 Kubernetes 提供程序配置,帮助将任何 Kubernetes 配置应用到目标集群。我们的组合将构建一个 Argo CD 配置,用于从仓库部署 Helm chart。有关如何使用 Argo CD 进行 Helm 部署的更多信息,请参见 cloud.redhat.com/blog/continuous-delivery-with-helm-and-argo-cd

我们将构建一个嵌套的 XR 来满足前述要求。XWebApplication 将是父 API,XGCPdb 将是嵌套的内部 XR。父 API 捕获产品 Git 组和数据库大小作为输入。微前端名称将是从声明名称衍生的另一个输入。父级组合将组合 Argo CD 配置和 XGCPdb 资源(内部 XR)。请参阅我们示例仓库中的应用程序和数据库文件夹,了解两个 XR 的 XRD 和组合。以下是几个关键代码片段,有助于理解。在 Argo CD 对象中,以下是用于仓库 URL 的修补代码。我们根据组名和声明名(仓库名)构建 GitLab URL。查看声明以查看实际输入(Claim-Application.yaml)。以下是仓库 URL 修补代码:

- type: CombineFromComposite
  toFieldPath: spec.forProvider.manifest.spec.source.repoURL
  combine:
    variables:
    - fromFieldPath: spec.parameters.productGitGroup
    - fromFieldPath: spec.claimRef.name
    strategy: string
    string:
      fmt: "https://gitlab.com/%s/%s.git"

我们使用可预测的命名策略动态修补 Kubernetes 提供程序配置名称。以下是相关的代码片段:

- fromFieldPath: spec.claimRef.namespace
  toFieldPath: spec.providerConfigRef.name
  transforms:
    - type: string
      string:
        fmt: "%s-cluster-k8s-provider-config"

另一个重要的修补是动态绑定 Docker 镜像名称。在我们的 CI 管道中,我们使用仓库名称作为 Docker 镜像名称。由于声明名称和仓库名称相同,我们可以使用声明名称动态构建 Docker 镜像名称。以下是相关的修补代码片段:

- fromFieldPath: spec.claimRef.name
  toFieldPath: spec.forProvider.manifest.spec.source.helm.parameters[0].value
  transforms:
    - type: string
      string:
        fmt: "arunramakani/%s

sourcedestination 是 Argo CD 配置中的两个关键部分。该配置提供有关 Helm chart 来源以及如何在目标 Kubernetes 集群中部署的相关信息。以下是相关的代码片段:

source:
  # we just saw how this patched 
  repoURL: # To be patched
  # The branch in which Argo CD looks for change
  # When the code is ready for release, move to this branch
  targetRevision: HEAD
  # Folder in the repository in which ArgoCD will look for automatic sync
  path: template-helm
  helm:
    # We will patch our clime name here 
    releaseName: # To be patched
    parameters:
    - name: "image.repository"
      # we just saw how this patched
      value: # To be patched
    - name: "image.tag"
      value: latest
    - name: "service.port"
      value: "3000"
destination:
  # Indicates that the target Kubernetes cluster is the same local Kubernetes cluster in which ArgoCD is running.  
  server: https://kubernetes.default.svc
  # Namespace in which the application is deployed
  namespace: # to be patched

要在控制平面中建立和验证我们的 API,请执行以下命令:

kubectl apply -f Step-4-WebApplication/Platform-OPS/Application
kubectl apply -f Step-4-WebApplication/Platform-OPS/DB
kubectl get xrd
kubectl get composition

请参考下面的截图,其中展示了 API 的建立和验证过程:

图 10.9 – 应用程序 API 的入驻

图 10.9 – 应用程序 API 的入驻

提示

请注意,我们没有为 Argo CD 配置任何访问令牌来访问 GitLab,因为它是一个公共仓库。实际生活中,我们会有私有仓库,且需要访问令牌。请参考argo-cd.readthedocs.io/en/release-1.8/operator-manual/declarative-setup/#repositories查看如何设置访问令牌。再次强调,这可以作为仓库入驻的一部分进行自动化。

作为应用程序部署自动化阶段的最后一步,应用程序运维人员可以将数据库作为基础设施依赖项进行部署,并使用以下 claim 配置来配置 CD 设置:

apiVersion: learn.unified.devops/v1alpha1
kind: WebApplication
metadata:
  # Use the same name as the repository 
  name: micro-frontend-one
  namespace: product-a
spec:
  compositionRef:
    name: web-application-dev
  parameters:
    # Group name in gitlab for the product-a
    productGitGroup: unified-devops-project-x
    databaseSize: SMALL

应用程序运维人员将使用以下命令:

# Apply the claim
kubectl apply -f Step-4-WebApplication/Application-OPS
# Verify the application status, including the database and  ArgoCD config
kubectl get webapplications -n product-a
kubectl get XGCPdb
kubectl get object

请参考以下截图,展示了应用程序基础设施依赖关系和 CD 配置的部署:

图 10.10 – API 的入驻

图 10.10 – API 的入驻

提示

我们使用了 Argo CD 和 Helm chart 部署来处理应用程序自动化。我们可以根据团队的需求,将 Helm 替换为 KubeVela,将 Helm/KubeVela 与 Kustomize 结合使用,甚至使用普通的 Kubernetes 对象。甚至 Argo CD 也可以被其他 GitOps 工具替代,如 Flex。

这带我们走到了从头到尾自动化应用程序的实践之旅的终点。我们的微前端示例及其依赖的数据库现在已经启动并运行。在本章的下一部分,我们将讨论我们 XR/claim API 边界背后的推理。

API 边界分析

我们将端到端自动化分为四个阶段。我们可以忽略第一阶段,因为它是关于准备 Crossplane 控制平面的。理解为什么我们将其余阶段分为三个,并使用四个 XR/claim API 是非常重要的。以下是我们 API 边界背后的思路:

  • product-a。所有现代工作负载通常都部署在 Kubernetes 上,组织未来将有许多类似的集群设置活动。构建一个单独的 API 以实现可重用性和集中管理策略是有意义的。保持 API 独立的另一个重要原因是集群设置是一次性活动,并且作为进一步应用程序工作负载部署的跨切面操作。

  • 入驻 API:GitLab 项目入驻的 XR/claim 作为一个独立的 API 进行开发。我们不需要为每个环境(生产、预发布和开发)入驻仓库和 CI 流水线。这就是为什么我们决定将 XGitProjectAPI/GitProject API 分开的原因。

  • 应用程序 API:这是我们将应用程序基础设施依赖项和 CI 设置进行入职的步骤,每个环境只需做一次。因此,我们将 XWebApplication/WebApplication 作为一个独立的 API 来开发。需要注意的是,数据库配置有一个内部嵌套 API。这个设计的想法是将其独立出来,因为数据库配置在组织范围内有政策要求。注意,数据库 API 没有声明,因为它仅作为嵌套 API 使用。该政策要求是一个假设,可能并不适用于你的情况。

    提示

    在应用程序 API 中设置 CI 时,需要提供通过入职 API 创建的仓库 URL 和访问令牌。入职 API 是一次性操作,而应用程序 API 在每个环境中都会使用。如果我们为每个环境(生产、预发布和开发)配置不同的 Crossplane,那么以自动化方式共享凭证可能会变得具有挑战性。可以考虑使用外部密钥保管库来同步入职 API 中的仓库详情。其他 Crossplane 环境可以通过诸如 External Secrets (external-secrets.io/v0.5.3/) 等工具来同步这些密钥。

总结

本章讨论了处理应用程序、基础设施和服务端到端自动化的一种方法。利用我们在全书中学到的方式,实际上有多种方法可以实现基于控制平面的端到端自动化。我迫不及待地想看到你们提出的独特方式。本章将带领我们完成对 Crossplane 概念和模式的学习,并结束我们的实操之旅。

在最后一章,我们将探讨一些灵感,如何将平台作为产品来运行。你将学到使我们 Crossplane 平台团队成功的关键工程实践。

第十一章:第十一章:推动平台采纳

到现在为止,你可能正在考虑在你的组织中采用 Crossplane。正如我们多次讨论的那样,我们必须建立一个平台团队来支持组织内每个产品团队采用 Crossplane。但许多组织在其技术平台项目上失败,因为这些平台以多种方式影响了消费团队的敏捷性。为产品团队与平台团队之间设立适当的边界和交互模型是平台成功的关键。本章旨在帮助你理解构建和采用成功基础设施平台所需的各个方面。我们还将看看一些常见的陷阱,这些陷阱可能导致平台采用不力以及投资回报低下。

本章涵盖的主题如下:

  • 为什么我们需要将基础设施平台视为产品

  • 理解客户需求

  • 平台产品生命周期和团队互动

  • OAM 角色

在接下来的章节中,我们将开始理解为什么我们需要将基础设施平台视为产品开发的方式。

为什么我们需要将基础设施平台视为产品

我们在第二章中稍微提到了这个话题。现在是时候回顾一下,看看为什么我们需要一个平台团队来开发 Crossplane API。以下是三个关键原因:

  • 认知负担:任何组织都倾向于使用大量的云资源和其他外部服务。这些资源和服务由成千上万的配置属性组成,用以根据组织的需求进行配置。记住每个配置属性的使用涉及到巨大的认知负担。假设我们试图在产品团队内构建这些知识,那么团队将更多关注技术复杂性,而非直接与业务相关的产品功能开发。如果你看看 CNCF 的云原生景观,它是如此庞大(landscape.cncf.io/)。并不是每个团队都能驾驭这个领域。这需要一个专业团队将这种认知能力构建到组织中。既然每个产品团队都面临相同的情况,将这一努力抽象为由平台团队管理的共享服务是有意义的。

  • 政策管理:基础设施和其他外部服务的使用政策来源于安全、合规、产品和架构团队。需要一个集中式团队来追踪、整合和编码这些政策,以便在这些资源被提供和使用时执行。如果我们将这些资源的自动化作为各个产品团队的一部分,保持政策的执行和更新将变得非常困难。

  • 投资回报率与重用:通常,许多工具、架构模式和基础设施设置模式在整个组织中看起来相似。这是因为集中式的 IT 战略、成功设计的交叉传播、组织的认知能力等因素。这些共同点将转化为基础设施参考架构的重用。只要我们拥有认知负载和政策管理的优势,重用的投资回报经济性就是一个额外的优势。

既然我们已经相信需要一个基础设施平台团队,接下来的部分将探讨产品团队对基础设施平台团队的期望。

理解客户需求

对于基础设施而言,平台团队意味着外部交付依赖。这是敏捷产品开发团队始终避免的事情。如果你在大型组织中工作过,可能会注意到,产品团队常常对平台团队提供的内容不满意。他们在交付时间表、待办事项耦合以及合同弱化方面存在问题。为减轻这些风险,关键是要建立产品团队与平台团队之间的适当互动模型。以下是平台团队应具备的特质,以匹配现代软件工程时代中产品团队的交付预期:

  • 产品开发实践

  • 自助服务

  • 协同管理待办事项

接下来的部分将探讨平台团队的产品开发工程实践。

产品开发实践

采用产品开发实践是使平台团队成功的关键组成部分。以下是一些可供采纳的产品开发实践:

  • 快速入门:平台的使用必须迅速而简便。Crossplane 作为一个基于 API 的平台,解决了部分问题。API 具有明确的契约,且易于理解。但这还不够。我们必须付出额外的努力,使平台易于采用。我们应该提供快速入门指南、带有示例的代码库、支持系统以帮助采用团队,以及详细的 API 文档。更重要的是,快速入门能力应该是一个关键指标,并持续改进。我们可以通过产品团队与平台团队之间的持续反馈循环来实现这一目标。反馈应来自多个来源,例如客户调查、各种快速启动资源的使用统计数据、平台本身的使用统计数据以及采用时间。平台团队应不断改进这些指标,并获得激励。

  • 实践社区:构建一个由平台和产品团队成员参与的社区是另一个重要方面。应用运维人员可能是产品团队的天然社区成员。由于他们与产品团队的需求紧密相连,他们能够对我们在平台上构建的 API 进行现实检查。同样,应该有指标和激励措施来确保社区的成功运作。我们可以通过多种方式利用社区,比如共创、作为知识载体、提供双向反馈渠道,以及加速采用。我们将在本章的后续部分详细探讨这些选项。

  • 可组合的 API:技术平台面临的一个关键挑战是,它们强制采用某种开发方式,限制了创新。平台应该有一套基本的精细化 XRs,在此基础上,我们可以快速组合出产品团队所需的 claim 配方。建议使用两层 API。第一层应该有精细化的 XRs,组合组织集中管理的策略;第二层应该是配方层,组合产品级的策略和需求。我们可以有一套组织内通用的基础配方,并为社区留出空间,创新新的配方。一些组织架构可能需要金发女孩治理(Goldilocks governance),以避免社区中过多新配方的泛滥。

    信息

    金发女孩治理架构在现代软件工程实践中逐渐获得关注,它在治理与灵活性之间采取了平衡的方式。

在接下来的章节中,我们将讨论平台工程中的自助服务方面。

自助服务

自助服务是构建平台的关键方面之一。想一想任何云服务提供商是如何运作的。我们有控制台和 API 来创建/操作所需的资源。这是一个完全自助的模型,用户可以根据自己的权限管理资源。在云服务提供商环境中,精细的资源粒度适用于任何组织和角色。我们必须为我们的平台也提供类似的体验。由于 Crossplane 是一个基于 API 的资源组合平台,部分问题已经得到解决。但我们必须深入思考,定义产品团队将要使用的 XR/claim API 的范围和粒度。确定适合你们组织的粒度级别是一个至关重要的问题。这可能取决于组织的规模、团队边界、开发者/平台团队的技术成熟度等等。定义错误的边界将会破坏自助服务的目标。

一开始精准确定边界是很难的。通过衡量自助服务指标,它会在迭代中不断改进。除了 API 边界外,我们还需要关注快速入门指南、技术支持系统和构建社区,正如前面所讨论的,以进一步促进自助服务。整个体验应该类似于一个成功的云服务平台的运作。唯一的区别是我们在更高的抽象层次上操作,以应对复杂性。

协同待办事项管理

一个服务于组织内部多个团队的单一集中平台团队并不是一个愉快的工作场所。我们必须管理待办事项,以满足每个人的需求。来自各方面的持续请求将要求增强现有的 XR/声明或创建新的功能。我们可能会陷入许多不希望出现的情况,例如平台团队的持续交付压力、对产品团队交付吞吐量的影响,或是产品团队在本地开发能力时的沮丧。以下是一些缓解这些风险的方法:

  • 待办事项优先级排序:在优先级排序会议中,根据时间紧急性、错失机会以及短期/长期业务价值等指标为每个待办事项评分。记录每个评分的理由,以便查看。所有拥有待办事项的产品团队应积极参与优先级排序会议。在优先级排序会议中,每个产品团队可能会试图提高自己的评分。我们可以通过评分滑块来解决这个问题,在该滑块上设置一个总分值,且每个待办事项的评分总和不得超过设定值。

  • 期望管理:产品团队在等待他们的待办事项交付,他们应当像最终客户一样对待。为客户设定期望非常重要,明确他们可以期待的交付时间和内容。使用 Scrum of Scrums 或任何其他大规模敏捷框架来找到提供定期更新的方法。提供进展和交付日期的更新,公开并透明地说明任何交付延迟。

  • 社区和治理:看看像 Kubernetes 和 Crossplane 这样的开源项目是如何运作的。它们同样面临着许多用户对新特性、增强功能和修复漏洞的期待。这些项目最初由少数专注的开发者启动。当产品从α-β阶段转向正式发布时,许多客户开始采纳它。此时,管理待办事项变得至关重要。这些开源项目通过围绕它们建立一个强大的社区来加速交付速度。社区的开发活动由技术指导小组管理,技术指导小组由核心贡献者组成。我们也可以在内部产品中采纳相同的模型。正如我们已经提到的,靠近产品团队的应用运维人员会自然地被吸引到社区中。面临紧迫需求的产品团队可以分配他们的应用运维人员来共同创建产品。

在接下来的部分,我们将探讨平台 API 开发的各个阶段,以及它们如何影响平台团队与产品团队之间的互动。

平台产品生命周期和团队互动

我们可能会从一个小规模的概念验证开始,与其中一个产品团队合作。随后,当我们对概念验证感到满意时,我们将发布α版本,邀请一些团队来使用和测试它。同样,当我们对α版本满意时,我们将邀请更多组织部分采用并上线该 API。如果一切顺利,我们可以将 API 发布到正式版本,供组织中的任何人使用。但事情不会就此结束。有时,我们会有需求将某些 API 弃用,并标记它们在未来移除。下图展示了 API 的生命周期:

图 11.1 – Crossplane API 生命周期

图 11.1 – Crossplane API 生命周期

每个 API 可能并不会经历所有提到的阶段。它可能根据情况走自己的路,从左到右移动并跳过某些阶段。

信息

有时,如果我们发现没有业务价值或技术可行性,我们可能会直接从α或β阶段移动到弃用状态。

每个步骤都需要不同的参与模型和开发者能力。以下列表描述了生命周期的各个阶段:

  • 概念验证和α阶段:在这些阶段,有两件事是至关重要的。首先,产品团队和平台团队之间的互动模型应是合作模式。这意味着两个团队应该尽可能紧密地合作。参与的开发者能力应是快速创新者。在这一阶段,我们不需要 API 具备可扩展性或可靠性。我们的唯一目标应该是尽快实验其可行性和价值。

  • Beta 阶段:这个阶段应当继续采用协作工作模式,但我们应该逐渐过渡到自助服务。产品团队应能够通过某个自助服务门户请求访问新的 API 并开始使用它。创建自助服务工件应当在此阶段进行。平台团队应与产品团队密切合作,完善自助服务门户和 API。参与的开发人员应当具备快速创新的能力,同时能够带来稳定性和可靠性。我们的目标是为组织使用做好准备。

  • 正式发布:这是组织范围内使用的阶段。两个关键方面是完全自助服务和拥有稳定可靠的 API。

  • 弃用和移除阶段:有时,我们需要弃用并移除对某个 API 的支持。推动产品团队摆脱 API 的使用并不容易,因为他们被优先事项所束缚。平台团队应当使用促进交互模型来执行这一活动。平台开发人员为产品团队实际执行移除 API 的工作。在这个阶段,平台开发人员需要具备快速创新的能力。

    致谢

    本章讨论的许多概念受到以下来源的影响:teamtopologies.com/、Evan Bottcher 在平台工程方面的工作(martinfowler.com/articles/talk-about-platforms.html),以及一些在工作中实施这些概念的经验。

以下图表展示了前述的 API 阶段和团队交互模型:

图 11.2 – API 阶段和团队交互模型

图 11.2 – API 阶段和团队交互模型

这标志着平台生命周期讨论的结束。在接下来的部分,我们将探讨开放应用模型OAM)规范,从平台工程的角度获取一些关于组织级团队结构的灵感。

OAM 角色

我们在本书中多次提到 OAM 规范。我们将从组织结构的角度刷新这一话题。我们将从 OAM 模型中汲取灵感,组织平台及其生态系统。OAM 提出了以下三种角色来部署和管理云原生应用:

  • 应用开发人员:专注于应用程序开发,始终将重点放在开发能够直接为客户带来价值的功能上。

  • 应用操作员:将配置云原生生态系统中的应用程序的复杂性从应用开发人员身上卸载下来。使应用开发团队能够在功能开发方面更快推进。应用操作员间接地为最终消费者做出贡献。

  • 基础设施操作员:将配置云、其他基础设施和服务的复杂性从组织中卸载出去。它使得应用程序操作员能够专注于配置和操作应用程序。

将 OAM 角色和我们在本章中学习的其他概念进行重叠,我们可以按照以下示意图来构建平台团队及其生态系统:

图 11.3 – 平台生态系统

图 11.3 – 平台生态系统

以下方面解释了前面示意图中表示的团队构成:

  • 组织x被拆分成多个产品。

  • 每个产品被拆分为多个 Scrum 团队,处理不同的子领域。

  • 一个或多个应用程序操作员支持配置和操作给定产品的所有应用程序。

  • 平台团队作为一个跨职能服务,支持所有产品团队进行基础设施、服务和应用程序的自动化。

  • 平台团队和应用程序操作员共同形成社区,支持共创和加速采用等活动。

我们可以探索更多模型来构建平台及其生态系统。但核心思想是将平台开发作为一种产品工程实践,视所有内部用户为客户来进行管理。

总结

本书一开始的目的是支持云原生架构师、平台工程师、基础设施操作员和应用程序操作员,他们希望通过 Kubernetes APIs 简化基础设施、应用程序和服务的自动化。在整本书中,我们涵盖了构建完全声明式自服务控制平面以自动化基础设施、应用程序和服务的不同方面,使用的工具有 Crossplane 和 Kubernetes。我希望你在学习这些必要的概念的同时,也能享受过程。让我们一起将软件工程带入新的时代。

Packt.com

订阅我们的在线数字图书馆,全面访问超过 7,000 本书籍和视频,并利用行业领先的工具来帮助你规划个人发展并推进职业生涯。欲了解更多信息,请访问我们的网站。

第十二章:为什么要订阅?

  • 通过来自 4000 多位行业专家的实用电子书和视频,减少学习时间,增加编码时间

  • 通过为你量身定制的技能计划提高学习效果

  • 每月获取一本免费的电子书或视频

  • 完全可搜索,便于快速访问关键信息

  • 复制和粘贴、打印和收藏内容

你知道 Packt 提供所有已出版书籍的电子书版本,包含 PDF 和 ePub 文件吗?你可以在packt.com升级为电子书版本,作为纸质书的顾客,你还可以享受电子书版本的折扣。如需更多详情,请联系 customercare@packtpub.com。

www.packt.com,你还可以阅读一系列免费的技术文章,注册各种免费的新闻通讯,并获得 Packt 书籍和电子书的独家折扣和优惠。

你可能会喜欢的其他书籍

如果你喜欢这本书,可能还会对 Packt 出版的其他书籍感兴趣:

Kubernetes 圣经

Nassim Kebbani, Piotr Tylenda, Russ McKendrick

ISBN:9781838827694

  • 使用 Kubernetes 管理容器化应用程序

  • 理解 Kubernetes 架构及各组件的职责

  • 在 Amazon Elastic Kubernetes Service、Google Kubernetes Engine 和 Microsoft Azure Kubernetes Service 上设置 Kubernetes

  • 使用 Helm 图表部署 Prometheus 和 Elasticsearch 等云应用

  • 探索 Pod 调度和集群自动扩展的高级技术

  • 理解 Kubernetes 中的流量路由的可能方法

Docker 和 Jenkins 的持续交付 - 第三版

Rafał Leszko

ISBN:9781803237480

  • 掌握 Docker 基础知识,并为 CD 过程对应用程序进行 Docker 化

  • 理解如何在本地和云端使用 Jenkins

  • 使用 Kubernetes 扩展 Docker 服务器池

  • 使用 Cucumber 编写验收测试

  • 使用 Jenkins 在 Docker 生态系统中运行测试

  • 使用 Ansible 和 Terraform 配置你的服务器和基础设施

  • 将构建的 Docker 镜像发布到 Docker 注册中心

  • 使用社区最佳实践部署 Jenkins 流水线的周期

Packt 正在寻找像你一样的作者

如果你有兴趣成为 Packt 的作者,请访问 authors.packtpub.com 并立即申请。我们已经与成千上万的开发者和技术专家合作,帮助他们将自己的见解分享给全球技术社区。你可以提交一般申请,申请我们正在招募作者的特定热门话题,或者提交你自己的创意。

分享你的想法

现在你已经读完了《Kubernetes 与 Crossplane 的端到端自动化》,我们很希望听听你的想法!如果你是从亚马逊购买的这本书,请点击这里直接进入亚马逊的评论页面,分享你的反馈或者在你购买的站点上留下评论。

你的评论对我们以及技术社区非常重要,能够帮助我们确保提供优质的内容。

你可能会喜欢的其他书籍

你可能会喜欢的其他书籍

posted @ 2025-06-30 19:27  绝不原创的飞龙  阅读(1)  评论(0)    收藏  举报