正如我们在第 1 章中所讨论的,Kubernetes 是为无状态工作负载设计的。由此得出的推论是,无状态工作负载是 Kubernetes 最擅长的。正因为如此,有些人认为你不应该尝试在 Kubernetes 上运行有状态的工作负载,你可能会听到关于你应该做什么的各种建议:“使用托管服务”,或者“将数据留在本地数据中心的遗留数据库中”,甚至可能“在云中运行数据库,但在传统的虚拟机而不是容器中运行。
虽然这些建议仍然是可行的选择,但我们在本书中的主要目标之一是证明在 Kubernetes 中运行数据基础设施不仅是一个可行的选择,而且是一个首选的选择。Chris Bradford 在他的文章《一个 Kubernetes 数据库案例》中描述了他的旅程,从对在 Kubernetes 中运行任何有状态工作负载持怀疑态度,到勉强接受在 Kubernetes 上运行数据基础设施进行开发和测试工作负载,再到在生产环境中部署 K8s 数据库的热情宣传。这个旅程是 Data on Kubernetes 社区中许多旅程的典型特征。到 2020 年年中,Boris Kurktchiev 在他的文章中引用了一个,即在 Kubernetes 上管理有状态工作负载已经达到了可行性甚至成熟的程度
这种变化是如何产生的?在过去的几年里,Kubernetes 社区已经将重点转移到添加支持在 Kubernetes 上以云原生方式管理状态的功能上。存储元素代表了我们在上一章中介绍的这种转变的重要组成部分,包括 Kubernetes PersistentVolume 子系统和容器存储接口的采用。在本章中,我们将通过查看用于在此存储基础上构建有状态应用程序的 Kubernetes 资源来完成这部分故事。我们将特别关注特定类型的有状态应用程序:数据基础结构。
艰难之路
“以艰难的方式做这件事”这句话已经与避免简单的选择联系在一起,而倾向于投入完成具有持久意义的结果所需的详细工作。纵观历史,所有派别的先驱都以牺牲鲜血、汗水和眼泪而自豪,这些牺牲使后代的生活变得更加可以忍受。经常听到这些长老在他们的门徒无法理解他们必须经历的深度时哀叹。
在科技界也不例外。虽然 API 和“无代码”环境等新创新具有在全球范围内培养新一批开发人员的巨大潜力,但为了在全球范围内管理高度可用和安全的系统,仍然需要对底层技术有更深入的了解。当事情出错时,这些详细的知识证明了它的价值。这就是为什么我们中的许多软件开发人员在日常工作中从未接触过物理服务器,通过手工连接芯片和电路板来构建自己的 PC 而受益匪浅的原因。这也是为我们的朋友和家人担任非正式 IT 顾问的隐藏好处之一。
当然,对于 Kubernetes 社区来说,“艰难的方式”有更具体的含义。谷歌工程师Kelsey Hightower的Kubernetes 已经成为那些想要更深入地了解构成Kubernetes集群的元素的人的一种仪式。这个受欢迎的教程将引导您下载、安装和配置构成 Kubernetes 控制平面的每个组件。结果是一个工作的 Kubernetes 集群,虽然不适合部署生产工作负载,但肯定有足够的功能来进行开发和学习。这种方法的吸引力在于所有指令都是手动输入的,而不是下载一堆为您完成所有操作的脚本,以便您了解每一步发生的事情。
在本章中,我们将模拟这种方法,并引导您自己以艰难的方式部署一些示例数据基础架构。在此过程中,我们将获得更多您在第 2 章中了解的存储资源的实践经验,我们将介绍用于管理计算和网络的其他 Kubernetes 资源类型,以完成我们在第 1 章中介绍的“计算、网络、存储”三元组。你准备好弄脏你的手了吗?我们走吧!
警告
警告:示例不是生产级
我们在本章中介绍的示例主要用于介绍 Kubernetes API 的新元素,并不代表我们建议在生产环境中运行的部署。我们将确保突出显示存在差距的地方,以便我们可以在接下来的章节中演示如何填补它们。
在 Kubernetes 上运行数据基础架构的先决条件
为了遵循本章中的示例,您需要一个 Kubernetes 集群来处理。如果您以前从未尝试过,也许您会希望使用 说明构建一个集群,然后使用相同的集群以困难的方式添加数据基础设施。您也可以使用简单的桌面 K8,因为我们不会使用大量资源。如果您使用的是共享群集,则可能需要将这些示例安装在其自己的命名空间中,以将它们与其他示例的工作隔离开来。
kubectl config set-context --current --namespace=
您还需要确保集群中有一个 StorageClass。如果您从以艰难方式构建的集群开始,您将不会有一个集群。您可能需要按照存储类部分中的说明安装公开本地存储()的简单存储类和预配程序。
您需要使用支持 WaitForFirstConsumer 的 的 StorageClass 。这使 Kubernetes 能够灵活地推迟配置存储,直到我们需要它。此行为通常是生产部署的首选,因此您不妨开始养成这种习惯。
在 Kubernetes 上运行 MySQL
首先,让我们从一个超级简单的例子开始。MySQL由于其可靠性和可用性而成为使用最广泛的关系数据库之一。对于此示例,我们将在官方 Kubernetes 文档中的 的基础上构建,但有一些转折。您可以在找到本节中使用的源代码。本教程包括两个 Kubernetes 部署:一个用于运行 MySQL pod,另一个用于运行示例客户端,在本例中为 Wordpress。此配置如图
MySQL 的 Kubernetes 部署示例
在这个例子中,我们看到每个 pod 都有一个 PersistentVolumeClaim。出于此示例的目的,我们假设默认 StorageClass 提供的单个卷满足这些声明。您还会注意到,每个 Pod 都显示为副本集的一部分,并且有一个为 MySQL 数据库公开的服务。让我们暂停一下,介绍一下这些概念。
副本集
Kubernetes 上的生产应用程序部署通常不会部署单个 Pod,因为当节点消失时,单个 Pod 很容易丢失。相反,Pod 通常部署在管理其生命周期的 Kubernetes 资源的上下文中。ReplicaSet 是这些资源之一,另一个是 StatefulSet,我们将在本章后面介绍它。
副本集 (RS) 的目的是确保给定 Pod 的指定数量的副本在任何给定时间保持运行。销毁 Pod 时,会创建其他 Pod 来替换它们,以满足所需数量的副本。副本集由容器模板、多个副本和一个选择器定义。Pod 模板定义了将由 ReplicaSet 管理的 Pod 规范,类似于我们在第 2 章示例中为创建的各个 Pod 看到的规范。副本数可以是 0 或更多。选择器标识属于副本集的 Pod。
让我们看一下 Wordpress 应用程序的 ReplicaSet 示例定义的一部分,如图 所示:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
replicas: 1
selector:
matchLabels:
app: wordpress
tier: mysql
template:
metadata:
labels:
app: wordpress
tier: mysql
spec:
containers:
- image: mysql:5.6
name: mysql
...
副本集负责创建或删除 Pod,以满足指定数量的副本。您可以通过更改此值来放大或缩小 RS 的大小。创建新容器时使用容器模板。由副本集管理的 Pod 在其元数据 .ownerReferences 字段中包含对 RS 的引用。如果选择器匹配且容器未引用其他所有者,则副本集实际上可以负责管理它未创建的容器。副本集的这种行为称为 Pod。
您可能想知道为什么我们没有在上面提供副本集的完整定义。事实证明,大多数应用程序开发人员最终不会直接使用 ReplicaSets,因为 Kubernetes 提供了另一种以声明方式管理 ReplicaSet 的资源类型:部署。
警告
警告:请仔细定义副本集选择器
如果确实直接创建了副本集,请确保您使用的选择器是唯一的,并且与任何不打算获取的裸 Pod 都不匹配。如果选择器匹配,则可能会获取与容器模板不匹配的 Pod。
有关管理 ReplicaSet 及其管理的 Pod 的生命周期的更多信息,请参阅 Kubernetes 文档。
部署
Kubernetes 是一种构建在 ReplicaSet 之上的资源,具有用于生命周期管理的附加功能,包括推出新版本和回滚到以前版本的能力。如图 3-2 所示,创建部署也会导致创建副本集。
部署和副本集
此图突出显示了副本集(以及管理它们的部署)在 Pod 的克隆副本上运行,这意味着 Pod 的定义是相同的,甚至精确到 PersistentVolumeClaims 级别。副本集的定义引用提供给它的单个 PVC,并且没有提供克隆其他 Pod 的 PVC 定义的机制。因此,如果您的目的是让每个容器都可以访问自己的专用存储,则部署和副本集不是一个好的选择。
如果您的应用程序 Pod 不需要访问存储,或者您的意图是它们访问同一块存储,则部署是一个不错的选择。但是,需要这样做的情况非常罕见,因为您可能不希望出现多个同时写入器到同一存储的情况。
让我们创建一个示例部署。首先,创建一个表示数据库密码的密钥(用您想要的任何字符串替换密码):
kubectl create secret generic mysql-root-password --from-literal=password=
接下来,创建一个表示数据库可以使用的存储的 PVC(在这种情况下,单个 PVC 就足够了,因为您要创建单个节点。只要您具有前面提到的适当存储类,这应该有效。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pv-claim
labels:
app: wordpress
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
接下来,使用运行 MySQL()的 Pod 规范创建部署。请注意,它包括对刚创建的 PVC 的引用以及包含数据库 root 密码的密钥。
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
tier: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: mysql
spec:
containers:
- image: mysql:5.7
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-root-password
key: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-pv-claim
关于此部署的规范,有几个有趣的事情需要注意。
- 首先,请注意,部署具有重新创建策略。这是指在更新容器模板时,部署如何处理 Pod 的替换,我们稍后将对此进行讨论。
- 接下来,请注意在 Pod 模板下,密码将作为通过您在上面创建的密钥提取的环境变量传递给 pod。覆盖默认密码是保护任何数据库部署的一个重要方面。
- 另请注意,MySQL 映像上公开单个端口以供数据库访问,因为这是一个相对简单的示例。在本书的其他示例中,我们将看到 Pod 的情况,这些 Pod 公开了用于管理操作、指标收集等的其他端口。默认情况下禁用访问这一事实是 Kubernetes 的一个重要功能。
- MySQL 映像使用上面定义的 PVC 为其持久存储装载卷。
- 最后,请注意,规范中未提供副本数。这意味着将使用默认值 1。
应用上述配置后,尝试使用 kubectl get deployments、rs、pods 等命令来检查并查看 Kubernetes 为您创建的项目。您会注意到一个以部署命名的单个副本集,其中包含一个随机字符串,例如:wordpress-mysql-655c8d9c54 。Pod 的名称引用了 ReplicaSet 的名称,并添加了一些额外的随机字符,例如:wordpress-mysql-655c8d9c54-tgswd 。这些名称提供了一种快速识别这些资源之间关系的方法。
下面是部署为管理副本集的生命周期而采取的一些操作。为了与 Kubernetes 对声明式操作的强调保持一致,其中大多数是通过更新部署规范来触发的:
初始推出
当您创建部署时,Kubernetes 使用您提供的规范来创建副本集。创建此副本集及其 Pod 的过程称为推出也作为滚动更新的一部分执行,如下所述。
纵向扩展或缩减
更新部署以更改副本数时,基础副本集会相应地纵向扩展或缩减。
滚动更新
当您更新部署的容器模板时,例如,通过为 Pod 指定不同的容器映像,Kubernetes 会基于新的容器模板创建新的副本集。Kubernetes 管理新旧 ReplicaSet 之间转换的方式由部署的 spec.strategy 属性描述,该属性默认为名为 RollingUpdate 的值。在滚动更新中,随着现有副本集中的 Pod 数减少,通过创建符合新模板的 Pod,新副本集会慢慢扩展。在此转换期间,部署将强制实施最大和最小数量的 pod,以百分比表示,由 spec.strategy.rollingupdate.maxSurge 和 maxUnavailable 属性设置。这些值中的每一个都默认为 25%。
重新创建更新
更新容器模板时使用的另一个策略选项是重新创建。这是在上面的部署中设置的选项。使用此选项,现有副本集将在创建新副本集之前立即终止。此策略对于开发环境很有用,因为它可以更快地完成更新,而 RollingUpdate 更适合生产环境,因为它强调高可用性。
回滚更新
在创建或更新部署时,可能会引入错误,例如,使用包含错误的版本更新容器中的容器映像。在这种情况下,部署管理的 Pod 甚至可能无法完全初始化。您可以使用 kubectl 推出状态等命令检测这些类型的错误。Kubernetes 提供一系列操作来管理部署的推出历史记录。您可以通过 kubectl 命令访问这些命令,例如 kubectl 推出历史记录,它提供部署的部署的编号部署历史记录,以及 kubectl 推出撤消,它将部署恢复到以前的部署。您还可以使用 --to-version 选项撤消到特定的部署版本。由于 kubectl 支持我们将在下面介绍的其他资源类型(StatefulSet 和 DaemonSets)的推出,因此在使用这些命令时需要包含资源类型和名称,例如:
kubectl rollout history deployment/wordpress-mysql
产生如下输出:
deployment.apps/wordpress-mysql
REVISION CHANGE-CAUSE
1
如您所见,Kubernetes 部署提供了一些复杂的行为来管理一组克隆 Pod 的生命周期。您可以通过更改部署的 YAML 规范并重新应用它来测试这些生命周期操作(回滚除外)。尝试将副本数扩展到 2 并再次缩放回来,或使用不同的 MySQL 映像。更新部署后,您可以使用像 kubectl 描述部署 wordpress-mysql 这样的命令来观察 Kubernetes 启动的事件,以使您的部署达到您想要的状态。
还有其他可用于部署的选项,我们在这里没有空间讨论,例如,如果您尝试更新失败,如何指定 Kubernetes 的功能。有关部署行为的更深入说明,请参阅 。
服务业
在上述步骤中,你已创建用于指定数据库存储需求的 PVC、用于提供管理员凭据的密钥以及用于管理单个 MySQL Pod 生命周期的部署。现在,您已经有一个正在运行的数据库,您需要使应用程序可以访问它。在第 1 章介绍的计算、网络和存储方案中,这是网络部分。
Kubernetes 服务是我们需要用来将数据库的访问作为网络服务公开的原语。服务为在其后面运行的一组 Pod 提供抽象。对于本例中的单个 MySQL 节点,您可能想知道我们为什么要费心创建此抽象。服务支持的一项关键功能是提供名称一致且不会更改的终结点。您不希望在数据库 Pod 重新启动并获取新 IP 地址时必须更新客户端的情况。您可以使用如下 YAML 配置()创建用于访问 MySQL 的服务:
apiVersion: v1
kind: Service
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
ports:
- port: 3306
selector:
app: wordpress
tier: mysql
clusterIP: None
以下是有关此配置的几点注意事项:
- 首先,此配置指定在服务上公开的端口:3306。在定义服务时,实际上涉及两个端口:向服务的客户端公开的端口,以及由服务所面向的底层 Pod 公开的目标端口。由于尚未指定 目标端口 ,因此它默认为端口值。
- 其次,选择器定义服务将流量定向到的 Pod。在此配置中,部署将只有一个 MySQL pod,这很好。
- 最后,如果您以前使用过 Kubernetes 服务,您可能会注意到没有为此服务定义 serviceType,这意味着它是默认类型,称为 ClusterIP .此外,由于 clusterIP 属性设置为“无”,这就是,即服务的 DNS 名称直接映射到所选 Pod 的 IP 地址的服务。
Kubernetes 支持多种类型的服务来解决不同的用例,如图 3-3 所示。我们将在这里简要介绍它们,以突出它们对数据基础架构的适用性:
这种类型的服务在只能从 Kubernetes 集群内访问的 IP 地址上公开。这是您会看到最常用于数据基础架构(例如 Kubernetes 中的数据库)的服务类型,尤其是无头服务,因为此基础架构通常与使用它的应用程序一起部署在 Kubernetes 中。
节点端口服务
节点端口服务在每个工作器节点的 IP 地址上向群集外部公开。还会在内部创建 ClusterIP 服务,NodePort 将流量路由到该服务。您可以允许 Kubernetes 选择使用的外部端口,或使用 NodePort 属性指定所需的端口。NodePort 服务最适合开发环境,当您需要调试数据基础架构应用程序的特定实例上发生的情况时。
负载均衡器
负载均衡器服务表示来自 Kubernetes 运行时的请求,用于设置底层云提供商提供的负载均衡器。例如,在 Amazon 的 Elastic Kubernetes Service (EKS) 上,请求 LoadBalancer 服务会导致创建弹性负载均衡器 (ELB) 的实例。在多节点数据基础架构部署之前通常不需要使用负载均衡器,因为这些数据技术通常有自己的方法来分配负载。例如,Apache Cassandra 驱动程序知道 Cassandra 集群的拓扑,并为客户端应用程序提供负载平衡功能,从而消除了对负载均衡器的需求。
ExternalName 服务通常用于表示对集群外部服务的访问,例如在 Kubernetes 外部运行的数据库。外部名称服务没有选择器,因为它不会映射到任何 Pod。相反,它将服务名称映射到 CNAME 记录。例如,如果创建 externalName 为 database.mydomain.com 的 my-external-database 服务,则应用程序 Pod 中对 my-external-database 的引用将映射到 database.mydomain.com 。
Kubernetes 服务类型
另请注意图中包含入口。虽然 Kubernetes Ingress 不是一种服务类型,但它是相关的。入口用于提供从集群外部对 Kubernetes 服务的访问,通常通过 HTTP。可以使用多种入口实现,包括Nginx,Traefik,Ambassador(基于Envoy)等。入口实现通常提供包括 SSL 终止和负载平衡在内的功能,甚至可以跨多个不同的 Kubernetes 服务。与负载均衡器服务一样,入口更常用于应用层。
访问 MySQL
现在您已经部署了数据库,您可以部署使用它的应用程序 - Wordpress 服务器。
首先,服务器需要自己的 PVC。这有助于说明,有些应用程序直接利用存储(可能用于存储文件)和应用程序使用数据基础结构,而应用程序则同时使用这两种操作。您可以提出一个小请求,因为这仅用于演示目的():
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wp-pv-claim
labels:
app: wordpress
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
接下来,为单个 Wordpress 节点创建部署():
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
tier: frontend
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: frontend
spec:
containers:
- image: wordpress:4.8-apache
name: wordpress
env:
- name: WORDPRESS_DB_HOST
value: wordpress-mysql
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-root-password
key: password
ports:
- containerPort: 80
name: wordpress
volumeMounts:
- name: wordpress-persistent-storage
mountPath: /var/www/html
volumes:
- name: wordpress-persistent-storage
persistentVolumeClaim:
claimName: wp-pv-claim
请注意,用于访问MySQL的数据库主机和密码作为环境变量传递给Wordpress。主机的值是上面为MySQL创建的服务的名称。这就是将数据库连接路由到 MySQL 实例所需的全部内容。密码的值是从密钥中提取的,类似于上述 MySQL 部署的配置。
您还会注意到 Wordpress 在端口 80 处公开了一个 HTTP 接口,因此让我们创建一个服务来公开 Wordpress 服务器():
apiVersion: v1
kind: Service
metadata:
name: wordpress
labels:
app: wordpress
spec:
ports:
- port: 80
selector:
app: wordpress
tier: frontend
type: LoadBalancer
请注意,该服务的类型为 LoadBalancer,这应该使从本地计算机访问变得相当简单。执行命令 kubectl get services 以获取负载均衡器的 IP 地址,然后您可以在浏览器中打开 URL 的 Wordpress 实例尝试登录并创建一些页面。
注意
注意:从 Kubernetes 发行版访问服务
访问服务的确切细节将取决于您正在使用的 Kubernetes 发行版,以及您是在生产环境中部署应用程序,还是像我们在这里所做的那样快速测试某些内容。如果您使用的是桌面 Kubernetes 发行版,为了简单起见,您可能希望使用 NodePort 服务而不是 LoadBalancer。您还可以查阅文档以获取有关访问服务的具体说明,例如为 或 提供的说明。
当您完成对 Wordpress 实例的试验后,您可以使用以下命令清理在本地目录中使用的配置文件中指定的资源,包括存储在 PersistentVolumeClaim 中的数据:
kubectl delete -k ./
在这一点上,你可能会觉得这相对容易,尽管我们声称做事“艰难的方式”。从某种意义上说,你是对的。到目前为止,我们已经部署了一个简单数据库的单个节点,具有合理的默认值,我们不必花费太多时间来配置。如果您的应用程序只存储少量数据,则创建单个节点当然是可以的。这就是在 Kubernetes 上部署数据库的全部内容吗?当然不是!现在我们已经通过这个简单的数据库部署引入了一些基本的 Kubernetes 资源,是时候稍微提高复杂性了。让我们进入正题吧!
在 Kubernetes 上运行 Apache Cassandra
在本节中,我们将介绍如何使用 Apache Cassandra 在 Kubernetes 上运行多节点数据库。Cassandra是一个NoSQL数据库,最初由Facebook开发,于2010年成为Apache软件基金会的顶级项目。Cassandra 是一个提供表格数据模型的操作数据库,其 Cassandra 查询语言 (CQL) 类似于 SQL。
Cassandra是为云设计的数据库,因为它通过添加节点进行水平扩展,其中每个节点都是一个对等节点。这种分散式设计已被证明具有近乎线性的可扩展性。Cassandra 通过存储数据或副本的多个副本来支持高可用性,包括跨多个数据中心和云区域分发这些副本的逻辑。Cassandra 建立在与 Kubernetes 类似的原理之上,因为它旨在检测故障并继续运行,同时系统可以在后台恢复到预期状态。所有这些特性使 Cassandra 非常适合在 Kubernetes 上部署。
为了讨论这种部署是如何工作的,理解 Cassandra 从两个不同的角度分发数据的方法很有帮助:物理和逻辑。借用《》中的一些视觉效果,您可以在 中看到这些观点。从物理角度来看,Cassandra 节点(不要与 Kubernetes 工作节点混淆)是使用称为机架和数据中心的概念来组织的。虽然这些术语背叛了Cassandra的起源,当时内部部署数据中心是2000年代中期软件部署的主要方式,但它们可以灵活地应用。在云部署中,机架通常表示可用性区域,而数据中心表示云区域。无论这些表示如何,重要的是它们表示物理上独立的故障域。Cassandra 使用此拓扑的感知来确保它将副本存储在多个物理位置,以便在发生故障时最大限度地提高数据的可用性,无论这些故障是单台计算机、服务器机架、可用性区域还是整个区域。
Cassandra分布式架构的物理和逻辑视图
逻辑视图有助于我们理解 Cassandra 如何确定将在每个节点上放置哪些数据。Cassandra 中的每一行数据都由一个主键标识,主键由一个或多个分区键列组成,这些列用于跨节点分配数据,以及可选的聚类列,可用于组织分区内的多行数据以实现高效访问。Cassandra 中的每次写入(以及大多数读取)都通过提供分区键值来引用特定分区,Cassandra 将这些键值散列在一起以生成一个称为的值,该值介于 ?2 之间63和 263?1.Cassandra 为其每个节点分配一个或多个令牌范围的责任(为简单起见,在图 3-4 中显示为每个节点的单个范围)。在分配令牌范围时会考虑物理拓扑,以确保数据副本分布在机架和数据中心之间。
现在我们准备考虑 Cassandra 如何映射到 Kubernetes 上。重要的是要考虑Cassandra架构的两个含义:
有状态性
每个 Cassandra 节点都有它负责维护的状态。Cassandra 具有通过将数据从其他副本流式传输到新节点来替换节点的机制,这意味着节点使用本地临时存储的配置是可能的,但代价是启动时间更长。但是,将每个 Cassandra 节点配置为使用持久存储更为常见。无论哪种情况,每个 Cassandra 节点都需要有自己唯一的 PersistentVolumeClaim。
身份
尽管在完全对等架构中,每个 Cassandra 节点的代码、配置和功能都是相同的,但节点的实际角色却不同。每个节点都有一个标识,即它在数据中心和机架拓扑中的位置及其分配的令牌范围。
这些对标识以及与特定 PersistentVolumeClaim 关联的要求给部署和副本集带来了一些挑战,而这些挑战并非旨在处理这些挑战。从 Kubernetes 存在的早期开始,人们就意识到需要另一种机制来管理像 Cassandra 这样的有状态工作负载。
有状态集
Kubernetes 开始提供资源来管理有状态工作负载,在 1.3 版本中发布了 PetSets。此功能随着时间的推移而成熟,现在称为 StatefulSets(请参阅:侧栏:下面的有状态工作负载是宠物还是牛?)。StatefulSet 与 ReplicaSet 有一些相似之处,因为它负责管理一组 Pod 的生命周期,但它进行这种管理的方式有一些显著差异。为了满足有状态应用程序的需求,如上面列出的 Cassandra 应用程序,StatefulSet 演示了以下关键属性:
Pod 的稳定标识
首先,StatefulSets 为 Pod 提供稳定的名称和网络标识。每个 Pod 都会根据 StatefulSet 的名称加上序号分配一个名称。例如,一个名为 cassandra 的 StatefulSet 将具有名为 cassandra-1、cassandra-2、cassandra-3 等的 pod,如图 3-5 所示。这些是稳定的名称,因此如果 Pod 由于某种原因丢失并需要更换,则替换将具有相同的名称,即使它是在不同的工作节点上启动的。每个 Pod 的名称都设置为主机名,因此,如果您创建无头服务,您实际上可以根据需要对各个 Pod 进行寻址,例如:cassandra-1.cqlservice.default.svc.cluster.local 。我们将在本章后面的 中讨论有关为 Cassandra 运行 Kubernetes 服务的更多信息。
Cassandra 在 Kubernetes 上的 StatefulSet 部署示例
有序的生命周期管理
有状态集为管理 Pod 的生命周期提供了可预测的行为。在 StatefulSet 中增加 Pod 的数量时,将根据下一个可用数量添加新的 Pod。例如,扩展图 3-5 中的 StatefulSet 将导致创建 castandra-4 和 cassandra-5 等 pod。缩减具有相反的行为,因为首先删除序号最高的 Pod。这种可预测性简化了管理,例如,在减小群集大小之前明确应备份哪些节点。
永久磁盘
与创建在所有 Pod 之间共享的单个 PersistentVolumeClaim 的副本集不同,StatefulSet 创建与每个 Pod 关联的 PVC。如果 StatefulSet 中的容器被替换,则替换的容器将绑定到具有它正在替换的状态的 PVC。更换可能是因为 Pod 失败或调度程序选择在另一个节点上运行 Pod 以平衡负载。对于像 Cassandra 这样的数据库,当 Cassandra 节点丢失时,这可以快速恢复,因为替换节点可以立即从关联的 PersistentVolume 恢复其状态,而不需要从其他副本流式传输数据。
警告
警告:管理数据复制
规划应用程序部署时,请确保考虑是在数据层还是在存储层复制数据。像 Cassandra 这样的分布式数据库本身管理复制,根据您请求的复制因子将数据的副本存储在多个节点上,通常每个 Cassandra 数据中心 3 个。您选择的存储提供程序也可能提供复制。如果每个 Cassandra Pod 的 Kubernetes 卷有 3 个副本,你最终可能会存储 9 个数据副本。虽然这肯定会提高数据的生存能力,但这可能比您预期的成本更高。
边栏:您的有状态工作负载是宠物还是牛?
PetSet 对于 Kubernetes 资源来说可能看起来是一个奇怪的名称,并且已经被取代,但它为 Kubernetes 社区支持有状态工作负载的思维过程提供了一些有趣的见解。PetSets这个名字是对至少自2012年以来在DevOps世界中活跃的讨论的引用。最初的概念归因于比尔贝克,以前是Microsoft。
基本思想是,有两种处理服务器的方法:将它们视为需要照顾、喂养和养育的宠物,或者将它们视为牛,您不会对此产生依恋或提供大量个人关注。如果您定期登录服务器以执行维护活动,则将其视为宠物。
这意味着,通过能够将越来越多的元素视为牛而不是宠物,可以大大改善运营工程师的生活。随着向现代云原生架构的迁移,这一概念已从服务器扩展到虚拟机和容器,甚至扩展到单个微服务。它还有助于促进高可用性架构方法的使用,并在单个组件的丢失中幸存下来,这些组件使 Kubernetes 和 Cassandra 等技术取得了成功。
正如你所看到的,将 Kubernetes 资源命名为“PetSets”带来了很多麻烦,甚至可能对在 Kubernetes 上运行有状态工作负载持怀疑态度。然而,最终,PetSets帮助解决了Kubernetes上管理状态的照顾和喂养,并且名称更改为StatefulSets非常合适。总而言之,像StatefulSets,第2章中引入的PersistentVolume子系统以及运算符(在第4章中推出)这样的功能正在带来一定程度的自动化,在不久的将来,我们将像牛一样管理Kubernetes上的数据。
现在您已经了解了一些关于 StatefulSet 的知识,让我们来看看如何使用它们来运行 Cassandra。您将使用 Kubernetes StatefulSet 以“困难的方式”配置一个简单的 3 节点集群,以表示包含单个机架的单个 Cassandra 数据中心。虽然这个例子的灵感来自 Kubernetes 文档中的 教程,但它确实在某些方面与教程不同。本节中使用的源代码位于。这近似于图 3-5 中所示的配置。
要在 Kubernetes 中设置 Cassandra 集群,您首先需要一个无头服务。此服务表示 中所示的“CQL 服务”,它提供了一个终结点,客户端可以使用该终结点获取 StatefulSet 中所有 Cassandra 节点的地址():
apiVersion: v1
kind: Service
metadata:
labels:
app: cassandra
name: cassandra
spec:
clusterIP: None
ports:
- port: 9042
selector:
app: cassandra
您将在 StatefulSet 的定义中引用此服务,该 StatefulSet 将管理您的 Cassandra 节点()。您可能希望等到我们在下面进行一些快速解释之后,而不是立即应用此配置。配置如下所示:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: cassandra
labels:
app: cassandra
spec:
serviceName: cassandra
replicas: 3
podManagementPolicy: OrderedReady
updateStrategy: RollingUpdate
selector:
matchLabels:
app: cassandra
template:
metadata:
labels:
app: cassandra
spec:
containers:
- name: cassandra
image: cassandra
ports:
- containerPort: 7000
name: intra-node
- containerPort: 7001
name: tls-intra-node
- containerPort: 7199
name: jmx
- containerPort: 9042
name: cql
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- nodetool drain
env:
- name: CASSANDRA_CLUSTER_NAME
value: "cluster1"
- name: CASSANDRA_DC
value: "dc1"
- name: CASSANDRA_RACK
value: "rack1"
- name: CASSANDRA_SEEDS
value: "cassandra-0.cassandra.default.svc.cluster.local"
volumeMounts:
- name: cassandra-data
mountPath: /var/lib/cassandra
volumeClaimTemplates:
- metadata:
name: cassandra-data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: standard-rwo
resources:
requests:
storage: 1Gi
这是迄今为止我们一起查看的最复杂的配置,因此让我们通过一次查看一个部分来简化它。
我们已经命名并标记了这个StatefulSet cassandra,并且相同的字符串将用作属于StatefulSet的pod的选择器。
通过服务公开 StatefulSet Pods
StatefulSet 的规范从对上面创建的无外设服务的引用开始。虽然根据 Kubernetes 规范,serviceName 不是必填字段,但一些 Kubernetes 发行版和工具(如 Helm)希望填充它,如果您未能提供值,将生成警告或错误。
副本数
副本字段标识此状态集中应可用的 Pod 数。提供的 3 值反映了人们在实际生产部署中可能看到的最小 Cassandra 集群,并且大多数部署要大得多,这是 Cassandra 大规模提供高性能和可用性的能力真正开始显现的时候。
生命周期管理选项
podManagementPolicy 和 updateStrategy 分别描述了 Kubernetes 在集群扩展或缩减时应如何管理 Pod 的推出,以及如何管理 StatefulSet 中 Pod 的更新。我们将在中研究这些值的重要性。
吊舱规格
StatefulSet 规范的下一部分是用于创建由 StatefulSet 管理的每个 Pod 的模板。该模板有几个小节。首先,在元数据下,每个 pod 都包含一个标签 cassandra,用于将其标识为集合的一部分。
此模板包括容器字段中的单个项目,即 Cassandra 容器的规范。图像字段选择官方Cassandra 的最新版本,在撰写本文时是Cassandra 4.0。这就是我们与上面引用的 Kubernetes StatefulSet 教程的不同之处,该教程使用专门为该教程创建的自定义 Cassandra 3.11 映像。由于我们在此处选择使用的映像是官方 Docker 映像,因此无需包含注册表或帐户信息即可引用它,并且名称 cassandra 本身足以标识将使用的映像。
每个 Pod 将公开各种接口的端口:一个用于客户端的 cql 端口,一个用于 Cassandra 集群中节点之间通信的节点内和 tls-intra-node 端口,以及一个用于通过 Java 管理扩展 (JMX) 进行管理的 jmx 端口。
Pod 规范还包括帮助 Kubernetes 管理 Pod 生命周期的指令,包括 readyinessProbe、livenessProbe 和 preStop 命令。我们将在下面了解如何使用它们。
根据它,我们正在构建的映像是为了提供两种不同的方法来自定义Cassandra的配置,该配置存储在映像中的cassandra.yaml文件中。一种方法是用您提供的文件覆盖 cassandra.yaml 的全部内容。第二种方法是利用映像公开的环境变量来覆盖最常使用的 Cassandra 配置选项的子集。在 env 字段中设置这些值会导致更新 cassandra.yaml 文件中的相应设置:
- CASSANDRA_CLUSTER_NAME用于区分哪些节点属于群集。如果 Cassandra 节点与与其集群名称不匹配的节点接触,它将忽略它们。
- CASSANDRA_DC和CASSANDRA_RACK标识每个节点将属于的数据中心和机架。这有助于突出 StatefulSet 公开 pod 规范方式的一个有趣问题。由于模板应用于每个容器和容器,因此无法在 Cassandra 容器之间更改配置的数据中心和机架名称。出于这个原因,通常在 Kubernetes 中使用每个机架的 StatefulSet 部署 Cassandra。
- CASSANDRA_SEEDS定义 Cassandra 集群中节点的已知位置,新节点可以使用这些位置将自己引导到集群中。最佳做法是指定多个种子,以防其中一个种子在新节点加入时碰巧关闭或脱机。但是,对于此初始示例,通过 DNS 名称 cassandra-0.cassandra.default.svc.cluster.local 将初始 Cassandra 副本指定为种子就足够了。在第 4 章中,我们将研究一种使用服务指定种子的更健壮的方法,如图 3-5 所示的“种子服务”所暗示的那样。
容器规范中的最后一项是 volumeMount,它请求将 PersistentVolume 挂载到 /var/lib/cassandra 目录,Cassandra 映像配置该目录以存储其数据文件。由于每个 Pod 都需要自己的 PersistentVolumeClaim,因此名称 cassandra-data 是对下面定义的 PersistentVolumeClaim 模板的引用。
卷声明模板
StatefulSet 规范的最后一部分是 volumeClaimTemplates。规范必须包括上述容器规范之一中引用的每个名称的模板定义。在本例中,cassandra 数据模板引用了我们在这些示例中使用的标准存储类。Kubernetes 将使用此模板创建一个请求大小为 1GB 的 PersistentVolumeClaim,每当它在此 StatefulSet 中启动一个新 pod 时。
现在我们有机会讨论 StatefulSet 规范的组件,您可以继续应用源代码:
kubectl apply -f cassandra-statefulset.yaml
当它被应用时,你可以执行以下命令来观察StatefulSet启动Cassandra pods:
kubectl get pods -w
让我们描述一下您可以从此命令的输出中观察到的一些行为。首先,你会看到一个豆荚Cassandra-0。一旦该吊舱进入就绪状态,您将看到 cassandra-1 吊舱,然后在 cassandra-2 准备就绪后看到 cassandra-1。此行为是通过为 StatefulSet 选择 podManagementPolicy 来指定的。让我们探讨一下可用的选项和一些其他设置,这些设置有助于定义如何管理 StatefulSet 中的 Pod。
pod管理策略确定从 StatefulSet 中添加或删除 Pod 的时间。在我们的 Cassandra 示例中应用的 OrderedReady 策略是默认策略。当此策略到位并添加 Pod 时,无论是在初始创建还是纵向扩展时,Kubernetes 都会一次扩展一个 Pod。添加每个 Pod 时,Kubernetes 会等到 Pod 报告状态为 Ready,然后再添加后续 Pod。如果 pod 规范包含 readyinessProbe,就像我们在本例中所做的那样,Kubernetes 会迭代执行提供的命令,以确定 Pod 何时准备好接收流量。当探测器成功完成(即使用零返回代码)时,它将继续创建下一个 pod。对于 Cassandra,就绪情况通常由 CQL 端口 (9042) 的可用性来衡量,这意味着节点能够响应 CQL 查询。
同样,当删除或缩减 StatefulSet 时,将一次删除一个 Pod。在删除 Pod 时,将执行为其容器提供的任何 preStop 命令,以使它们有机会正常关闭。在我们当前的例子中,执行 nodetool drain 命令是为了帮助 Cassandra 节点干净地退出集群,将其令牌范围的责任分配给其他节点。因为 Kubernetes 会等到一个 Pod 完全终止后再删除下一个 Pod。livenessProbe 中指定的命令用于确定 Pod 何时处于活动状态,当它不再正确完成时,Kubernetes 可以继续删除下一个 pod。有关配置就绪和活动探测的详细信息,请参阅 。
另一个容器管理策略是 并行 。当此策略生效时,Kubernetes 会同时启动或终止多个 Pod,以便扩展或缩减。这样可以更快地将 StatefulSet 恢复到所需数量的副本,但也可能导致某些有状态工作负载需要更长的时间来稳定。例如,像Cassandra这样的数据库在集群大小变化时在节点之间打乱数据以平衡负载,并且在一次添加或删除一个节点时往往会更快地稳定下来。
无论使用哪种策略,Kubernetes 都会根据序号管理 Pod,在纵向扩展时始终添加带有下一个未使用的序号的 Pod,并在缩减时删除序号最高的 Pod。
容器管理策略更新策略
updateStrategy 描述了在容器模板规范中进行更改(例如更改容器映像)时如何更新 StatefulSet 中的 Pod。默认策略是 滚动更新 ,如本例中选择的。使用另一个选项 OnDelete 时,您必须手动删除容器才能应用新的容器模板。
在滚动更新中,Kubernetes 将删除并重新创建 StatefulSet 中的每个 pod,从序号最大的 pod 开始,到最小的 pod。Pod 一次更新一个,您可以指定多个称为分区的 Pod,以便执行分阶段推出或 Canary。请注意,如果在部署期间发现错误的容器配置,则需要将容器模板规范更新为已知的良好状态,然后手动删除使用错误规范创建的所有容器。由于这些 Pod 永远不会达到就绪状态,因此 Kubernetes 不会决定它们是否准备好用良好的配置替换。
请注意,Kubernetes 为部署、副本集和守护程序集提供了类似的生命周期管理选项,包括修订历史记录。
注意
注意:有状态集的更复杂的生命周期管理
关于 StatefulSet 的其他生命周期选项的一组有趣的观点来自 OpenKruise,这是一个 CNCF 沙盒项目,它提供了一个。高级状态集添加的功能包括:
- 具有最大数量不可用 Pod 的并行更新
- 根据提供的优先级策略,使用备用替换顺序滚动更新
- 根据更新的 Pod 模板规范重新启动容器,从而“就地”更新容器
此 Kubernetes 资源也称为 StatefulSet,以方便其使用,同时将对现有配置的影响降至最低。您只需要更改 apiVersion : 从 apps/v1 到 apps.kruise.io/v1beta1 .
我们建议您获得更多管理 StatefulSet 的实践经验,以巩固您的知识。例如,可以在 StatefulSet 纵向扩展时监视 PersistentVolumeClaims 的创建。另一件事可以尝试:删除 StatefulSet 并重新创建它,验证新 Pod 是否从原始 StatefulSet 中恢复以前存储的数据。有关更多想法,您可能会发现这些指导教程很有帮助:来自 Kubernetes 文档的 ,以及来自 Kubernetes 博客的 。
StatefulSets are extremely useful for managing stateful workloads on Kubernetes, and that’s not even counting some capabilities we didn’t address, such as pod affinity, anti-node affinity, managing resource requests for memory and CPU, and availability constraints such as PodDisruptionBudgets. On the other hand, there are capabilities you might desire that StatefulSets don’t provide, such as backup/restore of persistent volumes, or secure provisioning of access credentials. We’ll discuss how to leverage or build these capabilities on top of Kubernetes in Chapter 4 and beyond.
边栏:有状态集:过去、现在和未来
与 Maciej Szulik,RedHat 工程师和 Kubernetes SIG Apps 成员
Kubernetes 应用程序特别兴趣组 (SIG Apps) 负责开发有助于管理 Kubernetes 应用程序工作负载的控制器。这包括批处理工作负载(如作业和 CronJobs)、其他无状态工作负载(如部署和副本集),当然还有用于有状态工作负载的有状态集。
StatefulSet 控制器的工作方式与其他控制器略有不同。当您考虑部署或作业时,控制器只需要管理 Pod。您不必担心底层数据,因为这要么由持久卷处理,要么可以在销毁和重新创建每个 Pod 的数据时丢弃它。但是,当您尝试运行数据库或需要在运行之间保持状态的任何类型的工作负载时,这种行为是不可接受的。这会导致 StatefulSet 控制器中显著增加复杂性。编写和成熟 Kubernetes 控制器的主要挑战是处理边缘情况。StatefulSets在这方面是相似的,但对于StatefulSets来说,正确处理失败情况更为紧迫,这样我们就不会丢失数据。
我们遇到了一些有趣的 StatefulSet 用例,以及用户希望更改核心实现中设置的边界的情况。例如,我们已经提交了拉取请求,以更改 StatefulSet 在更新期间处理 Pod 的方式。在原始实现中,StatefulSet 控制器一次更新一个 Pod,如果在推出过程中出现问题,整个推出将暂停,并且 StatefulSet 需要手动干预以确保数据不会损坏或丢失。一些用户希望 StatefulSet 控制器忽略 Pod 停滞在挂起状态或无法运行的问题,只需重新启动这些 Pod 即可。但是,使用 StatefulSet 要记住的是,保护基础数据是最重要的优先事项。我们最终可能会进行建议的更改,以便为开发环境提供更快的并行更新,其中数据保护不太重要,但需要使用功能标志选择加入。
另一个经常请求的功能是能够在删除 StatefulSet 时自动删除 StatefulSet 的 PersistentVolumeClaims。最初的行为是保留 PVC,再次作为一种数据保护机制,但有一个用于的 Kubernetes 增强提案 (KEP),正在考虑用于 Kubernetes 1.23 版本。
尽管 StatefulSet 管理 Pod 的方式与其他控制器存在一些显著差异,但我们正在努力使不同控制器的行为尽可能相似。一个例子是在 pod 模板中添加了一个 设置,它允许您说,我希望这个应用程序在向它发送流量之前有一点额外的时间不可用。这对于某些需要更多时间来初始化自身的有状态工作负载很有帮助,例如预热缓存,并使 StatefulSet 与其他控制器保持一致。
另一个示例是正在进行的统一所有应用程序控制器的状态报告的工作。目前,如果要构建任何类型的更高级别的业务流程或管理工具,则需要具有不同的行为来处理 StatefulSets、Deployments、DaemonSet 等的状态,因为它们中的每一个都是由不同的作者编写的。每个作者对状态中应该包含的内容、资源应如何表达有关它是否可用、是否在滚动更新中、不可用或发生的任何情况的信息都有不同的要求。守护程序集在报告状态的方式上尤其不同。
还有一个功能正在进行中,允许您为 StatefulSet 设置。此数字将在 StatefulSet 的初始推出期间应用,并允许更快地扩展副本数。这是使 StatefulSet 与其他控制器的工作方式更加一致的另一个功能。如果你想了解 SIG Apps 团队正在进行的工作,最好的方法是查看标记为 sig/apps 的 。
将有状态集构建为满足所有有状态工作负载需求的功能可能很困难;我们尝试以这样一种方式构建它们,以便以一致的方式处理最常见的需求。我们显然可以添加对越来越多的边缘情况的支持,但这往往会使用户掌握的功能变得更加复杂。总会有用户不满意,因为他们的用例没有被涵盖,而且在不影响功能和性能的情况下,我们可以投入多少总是有一个平衡。
在大多数情况下,用户需要更具体的行为,例如处理边缘情况,这是因为他们试图管理像Postgres或Cassandra这样的复杂应用程序。这就是创建自己的控制器甚至操作员来处理这些特定情况的充分论据的地方。尽管这听起来可能非常可怕,但编写自己的控制器并不难。您可以使用一些简单示例(包括示例控制器)在几天内合理快速地启动并运行基本控制器,该是 Kubernetes 代码库的一部分,由项目维护。O'Reilly 的《》一书也有一章是关于编写控制器的。不要只是假设你被开箱即用的行为所困。Kubernetes 应该是开放和可扩展的,无论是网络、控制器、CSI、插件等等。如果你需要定制 Kubernetes,你应该去做!
访问卡桑德拉
应用上面列出的配置后,可以使用Cassandra的CQL shell cqlsh来执行CQL命令。如果您碰巧是 Cassandra 用户,并且在本地计算机上安装了 cqlsh 的副本,则可以像客户端应用程序一样使用与 StatefulSet 关联的 CQL 服务访问 Cassandra。但是,由于每个 Cassandra 节点也包含 cqlsh,这让我们有机会通过直接连接到 StatefulSet 中的单个 pod,演示与 Kubernetes 中的基础设施交互的不同方式:
kubectl exec -it cassandra-0 -- cqlsh
这应该会弹出 cqlsh 提示符,然后您可以使用 DESCRIBE KEYSPACES 探索 Cassandra 内置表的内容,然后使用 USE 选择特定的键空间并运行 DESCRIBE TABLES。网上有许多 Cassandra 教程可以指导您完成更多创建自己的表、插入和查询数据等的示例。当你完成 cqlsh 的试验后,你可以键入 exit 退出外壳。
删除 StatefulSet 与任何其他 Kubernetes 资源相同 - 您可以按名称删除它,例如:
kubectl delete sts cassandra
您还可以删除引用用于创建它的文件的 StatefulSet:
kubectl delete sts cassandra
如本例所示,删除策略为“保留”的 StatefulSet 时,不会删除它创建的 PersistentVolumeClaims。如果重新创建 StatefulSet,它将绑定到相同的 PVC 并重用现有数据。不再需要声明时,需要手动删除它们。本练习的最终清理是删除 CQL 服务:kubectl 删除服务 cassandra 。
程序呢
如果你熟悉 Kubernetes 为管理工作负载提供的资源,你可能已经注意到我们还没有提到 。DaemonSets 允许您请求在 Kubernetes 集群中的每个工作节点上运行一个 Pod,如图 3-6 所示。DaemonSet 不是指定多个副本,而是随着在群集中添加或删除工作器节点而扩展或缩减。默认情况下,DaemonSet 将在每个工作节点上运行您的 Pod,但您可以使用来覆盖此行为,例如,限制某些工作节点,或选择在 Kubernetes 主节点上运行 Pod。DaemonSets 支持滚动更新的方式与 StatefulSet 类似。
守护程序集在选定的工作节点上运行单个 pod。
从表面上看,DaemonSets对于运行数据库或其他数据基础结构可能听起来很有用,但这似乎不是一种普遍的做法。相反,DaemonSets 最常用于与工作节点相关的功能及其与底层 Kubernetes 提供程序的关系。例如,我们在第 2 章中看到的许多容器存储接口 (CSI) 实现都使用 DaemonSet 在每个工作节点上运行存储驱动程序。另一种常见用法是在工作器节点(例如日志和指标收集器)上运行执行监视任务的 Pod。
总结
在本章中,我们通过动手示例学习了如何在 Kubernetes 上部署单节点和多节点分布式数据库。在此过程中,您已经熟悉了 Kubernetes 资源,例如部署、副本集、StatefulSet 和 DaemonSets,并了解了每种资源的最佳用例:
- 使用部署/副本集管理无状态工作负载或简单的有状态工作负载,如单节点数据库或可依赖临时存储的缓存
- 使用有状态集管理涉及多个节点并需要与特定存储位置关联的有状态工作负载
- 使用守护程序集管理利用特定工作节点功能的工作负载
您还了解了这些资源所能提供的功能的局限性。现在,您已经获得了在 Kubernetes 上部署有状态工作负载的经验,下一步是学习如何自动执行保持此数据基础架构运行所涉及的所谓“第 2 天”操作。