我们(不仅仅是我们)等待已久的事情发生了:
如果它很短,那么我们把 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
- 等等
乍一看,这种方法似乎很方便。 但也存在问题:
- 这个很难(硬 自动化.
- 如 反映配置 在 Git 中? 如何查看集群发生的变化?
- 如何提供 再现性 重启时的配置?
- ...
显然,这种方法不太适合将应用程序和基础设施存储为代码(IaC;甚至
创建、获取、替换和删除操作
与初级 的创造 很简单:将清单发送给操作人员 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路合并补丁
中心思想
- 添加到目标版本的新字段是使用补丁添加的;
- 使用补丁重置上次应用版本中先前存在且目标版本中不存在的字段;
- 对象当前版本中与清单目标版本不同的字段将使用补丁进行更新。
就是根据这个原理生成补丁的 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 通道中,它已经准备好版本
适用于所有版本的三向合并补丁
从 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将不会在每次部署上重置相应的值,而只会在最初创建资源时设置它们。
禁止使用3路合并补丁
用户目前可以使用环境变量禁止在werf中使用新补丁 WERF_THREE_WAY_MERGE_MODE=disabled
。 然而,开始 自1年2020月XNUMX日起,该禁令将不再适用。 并且只能使用 3 路合并补丁。
采用werf中的资源
掌握了通过 3 路合并补丁应用更改的方法,使我们能够立即实现将集群中现有的资源采用到 Helm 版本中这样的功能。
Helm 2 有一个问题:如果不从头开始重新创建该资源,则无法将资源添加到集群中已存在的图表清单中(请参阅。 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
值得特别注意的
Werf 目前已经放弃使用 Tiller,改用 3-way-merge 并添加了
然而,werf 到 Helm 3 代码库的切换是不可避免的,并且将在不久的将来发生。 据推测,这将是 werf 1.1 或 werf 1.2(目前,werf 的主要版本是 1.0;有关 werf 版本控制设备的更多信息,请参阅
PS
另请阅读我们的博客:
- 关于werf创新的一系列注释:
- «
werf - 我们在 Kubernetes 中的 CI / CD 工具(概述和视频报告) “; - «
使用 werf 和 GitLab CI 构建和部署相同类型的微服务 “; - «
介绍 Helm 3 “。
来源: habr.com