正确比较 Kubernetes 应用、替换和修补

Kubernetes 有多种更新资源的选项:应用、编辑、修补和替换。 对于每种方法的作用以及何时使用它们存在混乱。 让我们弄清楚一下。

正确比较 Kubernetes 应用、替换和修补

如果 在谷歌上搜索 短语“kubernetes apply vs replacement”位于 回复 StackOverflow,这是不正确的。 搜索时 “kubernetes apply vs patch”第一个链接是文档 kubectl patch,其中不包括比较 apply и patch。 本文将介绍不同的选项,以及每个选项的正确使用方法。

在 Kubernetes 资源的生命周期(服务、部署、入口等)中,有时您需要更改、添加或删除该资源的某些属性。 例如,添加注释、增加或减少副本数量。

Kubernetes CLI

如果您已经通过 CLI 使用 Kubernetes 集群,那么您已经熟悉 apply и edit. 库玛纳达 apply 从文件中读取资源规范并向 Kubernetes 集群进行“upsert”,即如果资源不存在则创建资源,如果存在则更新资源。 团队 edit 通过 API 读取资源,然后将资源规范写入本地文件,然后在文本编辑器中打开该文件。 编辑并保存文件后, kubectl 将通过 API 发回所做的更改,API 会仔细地将这些更改应用到资源。

并不是每个人都知道这些命令 patch и replace. 库玛纳达 patch 允许您更改资源规范的一部分,仅在命令行上提供更改的部分。 团队 replace 工作原理与 edit,但一切都需要手动完成:您需要下载当前版本的资源规范,例如使用 kubectl get -o yaml,编辑它,然后使用 replace 根据更改的规范更新资源。 团队 replace 如果读取和替换资源之间发生任何更改,则将不起作用。

库伯内斯API

您可能熟悉这些方法 CoreV1().Pods().Update(), replaceNamespacedService или patch_namespaced_deployment,如果您通过以下方式使用集群 Kubernetes API 的客户端库 使用某种编程语言。 该库使用以下方法通过 HTTP 请求处理这些方法 PUT и PATCH。 在这种情况下, update и replacePUTpatch,无论它多么微不足道,都会使用 PATCH.

值得一提的是 kubectl 还可以通过 API 与集群配合使用。 换句话说, kubectl是 Go 语言客户端库之上的包装器,除了标准 API 功能之外,它在很大程度上提供了以更紧凑和可读的形式提供子命令的能力。 例如,您可能已经注意到,该方法 apply 上一段中没有提到。 目前(2020年XNUMX月, 约译者)所有逻辑 kubectl apply, IE。 创建不存在的资源并更新现有资源,完全在代码端工作 kubectl。 正在努力 关于逻辑传输 apply API 方面,但仍处于测试阶段。 下面我会详细写一下。

默认打补丁

最佳使用 patch,如果您想更新资源。 这就是两个客户端库在 Kubernetes API 之上工作的方式 kubectl (这并不奇怪,因为它是客户端库的包装器, 约译者).

战略性地工作

所有球队 kubectl apply, edit и patch 使用方法 PATCH 在 HTTP 请求中更新现有资源。 如果您更详细地研究命令的实现,那么它们都使用该方法 战略合并修补 更新资源,尽管命令 patch 可以使用其他方法(更多内容见下文)。 战略合并修补方法试图通过将提供的规范与现有规范合并来“做到正确”。 更具体地说,它尝试组合对象和数组,这意味着更改往往是相加的。 例如,运行命令 patch 在 pod 容器规范中使用新的环境变量,会导致该环境变量被添加到现有的环境变量中,而不是覆盖它们。 要删除使用此方法,您必须在提供的规范中将参数值强制设置为 null。 哪支球队 kubectl 最好用于更新吗?

如果您使用以下方式创建和管理资源 kubectl apply,更新时最好始终使用 kubectl applykubectl 可以管理配置并正确跟踪应用程序之间请求的更改。 优点一直用 apply 其优点是它会跟踪先前应用的规范,使其能够知道何时显式删除规范属性和数组元素。 这允许您使用 apply 删除属性和数组元素,而正常的策略合并将不起作用。 团队 edit и patch 不要更新注释 kubectl apply 用于跟踪其更改,因此通过 Kubernetes API 跟踪和进行的任何更改,但通过命令进行 edit и patch,对后续命令不可见 apply即, apply 即使它们没有出现在输入规范中,也不会删除它们 apply (文档说 edit и patch 更新所使用的注释 apply,但实际上 - 不)。

如果你不使用该命令 apply,可以用作 editpatch,选择最适合所做更改的命令。 添加和更改 BOM 属性时,两种方法大致相同。 删除规格属性或数组元素时 edit 行为类似于一次性启动 apply,包括跟踪规范编辑前后的情况,以便您可以显式地从资源中删除属性和数组元素。 您需要在规范中显式地将属性值设置为 null patch将其从资源中删除。 使用策略合并修补删除数组元素更为复杂,因为它需要使用合并指令。 请参阅下面的其他升级方法,了解更可行的替代方案。

在客户端库中实现更新方法,其行为与上述命令类似 kubectl,应该在请求中设置 content-type в application/strategic-merge-patch+json。 如果要删除规范中的属性,则需要以类似的方式将它们的值显式设置为 null kubectl patch。 如果需要删除数组元素,则应在更新规范中包含合并指令或使用不同的更新方法。

其他更新方法

Kubernetes 支持另外两种更新方法: JSON 合并补丁 и JSON补丁。 JSON 合并修补方法采用部分 Kubernetes 规范作为输入,并支持与策略合并修补方法类似的合并对象。 两者的区别在于它只支持数组替换,包括pod规范中的容器数组。 这意味着在使用 JSON 合并补丁时,您需要为所有容器提供完整的规范,以防任何容器的任何属性发生更改。 因此,此方法对于从 BOM 中的数组中删除元素非常有用。 在命令行上,您可以使用以下命令选择 JSON 合并补丁 kubectl patch --type=merge。 使用 Kubernetes API 时,应该使用 request 方法 PATCH 和安装 content-type в application/merge-patch+json.

JSON 修补方法不是提供资源的部分规范,而是以数组的形式提供您想要对资源进行的更改,其中数组的每个元素代表对资源所做更改的描述。 这种方法是一种更灵活、更强大的方式来表达所做的更改,但代价是以单独的非 Kubernetes 格式列出所做的更改,而不是发送部分资源规范。 在 kubectl 您可以使用选择 JSON 补丁 kubectl patch --type=json。 使用 Kubernetes API 时,此方法使用请求方法 PATCH 和安装 content-type в application/json-patch+json.

我们需要信心 - 使用替换

在某些情况下,您需要确保在读取资源和更新资源之间没有对资源进行任何更改。 换句话说,您应该确保所有更改都会 原子。 在这种情况下,要更新资源,您应该使用 replace。 例如,如果您有一个带有由多个源更新的计数器的 ConfigMap,则应确保两个源不会同时更新计数器,从而导致更新丢失。 为了进行演示,请使用该方法想象一系列事件 patch:

  • A和B从API获取资源的当前状态
  • 每个都通过将计数器增加 XNUMX 并分别在“更新者”注释中添加“A”或“B”来本地更新规范
  • 而且资源更新速度快一点
  • B 更新资源

结果,更新 A 丢失。 上次操作 patch 获胜,计数器增加 XNUMX 而不是 XNUMX,并且“updated-by”注释的值以“B”结尾且不包含“A”。 让我们将上述内容与使用该方法完成更新时发生的情况进行比较 replace:

  • A和B从API获取资源的当前状态
  • 每个都通过将计数器增加 XNUMX 并分别在“更新者”注释中添加“A”或“B”来本地更新规范
  • 而且资源更新速度快一点
  • B 尝试更新资源,但更新被 API 拒绝,因为资源版本在规范中 replace 与 Kubernetes 中资源的当前版本不匹配,因为 A 的替换操作增加了资源的版本。

在上述情况下,B 必须重新获取资源,更改新状态并重试 replace。 这将导致计数器增加 XNUMX,并且“更新者”注释在末尾包含“AB”。

上面的例子意味着当执行 replace 整个资源被完全替换。 规格用于 replace,不能是部分的,或者像这样的部分 apply,但完整,包括添加 resourceVersion 到规范元数据中。 如果您还没有启用 resourceVersion 或者您提供的版本不是最新版本,更换将被拒绝。 所以最好的使用方法是 replace – 立即读取资源、更新并替换。 使用 kubectl,它可能看起来像这样:

$ kubectl get deployment my-deployment -o json 
    | jq '.spec.template.spec.containers[0].env[1].value = "new value"' 
    | kubectl replace -f -

值得注意的是,以下两个命令,顺序执行,将会执行成功,因为 deployment.yaml 不包含财产 .metadata.resourceVersion

$ kubectl create -f deployment.yaml
$ kubectl replace -f deployment.yaml

这似乎与上面所说的相矛盾,即“添加 resourceVersion 到规范元数据中。”这样说是错误的吗?不,不是,因为如果 kubectl 注意到你没有指定 resourceVersion,它会从资源中读取它并将其添加到您指定的规范中,然后才执行它 replace。 因为如果你依赖原子性,这可能是危险的,所以魔法完全在侧面起作用 kubectl,在使用与 API 配合使用的客户端库时,不应依赖它。 在这种情况下,您将必须读取当前的资源规范,更新它然后执行 PUT 要求。

你无法打补丁——我们会进行替换

有时您需要进行一些 API 无法处理的更改。 在这些情况下,您可以通过删除并重新创建资源来强制替换资源。 这是使用完成的 kubectl replace --force。 运行该命令会立即删除资源,然后根据提供的规范重新创建它们。 API 中没有“强制替换”处理程序,为了通过 API 执行此操作,您需要执行两个操作。 首先,您需要通过设置来删除资源 gracePeriodSeconds 为零 (0) 和 propagationPolicy 在“背景”中,然后使用所需的规范重新创建该资源。

警告:这种方法有潜在危险,可能会导致未定义的状态。

在服务器端应用

如上所述,Kubernetes 开发人员正在致力于实现逻辑 apply из kubectl 在 Kubernetes API 中。 逻辑学 apply 在 Kubernetes 1.18 中可用: kubectl apply --server-side 或通过 API 使用该方法 PATCH с content-type application/apply-patch+YAML.

注意:JSON 也是有效的 YAML,因此您可以将规范作为 JSON 发送,即使 content-typeapplication/apply-patch+yaml.

除了这个逻辑 kubectl 通过 API 可供所有人使用, apply 在服务器端,跟踪谁负责规范中的字段,从而允许安全的多重访问以实现无冲突编辑。 换句话说,如果 apply 服务器端将变得更加广泛,针对不同客户端将出现通用的安全资源管理界面,例如 kubectl、Pulumi 或 Terraform、GitOps,以及使用客户端库自行编写的脚本。

结果

我希望这篇关于更新集群中资源的不同方法的简短概述对您有所帮助。 很高兴知道这不仅仅是应用与替换;还可以使用应用、编辑、修补或替换来更新资源。 毕竟,原则上,每种方法都有其自己的应用领域。 对于原子更改,替换是更好的选择;否则,您应该通过 apply 使用策略合并补丁。 至少,我希望您明白,在搜索“kubernetes apply vs replacement”时不能信任 Google 或 StackOerflow。 至少在本文取代当前答案之前。

正确比较 Kubernetes 应用、替换和修补

来源: habr.com

添加评论