14

使用Deployment管理Pod

本章涵盖

•      使用 Deployment 对象部署无状态工作负载

•      水平扩展 Deployment以声明方式

•      更新工作负载

•      防止有缺陷工作负载的发布

•      实施各种部署策略

在上一章中,你学习了如何通过 ReplicaSets 部署 Pods。然而,工作负载很少通过这种方式部署,因为 ReplicaSets 不提供轻松更新这些 Pods 所需的功能。这一功能由 Deployment 对象类型提供。在本章结束时,Kiada 套件中的三项服务每一项都会有自己的 Deployment 对象。在开始之前,请确保 Kiada 套件的 Pods、Services 和其他对象已存在于你的集群中。如果你完成了上一章的练习,它们应该已经存在。如果没有,你可以通过创建 kiada 命名空间并使用以下命令应用 Chapter14/SETUP/ 目录下的所有清单来创建它们:$ kubectl apply -f SETUP -R

注意  你可以在以下网址找到本章的代码文件:https://github.com/luksa/kubernetes-in-action-2ndedition/tree/master/Chapter14.

14.1 引入部署

当你将工作负载部署到 Kubernetes 时,通常是通过创建一个 Deployment(部署)对象来完成。Deployment 对象不会直接管理 Pod 对象,而是通过在创建 Deployment 对象时自动创建的 ReplicaSet 对象来管理它们。如下图所示,Deployment 控制 ReplicaSet,而 ReplicaSet 反过来控制各个 Pod。图 14.1 Deployment、ReplicaSet 与 Pod 之间的关系。

部署(Deployment)允许您以声明式的方式更新应用程序。这意味着,与其手动执行一系列操作来将一组 Pod 替换为运行更新版本的 Pod,您只需更新 Deployment 对象中的配置,然后让 Kubernetes 自动执行更新。与 ReplicaSet 一样,您可以在 Deployment 中指定 Pod 模板、所需副本数量和标签选择器。基于该 Deployment 创建的 Pod 相互之间完全相同且可以互换。由于这一点以及其他原因,Deployment 主要用于无状态工作负载,但您也可以使用它来运行单个有状态工作负载实例。然而,由于没有内置的方法来防止用户将 Deployment 扩展到多个实例,因此当多个副本同时运行时,应用程序本身必须确保只有一个实例处于活动状态。

注意  要运行可复制的有状态工作负载,StatefulSet 是更好的选择。你将在下一章中学习它们。

14.1.1 创建部署

在本节中,您将用 Deployment 替换 kiada ReplicaSet。按如下方式删除 ReplicaSet,但不删除 Pod:$ kubectl delete rs kiada --cascade=orphan接下来我们来看在 Deployment 的 spec 部分需要指定哪些内容,以及它与 ReplicaSet 的 spec 有何不同。

介绍 Deployment 规范Deployment 对象的 spec 部分与 ReplicaSet 并没有太大区别。正如你在下表中看到的,主要字段与 ReplicaSet 中的字段相同,仅多了一个附加字段。

表 14.1 Deployment 的 spec 部分中指定的主要字段

字段名

描述

Replicas

所需的副本数量。当您创建 Deployment 对象时,Kubernetes 会根据 Pod 模板创建相应数量的 Pod。它会保持这些 Pod 的数量,直到您删除 Deployment。

Selector

标签选择器包含 matchLabels 子字段中的标签映射,或 matchExpressions 子字段中的标签选择器要求列表。匹配标签选择器的 Pod 被视为属于此 Deployment 的一部分。

Template

Deployment 的 Pod 模板。当需要创建新的 Pod 时,会使用此模板来创建对象。

strategy

更新策略定义了在更新 Pod 模板时,此 Deployment 中的 Pod 如何被替换。

副本、副本选择器和模板字段的作用与 ReplicaSets 中的字段相同。在额外的策略字段中,您可以配置在更新此 Deployment 时使用的更新策略。

从零创建 Deployment 清单

当我们需要创建一个新的 Deployment 清单时,我们大多数人通常会复制一个现有的清单文件并进行修改。然而,如果你手头没有现成的清单文件,有一个巧妙的方法可以从零创建清单文件。你可能还记得,你在本书第3章首次创建了一个 Deployment。你当时使用的命令是:

$ kubectl create deployment kiada --image=luksa/kiada:0.1

但是,由于该命令是直接创建对象,而不是创建清单文件,所以它并不是你想要的。不过,你可能记得在第5章中你学到,如果想在不提交到 API 的情况下创建对象清单,可以向 kubectl create 命令传递 --dry-run=client 和 -o yaml 选项。因此,要创建 Deployment 清单文件的初步版本,可以使用以下命令:

$ kubectl create deployment my-app --image=my-image --dry-run=clie

然后,你可以编辑清单文件以进行最终修改,例如添加额外的容器和卷,或更改现有的容器定义。然而,由于你已经拥有 kiada ReplicaSet 的清单文件,最快的方法是将其转换为 Deployment 清单。创建 Deployment 对象清单如果你已经有了 ReplicaSet 清单,那么创建 Deployment 清单非常简单。你只需将 rs.kiada.versionLabel.yaml 文件复制为 deploy.kiada.yaml,然后编辑它,将 kind 字段从 ReplicaSet 更改为 Deployment。在此过程中,请将副本数量从两个改为三个。你的 Deployment 清单应该如下所示。

清单 14.1 kiada Deployment 对象清单

创建和检查 Deployment 对象

要根据清单文件创建 Deployment 对象,请使用 kubectl apply 命令。你可以使用常用命令,如 kubectl get deployment 和 kubectl describe deployment 来获取所创建 Deployment 的信息。例如:

       注意  deployment的简写是deploy.

kubectl get 命令显示的 Pod 数量信息是从 Deployment 对象的 status 部分的 readyReplicas、replicas、updatedReplicas 和 availableReplicas 字段读取的。使用 -o yaml 选项可以查看完整的状态。

注意  使用 kubectl get deploy 时的宽输出选项(-o wide)可以显示标签选择器以及 Pod 模板中使用的容器名称和镜像。

如果你只是想知道 Deployment 是否成功滚动,你也可以使用以下命令:

如果你在创建 Deployment 后立即运行此命令,你可以跟踪 Pods 部署的进展情况。根据命令输出,该 Deployment 已成功推出三个 Pod 副本。现在列出属于该 Deployment 的 Pods。它使用与上一章 ReplicaSet 相同的选择器,所以你应该会看到三个 Pods,对吗?要检查,请使用标签选择器 app=kiada,rel=stable 列出 Pods,如下所示:

令人惊讶的是,有五个 Pod 符合选择器。前两个是由上一章的 ReplicaSet 创建的,而最后三个是由 Deployment 创建的。虽然 Deployment 中的标签选择器匹配了这两个现有的 Pod,但它们并没有像你预期的那样被管理。为什么会这样呢?在本章开始时,我解释过,Deployment 并不直接控制 Pod,而是将此任务委托给底层的 ReplicaSet。让我们快速看看这个 ReplicaSet:

你会注意到,ReplicaSet 的名称不仅仅是 kiada,还包含一个字母数字后缀(-7bffb9bf96),这个后缀看起来是随机生成的,就像 Pod 的名称一样。让我们找出它是什么。请按如下方式仔细查看 ReplicaSet:

“Controlled By” 行表示此 ReplicaSet 已由 kiada Deployment 创建并由其拥有和控制。你会注意到,Pod 模板、选择器以及 ReplicaSet 本身包含了一个额外的标签键 pod-template-hash,这在 Deployment 对象中你从未定义过。该标签的值与 ReplicaSet 名称的最后部分相匹配。这个额外的标签是导致这两个现有的 Pods 没有被该 ReplicaSet 获取的原因。列出所有带有标签的 Pods,以查看它们有何不同:

如你在下图所示,当 ReplicaSet 创建时,ReplicaSet 控制器找不到任何匹配标签选择器的 Pod,所以它创建了三个新的 Pod。如果你在创建 Deployment 之前将此标签添加到两个现有的 Pod,它们就会被 ReplicaSet 识别。

图 14.2 Deployment 和 ReplicaSet 中的标签选择器,以及 Pod 中的标签。

pod-template-hash 标签的值不是随机的,而是根据 Pod 模板的内容计算得出的。由于相同的值也用于 ReplicaSet 的名称,因此名称取决于 Pod 模板的内容。由此可见,每次你更改 Pod 模板时,都会创建一个新的 ReplicaSet。你将在第 14.2 节中了解到更多关于 Deployment 更新的内容。现在,你可以删除不属于该 Deployment 的两个 kiada Pod。要做到这一点,你需要使用带有标签选择器的 kubectl delete 命令,该选择器只选择具有标签 app=kiada 和 rel=stable 且不具有 pod-template-hash 标签的 Pod。完整命令如下所示:

故障排除未能产生任何 Pod 的 Deployment

在某些情况下,当创建 Deployment 时,Pod 可能不会出现。如果你知道该查看哪里,进行故障排除其实很简单。要亲自尝试,请应用清单文件 deploy.where-are-thepods.yaml。这将创建一个名为 where-are-thepods 的 Deployment 对象。你会发现,即使期望的副本数是三个,也没有为该 Deployment 创建任何 Pod。要进行故障排除,可以使用 kubectl describe 检查 Deployment 对象。Deployment 的事件没有显示任何有用信息,但其条件(Conditions)显示了相关信息:

ReplicaFailure 状态为 True,表示出现了错误。错误原因是 FailedCreate,这本身意义不大。然而,如果你查看 Deployment YAML 清单的状态部分的条件,你会注意到 ReplicaFailure 条件的 message 字段会具体告诉你发生了什么。或者,你也可以检查 ReplicaSet 及其事件来查看相同的消息,如下所示:

ReplicaSet 控制器无法创建 Pod 的原因有很多,但它们通常与用户权限有关。在这个示例中,ReplicaSet 控制器无法创建 Pod 是因为缺少服务账户。在第 25 章中,你将进一步了解服务账户。本练习中最重要的结论是,如果在创建(或更新)Deployment 后 Pods 没有出现,应检查底层的 ReplicaSet 中的原因。

14.1.2 扩展部署

扩展部署与扩展副本集没有区别。当你扩展一个部署时,部署控制器只会扩展其底层的副本集,其余的工作由副本集控制器完成,如下图所示。

图14.3 扩容部署

扩展 Deployment您可以通过使用 kubectl edit 命令编辑对象并更改 replicas 字段的值,或者更改清单文件中的值并重新应用它,或者使用 kubectl scale 命令来扩展 Deployment。例如,将 kiada Deployment 扩展到 5 个副本如下所示:

如果你列出 Pods,你会看到现在有五个 kiada Pods。如果你使用 kubectl describe 命令检查与 Deployment 相关的事件,你会看到 Deployment 控制器已经扩展了 ReplicaSet。

如果使用 kubectl describe rs kiada 检查与 ReplicaSet 相关的事件,你会看到确实是 ReplicaSet 控制器创建了这些 Pods。你所学到的关于 ReplicaSet 扩缩以及 ReplicaSet 控制器如何确保实际 Pod 数量始终与所需副本数匹配的知识,也同样适用于通过 Deployment 部署的 Pods。

扩缩由 Deployment 拥有的 ReplicaSet

你可能会想,当扩缩一个由 Deployment 拥有和控制的 ReplicaSet 对象时会发生什么。让我们来看看。首先,通过运行以下命令开始观察 ReplicaSets:

$ kubectl get rs -w

现在通过在另一个终端运行以下命令来扩展 kiada-7bffb9bf96 ReplicaSet:

如果你查看第一个命令的输出,你会看到期望的副本数量上升到七个,但很快又恢复为五个。这是因为 Deployment 控制器检测到 ReplicaSet 中的期望副本数量不再与 Deployment 对象中的数量匹配,所以它将其改回。

重要提示

如果你对由其他对象拥有的对象进行更改,你应该预期由该对象管理的控制器会撤销你的更改。根据 ReplicaSet 控制器是否在 Deployment 控制器撤销更改之前注意到该更改,它可能已经创建了两个新的 Pod。但当 Deployment 控制器将期望副本数量重置回五个时,ReplicaSet 控制器会删除这些 Pod。

正如你可能预料的那样,Deployment 控制器会撤销你对 ReplicaSet 所做的任何更改,而不仅仅是在你调整副本数量时。即使你删除 ReplicaSet 对象,Deployment 控制器也会重新创建它。现在可以随意尝试。

意外地调整 Deployment 的副本数量

在本节关于 Deployment 扩展的部分结束之前,我需要提醒你一个你可能无意中调整 Deployment 副本数量的场景。在你应用到集群的 Deployment 清单中,期望的副本数量是三个。然后你使用 kubectl scale 命令将其更改为五个。试想在生产集群中做同样的事情。例如,因为你需要五个副本来处理应用正在接收的所有流量。

然后你注意到忘记在 Deployment 对象上添加 app 和 rel 标签。你已经将它们添加到了 Deployment 对象内部的 Pod 模板中,但没有添加到对象本身。这不会影响 Deployment 的运行,但你希望所有对象都有良好的标签,所以决定现在添加这些标签。你可以使用 kubectl label 命令,但你更愿意修复原始的清单文件并重新应用。这样,当你将来使用该文件创建 Deployment 时,它将包含你想要的标签。要查看在这种情况下会发生什么,请应用清单文件 deploy.kiada.labelled.yaml。与原始清单文件 deploy.kiada.yaml 唯一的区别是 Deployment 添加了标签。如果在应用清单后列出 Pods,你会看到 Deployment 中不再有五个 Pod。有两个 Pod 已被删除:

要查看两个pod被删除的原因,请检查Deployment对象:

现在,该 Deployment 已配置为只有三个副本,而不是在你应用清单之前的五个副本。然而,你原本并不打算更改副本数量,只是想向 Deployment 对象添加标签。那么,发生了什么呢?应用清单之后副本数发生变化的原因是清单文件中的 replicas 字段被设置为 3。你可能会认为从更新后的清单中移除此字段可以避免问题,但实际上这样会使问题更严重。你可以尝试应用不包含 replicas 字段的 deploy.kiada.noReplicas.yaml 清单文件,看看会发生什么。

如果你应用这个文件,你将只剩下一个副本。这是因为当 replicas 字段被省略时,Kubernetes API 会将其值设为 1。即使你明确将其值设置为 null,效果也是一样的。想象一下,如果在生产集群中发生这种情况,而你的应用负载非常高,需要几十或上百个副本来处理负载,那么像本例中这样无害的更新也会严重干扰服务。你可以通过在创建 Deployment 对象时不在原始清单中指定 replicas 字段来防止这种情况发生。如果你忘记这样做,你仍然可以通过运行以下命令来修复现有的 Deployment 对象:

$ kubectl apply edit-last-applied deploy kiada

这会在文本编辑器中打开 Deployment 对象的 kubectl.kubernetes.io/last-applied-configuration 注解的内容,并允许你移除 replicas 字段。当你保存文件并关闭编辑器后,Deployment 对象中的注解会被更新。从那时起,只要你不包含 replicas 字段,使用 kubectl apply 更新 Deployment 时就不会再覆盖期望的副本数量。

注意  当你使用 kubectl apply 时,kubectl.kubernetes.io/last-applied-configuration 的值会被用来计算需要对 API 对象进行的更改。

提示  为了避免在每次重新应用清单文件时意外扩展 Deployment,请在创建对象时在清单中省略 replicas 字段。最初你只能得到一个副本,但你可以轻松按需扩展 Deployment。

14.1.3 删除 Deployment

在我们了解 Deployment 更新之前——这是 Deployment 最重要的方面——让我们先快速看看删除 Deployment 时会发生什么。在了解删除 ReplicaSet 时的情况后,你可能已经知道,当你删除一个 Deployment 对象时,其底层的 ReplicaSet 和 Pod 也会被删除。

在删除 Deployment 时保留 ReplicaSet 和 Pod

如果你想保留 Pod,可以像操作 ReplicaSet 一样,使用 --cascade=orphan 选项运行 kubectl delete 命令。如果你对 Deployment 使用这种方法,你会发现这不仅可以保留 Pod,还可以保留 ReplicaSet。这些 Pod 仍然属于该 ReplicaSet 并受到其控制。

采用现有的 ReplicaSet 和 Pods

如果你重新创建 Deployment,只要在此期间没有更改 Deployment 的 Pod 模板,它就会使用现有的 ReplicaSet。这是因为 Deployment 控制器会找到一个与其原本会创建的 ReplicaSet 名称匹配的现有 ReplicaSet。

14.2 更新 Deployment

在上一节中,你了解了 Deployment 的基础知识,你可能没有看到使用 Deployment 而不是 ReplicaSet 的任何优势。只有在更新 Deployment 中的 Pod 模板时,这种优势才会显现。你可能还记得,使用 ReplicaSet 时,这不会立即生效。更新的模板仅在 ReplicaSet 控制器创建新的 Pod 时使用。然而,当你更新 Deployment 中的 Pod 模板时,Pods 会立即被替换。目前 kiada Pods 正在运行应用程序的 0.5 版本,你现在将其更新到 0.6 版本。你可以在目录 Chapter14/kiada-0.6 中找到新版本的文件。你可以自行构建容器镜像,也可以使用我创建的镜像 luksa/kiada:0.6。

介绍可用的更新策略

当您更新 Pod 模板以使用新的容器镜像时,Deployment 控制器会停止运行旧镜像的 Pod,并使用新的 Pod 进行替换。Pod 的替换方式取决于 Deployment 中配置的更新策略。在撰写本文时,Kubernetes 支持下表中描述的两种策略。

表 14.2 Deployment 支持的更新策略

策略类型

描述

Recreate

在 Recreate 策略中,所有 Pod 会同时被删除,然后在所有容器完成后,新的 Pod 会同时被创建。在旧 Pod 被终止且新 Pod 准备就绪之前的短时间内,服务将不可用。如果您的应用程序不允许同时运行旧版本和新版本,并且服务停机时间不是问题,则可以使用此策略。

RollingUpdate

滚动更新策略会导致旧的 Pod 被逐步移除并替换为新的 Pod。当一个 Pod 被移除时,Kubernetes 会等待新的 Pod 准备就绪后才移除下一个 Pod。通过这种方式,Pod 提供的服务在整个升级过程中保持可用。这是默认策略。

下图说明了这两种策略之间的差异。它展示了在每种策略下,Pods 随时间的替换情况。

图 14.4 Recreate 策略和 RollingUpdate 策略之间的差异

Recreate 策略没有配置选项,而 RollingUpdate 策略允许你配置 Kubernetes 每次替换多少个 Pod。你稍后会对此有更多了解。

14.2.1 重新创建策略

重新创建策略比滚动更新要简单得多,所以我会先讲它。由于你在 Deployment 对象中没有指定策略,它默认使用滚动更新,因此在触发更新之前,你需要先修改它。

配置 Deployment 以使用重新创建策略

要配置 Deployment 使用重新创建更新策略,你必须在 Deployment 清单中包含以下清单中高亮显示的行。你可以在 deploy.kiada.recreate.yaml 文件中找到该清单。

清单 14.2 在 Deployment 中启用重新创建更新策略

您可以通过使用 kubectl edit 命令编辑 Deployment 对象或通过 kubectl apply 应用更新后的清单文件,将这些行添加到 Deployment 对象中。由于此更改不影响 Pod 模板,因此不会触发更新。更改 Deployment 的标签、注释或所需副本数也不会触发更新。

使用 kubectl set image 更新容器镜像

要将 Pod 更新为 Kiada 容器镜像的新版本,您需要更新 Pod 模板中 kiada 容器定义的 image 字段。您可以通过使用 kubectl edit 或 kubectl apply 更新清单来实现,但对于简单的镜像更改,也可以使用 kubectl set image 命令。通过此命令,您可以更改任何包含容器的 API 对象中任意容器的镜像名称。这包括 Deployments、ReplicaSets,甚至 Pods。例如,您可以使用以下命令将 kiada Deployment 中的 kiada 容器更新为使用 luksa/kiada 容器镜像的 0.6 版本:

$ kubectl set image deployment kiada kiada=luksa/kiada:0.6

但是,由于 Deployment 中的 Pod 模板也在 Pod 标签中指定了应用程序版本,如果更改镜像而不同时更改标签值,就会导致不一致。

使用 kubectl 补丁更新容器映像和标签

要同时更改图像名称和标签值,您可以使用kubectl patch 命令,它允许您更新多个清单字段,而无需手动编辑清单或应用整个清单文件。要同时更新图像名称和标签值,您可以运行以下命令:

$ kubectl patch deploy kiada --patch '{“spec”: {“template”: {“metadata”:

”您可能很难解析此命令,因为补丁是作为单行 JSON 字符串。在此字符串中,你将找到一个部分 Deployment清单,该清单仅包含要更改的字段。如果指定patch 在多行 YAML 字符串中,它会更清晰。完整的命令如下所示:

注意  你也可以将这个部分清单写入文件,并使用 --patch-file 而不是 --patch。

现在运行其中一个 kubectl patch 命令来更新 Deployment,或者应用清单文件 deploy.kiada.0.6.recreate.yaml 来获得相同的结果。

在更新过程中观察 Pod 状态的变化

在你更新 Deployment 之后,立即重复运行以下命令以观察 Pod 的变化:$ kubectl get po -l app=kiada -L ver该命令列出 kiada Pods 并在 VER 列中显示它们的版本标签值。你会注意到,这些 Pod 的状态同时变为 Terminating,如下所示:

pod很快就消失了,但会被运行新版本的pod所取代:

几秒钟后,所有新的 Pod 都已就绪。整个过程非常快,但你可以根据需要重复多次。通过应用 deploy.kiada.recreate.yaml 文件中之前版本的清单来回滚 Deployment,等待 Pod 被替换,然后再次应用 deploy.kiada.0.6.recreate.yaml 文件以更新到 0.6 版本。

理解使用 Recreate 策略进行更新如何影响服务可用性

除了观察 Pod 列表之外,还可以在更新进行时,通过浏览器访问 Ingress 来尝试访问服务,如第 12 章所述。你会注意到 Ingress 代理返回状态 503 服务暂时不可用的短暂时间间隔。如果你尝试使用内部集群 IP 直接访问服务,会发现此期间连接会被拒绝。

理解 Deployment 与其 ReplicaSets 之间的关系

当你列出 Pods 时,你会注意到运行 0.5 版本的 Pods 的名称与运行 0.6 版本的 Pods 不同。旧 Pods 的名称以 kiada-7bffb9bf96 开头,而新 Pods 的名称以 kiada-5d5c5f9d76 开头。你可能还记得,由 ReplicaSet 创建的 Pods 会从该 ReplicaSet 获取名称。名称的变化表明这些新 Pods 属于不同的 ReplicaSet。列出 ReplicaSets 以确认,如下所示:

注意  您在 Deployment 的 Pod 模板中指定的标签也会应用于 ReplicaSet。因此,如果您添加了应用程序版本号的标签,在列出 ReplicaSet 时就可以看到版本号。通过这种方式,您可以轻松区分不同的 ReplicaSet,因为仅通过查看它们的名称是无法做到的。

当您最初创建 Deployment 时,只创建了一个 ReplicaSet,所有 Pod 都属于该 ReplicaSet。当您更新 Deployment 时,会创建一个新的 ReplicaSet。现在,该 Deployment 的所有 Pod 都由这个 ReplicaSet 控制,如下图所示。

图 14.5 更新 Deployment

了解 Deployment 的 Pod 是如何从一个 ReplicaSet 转移到另一个的

如果你在触发更新时正在观察 ReplicaSet,你会看到以下过程。一开始,只有旧的 ReplicaSet 存在:

然后部署控制器将ReplicaSet缩放到零副本,导致ReplicaSet控制器删除所有pod:

接下来,部署控制器创建新的ReplicaSet,并用三个副本配置它。

ReplicaSet 控制器会创建三个新的 Pod,如 CURRENT 列中的数字所示。当这些 Pod 中的容器启动并开始接受连接时,READY 列中的值也会变为三。

注意  您可以通过查看与 Deployment 对象以及两个 ReplicaSet 关联的事件来了解 Deployment 控制器和 ReplicaSet 控制器所做的操作。

更新现在已完成。如果您在网页浏览器中打开 Kiada 服务,应该会看到更新后的版本。在右下角,您会看到四个框,显示处理浏览器请求的 Pod 所使用的 HTML、CSS、JavaScript 以及主图像文件的版本。当您在下一节执行滚动更新到 0.7 版本时,这些框将非常有用。

14.2.2 滚动更新策略

使用 Recreate 策略通常会造成服务中断,这是不可接受的。这就是为什么在 Deployments 中默认的策略是滚动更新(RollingUpdate)。当你使用这种策略时,Pods 会逐步替换,通过同时缩减旧的 ReplicaSet 并增加新的 ReplicaSet 相同数量的副本来实现。服务始终不会没有可用于转发流量的 Pods。

图 14.6 滚动更新过程中,ReplicaSet、Pods 和服务的变化情况。

配置部署以使用滚动更新策略要将部署配置

为使用滚动更新策略,必须像下面的清单中所示设置其 strategy 字段。您可以在文件 deploy.kiada.0.7.rollingUpdate.yaml 中找到此清单。

清单14.3 在部署中启用 Recreate 更新策略

在策略部分,type 字段将策略设置为 RollingUpdate,而 rollingUpdate 子部分中的 maxSurge 和 maxUnavailable 参数则配置了更新的执行方式。你可以省略整个子部分,仅设置 type,但由于 maxSurge 和 maxUnavailable 参数的默认值使得解释更新过程较为困难,所以你设置它们为列表中显示的值,以便更容易跟随更新过程。暂时不用担心这两个参数,因为稍后会进行讲解。你可能已经注意到列表中的 Deployment 的 spec 还包含 minReadySeconds 字段。虽然该字段并不是更新策略的一部分,但它会影响更新的速度。将该字段设置为 10,你将能够更好地跟随滚动更新的进展。你将在本章结束时学到该属性的具体作用。

在清单中更新镜像名称

除了在 Deployment 清单中设置策略和 minReadySeconds 外,我们还将镜像名称设置为 luksa/kiada:0.7 并更新版本标签,这样当你应用这个清单文件时,就会立即触发更新。这是为了展示你可以在一次 kubectl apply 操作中更改策略并触发更新。你不需要事先更改策略,它也可以在更新中使用。

触发更新并观察新版本的发布

要开始滚动更新,请应用清单文件 deploy.kiada.0.7.rollingUpdate.yaml。你可以使用 kubectl rollout status 命令跟踪发布进度,但它只显示以下内容:

要准确地了解 Deployment 控制器如何执行更新,最好查看底层 ReplicaSet 的状态如何变化。首先,版本 0.6 的 ReplicaSet 运行着所有三个 Pod。版本 0.7 的 ReplicaSet 还不存在。之前版本 0.5 的 ReplicaSet 也存在,但我们暂且忽略它,因为它不参与此次更新。0.6 ReplicaSet 的初始状态如下:

当更新开始时,运行版本 0.6 的 ReplicaSet 会缩减一个 Pod,同时会创建版本 0.7 的 ReplicaSet,并配置为运行一个副本:

由于旧的 ReplicaSet 已被缩减规模,ReplicaSet 控制器已将其中一个旧 Pod 标记为删除。该 Pod 正在终止,不再被视为就绪,而其他两个旧 Pod 接管所有服务流量。属于新 ReplicaSet 的 Pod 刚刚启动,因此尚未就绪。Deployment 控制器会等待此新 Pod 就绪后才会继续更新过程。此时,ReplicaSet 的状态如下:

此时,流量再次由三个 Pod 处理。其中两个仍在运行 0.6 版本,一个正在运行 0.7 版本。由于您将 minReadySeconds 设置为 10,Deployment 控制器会等待这么多秒数后才继续进行更新。然后,它将旧的 ReplicaSet 缩减一个副本,同时将新的 ReplicaSet 扩展一个副本。ReplicaSet 现在如下所示:

服务负载现在由一个旧的 Pod 和一个新的 Pod 处理。第二个新的 Pod 还未准备就绪,因此尚未接收流量。在 Pod 准备就绪后的十秒钟,Deployment 控制器对两个 ReplicaSet 进行最终修改。同样,旧的 ReplicaSet 缩减了一个副本,使所需副本数降为零。新的 ReplicaSet 增加副本数,使所需副本数达到三,如下所示:

最后剩下的旧 Pod 已被终止,不再接收流量。所有客户端流量现在都由应用程序的新版本处理。当第三个新 Pod 准备就绪时,滚动更新即完成。在更新过程中,服务始终可用。始终有至少两个副本处理流量。你可以通过恢复到旧版本并再次触发更新来亲自验证。要执行此操作,请重新应用 deploy.kiada.0.6.recreate.yaml 清单文件。由于此清单使用 Recreate 策略,所有 Pod 会立即被删除,然后版本为 0.6 的 Pod 会同时启动。在再次触发更新到 0.7 之前,运行以下命令以从客户端的角度跟踪更新过程:

当你运行此命令时,你会创建一个名为 kiada-client 的 Pod,该 Pod 使用 curl 持续向 kiada 服务发送请求。它不会打印整个响应,而只打印包含版本号以及 Pod 和节点名称的那一行。在客户端向服务发送请求时,通过重新应用清单文件 deploy.kiada.0.7.rollingUpdate.yaml 来触发另一次更新。观察在滚动更新期间 curl 命令的输出如何变化。以下是简要总结:

在滚动更新过程中,一些客户端请求会由运行0.6版本的新Pods处理,而其他请求则由0.6版本的Pods处理。随着新Pods的比例增加,越来越多的响应来自应用程序的新版本。当更新完成后,响应将仅来自新版本。

14.2.3 配置一次替换多少个Pods

在上一节展示的滚动更新中,Pods是一个接一个地被替换的。你可以通过更改滚动更新策略的参数来改变这一点。

引入 maxSurge 和 maxUnavailable 配置选项

影响滚动更新过程中Pods替换速度的两个参数是 maxSurge 和 maxUnavailable,在我介绍 RollingUpdate 策略时已简要提到。你可以在 Deployment 的 strategy 字段的 rollingUpdate 子字段中设置这些参数,如下面的示例所示。

清单14.4为rollingUpdate策略指定参数

下表解释了每个参数的影响。

表14.3关于maxSurge和maxUnavailable配置选项

属性

描述

maxSurge

在滚动更新期间,部署可以拥有的超出期望副本数的最大 Pod 数。该值可以是绝对数,也可以是期望副本数的百分比。

maxUnavailable

在滚动更新期间,相对于期望副本数可以不可用的最大 Pod 数量。该值可以是绝对数,也可以是期望副本数的百分比。

关于这两个参数,最重要的一点是它们的值是相对于期望副本数而言的。例如,如果期望的副本数是三个,maxUnavailable 是一,而当前的 Pod 数量是五,则必须可用的 Pod 数量是两个,而不是四个。让我们看看这两个参数如何影响 Deployment 控制器执行更新。这最好通过逐一查看可能的组合来解释。MaxSurge=0,maxUnavailable=1 当你在上一节中执行滚动更新时,期望的副本数是三,maxSurge 为零,maxUnavailable 为一。下图展示了 Pod 随时间更新的情况。

图 14.7 当 maxSurge 为 0 且 maxUnavailable 为 1 时 Pod 如何被替换

因为 maxSurge 被设置为 0,Deployment 控制器不被允许增加超过期望副本数的 Pods。因此,与该 Deployment 关联的 Pods 从未超过 3 个。因为 maxUnavailable 被设置为 1,Deployment 控制器必须保持可用副本数超过两个,因此每次只能删除一个旧的 Pod。在新 Pod 替代已删除 Pod 并变为可用之前,它不能删除下一个 Pod。

MaxSurge=1, maxUnavailable=0

如果将两个参数调换,将 maxSurge 设置为 1 而 maxUnavailable 设置为 0,会发生什么?如果期望的副本数是三个,在整个过程中必须始终至少有三个副本可用。因为 maxSurge 参数被设置为 1,总 Pods 数不应超过四个。下图展示了更新的展开过程。

图 14.8 当 maxSurge 为 1 且 maxUnavailable 为 0 时,Pods 的替换方式

首先,Deployment 控制器无法缩减旧的 ReplicaSet,因为那样会导致可用 Pod 数量低于期望的副本数。但控制器可以将新的 ReplicaSet 扩展一个 Pod,因为 maxSurge 参数允许 Deployment 的 Pod 数量超过期望副本数一个。此时,Deployment 有三个旧的 Pod 和一个新的 Pod。当新的 Pod 可用时,所有四个 Pod 会暂时共同处理流量。Deployment 控制器现在可以缩减旧的 ReplicaSet 一个 Pod,因为仍然会有三个 Pod 可用。然后控制器可以再将新的 ReplicaSet 扩展。这个过程会重复,直到新的 ReplicaSet 有三个 Pod,而旧的 ReplicaSet 一个也没有。在整个更新过程中,始终保证了所需的 Pod 数量可用,并且 Pod 总数从未超过期望副本数加一。

注意  你不能将 maxSurge 和 maxUnavailable 都设置为 0,因为这样不会允许 Deployment 超过期望的副本数或移除 Pod,因为这样会导致至少有一个 Pod 不可用。

maxSurge=1, maxUnavailable=1

如果将 maxSurge 和 maxUnavailable 都设置为 1,Deployment 中的副本总数最多可以达到四个,并且必须始终有两个可用。下图显示了随时间的变化过程。

图 14.9 当 maxSurge 和 maxUnavailable 都为 1 时 Pod 的替换方式

部署控制器会立即将新的副本集扩展一个副本,同时将旧的副本集缩减相同数量。一旦旧副本集报告它已标记其中一个旧 Pod 进行删除,部署控制器会再将新的副本集扩展一个 Pod。每个副本集现在配置了两个副本。旧副本集中的两个 Pod 仍在运行并可用,而两个新的 Pod 正在启动。当其中一个新 Pod 可用时,会删除另一个旧 Pod,并创建另一个新的 Pod。这一过程会持续,直到所有旧 Pod 被替换。Pod 的总数从不超过四个,并且在任何时刻至少有两个 Pod 可用。

注意  由于 Deployment 控制器本身不计算 Pod 的数量,而是从底层 ReplicaSet 的状态获取 Pod 数量的信息,并且因为 ReplicaSet 从不计算正在终止的 Pod,总的 Pod 数量实际上可能超过 4(如果你计算正在终止的 Pod)。

使用较高的 maxSurge 和 maxUnavailable 值

如果 maxSurge 设置为大于 1 的值,Deployment 控制器允许一次添加更多的 Pod。如果 maxUnavailable 大于 1,控制器允许删除更多的 Pod。

使用百分比

与其将 maxSurge 和 maxUnavailable 设置为绝对数值,不如将它们设置为期望副本数的百分比。控制器会通过向上取整计算绝对的 maxSurge 数值,通过向下取整计算 maxUnavailable。考虑这样一个情况:replicas 设置为 10,maxSurge 和 maxUnavailable 设置为 25%。如果你计算绝对值,maxSurge 变为 3,maxUnavailable 变为 2。因此,在更新过程中,Deployment 最多可以有 13 个 Pod,其中至少有 8 个始终可用并处理流量。

注意  maxSurge 和 maxUnavailable 的默认值为 25%

14.2.4 暂停发布过程

滚动更新过程是完全自动化的。一旦在 Deployment 对象中更新了 Pod 模板,发布过程将开始,并且不会结束,直到所有 Pods 都被新版本替换。然而,你可以随时暂停滚动更新。你可能希望这样做,以检查系统在运行两个版本的应用程序时的行为,或者在替换其他 Pods 之前看看第一个新 Pod 是否按预期运行。

暂停发布

要在滚动更新过程的中途暂停更新,请使用以下命令:

此命令将 Deployment 的 spec 部分中的 paused 字段值设置为 true。Deployment 控制器在对底层 ReplicaSets 进行任何更改之前会检查此字段。再次尝试从版本 0.6 更新到版本 0.7,并在第一个 Pod 被替换时暂停 Deployment。在您的 Web 浏览器中打开应用程序并观察其行为。阅读侧边栏以了解需要注意的事项。

在 Web 应用程序中使用滚动更新时请小心

如果在 Deployment 运行时暂停更新,同时访问应用程序的旧版本和新版本,您会注意到在使用这种策略进行 Web 应用程序更新时可能出现的问题。

在浏览器中多次刷新页面,并观察右下角四个框中显示的颜色和版本号。你会注意到,有些资源显示的是版本 0.6,而有些显示的是版本 0.7。这是因为浏览器发送的某些请求会被路由到运行版本 0.6 的 Pod,而有些请求会被路由到运行版本 0.7 的 Pod。对于 Kiada 应用程序来说,这无关紧要,因为在这两个版本之间,CSS、JavaScript 和图像文件没有重大变化。然而,如果情况不同,HTML 可能会被错误渲染。为了防止这种情况,你可以使用会话亲和性或分两步更新应用程序。首先,将新功能添加到 CSS 和其他资源中,同时保持向后兼容性。在完全推出此版本之后,再推出带有 HTML 变更的版本。或者,你可以使用本章稍后解释的蓝绿部署策略。

恢复部署要恢复暂停的部署,请执行以下命令:

使用暂停功能阻止发布

暂停部署也可以用来防止对部署的更新立即触发更新过程。这样可以让你对部署进行多次更改,而不启动发布,直到你完成所有必要的更改。一旦你准备好让更改生效,就恢复部署,发布过程便会开始。

14.2.5 更新到错误版本

当你发布应用程序的新版本时,可以使用 kubectl rollout pause 命令来验证运行新版本的 Pod 是否按预期工作,然后再继续发布。你也可以让 Kubernetes 自动为你执行此操作。

了解 Pod 可用性

在第11章中,你了解了一个 Pod 及其容器被认为是就绪的含义。然而,当你使用 kubectl get deployments 列出 Deployments 时,你会看到不仅显示有多少 Pods 已就绪,还显示有多少 Pods 可用。例如,在滚动更新期间,你可能会看到如下输出:

尽管有三个 Pod 已经就绪,但并非全部三个都可用。要使 Pod 可用,它必须在一定时间内保持就绪状态。这个时间可以通过我在介绍 RollingUpdate 策略时简要提到的 minReadySeconds 字段进行配置。

注意  一个已经就绪但尚不可用的 Pod 会被包含在你的服务中,因此会接收客户端请求。

通过 minReadySeconds 延迟 Pod 的可用性

在滚动更新中创建新 Pod 时,Deployment 控制器会等待 Pod 可用后才继续执行更新过程。默认情况下,当 Pod 就绪(由 Pod 的就绪探针指示)时,即视为可用。如果您指定了 minReadySeconds,Pod 在就绪后经过指定时间之前不会被视为可用。如果在此期间 Pod 的容器崩溃或就绪探针失败,计时器将被重置。在前面的章节中,您将 minReadySeconds 设置为 10,以减慢更新速度,从而更容易跟踪。实际上,您可以将此属性设置为更高的值,以在创建新 Pod 后自动暂停更新更长时间。例如,如果将 minReadySeconds 设置为 3600,您可以确保在第一批新版本 Pod 证明其能连续正常运行一小时之前,更新不会继续。

尽管在将应用程序迁移到生产环境之前,你显然应该在测试环境和预发布环境中测试你的应用程序,但使用 minReadySeconds 就像一个安全气囊,如果有缺陷的版本通过了所有测试,它可以帮助避免灾难。缺点是它会减慢整个发布过程,而不仅仅是第一阶段。

部署一个有缺陷的应用程序版本

为了了解就绪探针和 minReadySeconds 的组合如何帮助你避免发布有缺陷的应用程序版本,你将部署 Kiada 服务的 0.8 版本。这个特殊版本会在进程启动一段时间后返回 500 内部服务器错误响应。这个时间可以通过 FAIL_AFTER_SECONDS 环境变量进行配置。

要部署此版本,请应用 deploy.kiada.0.8.minReadySeconds60.yaml 清单文件。清单的相关部分如下所示。

清单 14.5 带有就绪探针和 minReadySeconds 的部署清单

正如你在列表中看到的,minReadySeconds 被设置为 60,而 FAIL_AFTER_SECONDS 被设置为 30。就绪探针每 10 秒运行一次。在滚动更新过程中创建的第一个 Pod 在最初的三十秒运行顺利。它被标记为就绪,因此会接收客户端请求。但是在 30 秒之后,这些请求以及就绪探针发出的请求都会失败。由于 minReadySeconds 设置,该 Pod 被标记为未就绪,因此从未被认为是可用的。这会导致滚动更新停止。最初,客户端收到的一些响应会由新版本发送。然后,一些请求会失败,但很快之后,所有响应又会来自旧版本。

将 minReadySeconds 设置为 60 可以将故障版本的负面影响降到最低。如果你没有设置 minReadySeconds,新 Pod 将被立即认为可用,并且部署将替换掉所有旧 Pod 为新版本。这些新 Pod 很快就会失败,导致服务完全中断。如果你想亲自体验一下,可以稍后尝试应用 deploy.kiada.0.8.minReadySeconds0.yaml 清单文件。但首先,让我们看看当部署长时间卡住时会发生什么。

检查部署是否在进行中

Deployment 对象通过 Progressing 条件指示部署过程是否在进行中,你可以在对象的 status.conditions 列表中找到该条件。如果在 10 分钟内没有进展,该条件的状态将变为 false,并且原因会变为 ProgressDeadlineExceeded。你可以通过运行以下 kubectl describe 命令查看:

注意  您可以通过在 Deployment 对象中设置 spec.progressDeadlineSeconds 字段来配置不同的进度截止时间。如果您将 minReadySeconds 增加到超过 600,则必须相应地设置 progressDeadlineSeconds 字段。

如果在触发更新后运行 kubectl rollout status 命令,它会打印已超出进度截止时间的消息,并终止操作。

除了报告部署已经停滞之外,Kubernetes 不会采取进一步的操作。部署过程从未完全停止。如果 Pod 变为就绪状态并在 minReadySeconds 指定的时间内保持就绪,部署过程将继续。如果 Pod 再次无法变为就绪,部署过程就不会继续。你可以按照下一节的说明取消部署。

14.2.6 回滚 Deployment

如果你更新了 Deployment 但更新失败,可以使用 kubectl apply 命令重新应用之前版本的 Deployment 清单,或者告诉 Kubernetes 回滚上一次更新。回滚 Deployment你可以通过运行 kubectl rollout undo 命令将 Deployment 回滚到之前的版本,如下所示:

运行此命令的效果类似于应用对象清单文件的前一个版本。撤销过程遵循与正常更新相同的步骤。它按照 Deployment 对象中指定的更新策略进行操作。因此,如果使用的是 RollingUpdate 策略,则 Pod 会逐步回滚。

提示 在回滚过程中,可以使用 kubectl rollout undo 命令来取消回滚,或在回滚完成后撤销回滚。

注意 当使用 kubectl pause 命令暂停 Deployment 时,kubectl rollout undo 命令不会执行任何操作,直到使用 kubectl rollout resume 恢复 Deployment。

显示部署的发布历史

你不仅可以使用 kubectl rollout undo 命令回滚到上一个版本,还可以回滚到之前的某个版本。当然,你可能希望先看看那些版本的具体情况。你可以使用 kubectl rollout history 命令来做到这一点。不幸的是,在我写这篇文章的时候,这个命令几乎没什么用。看到它的输出时,你就会明白我的意思了:

从这个命令中我们唯一能获得的信息是该 Deployment 已经过了两次修订。列 CHANGE-CAUSE 是空的,因此我们无法看到每次更改的原因。如果在运行修改 Deployment 的 kubectl 命令时使用了 --record 选项,这一列的值才会被填充。不过,该选项现在已被弃用,并将在将来移除。希望届时会引入另一种机制,使 rollout history 命令能够显示每次更改的更多信息。目前,你可以通过运行带有 --revision 选项的 kubectl rollout history 命令逐个查看每次修订。例如,要查看第二次修订,可以运行以下命令:

你可能会想知道修订历史存储在哪里。你在 Deployment 对象中找不到它。相反,Deployment 的历史记录由与该 Deployment 关联的 ReplicaSet 表示,如下图所示。每个 ReplicaSet 代表一个修订版本。这就是为什么在更新过程完成后,Deployment 控制器不会删除旧的 ReplicaSet 对象的原因。

图 14.10 Deployment 的修订历史

注意  修订历史的大小,即部署控制器为特定部署保留的 ReplicaSets 数量,是由部署规格中的 revisionHistoryLimit 字段决定的。默认值为 10。作为练习,尝试找到 Kiada 服务 0.6 版本部署的修订号。在下一节中,你将需要这个修订号。

提示  与使用 kubectl rollout history 查看部署历史相比,使用 -o wide 列出 ReplicaSets 是更好的选择,因为它显示了 Pod 使用的镜像标签。要查找每个 ReplicaSet 的修订号,请查看 ReplicaSet 的注解。

回滚到特定的 Deployment 修订版

您使用了 kubectl rollout undo 命令将部署从有问题的 0.8 版本回滚到 0.7 版本。但是,用户界面中“每日提示”和“抢答”部分的黄色背景看起来没有 0.6 版本的白色背景美观,因此我们需要回滚到该版本。您可以通过在 kubectl rollout undo 命令中指定修订号来回滚到特定的修订版本。例如,如果您想回滚到第一个修订版,请运行以下命令:

$ kubectl rollout undo deployment kiada --to-revision=1

如果您找到了包含 Kiada 服务 0.6 版本的修订号,请使用 kubectl rollout undo 命令将其回滚到该版本。

理解回滚与应用旧版本清单文件之间的区别

你可能会认为使用 kubectl rollout undo 回到 Deployment 清单的前一个版本等同于应用之前的清单文件,但事实并非如此。kubectl rollout undo 命令仅会回滚 Pod 模板,并保留你对 Deployment 清单所做的其他修改。这包括更新策略和期望副本数的更改。另一方面,kubectl apply 命令会覆盖这些修改。

使用 kubectl rollout restart 重启 Pods

除了本节及前面章节中解释的 kubectl rollout 命令之外,还有一个命令我也应该提到。在某些情况下,你可能希望重新启动属于某个 Deployment 的所有 Pod。你可以使用 kubectl rollout restart 命令来实现。这条命令会使用更新时相同的策略删除并替换 Pod。如果 Deployment 配置了 RollingUpdate 策略,Pod 会逐步重新创建,从而在整个过程中保持服务可用性。如果使用 Recreate 策略,则所有 Pod 会被同时删除并重新创建。

14.3 实现其他部署策略

在前面的章节中,您已经了解了 Recreate 和 RollingUpdate 策略的工作原理。虽然这两个是 Deployment 控制器支持的唯一策略,但您也可以实现其他知名策略,只是需要多一些工作量。您可以手动实现,或者让一个更高级别的控制器来自动化这个过程。在撰写本文时,Kubernetes 并未提供此类控制器,但您可以在 Flagger (github.com/fluxcd/flagger) 和 Argo Rollouts (argoproj.github.io/argorollouts) 等项目中找到它们。在本节中,我将为您概述最常见的部署策略是如何实现的。下表解释了这些策略,而随后章节则说明它们在 Kubernetes 中的实现方式。

表 14.4 常见部署策略

策略

描述

Recreate

停止运行所有的旧版本Pod,然后创建使用新版本的所有 Pod。

Rolling Update

逐步用新pod替换旧pod,一次一个或多个。这个策略也被称为Ramped或者Incremental

Canary

创建一个或非常少量的新pod,将少量流量重定向到这些pod,以确保它们按预期运行。然后替换所有剩余的pod。

A/B testing

创建少量新pod,并根据某些条件将一部分用户重定向到这些pod。单个用户总是被重定向到应用程序的相同版本。

通常,您使用此策略来收集关于每个版本在实现特定目标方面的有效性的数据。

Blue/Green

将新版本的Pods与旧版本并行部署。等待直到新的pod准备好,然后将所有流量切换到新的pod。然后删除旧的pod。

Shadowing

将新版本的Pods与旧版本一起部署。

将每个请求转发给两个版本,但只向用户返回旧版本的响应,同时丢弃新版本的响应。这样,您就可以在不影响用户的情况下查看新版本的行为。这种策略也被称为流量镜像或暗启动。

如您所知,Recreate 和 RollingUpdate 策略得到了 Kubernetes 的直接支持,但您也可以将 Canary 策略视为部分支持。让我来解释一下。

14.3.1 Canary 部署策略

如果将 minReadySeconds 参数设置为足够高的值,更新过程会类似于 Canary 部署,因为在第一个新 Pod 证明其可用性之前,该过程会暂停。与真正的 Canary 部署不同的是,这种暂停不仅适用于第一个 Pod(或多个 Pod),而是适用于更新过程中每一个步骤。

或者,您可以在创建第一个 Pod 后立即使用 kubectl rollout pause 命令,并手动检查这些金丝雀 Pod。当您确定新版本按预期工作后,可以使用 kubectl rollout resume 命令继续更新。另一种实现相同目标的方法是为金丝雀 Pod 创建一个单独的 Deployment,并将所需副本数设置为远低于稳定版本 Deployment 的数量。您可以配置 Service 将流量转发到两个 Deployment 中的 Pods。由于 Service 会将流量均匀分配到各个 Pod,而且金丝雀 Deployment 的 Pod 数量远少于稳定 Deployment,因此只有少量流量会发送到金丝雀 Pod,而大部分流量将发送到稳定 Pod。这种方法如下面的图所示。

图 14.11 使用两个 Deployment 实现金丝雀部署策略

当您准备好更新其他 Pod 时,可以对旧 Deployment 执行常规滚动更新,并删除 canary Deployment。

14.3.2 A/B 策略

如果您想实施 A/B 部署策略,以便仅根据特定条件(如位置、语言、用户代理、HTTP Cookie 或头信息)将新版本推送给特定用户,则需要创建两个 Deployment 和两个 Service。您可以配置 Ingress 对象,根据所选条件将流量路由到其中一个 Service,如下图所示。

图 14.12 使用两个 Deployment、Service 和 Ingress 实现 A/B 策略

截至本文撰写时,Kubernetes 并未提供原生方式来实现这种部署策略,但一些 Ingress 实现支持。有关更多信息,请参阅所选 Ingress 实现的文档。

14.3.3 蓝绿(Blue/Green)策略

在蓝绿策略中,会在第一个称为蓝色(Blue)Deployment 的部署旁边创建另一个称为绿色(Green)Deployment 的部署。Service 被配置为仅将流量转发到蓝色 Deployment,直到你决定将所有流量切换到绿色 Deployment。这两组 Pod 因此使用不同的标签,而 Service 中的标签选择器每次匹配一组。你可以通过更新 Service 中的标签选择器,将流量从一组切换到另一组,如下图所示。

图 14.13 使用标签和选择器实现蓝绿部署

正如您所知,Kubernetes 提供了实施此策略所需的一切工具,无需额外工具。

14.3.4 影子流量

有时您不太确定应用程序的新版本在实际生产环境中能否正常工作,或者能否承载负载。在这种情况下,您可以通过创建另一个 Deployment 对象并配置 Pod 标签,使该 Deployment 的 Pods 不匹配 Service 中的标签选择器,从而将新版本与现有版本同时部署。您可以配置位于 Pods 前端的 Ingress 或代理,将流量发送到现有 Pods,同时也镜像到新 Pods。代理会将现有 Pods 的响应发送给客户端,并丢弃新 Pods 的响应,如下图所示。图 14.14 实施流量映射

与 A/B 测试类似,Kubernetes 本身并不提供实现流量影子(traffic shadowing)所需的功能,但一些 Ingress 实现是支持的。

14.4 总结

在本章中,你为 Kiada 服务创建了一个 Deployment,现在请为 Quote 和 Quiz 服务执行相同操作。如果需要帮助,你可以在本书的代码仓库中找到 deploy.quote.yaml 和 deploy.quiz.yaml 文件。

以下是你在本章中学到的内容总结:

  • 部署(Deployment)是对副本集(ReplicaSet)的一层抽象。除了副本集提供的所有功能之外,部署还允许你以声明式的方式更新 Pod。当你更新 Pod 模板时,旧的 Pod 会被使用更新后的模板创建的新 Pod 所替换。
  • 在更新过程中,部署控制器会根据部署中配置的策略替换 Pod。在 Recreate 策略中,所有 Pod 会一次性被替换,而在 RollingUpdate 策略中,它们会逐步被替换。
  • 由ReplicaSet创建的 Pod 属于该ReplicaSet。ReplicaSet通常由部署拥有。如果删除拥有者,垃圾回收器会删除其依赖对象,但你可以通过 kubectl 指定将它们变为孤立状态。
  • 其他部署策略 Kubernetes 并不直接支持,但可以通过适当配置部署、服务和 Ingress 来实现。

你还了解到,Deployments 通常用于运行无状态应用程序。在下一章,你将学习 StatefulSets,它们专门用于运行有状态应用程序。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐