基于 Kubernetes 的平台工程——多云(应用)基础设施

87 阅读43分钟

本章内容涵盖:

  • 为云原生应用定义和管理基础设施
  • 识别管理基础设施组件时面临的挑战
  • 学习如何使用 Crossplane 以 Kubernetes 的方式处理基础设施

在前几章中,我们搭建了一个“行走骨架”应用,并学习了如何使用服务流水线构建各个独立组件,然后通过环境流水线将它们部署到不同环境中。现在我们面临一个重大挑战:如何处理应用程序基础设施,也就是不仅要运行和维护我们的应用服务,还要管理这些服务运行所依赖的组件。这些服务依赖于其他组件正常工作,例如数据库、消息代理、身份管理解决方案、邮件服务器等。虽然已经有许多工具可以自动化安装(针对本地部署)或在不同云提供商上配置这些组件,本章将重点介绍一个以 Kubernetes 方式实现的工具。本章分为三个主要部分:

  1. 处理基础设施的挑战
  2. 使用 Kubernetes 构建管理基础设施
  3. 使用 Crossplane 为我们的行走骨架应用配置基础设施

让我们开始吧:为什么管理应用程序基础设施如此困难?

5.1 Kubernetes 中管理基础设施的挑战

当你设计类似于第 1 章介绍的行走骨架应用时,你会面临一些特定挑战,这些挑战并非实现业务目标的核心内容。安装、配置和维护支持应用服务的基础设施组件是一项重大任务,需要由具备相应专业知识的团队仔细规划。

这些组件被归类为应用程序基础设施,通常涉及非自研的第三方组件,如数据库、消息代理、身份管理解决方案等。现代云提供商之所以成功,很大程度上是因为他们擅长提供和维护这些组件,使开发团队能够专注于构建核心应用功能,从而为业务创造价值。

需要明确区分应用程序基础设施与硬件基础设施,因为本书不关注硬件配置,其余内容专注于应用层。我假设,对于公有云服务,提供商会处理所有与硬件相关的问题。而对于本地部署场景,你可能有专门的团队负责硬件管理(按需增加、移除或维护硬件)。

依赖云提供商服务来配置应用程序基础设施是常见做法,这样做有很多优势,例如按需付费服务、易于大规模配置以及自动化维护。但此时,你将严重依赖特定提供商的工具和方式。一旦在云提供商创建了数据库或消息代理,就跳出了 Kubernetes 的管理范围,此时你依赖他们的工具和自动化机制,从而在业务与云提供商之间形成强依赖。

下面我们来看一下在配置和维护应用基础设施时的具体挑战,以便团队能规划并选择合适的工具:

  1. 组件扩展配置:每个组件需要不同的专业知识进行配置(数据库管理员负责数据库,消息代理专家、机器学习专家等),并且需要深入理解应用服务如何使用这些组件以及可用硬件。配置需要版本化并严格监控,以便快速创建新环境来复现问题或测试新版本。
  2. 组件长期维护:数据库和消息代理会不断发布新版本并打补丁以提升性能和安全性。这种持续变化要求运维团队能够升级到新版本,同时保证数据安全,而不影响整个应用的运行。所有这些复杂性需要各团队之间大量的协调和影响分析。
  3. 云提供商服务对多云策略的影响:如果依赖云特定的应用基础设施和工具,需要找到方法让开发者能够创建和配置自己的组件来开发和测试服务。必须抽象基础设施的配置方式,让应用可以定义所需基础设施,而无需直接依赖云提供商的工具。

有趣的是,即使在分布式应用出现之前,这些挑战就已经存在。配置和部署架构组件一直很困难,通常离开发者很远。云提供商通过把这些问题更靠近开发者,使他们更加自主、迭代更快。遗憾的是,在使用 Kubernetes 时,我们有更多选择,需要仔细考虑权衡。下一节将介绍如何在 Kubernetes 内管理应用基础设施,虽然通常不推荐这样做,但在某些场景下可能更实用、更经济。


5.1.1 管理应用程序基础设施

应用程序基础设施已成为一个令人兴奋的领域。随着容器的兴起,每位开发者都可以通过几条命令启动数据库或消息代理,这通常足够用于开发环境。在 Kubernetes 世界中,这对应于 Helm Charts,通过容器来配置和部署数据库(关系型和 NoSQL)、消息代理、身份管理解决方案等。正如第 2 章所示,你可以用一条命令安装包含四个服务、两个数据库(Redis 和 PostgreSQL)以及一个消息代理(Kafka)的行走骨架应用。

对于我们的行走骨架应用,我们为 Agenda 服务配置了一个 Redis NoSQL 数据库实例,为 Call for Proposals (C4P) 服务配置了一个 PostgreSQL 数据库实例,并配置了一个 Kafka 集群实例,这些都是通过 Helm Charts 完成的。如今可用的 Helm Charts 数量令人印象深刻,因此很多人会认为安装 Helm Chart 是最佳方式。示例应用中使用的 Helm Charts 可在 Bitnami Helm Chart 仓库找到:bitnami.com/stacks/helm

如第 2 章讨论,如果我们想扩展有状态服务,必须配置专用组件,如数据库。应用开发者会根据所需存储的数据类型及其结构来选择最合适的数据库。图 5.1 展示了行走骨架应用服务对部分应用基础设施组件的依赖关系。

image.png

在 Kubernetes 集群中设置这些组件(PostgreSQL、Redis 和 Kafka)的过程包括以下步骤:

  1. 寻找或创建合适的 Helm Chart:为你想要启动的组件找到或创建一个 Helm Chart。对于行走骨架应用,PostgreSQL(bitnami.com/stack/postg…)、Redis(bitnami.com/stack/redis…)和 Kafka(bitnami.com/stack/kafka…)都可以在 Bitnami Helm Chart 仓库找到。如果找不到 Helm Chart,但你有该组件的 Docker 容器,也可以在定义好部署所需的 Kubernetes 基本资源后自己创建 Chart。
  2. 研究 Chart 配置和参数:每个 Chart 都提供一组参数,可根据不同使用场景进行调整。查看 Chart 官方网站了解可用选项。邀请运维团队和数据库管理员(DBA)参与,确保数据库配置最优;这不是开发者可以独立完成的工作。同时,需要 Kubernetes 专业知识来确保组件可以在 Kubernetes 内部以高可用(HA)模式运行。
  3. 使用 helm install 安装 Chart:运行 helm install 时,你会下载一组 Kubernetes manifests(YAML 文件),描述组件如何部署。Helm 会将这些 YAML 文件应用到你的集群中。在第 2 章(2.1.3 节)安装的 Conference 应用 Helm Chart 中,所有应用基础设施组件都作为依赖被添加到 Chart 中。
  4. 配置服务以连接新建组件:将服务配置为使用新建实例的 URL 和凭证进行连接。例如,对于数据库,需要提供数据库 URL 以及可能的用户名和密码。值得注意的是,你的应用还需要相应的驱动程序才能连接到目标数据库,相关内容将在第 8 章详细介绍。
  5. 长期维护这些组件:执行备份,并确保故障切换机制(fail-over)按预期工作。

图 5.2 展示了将这些应用基础设施组件安装并与应用服务连接的各个步骤。

image.png

如果你正在使用 Helm Charts,需要注意以下几个注意事项和技巧:

  1. 参数配置限制:如果某个 Chart 不允许你配置某个你想修改的参数,可以使用 helm template 生成模板,然后修改输出 YAML 来添加或更改所需参数,最后使用 kubectl apply -f 安装组件。另一种方式是向 Chart 仓库提交 Pull Request。通常做法是不会暴露所有可能的参数,而是等待社区成员提出需求来增加可配置参数。如果遇到这种情况,不要犹豫,联系维护者。无论做何修改,都必须维护和记录 Chart 的内容。使用 helm template 时,会失去 Helm 的 release 管理功能,即无法通过 Helm 自动升级到新版本。
  2. 默认配置可能资源消耗大:大多数 Charts 的默认配置是为了可扩展性而设计的,意味着默认部署目标是高可用场景。这可能会导致安装时消耗大量资源(CPU 和内存),在你使用 KinD 或 Minikube 这种本地 Kubernetes 环境时可能无法满足需求。通常,Chart 文档会提供针对开发环境和资源受限环境的特殊配置。
  3. 数据库存储需求:如果在 Kubernetes 集群中安装数据库,每个数据库容器(Pod)必须能够访问底层节点的存储。为了支持数据库弹性扩展,可能需要 Kubernetes 之外的高级存储配置。

以我们的行走骨架应用为例,我们将 Redis Chart 的 architecture 参数设置为 standalone(可以在环境流水线配置和 Agenda 服务 Helm Chart 的 values.yaml 文件中看到),这样便于在资源受限的环境(如笔记本或工作站)上运行。但这会影响 Redis 的容错能力,因为此时只会运行单副本,而默认设置会创建一个主节点和两个从节点。

5.1.2 将服务连接到新建基础设施

安装 Charts 并不会让应用服务自动连接到 Redis、PostgreSQL 或 Kafka 实例。我们需要提供服务所需的连接配置,同时考虑这些组件(如数据库)启动所需的时间。

图 5.3 展示了连接的常规方式,大多数 Charts 会自动创建一个 Kubernetes Secret,其中包含应用服务连接所需的所有信息。

image.png

一个常见的做法是使用 Kubernetes Secrets 来存储这些应用基础设施组件的凭证。我们为行走骨架应用使用的 Redis 和 PostgreSQL Helm Charts 会创建一个新的 Kubernetes Secret,其中包含连接所需的详细信息。这些 Helm Charts 还会创建一个 Kubernetes Service,作为实例运行的访问地址(URL)。

要将 Call for Proposals (C4P) 服务连接到 PostgreSQL 实例,需要确保 C4P 服务的 Kubernetes Deployment(conference-c4p-service-deployment)具有正确的环境变量(如清单 5.1 所示)。

清单 5.1 连接应用基础设施(PostgreSQL)的环境变量

- name: KAFKA_URL
  value: <KAFKA SERVICE URL>
- name: POSTGRES_HOST
  valueFrom:
    secretKeyRef:
      name: <POSTGRESQL SECRET NAME>
      key: postgres-url
- name: POSTGRES_PASSWORD
  valueFrom:
    secretKeyRef:
      name: <POSTGRESQL SECRET NAME>
      key: postgres-password

加粗部分显示了我们如何在安装 Chart 时使用动态生成的数据库密码,以及 PostgreSQL Kubernetes 服务的端点 URL。若使用不同的 Chart release 名称,数据库端点也会不同。

类似的配置也适用于 Agenda 服务(conference-agenda-service-deployment)和 Redis(清单 5.2)。

清单 5.2 连接应用基础设施(Redis)的环境变量

- name: KAFKA_URL
  value: <KAFKA SERVICE URL>
- name: REDIS_HOST
  valueFrom:
    secretKeyRef:
      name: <REDIS SECRET NAME>
      key: redis-url
- name: REDIS_PASSWORD
  valueFrom:
    secretKeyRef:
      name: <REDIS SECRET NAME>
      key: redis-password

同样,我们从安装 Redis Helm Chart 时生成的 Kubernetes Secret 中提取密码。Secret 的名称取决于 Helm Chart release 的名称。REDIS_HOST 则来自 Chart 创建的 Kubernetes Service 名称,也依赖于 Helm release 名称。

对于应用的所有服务,我们都需要设置 KAFKA_URL 环境变量,以便服务能连接到 Kafka。为应用基础设施组件配置不同实例,使我们可以将组件的部署和维护委派给其他团队,甚至云服务提供商。

5.1.3 我听说过 Kubernetes Operators,我应该使用吗?

现在,你在 Kubernetes 集群中有四个应用服务、两个数据库和一个消息中间件。相信不信,现在你需要维护和扩展七个组件,以满足应用的需求。构建这些服务的团队了解如何维护和升级每个服务,但他们不一定是数据库或消息中间件维护和扩展的专家。

根据服务的负载情况,你可能需要对数据库和消息中间件提供额外支持。例如,如果 Agenda 服务的请求量过多,你可能决定将 Agenda Deployment 的副本数扩展到 200。此时,Redis 必须有足够的资源来处理 200 个 Pod 连接到 Redis 集群。Redis 的优势在于它允许从副本读取数据,从而分散负载,适合在会议进行期间大量读取数据的场景。

图 5.4 显示了高负载的典型情况:我们可能会倾向于增加应用服务的副本数量,而不去检查或修改 PostgreSQL 实例的配置。在这种情况下,即使应用服务可以扩展,如果 PostgreSQL 没有相应配置(支持 200+ 并发连接),它也会成为性能瓶颈。

image.png

如果你使用 Helm 来安装应用基础设施,需要注意 Helm 并不会检查这些组件的健康状况——它只是完成安装而已。如今,在 Kubernetes 集群中安装组件的另一种常见方法是使用 Operators(操作者) 。通常与应用基础设施相关联,这类活跃组件不仅会安装,还会监控已安装的组件。例如,Zalando PostgreSQL Operator(可在 github.com/zalando/pos… 找到)就是其中一个例子。

这些 Operators 专注于让你能够创建新的 PostgreSQL 数据库实例,同时也提供一些维护相关的功能,例如:

  • 对 Postgres 集群变更执行滚动更新,包括快速的小版本升级
  • 无需 Pod 重启即可在线调整卷大小(支持 AWS EBS、PVC)
  • 使用 PGBouncer 实现数据库连接池
  • 支持快速就地的主要版本升级

总体来说,Kubernetes Operators 尝试封装与特定组件(此处为 PostgreSQL)相关的运维任务。虽然使用 Operators 可以在安装组件的基础上增加更多功能,但你仍然需要维护组件本身以及 Operator。每个 Operator 都有一套高度规范化的流程,你的团队需要研究并学习如何管理。在选择和使用 Operator 时,这一点必须考虑在内。

对于你和你的团队决定在集群中运行的应用基础设施组件,需要提前规划,以确保有足够的内部专业知识来管理、维护和扩展这些额外组件。

接下来的部分,我们将通过一个开源项目来探讨如何应对这些挑战,该项目旨在使用声明式方法简化云端和本地资源的应用基础设施组件配置。

5.2 使用 Crossplane 的声明式基础设施

使用 Helm 在 Kubernetes 内安装应用基础设施组件,对于大型应用和面向用户的环境而言,并不理想,因为维护这些组件及其依赖(例如高级存储配置)可能变得过于复杂。

云提供商在基础设施配置方面做得非常出色,但他们通常依赖于特定云的工具,这些工具不属于 Kubernetes 的范畴。

本节介绍一种替代工具——CNCF 项目 Crossplanecrossplane.io),它利用,-tl9fi90cbl6b/) Kubernetes API 和扩展点,使用户能够以声明式方式配置真实基础设施,完全通过 Kubernetes API 实现。Crossplane 支持多个云提供商,这意味着它可以与现有的 Kubernetes 工具链很好地集成。

理解 Crossplane 的工作方式及其可扩展性,你就可以构建多云策略,在不同云提供商上运行云原生应用及其依赖,而无需担心被单一厂商锁定。由于 Crossplane 采用与 Kubernetes 相同的声明式方法,你可以为要部署和维护的应用创建高层抽象。

使用 Crossplane 之前,你必须先在 Kubernetes 集群中安装其控制平面。可以参考官方文档(docs.crossplane.io/)或 5.3 节中的逐步教程。

仅安装 Crossplane 核心组件不会有太大效果。根据你的云提供商,你需要安装并配置一个或多个 Crossplane Providers

5.2.1 Crossplane Providers

Crossplane 通过安装一组称为 Crossplane Providers 的组件(docs.crossplane.io/v1.12/conce… Kubernetes。这些组件负责理解并与云提供商特定服务交互,从而代表我们配置云资源。

图 5.5 显示了通过安装 GCP Provider 和 AWS Provider,Crossplane 可以在两个云上配置资源的情况。

image.png

通过安装 Crossplane Providers,你实际上是在扩展 Kubernetes API 的功能,使其能够配置外部资源,例如数据库、消息代理、存储桶以及其他云资源。这些资源会存在于云提供商的环境中,而不在 Kubernetes 集群内部。Crossplane 提供了多个适用于主要云提供商(如 GCP、AWS 和 Azure)的 Providers,你可以在 Crossplane GitHub 组织中找到这些 Providers 的相关信息:docs.crossplane.io/latest/conc…

安装 Crossplane Provider 后,你可以以声明式方式创建特定于该 Provider 的资源,这意味着你可以创建 Kubernetes 资源,使用 kubectl apply -f 应用它们,将这些定义打包到 Helm Charts 中,或者通过环境管道将这些资源存储在 Git 仓库中。

例如,使用 Crossplane GCP Provider 在 Google Cloud 创建一个存储桶,可以参考清单 5.4:

cat <<EOF | kubectl create -f -
apiVersion: storage.gcp.upbound.io/v1beta1    
kind: Bucket                                  
metadata:
  generateName: crossplane-bucket-
  labels:
    docs.crossplane.io/example: provider-gcp 
spec:                                         
  forProvider:
    location: US
  providerConfigRef:
    name: default
EOF
  • apiVersionkind 由 Crossplane GCP Provider 定义。你可以在该 Provider 文档中查看支持的资源类型。
  • ② 在安装了 Crossplane 的 Kubernetes 集群中创建 Bucket 资源,相当于向 Crossplane 提交请求,由它代表你配置并监控这个资源。
  • ③ 每种资源类型都有一组可配置参数,这里我们希望存储桶位于美国。不同资源会暴露不同的配置选项。

依赖 Kubernetes API 来配置云特定资源是一个重要进步,但 Crossplane 不仅仅止步于此。如果你了解在任何主要云提供商中配置数据库实例的流程,会发现仅仅创建组件只是让组件可用的众多任务之一。你还需要额外的网络和安全配置、用户凭证以及其他云提供商特有的配置来访问这些资源。这就是 Crossplane Compositions 的用武之地。

5.2.2 Crossplane Compositions

Crossplane 面向两类用户:平台团队和应用团队。平台团队是云提供商专家,了解如何配置云提供商特定组件;应用团队则熟悉应用需求,知道从应用基础设施角度需要什么。Crossplane 的有趣之处在于:平台团队可以为特定云提供商定义复杂配置,同时为应用团队提供简化的接口。

在实际场景中,很少只创建单一组件。例如,要配置一个数据库实例,应用团队还需要正确的网络和安全配置来访问新创建的实例。能够将多个组件组合和关联在一起是一个非常方便的功能。为了实现这些抽象和简化接口,Crossplane 引入了两个概念:Composite Resource Definitions (XRDs)Composite Resources (XRs)

图 5.6 展示了如何使用 Crossplane XRD 为不同云提供商定义抽象。平台团队可能对 Google Cloud 或 Azure 非常熟悉,因此他们负责定义需要为特定应用组合的资源。应用团队只需通过简单的资源接口请求所需资源。

如同通常情况,抽象概念比较复杂,有助于明确职责分工。接下来,让我们通过一个具体示例来理解 Crossplane Compositions 的实际威力。

image.png

图 5.7 展示了应用团队如何创建一个简单的 PostgreSQL 资源,以便在 Google Cloud 中配置一个 CloudSQL 实例,同时包含网络配置和一个存储桶。应用团队关心的不是具体创建了哪些资源,也不关心这些资源是在哪个云提供商上创建的,他们只关心有一个可供应用连接的 PostgreSQL 实例。

image.png

这就引出了图中的 Secret 框,它表示一个 Kubernetes Secret,Crossplane 会为我们的应用/服务 Pod 创建这个 Secret,以便它们能够连接到已配置的资源。Crossplane 会将所有应用所需的连接信息写入这个 Kubernetes Secret(或者只包含与应用相关的信息)。这个 Secret 通常包含 URL、用户名、密码、证书或应用连接所需的其他信息。平台团队在定义 CompositeResources 时,会指定 Secret 中应包含哪些内容。在接下来的章节中,当我们为 Conference 应用添加真实基础设施时,将探索这些 CompositeResourceDefinitions 的具体样子,以及它们如何被应用来创建应用所需的所有组件。

5.2.3 Crossplane 组件及要求

要使用 Crossplane 提供的 Providers 和 CompositeResourceDefinitions,我们需要了解 Crossplane 的组件如何协同工作,在不同的云提供商中配置和管理这些资源。

本节将介绍 Crossplane 的工作需求,以及 Crossplane 组件如何管理我们的 CompositeResources。首先,需要明确的是,必须在 Kubernetes 集群中安装 Crossplane。这可以是运行应用的集群,也可以是单独用于运行 Crossplane 的集群。这个集群将包含一些 Crossplane 组件,它们能够理解我们的 CompositeResourceDefinitions,并在云平台上拥有足够的权限,以代表我们配置资源。

image.png

图 5.8 显示了 Crossplane 安装在 Kubernetes 集群中,并安装了 Crossplane GCP Provider,该 Provider 配置了一个具有足够权限的 Google Cloud Platform 账户,用于配置 PostgreSQL 和 Redis 实例。这意味着在某些情况下,需要拥有云提供商的管理员权限来创建资源。

要让图 5.8 在 GCP 中正常工作,需要在云提供商端进行如下配置:

创建 GCP Redis 实例:

  • 需要在 GCP 项目中启用 redis.googleapis.com API。
  • 需要对 Redis 资源拥有管理员权限 roles/redis.admin

创建 GCP PostgreSQL 实例:

  • 需要在 GCP 项目中启用 sqladmin.googleapis.com API。
  • 需要对 SQL 资源拥有管理员权限 roles/cloudsql.admin

每个可用的 Crossplane Provider 都需要特定的安全配置,并需要在我们希望创建资源的云提供商中有一个账户。安装并配置好 Crossplane Provider(在此案例中为 GCP Provider)后,我们就可以开始创建由该 Provider 管理的资源。每个 Provider 提供的资源可以在以下文档网站找到:doc.crds.dev/github.com/… 5.9)。

image.png

如前图所示,GCP Provider 版本 0.22.0 支持在 Google Cloud Platform 中创建 29 种不同的 CRD(Custom Resource Definitions,自定义资源定义)。Crossplane 将这些资源定义为托管资源(managed resources) 。每个托管资源都需要在 Crossplane Provider 中启用,以便该 Provider 可以访问、创建和修改这些资源。

在第 5.3 节中,我们将学习如何使用不同的 Crossplane Provider 和 Crossplane Compositions 为应用程序配置云端或本地资源。在深入技术细节之前,先了解一下 Crossplane 的核心行为,这些是使用 Kubernetes 工具时应关注的重点。

5.2.4 Crossplane 行为

与在 Kubernetes 集群中安装 Helm 组件不同,Crossplane 通过与云提供商特定 API 交互来在云基础设施中配置资源。这可以简化资源维护和相关成本。另一个重要区别是,Crossplane Provider(此例中为 GCP Provider)会持续观察已创建的托管资源。与仅使用 Helm 安装的资源相比,托管资源有一些显著优势,其行为定义明确。主要特性如下:

  1. 可视化如同普通 Kubernetes 资源:Crossplane 托管资源本质上就是 Kubernetes 资源,可以使用任何 Kubernetes 工具监控和查询其状态。
  2. 持续调和(Continuous reconciliation) :创建托管资源后,Provider 会持续监控资源,确保其存在且正常运行,并将状态回报给 Kubernetes 资源。托管资源中定义的参数被视为期望状态(source of truth),Crossplane Provider 会将这些配置应用到云资源上。可以使用标准 Kubernetes 工具监控状态变化并触发修复流程。
  3. 不可变属性(Immutable properties) :如果用户手动修改云资源属性,Provider 会回报给托管资源,以避免配置漂移。Crossplane 不会删除云资源,只会通知以便采取行动;而 Terraform 等工具会自动删除远程资源并重新创建。
  4. 延迟初始化(Late initialization) :托管资源中某些属性可选,Provider 会为这些属性选择默认值。Crossplane 会先用默认值创建资源,然后将选择的值设置到托管资源中。这简化了资源创建配置,同时可复用云提供商定义的合理默认值。
  5. 删除(Deletion) :删除托管资源时,云提供商会立即触发删除操作,但托管资源会保留,直到云资源完全删除。删除过程中可能出现的错误会记录在托管资源状态字段中。
  6. 导入现有资源(Importing existing resources) :Crossplane 并不一定要创建资源才能管理它们。可以创建托管资源来监控 Crossplane 安装前已经存在的组件,方法是为托管资源添加特定注解:crossplane.io/external-name

为了总结 Crossplane、Crossplane GCP Provider 和托管资源之间的交互关系,可参考图 5.10。

image.png

下列要点说明了图 5.10 中的执行顺序:

  1. 首先,我们需要创建一个资源。可以使用任何工具创建 Kubernetes 资源,这里以 kubectl 为例。
  2. 如果创建的资源是 Crossplane 托管资源(例如 GCP Crossplane Provider 管理的 CloudSQLInstance),Provider 会接管该资源。
  3. 管理资源的第一步是检查它是否已存在于基础设施中(即配置好的 GCP 账户)。如果不存在,Provider 会请求在基础设施中创建该资源。将根据资源属性(例如需要哪种 SQL 数据库)配置相应的数据库。假设我们选择 PostgreSQL 作为示例。
  4. 云提供商收到请求后,如果资源启用,将根据托管资源中的配置参数创建新的 PostgreSQL 实例。
  5. PostgreSQL 的状态会回报给托管资源,这意味着可以使用 kubectl 或其他工具监控已配置资源的状态。Crossplane Provider 会保持资源状态同步。
  6. 数据库启动并运行后,Crossplane Provider 会创建一个 Kubernetes Secret,用于存储应用程序连接新实例所需的凭据和属性。
  7. Crossplane 会定期检查 PostgreSQL 实例的状态,并更新托管资源。

遵循 Kubernetes 设计模式,Crossplane 使用控制器实现的调和(reconciliation)循环来跟踪外部资源。接下来,我们将看看如何在 walking skeleton 应用中使用 Crossplane。

5.3 为我们的 Walking Skeleton 配置基础设施

本节中,我们将使用 Crossplane 来抽象 Conference 应用的基础设施配置。由于你可能无法访问 GCP、AWS 或 Azure 等云提供商,我们将使用一个特殊的 Provider:Crossplane Helm Provider。该 Provider 允许我们将 Helm Chart 当作云资源来管理。目的是展示如何使用 Crossplane(尤其是 Crossplane Compositions),让用户通过简化的 Kubernetes 资源请求本地或不同云提供商托管的资源。

对于 Conference 应用,我们需要 Redis、PostgreSQL 和 Kafka 实例。从应用角度来看,只要这三类组件可用,我们就能直接连接使用。组件的具体配置由运维团队负责。

在第 2 章中安装的 Conference 应用 Helm Chart 包含 Redis、PostgreSQL 和 Kafka 的安装,作为 Helm 依赖项,并通过安装时可设置的条件值进行控制。下面快速查看 Helm Chart 的依赖配置:
Chart.yaml 示例链接

Listing 5.5 Conference 应用 Helm Chart 依赖
apiVersion: v2
description: A Helm chart for the Conference App
name: conference-app
version: v1.0.0
type: application
icon: https://www.salaboy.com/content/images/2023/06/avatar-new.png
appVersion: v1.0.0
home: http://github.com/salaboy/platforms-on-k8s
dependencies:                                     ①
- name: redis                                     ②
  version: 17.11.3 
  repository: https://charts.bitnami.com/bitnami
  condition: install.infrastructure               ③
- name: postgresql
  version: 12.5.7 
  repository: https://charts.bitnami.com/bitnami
  condition: install.infrastructure
- name: kafka
  version: 22.1.5
  repository: https://charts.bitnami.com/bitnami
  condition: install.infrastructure 
  • ① 可以为 Helm Chart 包含任意数量的依赖,从而实现复杂组合。
  • ② 每个依赖需要指定 Chart 名称、托管仓库(也可以使用 oci:// 引用)以及要安装的版本。
  • ③ 可以定义自定义条件,决定安装 Chart 时是否注入该依赖。

在此示例中,所有应用基础设施依赖都在应用级别(Chart.yaml 的 dependencies 部分)定义,但完全可以为每个服务单独定义 Helm Chart,并在内部定义其依赖。

这种依赖方式适合开发团队一次性安装整个应用及所有组件,但在更大场景中,我们希望将应用基础设施与应用服务解耦。幸运的是,Conference 应用 Helm Chart 允许关闭这些组件依赖,从而接入由不同团队托管和管理的 Redis、PostgreSQL 和 Kafka 实例(见图 5.11)。

image.png

通过区分谁请求和谁提供应用基础设施组件,我们允许不同团队控制和管理这些组件的更新、备份或在发生故障时的恢复方式。通过使用 Crossplane,我们可以让团队按需请求数据库,然后将其连接到应用服务。下一节将使用的机制中,一个重要方面是:我们请求的组件可以在本地(使用 Crossplane Helm Provider)或远程(使用 Crossplane 云提供商)进行部署。下面来看一下实际情况。你可以参考这篇分步教程来安装、配置并创建 Crossplane 组合资源:GitHub 链接

在这个示例中,我们将创建一个 KinD 集群,并配置 Crossplane,让团队在开发环境中通过 Crossplane Helm Provider 按需请求应用基础设施。在生产环境中,这些请求将通过可扩展的云资源满足。具体来说,我们允许团队使用简化接口请求 Redis、PostgreSQL 和 Kafka 实例。

对于 Conference 应用示例,平台团队决定创建两个概念:

  • 数据库:NoSQL 与 SQL 数据库,如 Redis 和 PostgreSQL。
  • 消息代理:托管或非托管消息代理,如 Kafka。

安装 Crossplane 和 Crossplane Helm Provider 后,平台团队需要定义两个 Kubernetes 资源:

  1. Crossplane Composite Resource Definitions (XRDs) :定义希望暴露给团队的资源类型——在本例中为 Database 和 MessageBroker。XRD 为多个 Composition 提供接口定义。
  2. Crossplane Composition:允许定义一组资源清单(manifests),并将 Composition 与 XRD 关联以实现该接口。当用户请求 XRD 定义的新资源时,Composition 中的所有资源清单会在集群中创建。可以提供多个 Composition(例如针对不同云提供商),都实现相同的 XRD,并通过资源标签选择使用哪一个 Composition。

下面看数据库 XRD 的具体示例(listing 5.6):

Listing 5.6 数据库 Composite Resource Definition
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: databases.salaboy.com              
spec:
  group: salaboy.com                       
  names:
    kind: Database                         
    plural: databases
    shortNames:
      - "db"
      - "dbs"
  versions:
  - additionalPrinterColumns:
    - jsonPath: .spec.parameters.size
      name: SIZE
      type: string
    - jsonPath: .spec.parameters.mockData
      name: MOCKDATA
      type: boolean  
    - jsonPath: .spec.compositionSelector.matchLabels.kind
      name: KIND
      type: string
    name: v1alpha1
    served: true
    referenceable: true
    schema: 
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              parameters:                  
                type: object
                properties:
                  size:
                    type: string 
                  mockData: 
                    type: boolean
                required:   
                - size
            required:                      
            - parameters 
  • ① 每个 Kubernetes 资源都需要唯一名称。
  • ② XRD 定义了一个新资源类型,需要指定 group 和 kind。
  • ③ 我们希望用户能请求的资源类型为 Database。
  • ④ 新定义的资源可以带自定义参数,本示例仅演示两个参数:size 和 mockData。
  • ⑤ Kubernetes API Server 可以验证资源,定义必填参数及其类型和其他验证,如果缺失或无效,资源请求会被拒绝。

我们定义了一个名为 Database 的新资源类型,包含两个参数:size 和 mockData。用户可通过 size 参数指定实例资源规模(small、medium、large),无需关心存储量或副本数。mockData 参数可实现按需注入数据机制,这只是示例,具体参数和接口可根据团队需求自定义。

下面看实现该 XRD 的 Crossplane Composition(listing 5.7):

Listing 5.7 Key/value Database Crossplane Composition
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: keyvalue.db.local.salaboy.com             
  labels:                                         
    type: dev
    provider: local
    kind: keyvalue
spec:
  writeConnectionSecretsToNamespace: crossplane-system
  compositeTypeRef:                               
    apiVersion: salaboy.com/v1alpha1
    kind: Database
  resources:
    - name: redis-helm-release                    
      base:
        apiVersion: helm.crossplane.io/v1beta1
        kind: Release
        metadata:
          annotations:
            crossplane.io/external-name: # patched
        spec:
          rollbackLimit: 3
          forProvider:
            namespace: default
            chart:                                
              name: redis
              repository: https://charts.bitnami.com/bitnami
              version: "17.8.0"
            values:
              architecture: standalone
          providerConfigRef:                      
            name: default
      patches:                                    
        - fromFieldPath: metadata.name
          toFieldPath: metadata.annotations[crossplane.io/external-name]
          policy:
            fromFieldPath: Required
        - fromFieldPath: metadata.name
          toFieldPath: metadata.name
          transforms:
            - type: string
              string:
                fmt: "%s-redis"         
      readinessChecks:                            
      - type: MatchString
        fieldPath: status.atProvider.state
        matchString: deployed
  • ① Composition 资源也需要唯一名称。
  • ② 可以为 Composition 定义标签,用于与请求的 Database 资源匹配。
  • compositeTypeRef 将 Database XRD 与该 Composition 关联。
  • resources 数组定义 Composition 将创建的资源,本例配置单个 Helm Release 类型资源。
  • ⑤ 为 Release 资源提供详细值,本例为 Bitnami Redis Helm Chart。
  • providerConfigRef 指定使用的 Crossplane Helm Provider 配置,可指向不同集群,本例使用默认本地配置。
  • ⑦ 通过 patch 机制,可配置资源间关联或将请求资源的参数应用于资源。
  • ⑧ 可为每个 Composition 定义条件标记资源状态,本例当 Helm Release 的 .atProvider.statedeployed 时标记为就绪。

通过该 Composition,我们将 Database 请求与一组资源关联,本例中是在 Kubernetes 集群中使用 Crossplane 安装 Redis Helm Chart。图 5.12 展示了两个用户请求同一数据库类型的示例。

image.png

需要注意的是,这个 Helm Chart 会安装在与 Crossplane 相同的 Kubernetes 集群中。但实际上,我们完全可以配置 Helm Provider,使其拥有正确的凭据,将 Charts 安装到一个完全不同的集群中。

在分步教程中(GitHub 链接),你将安装三个 Composite Resource Definitions(复合资源定义)和三个 Compositions(组合)。安装完成后,如图 5.12 所示,你就可以请求新的数据库和消息代理,每次请求时,组合中定义的所有资源都会被创建。为了演示简化,key-value 数据库组合仅安装 Redis,但理论上没有限制可创建的资源数量(受限于硬件或配额)。

一个 Database 资源只是集群现在可以理解的另一个 Kubernetes 资源,其示例如 listing 5.8 所示:

Listing 5.8 团队创建 Database 资源请求新数据库实例
apiVersion: salaboy.com/v1alpha1
kind: Database
metadata:
  name: my-db-keyavalue    
spec:
  compositionSelector:
    matchLabels:           
      provider: local
      type: dev
      kind: keyvalue
  parameters:              
    size: small
    mockData: false
  • ① 资源的唯一名称
  • ② 使用 matchLabels 选择适当的 Composition
  • ③ 设置 Database 资源所需的参数

该 Database 资源的 schema 在 Crossplane CompositeResourceDefinition 中定义。注意 spec.compositionSelector.matchLabels 与 Composition 使用的标签匹配,这个机制可以让你为同一 Database 定义选择不同的 Composition。

如果你按照教程操作,可以尝试创建多个资源,并参考 Crossplane 官方文档了解如何实现类似 smallmockData 这样的参数(目前仅用于演示)。

这些机制的真正威力在于可以为同一接口(XRD)提供不同的 Composition(实现)。例如,现在可以创建另一个 Composition,为 Call for Proposals 服务提供 PostgreSQL 实例,如 listing 5.9 所示。PostgreSQL Composition 与 Redis 类似,但安装的是 PostgreSQL Helm Chart。

Listing 5.9 SQL Database Crossplane Composition
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: sql.db.local.salaboy.com    
  labels:
    type: dev
    provider: local
    kind: sql                       
spec:
  ...
  compositeTypeRef:
    apiVersion: salaboy.com/v1alpha1
    kind: Database
  resources:
    - name: postgresql-helm-release
      base:
        apiVersion: helm.crossplane.io/v1beta1
        kind: Release
        spec:
          forProvider:
            chart:                  
              name: postgresql
              repository: https://charts.bitnami.com/bitnami
              version: "12.2.7"
          providerConfigRef:
            name: default
          
  • ① 为 Composition 指定唯一名称,以区分之前用于 Redis 的 keyvalue Composition
  • ② 使用不同标签描述该 Composition,provider 与之前相同
  • ③ 安装 Bitnami 提供的 PostgreSQL Helm Chart

下面看如何使用该 Composition 创建 PostgreSQL 实例。创建 PostgreSQL 实例与之前 Redis 的流程非常类似,如 listing 5.10 所示:

Listing 5.10 使用 kind: sql 标签选择实现的 Database 资源
apiVersion: salaboy.com/v1alpha1
kind: Database
metadata:
  name: my-db-sql      
spec:
  compositionSelector:
    matchLabels:
      provider: local
      type: dev
      kind: sql        
  parameters: 
    size: small
    mockData: false
  • ① PostgreSQL 数据库的唯一名称
  • ② 使用 “sql” 标签匹配之前定义的 Composition

这里仅通过标签选择触发哪一个 Composition 来实现 Database 资源的创建。图 5.13 展示了这些概念的实际应用,注意标签如何根据 kind 值选择正确的 Composition。

image.png

太好了!我们可以创建数据库了!但当然,这还不是终点。如果你有云提供商的访问权限,你可以提供在云提供商内部创建数据库实例的 Composition,这正是 Crossplane 的强项所在。

以 Google Cloud Platform(GCP)为例,对于使用 GCP 云资源的 Composition,你需要安装 Crossplane 的 GCP Provider 并进行相应配置,具体步骤可参考官方 Crossplane 文档:Getting Started with GCP Provider

image.png

我们仍然可以通过匹配标签选择不同的 Provider 来使用我们想要的 Composition。通过在图 5.14 中更改标签,我们可以使用本地 Helm Provider 或 GCP Provider 来实例化一个 Redis 实例。

注意:可以查看社区贡献的 AWS Compositions 示例,使用 Crossplane AWS Provider,地址为:github.com/salaboy/pla…

然后,在 Google Cloud Platform 上创建新的数据库资源将类似于清单 5.11 所示:

清单 5.11 请求新的 SQL 数据库

apiVersion: salaboy.com/v1alpha1
kind: Database
metadata:
  name: my-db-cloud-sql   # ①
spec:
  compositionSelector:
    matchLabels:
      provider: gcp       # ②
      type: dev
      kind: sql           # ③
  parameters: 
    size: small
    mockData: false

① 资源的唯一名称,需要与之前使用的名称不同。
② provider 标签选择标记为 provider: gcp 的 Composition,也就是说,通过这个标签我们选择数据库将被部署的位置。
③ kind 标签允许我们选择要创建的数据库类型。

无论我们的数据库或其他应用基础设施组件部署在哪里,我们都可以通过遵循一些约定来连接应用服务。可以使用资源名称(例如 my-db-cloud-sql)来确定用于服务发现的 Kubernetes 服务,也可以使用创建的 Secret 获取连接所需的凭据。

步骤教程还提供了针对消息代理的 CompositeResourceDefinition,以及安装 Kafka Helm Chart 的 Composition,可参考:app-messagebroker-kafka.yaml

需要特别注意的一点是,Google Cloud Platform 并未提供托管的 Kafka 服务。这意味着你的团队需要决定:当应用部署在 GCP 时,是替换 Kafka、在 GCP 计算实例上安装和管理 Kafka,还是使用第三方服务。而在 AWS 示例中,我们可以使用托管的 Kafka 服务,因此无需修改应用代码。不过,将如何连接到这些基础设施服务抽象出来会更方便——关于这一点将在第 7 章讨论。

图 5.15 展示了为 key/value 数据库提供 Composite Resource Definition 的便捷性,这些数据库既可以使用 Helm 在本地部署,也可以由云提供商管理。但对于 Kafka 来说,情况会复杂一些,因为可能需要集成第三方服务,或者安排团队来管理 Kafka 实例。

image.png

除了 Kafka 和 Google Cloud Platform,你的团队还需要一个策略来管理跨云提供商的基础设施,或者至少要对如何处理类似情况做出有意识的选择。从应用服务的角度来看,如果你决定替换 Kafka 并改用 Google Pub/Sub,你会维护同一服务的两个副本吗?一个副本包含 Kafka 依赖项,另一个包含用于连接 Google Pub/Sub 的 Google GCP SDK。如果只使用 Google Pub/Sub,那么应用将无法在 Google Cloud 之外运行。

5.3.1 使用新创建的基础设施连接服务

当我们创建新的数据库或消息代理资源时,Crossplane 会根据特定云提供商中已创建组件的状态监控这些 Kubernetes 资源的状态,保持它们同步,并确保应用了所需配置。这意味着 Crossplane 会确保我们的数据库和消息代理处于运行状态。如果状态发生变化,Crossplane 会尝试重新应用我们请求的配置,直到资源恢复正常。

如果我们的应用尚未部署在 KinD 集群中,可以在不安装 PostgreSQL、Redis 和 Kafka 的情况下部署应用。如第 2 章所示,可以通过设置一个标志来禁用基础设施部署:

helm install conference oci://docker.io/salaboy/conference-app \
--version v1.0.0 --set install.infrastructure=false

我强烈建议你参考该 分步教程 来实际操作 Crossplane 和 Conference 应用,实践是最好的学习方式!

如果只运行上面的命令,Helm 不会部署 Redis、PostgreSQL 或 Kafka 组件,但应用服务仍然不知道如何连接我们通过 Crossplane Compositions 创建的这些实例。我们需要在应用的 Helm Chart 中添加更多参数,让服务知道连接位置。首先,检查集群中可用的数据库,如清单 5.12 所示:

清单 5.12 列出所有数据库资源

kubectl get dbs
NAME              SIZE     KIND       SYNCED   READY   COMPOSITION                     
my-db-keyavalue   small    keyvalue   True     True    keyvalue.db.local.salaboy.com   
my-db-sql         small    sql        True     True    sql.db.local.salaboy.com        

教程还指导你创建一个 MessageBroker,并确认已有一个实例,如清单 5.13 所示:

清单 5.13 列出所有 MessageBroker 资源

kubectl get mbs
NAME          SIZE    KIND    SYNCED   READY   COMPOSITION                  
my-mb-kafka   small   kafka   True     True    kafka.mb.local.salaboy.com   

清单 5.14 显示了数据库实例和消息代理的 Kubernetes Pods:

清单 5.14 应用基础设施 Pods

kubectl get pods
NAME                             READY   STATUS    RESTARTS   AGE
my-db-keyavalue-redis-master-0   1/1     Running   0          25m
my-db-sql-postgresql-0           1/1     Running   0          25m
my-mb-kafka-0                    1/1     Running   0          25m

除了 Pods,还有四个 Kubernetes Secret 被创建:两个存储 Crossplane Compositions 使用的 Helm Release,两个存储新创建数据库的密码,供应用连接使用(见清单 5.15)。

清单 5.15 存储数据库凭据的 Kubernetes Secret

kubectl get secret
NAME                                    TYPE                 DATA   AGE
my-db-keyavalue-redis                   Opaque               1      26m
my-db-sql-postgresql                    Opaque               1      25m
sh.helm.release.v1.my-db-keyavalue.v1   helm.sh/release.v1   1      26m
sh.helm.release.v1.my-db-sql.v1         helm.sh/release.v1   1      25m
sh.helm.release.v1.my-mb-kafka.v1       helm.sh/release.v1   1      25m

清单 5.16 显示在我们创建数据库后,默认 namespace 中可用的服务:

清单 5.16 查看服务

kubectl get services
NAME                           TYPE        CLUSTER-IP       PORT(S)
kubernetes                     ClusterIP   10.96.0.1        443/TCP
my-db-keyavalue-redis-headless ClusterIP   None             6379/TCP
my-db-keyavalue-redis-master   ClusterIP   10.96.49.121     6379/TCP
my-db-sql-postgresql           ClusterIP   10.96.129.115    5432/TCP
my-db-sql-postgresql-hl        ClusterIP   None             5432/TCP
my-mb-kafka                    ClusterIP   10.96.239.45     9092/TCP
my-mb-kafka-headless           ClusterIP   None             9092/TCP

有了数据库和消息代理的服务名及 Secret,我们可以配置 Conference 应用的 Helm Chart,不仅不再部署 Redis、PostgreSQL 和 Kafka,还可以连接到正确的实例,使用命令:

helm install conference oci://docker.io/salaboy/conference-app \
--version v1.0.0 -f app-values.yaml

这里我们使用 app-values.yaml 文件来设置 Chart 的值,而不是在命令中设置所有参数。示例文件内容如下(清单 5.17):

清单 5.17 Helm Chart 自定义 values.yaml 文件

install:
  infrastructure: false                               # ①
frontend:
  kafka:
    url: my-mb-kafka.default.svc.cluster.local        # ②
agenda:
  kafka:
    url: my-mb-kafka.default.svc.cluster.local
  redis:
    host: my-db-keyavalue-redis-master.default.svc.cluster.local
    secretName: my-db-keyavalue-redis
c4p:
  kafka:
    url: my-mb-kafka.default.svc.cluster.local
  postgresql:
    host: my-db-sql-postgresql.default.svc.cluster.local
    secretName: my-db-sql-postgresql                  # ③
notifications:
  kafka:
    url: my-mb-kafka.default.svc.cluster.local

① 禁用 Redis、PostgreSQL 和 Kafka Helm 依赖,安装应用时不会部署这些组件。
② 使用 Kubernetes 服务连接 Kafka 集群,应用服务将连接到创建的实例。Redis 和 PostgreSQL 也采用相同方式。
③ 对于 Redis 和 PostgreSQL,Composite Resource 创建了 Kubernetes Secret,Helm Chart 会自动获取凭据,我们只需指定 Secret 名称。

在这个 app-values.yaml 文件中,我们不仅关闭了 PostgreSQL、Redis 和 Kafka 的 Helm 依赖,还配置了服务连接新建数据库所需的变量。注意,如果数据库创建在不同的 namespace 或使用了不同名称,kafka.urlpostgresql.hostredis.host 需要包含正确的 namespace,例如 my-db-sql-postgresql.default.svc.cluster.local(default 为 namespace)。

图 5.16 展示了 Conference 应用服务连接到使用 Crossplane 创建的基础设施。此时,开发者与平台团队的边界更加清晰,开发者可以通过平台团队提供的简化接口获取所需基础设施。

image.png

所有这些努力让我们能够将定义、配置和运行应用基础设施的责任拆分给另一个不直接负责应用服务开发的团队。服务可以独立发布,而无需担心使用的是哪种数据库或何时需要升级。开发者无需关心云提供商账户或是否有权限创建不同的资源。因此,另一个拥有完全不同技能的团队可以负责创建 Crossplane compositions 并配置 Crossplane 提供者。

我们还让团队可以通过 Kubernetes 资源来请求应用基础设施组件,使他们能够快速创建实验或测试环境,或者快速搭建应用的新实例。这对开发者而言是一个重大转变,因为以前开发者需要通过云提供商或公司内部的工单系统,请求另一个团队去创建资源,这通常可能需要数周时间!

总结迄今为止的成果,我们可以说:

  • 我们抽象了如何创建本地或云特定组件(如 PostgreSQL、Redis 数据库和 Kafka 消息代理)及访问这些新实例所需的所有配置。
  • 我们为应用团队提供了简化的接口,该接口与云提供商无关,因为它依赖于 Kubernetes API。
  • 最后,我们通过 Crossplane 创建的 Kubernetes Secret 将应用服务与新创建的实例连接起来,Secret 中包含连接新实例所需的全部信息。

如果使用像 Crossplane composition 这样的机制创建更高层次的抽象,你就可以创建团队可自助使用的领域特定概念。我们通过创建 Crossplane Composite Resource(使用 Crossplane compositions 指明要创建哪些资源以及在哪个云提供商中创建)实现了数据库和消息代理概念。

注意:你可以参考该 分步教程,完成本节描述的所有步骤。

5.4 回到平台工程

我们需要谨慎。不能指望每个开发者都理解或愿意使用我们讨论的工具(如 Crossplane、ArgoCD、Tekton 等)。我们需要降低这些工具带来的复杂性。正如第 1 章讨论 Google Cloud Platform 时所述,平台的目标是降低用户的认知负担。GCP 用户无需了解底层细节、使用的工具或整个平台的设计,就可以创建 Kubernetes 集群。

Crossplane 的设计既服务于平台团队,也服务于开发团队(或消费者),他们的优先级、兴趣和技能不同。通过创建合适的抽象(XRDs),平台团队可以暴露简单资源,供开发团队按需配置,而在后台则设置了复杂的 composition 来创建和连接一组云资源。我们也看到,通过使用标签和选择器,可以在不同 composition 之间切换,实现跨云提供商的基础设施创建,同时保持开发团队请求的一致用户体验。Crossplane 通过扩展 Kubernetes API,统一了工作负载管理和跨云应用基础设施管理的方式。换句话说,安装 Crossplane 到 Kubernetes 集群后,我们不仅可以部署和运行工作负载,还能使用相同工具创建和管理云资源。

当然,Crossplane 带来的便利也有一些挑战。平台团队在考虑 Crossplane 时,还有其他更流行的工具可供选择,如 Hashicorp 的 Terraform 和 Pulumi。Crossplane 比 Terraform 更新,其专注于 Kubernetes,因此平台团队必须充分投入 Kubernetes。对于不熟悉 Kubernetes 集群管理的团队,Crossplane 初期可能较为困难,因此需要提升 Kubernetes 技能来运行和维护 Crossplane。

平台团队需要在 Crossplane 和 Terraform 等工具之间做出选择。我建议考虑希望在多大程度上将所用工具与 Kubernetes API 对齐。理论上,用相同方式管理基础设施(云资源)和应用程序很有意义,但同时也要考虑维护这些组件的团队是否能够接受。近年来,云原生领域在可观测性、安全性和运维方面的成熟度显著提高,越来越多团队能够在大规模下管理和运营 Kubernetes。对于这些团队,Crossplane 是一个很好的补充,因为它可以与现有的 Kubernetes 可观测性栈、策略执行器和仪表盘兼容。

拥有像 Crossplane 这样灵活的工具,可以开启跨云提供商的新可能性。平台团队现在有更多选项,这可能会带来复杂性,但有一点明确:如果使用合适的抽象,平台可以保持灵活性,同时消费者接口不变,平台团队可以在后台迭代并提供新的实现。

图 5.17 展示了如何使用 Crossplane 为开发团队提供自助服务抽象。他们可以请求数据库、消息代理、身份服务以及任何其他内部或外部服务。问题是,从应用的角度来看,需要做哪些改变?例如之前 Kafka 的示例,如果将 Kafka 替换为 Google Pub/Sub,应用程序需要做哪些调整?

image.png

到目前为止,我们已经涵盖了很多内容,从将一个简单的应用安装到集群,到构建服务并使用 GitOps 方法进行部署,再到现在以声明式方式提供应用基础设施。图 5.18 展示了如何使用 GitOps 方法,不仅可以定义哪些服务或应用应在环境中运行,还可以定义需要创建哪些云资源并将其连接到我们的应用服务。

image.png

现在是将所有内容整合到一个平台中的时候了,因为让我们的应用运行在与流水线和其他工具相同的集群中并没有太大意义。那么,在 Kubernetes 之上构建的平台会是什么样子?当你的团队尝试构建这样的平台时,会面临哪些主要挑战?只有一种方法可以弄清楚:让我们在 Kubernetes 之上构建一个平台吧!

总结

  • 云原生应用依赖应用基础设施来运行,因为每个服务可能需要不同的持久化存储、用于发送消息的消息中间件,以及其他组件。
  • 在云提供商内部创建应用基础设施很容易,并能节省大量时间,但这意味着我们依赖他们的工具和生态系统。
  • 通过依赖 Kubernetes API 和像 Crossplane 这样的工具,可以实现与云无关的基础设施供应,Crossplane 抽象了底层的云提供商,并允许我们使用 Crossplane 组合定义需要创建的资源。
  • Crossplane 支持主要云提供商,并且可以扩展到其他服务提供商,包括那些可能不在云提供商上运行的第三方工具(例如,我们希望使用 Kubernetes API 管理的遗留系统)。
  • 通过使用 Crossplane 的 Composite Resource Definitions(复合资源定义),我们为应用团队创建了一个接口,使其能够以自助式方式请求云资源。
  • 如果你跟随了分步教程,你就亲自体验了如何使用 Crossplane 通过多云方法提供应用基础设施。