3 路合并到 werf:使用 Helm 部署到 Kubernetes

我们(不仅仅是我们)等待已久的事情发生了: 韦尔夫,我们用于构建应用程序并将其交付到 Kubernetes 的开源实用程序,现在支持使用 3 路合并补丁来应用更改! 除此之外,还可以将现有的 K8s 资源采用到 Helm 版本中,而无需重建这些资源。

3 路合并到 werf:使用 Helm 部署到 Kubernetes

如果它很短,那么我们把 WERF_THREE_WAY_MERGE=enabled - 我们得到部署“如 kubectl apply”,与现有的 Helm 2 安装兼容,甚至更多。

但让我们从理论开始:三向合并补丁到底是什么,人们是如何想出生成它们的方法的,以及为什么它们在基于 Kubernetes 基础设施的 CI/CD 流程中很重要? 之后,让我们看看 werf 中的 3-way-merge 是什么,默认使用哪些模式以及如何管理它。

什么是三向合并补丁?

因此,我们首先将 YAML 清单中描述的资源部署到 Kubernetes 中。

为了使用资源,Kubernetes API 提供了以下基本操作:创建、修补、替换和删除。 假设在他们的帮助下,有必要构建一个方便的、连续的资源向集群的部署。 如何?

kubectl 命令式命令

在 Kubernetes 中管理对象的第一种方法是使用 kubectl 命令式命令来创建、修改和删除这些对象。 简单的说:

  • 团队 kubectl run 您可以运行部署或作业:
    kubectl run --generator=deployment/apps.v1 DEPLOYMENT_NAME --image=IMAGE
  • 团队 kubectl scale — 更改副本数量:
    kubectl scale --replicas=3 deployment/mysql
  • 等等

乍一看,这种方法似乎很方便。 但也存在问题:

  1. 这个很难(硬 自动化.
  2. 反映配置 在 Git 中? 如何查看集群发生的变化?
  3. 如何提供 再现性 重启时的配置?
  4. ...

显然,这种方法不太适合将应用程序和基础设施存储为代码(IaC;甚至 Git 操作 作为一种更现代的选择,在 Kubernetes 生态系统中越来越受欢迎)。 因此,这些命令在 kubectl 中没有得到进一步的开发。

创建、获取、替换和删除操作

与初级 的创造 很简单:将清单发送给操作人员 create kube api 和资源已创建。 清单的 YAML 表示形式可以存储在 Git 中并使用以下命令创建 kubectl create -f manifest.yaml.

С 去除 也很简单:替换相同的 manifest.yaml 从 Git 到团队 kubectl delete -f manifest.yaml.

手术 replace 允许您用新的配置完全替换资源配置,而无需重新创建资源。 这意味着在对资源进行更改之前,使用以下操作查询当前版本是合乎逻辑的 get,更改它并通过操作更新它 replace。 内置 kube apiserver 乐观锁 并且,如果手术后 get 对象发生了变化,那么操作 replace 不会通过。

要将配置存储在 Git 中并使用替换更新它,您需要执行以下操作 get,将 Git 中的配置与我们收到的配置合并,然后执行 replace。 默认情况下,kubectl只允许你使用命令 kubectl replace -f manifest.yaml哪里 manifest.yaml — 需要安装的已经完全准备好的(在我们的例子中是合并的)清单。 事实证明,用户需要实现合并清单,这不是一件小事......

还值得注意的是,虽然 manifest.yaml 并且存储在Git中,我们无法提前知道是否需要创建一个对象或更新它——这必须由用户软件来完成。

合计: 我们可以持续推出吗 仅使用创建、替换和删除,确保基础架构配置与代码一起存储在 Git 中并方便 CI/CD?

原则上,我们可以...为此 您将需要实施合并操作 宣言和某种约束力:

  • 检查集群中是否存在对象,
  • 执行初始资源创建,
  • 更新或删除它。

更新时请注意 资源可能已更改 自上次以来 get 并自动处理乐观锁定的情况 - 进行重复的更新尝试。

然而,当 kube-apiserver 提供了另一种更新资源的方式时,为什么要重新发明轮子:操作 patch,这可以缓解用户所描述的一些问题?

打补丁

现在我们来看看补丁。

补丁是将更改应用于 Kubernetes 中现有对象的主要方式。 手术 patch 它的工作原理如下:

  • kube-apiserver 用户需要发送 JSON 形式的补丁并指定对象,
  • apiserver 本身将处理对象的当前状态并将其转换为所需的形式。

在这种情况下不需要乐观锁定。 此操作比替换更具声明性,尽管乍一看似乎相反。

因此:

  • 使用操作 create 我们根据 Git 的清单创建一个对象,
  • 通过 delete — 如果不再需要该对象,则删除,
  • 通过 patch — 我们更改对象,将其变为 Git 中描述的形式。

然而,要做到这一点,您需要创建 正确的补丁!

补丁在 Helm 2 中的工作原理:2 路合并

当您首次安装版本时,Helm 会执行以下操作 create 用于图表资源。

更新每个资源的 Helm 版本时:

  • 考虑上一个图表的资源版本与当前图表版本之间的补丁,
  • 应用此补丁。

我们将这个补丁称为 2路合并补丁,因为它的创建涉及两个宣言:

  • 先前版本的资源清单,
  • 当前资源的资源清单。

删除操作时 delete 在 kube apiserver 中,会调用在上一版本中声明但在当前版本中未声明的资源。

2 路合并补丁方法有一个问题:它会导致 与集群中资源的真实状态和 Git 中的清单不同步.

用例子说明问题

  • 在 Git 中,图表存储一个清单,其中字段 image 部署很重要 ubuntu:18.04.
  • 用户通过 kubectl edit 将该字段的值更改为 ubuntu:19.04.
  • 重新部署 Helm 图表时 不生成补丁,因为场 image 之前版本的发行版和当前图表中的内容是相同的。
  • 重新部署后 image 遗体 ubuntu:19.04,尽管图表显示 ubuntu:18.04.

我们失去了同步性并失去了声明性。

什么是同步资源?

一般来说 充分 无法获得正在运行的集群中的资源清单与来自 Git 的资源清单之间的匹配。 因为在真实的清单中可能存在服务注释/标签、附加容器和其他由某些控制器动态添加和从资源中删除的数据。 我们不能也不想将这些数据保留在 Git 中。 但是,我们希望在 Git 中明确指定的字段在推出时采用适当的值。

原来如此一般 同步资源规则:推出资源时,您只能更改或删除 Git 清单中明确指定的字段(或在先前版本中指定但现在已删除)。

3路合并补丁

中心思想 3路合并补丁:我们在 Git 的清单的最后应用版本和 Git 的清单的目标版本之间生成补丁,同时考虑到正在运行的集群的清单的当前版本。 生成的补丁必须符合同步资源规则:

  • 添加到目标版本的新字段是使用补丁添加的;
  • 使用补丁重置上次应用版本中先前存在且目标版本中不存在的字段;
  • 对象当前版本中与清单目标版本不同的字段将使用补丁进行更新。

就是根据这个原理生成补丁的 kubectl apply:

  • 清单的最后应用版本存储在对象本身的注释中,
  • target - 取自指定的 YAML 文件,
  • 当前的一个来自正在运行的集群。

现在我们已经理清了理论,是时候告诉你我们在 werf 中做了什么。

将更改应用于 werf

此前,werf 与 Helm 2 一样,使用 2 路合并补丁。

维修补丁

为了切换到新类型的补丁 - 3-way-merge - 第一步我们引入了所谓的 修复补丁.

部署时,使用标准的2路合并补丁,但werf另外生成一个补丁,将资源的真实状态与Git中编写的内容同步(这样的补丁是使用上述相同的同步资源规则创建的) 。

如果发生去同步,则在部署结束时,用户会收到一条警告,其中包含相应的消息以及必须应用的补丁才能使资源达到同步形式。 这个补丁还记录在专门的注释中 werf.io/repair-patch。 假设用户的手 他自己 将应用此补丁:werf 根本不会应用它。

生成修复补丁是一种临时措施,允许您实际测试基于三向合并原则的补丁创建,但不会自动应用这些补丁。 目前,该操作模式默认启用。

仅适用于新版本的三向合并补丁

从 1 年 2019 月 XNUMX 日开始,werf 的 beta 和 alpha 版本开始 默认情况下 使用成熟的 3 路合并补丁仅将更改应用于通过 werf 推出的新 Helm 版本。 现有版本将继续使用2路合并+修复补丁的方式。

可以通过设置显式启用此操作模式 WERF_THREE_WAY_MERGE_MODE=onlyNewReleases 现在。

注意:该功能在 werf 中出现了多个版本:在 alpha 通道中,它已经准备好版本 v1.0.5-alpha.19,以及在测试版频道中 - 与 v1.0.4-beta.20.

适用于所有版本的三向合并补丁

从 15 年 2019 月 3 日开始,werf 的 beta 和 alpha 版本开始默认使用完整的 XNUMX 路合并补丁来将更改应用于所有版本。

可以通过设置显式启用此操作模式 WERF_THREE_WAY_MERGE_MODE=enabled 现在。

资源自动缩放该怎么办?

Kubernetes 中有两种类型的自动缩放:HPA(水平)和 VPA(垂直)。

水平方向自动选择副本数量,垂直方向-资源数量。 副本数量和资源需求均在资源清单中指定(请参阅资源清单)。 spec.replicas или spec.containers[].resources.limits.cpu, spec.containers[].resources.limits.memory и 他人).

问题:如果用户在图表中配置资源,以便为资源或副本指定某些值,并且为此资源启用自动缩放器,则每次部署时 werf 都会将这些值重置为图表清单中写入的值。

该问题有两种解决方案。 首先,最好避免在图表清单中显式指定自动缩放值。 如果由于某种原因该选项不适合(例如,因为可以方便地设置初始资源限制和图表中的副本数量),则 werf 提供以下注释:

  • werf.io/set-replicas-only-on-creation=true
  • werf.io/set-resources-only-on-creation=true

如果存在这样的注释,werf将不会在每次部署上重置相应的值,而只会在最初创建资源时设置它们。

有关更多详细信息,请参阅项目文档 HPA и VPA.

禁止使用3路合并补丁

用户目前可以使用环境变量禁止在werf中使用新补丁 WERF_THREE_WAY_MERGE_MODE=disabled。 然而,开始 自1年2020月XNUMX日起,该禁令将不再适用。 并且只能使用 3 路合并补丁。

采用werf中的资源

掌握了通过 3 路合并补丁应用更改的方法,使我们能够立即实现将集群中现有的资源采用到 Helm 版本中这样的功能。

Helm 2 有一个问题:如果不从头开始重新创建该资源,则无法将资源添加到集群中已存在的图表清单中(请参阅。 #6031, #3275)。 我们教 werf 接受现有资源以进行释放。 为此,您需要在正在运行的集群中的资源的当前版本上安装注释(例如,使用 kubectl edit):

"werf.io/allow-adoption-by-release": RELEASE_NAME

现在需要在图表中描述资源,下次 werf 部署具有适当名称的版本时,现有资源将被接受到该版本中并保持在其控制之下。 此外,在接受资源释放的过程中,werf 会使用相同的 3 路合并补丁和同步资源规则,将资源的当前状态从正在运行的集群带到图表中描述的状态。

注意: 环境 WERF_THREE_WAY_MERGE_MODE 不影响资源的采​​用 - 在采用的情况下,始终使用 3 路合并补丁。

详细信息 - 在 文件资料.

结论和未来计划

我希望读完这篇文章后,大家能够更清楚什么是三向合并补丁以及它们为何出现。 从werf项目开发的实际角度来看,它们的实施是改进Helm-like部署的又一步。 现在您可以忘记使用 Helm 3 时经常出现的配置同步问题。同时,Helm 版本中添加了一个新的有用功能,即采用已下载的 Kubernetes 资源。

类似 Helm 的部署仍然存在一些问题和挑战,例如 Go 模板的使用,我们将继续解决这些问题和挑战。

有关资源更新方法和采用的信息也可以在以下位置找到: 本文档页面.

头盔3

值得特别注意的 释放 就在前几天,Helm 的新主要版本 - v3 - 也使用了 3 路合并补丁并摆脱了 Tiller。 新版本的 Helm 需要 移民 现有安装将其转换为新版本的存储格式。

Werf 目前已经放弃使用 Tiller,改用 3-way-merge 并添加了 多得多,同时保持与现有 Helm 2 安装的兼容性(无需执行迁移脚本)。 因此,在 werf 切换到 Helm 3 之前,werf 用户不会失去 Helm 3 相对于 Helm 2 的主要优势(werf 也有)。

然而,werf 到 Helm 3 代码库的切换是不可避免的,并且将在不久的将来发生。 据推测,这将是 werf 1.1 或 werf 1.2(目前,werf 的主要版本是 1.0;有关 werf 版本控制设备的更多信息,请参阅 这里)。 在此期间,Helm 3 将有时间稳定下来。

PS

另请阅读我们的博客:

来源: habr.com

添加评论