我们(不仅是我们)一直在等待发生的事情:
werf ,我们的开源实用程序,用于构建应用程序并将其交付给Kubernetes,现在支持使用三向合并补丁应用更改! 除此之外,无需重新创建这些资源就可以将现有的K8s资源用于Helm版本。

如果很短,请设置
WERF_THREE_WAY_MERGE=enabled
我们将“像
kubectl apply
”部署,与Helm 2上的现有安装兼容,甚至更多。
但是,让我们从理论开始:一般来说,三向合并补丁是什么,人们是如何开始使用这种方法的?为什么它们在基于Kubernetes的基础架构的CI / CD流程中很重要? 之后,让我们看看werf中的3向合并是什么,默认情况下使用哪种模式以及如何对其进行管理。
什么是三向合并补丁?
因此,让我们从部署Kubernetes的YAML清单中描述的资源开始。
为了使用资源,Kubernetes API提供了以下基本操作:创建,修补,替换和删除。 假设在他们的帮助下,有必要为集群构建一个方便的,连续的资源部署。 怎么了
命令式kubectl团队
在Kubernetes中管理对象的第一种方法是使用命令式kubectl命令创建,修改和删除这些对象。 简单地说:
乍一看,这种方法似乎很方便。 但是,存在一些问题:
- 很难实现自动化 。
- 如何在Git中反映配置 ? 如何查看集群发生的更改?
- 如何确保重启时配置的可重复性 ?
- ...
显然,这种方法不适合将应用程序和基础结构存储为代码(例如IaC;甚至是
GitOps,这是一种更现代的选择,在Kubernetes生态系统中越来越流行)。 因此,这些团队没有在kubectl中得到进一步的发展。
创建,获取,替换和删除操作
使用主
创建,一切都很简单:我们将清单发送到kube api的
create
操作,并创建资源。 清单的YAML表示形式可以存储在Git中,并使用
kubectl create -f manifest.yaml
命令进行
kubectl create -f manifest.yaml
。
删除也很简单:我们将Git中的相同
manifest.yaml
替换为
kubectl delete -f manifest.yaml
命令。
replace
操作使您可以用新配置完全替换资源配置,而无需重新创建资源。 这意味着在对资源进行更改之前,使用
get
操作请求当前版本,进行更改并使用
replace
操作进行更新是合乎逻辑的。 乐观锁定已内置在kube apiserver中,如果在
get
操作之后更改了对象,则
replace
操作将失败。
要将配置存储在Git中并使用replace进行更新,您需要执行
get
操作,将Git中的配置保存在我们的配置中,然后执行
replace
。 通常,kubectl仅允许您使用
kubectl replace -f manifest.yaml
命令,其中
manifest.yaml
是需要安装的充分准备的
manifest.yaml
(在我们的情况下为邻接)。 事实证明,用户需要实现合并清单,但这不是一件小事……
还值得注意的是,尽管
manifest.yaml
存储在Git中,但我们无法事先知道是否需要创建对象或对其进行更新-这应该由用户软件完成。
底线:
我们能否仅通过创建,替换和删除
来构建连续的发布 ,以确保基础结构配置以及代码和方便的CI / CD都存储在Git中?
基本上,我们可以...为此,我们
需要实现清单
的合并操作和某种绑定:
- 检查集群中是否存在对象,
- 执行资源的初始创建,
- 更新或删除它。
更新时,您需要考虑到自上次
get
以来
资源可能已更改 ,并且会自动处理乐观锁定的情况-重复尝试更新。
但是,当kube-apiserver提供了另一种更新资源的方式:
patch
操作,从而消除了用户描述的一些问题时,为什么要重新发明轮子呢?
贴片
所以我们到了补丁。
补丁是将更改应用于Kubernetes中现有对象的主要方法。
patch
操作的工作原理是:
- kube-apiserver用户需要以JSON格式发送补丁并指定对象,
- 并且apiserver本身将处理对象的当前状态并将其转换为所需的形式。
在这种情况下,不需要乐观锁定。 相较于replace,此操作更具声明性,尽管起初看起来似乎相反。
通过这种方式:
- 使用
create
操作,我们从Git的清单创建对象, - 使用
delete
-如果不再需要该对象,则删除, - 使用
patch
-我们修改对象,使其达到Git中描述的形式。
但是,为此,您必须创建
正确的补丁 !
补丁在Helm 2中的工作原理:2路合并
首次安装发行版时,Helm对图表资源执行
create
操作。
在为每个资源更新Helm版本时:
- 计算上一个图表的资源版本与图表的当前版本之间的补丁,
- 应用此补丁。
我们将这种补丁称为2way
-merge补丁 ,因为2条宣言参与了其创建:
删除时,将针对在先前发行版中声明但在当前发行版中未声明的资源调用kube apiserver中的
delete
操作。
具有2种方式的合并修补程序的方法存在一个问题:它导致
集群中资源的真实状态与Git中的清单的
异步状态不同步 。
一个问题的例子
- 在Git中,清单存储在图表上,其中“部署
image
字段的ubuntu:18.04
值为ubuntu:18.04
。 - 用户通过
kubectl edit
将此字段的值更改为ubuntu:19.04
。 - 当您重新部署图表时,Helm 不会生成补丁 ,因为该发行版的先前版本和当前图表中的
image
字段相同。 - 在重复部署
image
之后,尽管在图表上写入了ubuntu:18.04
,但仍然保留ubuntu:19.04
。
我们变得不同步,失去了声明性。
什么是同步资源?
一般来说,不可能在运行的集群中的资源清单和Git的清单之间实现
完全匹配。 因为在真正的清单中可能存在服务注释/标签,其他容器以及其他控制器通过资源从资源动态添加和删除的其他数据。 我们不能也不希望将此数据保留在Git中。 但是,我们希望在推出时,我们在Git中明确指定的字段采用适当的值。
事实证明,这
是同步资源的一般
规则 :推出资源时,您只能更改或删除清单中从Git显式指定的字段(或在先前版本中已注册但现在已删除的字段)。
3路合并补丁
3-way-merge补丁程序的主要思想:考虑到工作集群中的清单的当前版本,我们在Git的清单的最后一个应用版本与Git的清单的目标版本之间生成一个补丁。 最终补丁必须符合同步资源规则:
- 使用补丁添加了添加到目标版本的新字段;
- 使用补丁重置上次应用版本中先前存在的字段,而不是目标字段中不存在的字段;
- 使用补丁更新对象的当前版本中与清单的目标版本不同的字段。
正是基于这个原理,生成了
kubectl apply
补丁:
- 清单的最后应用版本存储在对象本身的注释中,
- 目标-取自指定的YAML文件,
- 当前-来自工作群集。
现在我们已经弄清楚了理论,是时候告诉您我们在werf所做的事情了。
将更改应用于werf
之前,werf和Helm 2一样,使用了2路合并补丁。
维修补丁
为了切换到新型修补程序-三向合并-第一步,我们引入了所谓的
修复修补程序 。
部署时,将使用标准的2向合并补丁程序,但是werf还会生成一个补丁程序,该补丁程序会将资源的实际状态与Git中编写的内容进行同步(此类补丁程序是使用上述相同的同步资源规则创建的)。
如果发生同步错误,则在部署结束时,用户会收到带有适当消息和补丁的警告,必须应用这些消息和补丁才能将资源转换为同步形式。 同样,此补丁记录在特殊注释
werf.io/repair-patch
。 假定用户
本人将用双手应用此补丁:werf原则上不会应用。
生成修复补丁是一种临时措施,它使您可以根据三路合并的原则实际测试补丁的创建,但不会自动应用这些补丁。 目前,此操作模式默认为启用。
三向合并修补程序仅适用于新发行版
从2019年12月1日开始
,默认情况下将开始使用werf的beta和alpha版本使用完整的3向合并补丁程序,以仅对通过werf推出的新Helm版本应用更改。 现有版本将继续使用2向合并+修复补丁的方法。
您可以通过立即设置
WERF_THREE_WAY_MERGE_MODE=onlyNewReleases
显式启用此操作模式。
注意 :该功能出现在多个版本的werf中:在Alpha通道中已从v1.0.5-alpha.19版本开始提供该功能 ,在Beta通道中具有v1.0.4-beta.20版本 。适用于所有发行版的3向合并补丁
从2019年12月15日开始,默认情况下,werf的beta和alpha版本开始使用完整的3向合并修补程序,以对所有发行版应用更改。
可以
WERF_THREE_WAY_MERGE_MODE=enabled
设置
WERF_THREE_WAY_MERGE_MODE=enabled
现在
WERF_THREE_WAY_MERGE_MODE=enabled
来明确
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_THREE_WAY_MERGE_MODE=disabled
在werf中禁止使用新补丁。 但是,
从2020年3月1日开始
,该禁令将停止工作 ,并且只能使用3向合并补丁。
在werf中采用资源
掌握了在3向合并补丁中应用更改的方法,使我们能够立即实现诸如利用Helm版本中集群中现有资源的功能。
Helm 2有一个问题:无法将资源添加到群集中已经存在的图表清单中,而无需从头开始重新创建此资源(请参阅
#6031和
#3275 )。 我们教会werf在一个发行版中接受现有资源。 为此,您需要在正在运行的集群中的资源的当前版本上设置注释(例如,使用
kubectl edit
):
"werf.io/allow-adoption-by-release": RELEASE_NAME
现在,需要在图表上描述资源,并且在下一次部署时,使用具有相应名称的版本的werf版本进行发布,现有资源将被接受到此版本中并保持在其控制之下。 此外,在接受释放资源的过程中,werf会使用相同的三路合并补丁和同步资源规则将工作群集中资源的当前状态带到图表中描述的状态。
注意 :设置WERF_THREE_WAY_MERGE_MODE
不会影响资源的采用-在采用的情况下,始终使用3向合并补丁。详细信息在
文档中 。
结论和未来计划
我希望在本文之后,您可以更清楚地了解什么是三向合并补丁,以及为什么要使用它们。 从开发werf项目的实际角度来看,它们的实施是朝着改进类似Helm的部署的又一步。 现在您可以忘记配置同步的问题,该问题通常在使用Helm 2时发生。与此同时,添加了采用已经上传到Helm发行版的Kubernetes资源的新有用功能。
在类似Helm的部署中仍然存在一些问题和困难,例如使用Go模板,我们将继续解决它们。
有关资源更新方法和采用方法的信息也可以在
此文档页面上找到。
头盔3
特别值得一提的是最近发布的新的Helm主版本-v3,该版本还使用了3向合并补丁并摆脱了Tiller。 新版本的Helm需要
迁移现有安装,以便将其转换为新的发行存储格式。
就Werf而言,现在已经取消了使用Tiller的方式,切换为3路合并,并增加了
更多功能 ,同时仍与Helm 2上的现有安装兼容(无需迁移脚本)。 因此,在将werf切换到Helm 3之前,werf用户不会失去Helm 3相对于Helm 2的主要优势(它们也存在于werf中)。
但是,将werf切换到Helm 3代码库是不可避免的,并且将在不久的将来发生。 大概是werf 1.1或werf 1.2(目前,werf的主版本是1.0;有关werf版本控制设备的更多详细信息,请参见)。 在此期间,头盔3将有时间稳定下来。
聚苯乙烯
另请参阅我们的博客: