yuff100

V1

2022/04/16阅读:27主题:极简黑

GitOps的12个痛点

如今很多团队采用GitOps作为标准部署流程,这篇文章总结了GitOps的12个痛点,从而帮助我们在采用这一实践的过程中更好的理解GitOps的优势和缺陷,选择适合自己的解决方案。原文:The pains of GitOps 1.0[1]

GitOps作为软件发布实践有很多优点,但就像其他解决方案一样,它也有缺点,当我们从采用GitOps的狂热中冷静下来后,也需要讨论一下GitOps的问题了(以及现代GitOps工具)。

在本文中,我们将讨论GitOps的12个痛点:

  1. GitOps只覆盖软件生命周期的一个子集
  2. 用GitOps分割CI和CD并不容易
  3. GitOps无法将发布部署到不同的环境中
  4. 对于多环境配置建模没有标准实践
  5. 自动扩容和动态资源管理破坏GitOps
  6. 对于回滚没有最佳实践
  7. GitOps(和Git)的可观察性不成熟
  8. 尽管所有信息都在Git中,审计还是有问题
  9. 大规模运维GitOps很困难
  10. GitOps和Helm并不总是能够很好的合作
  11. 持续部署和GitOps不能混为一谈
  12. 没有管理密钥的标准方案

GitOps只覆盖软件生命周期的一个子集

这是当前GitOps工具的主要运作模式,尽管GitOps(方法论)有一些有趣的特点和卖点,但当前的GitOps工具只关注应用程序的部署部分,其他什么都没有。这些工具解决了“我想把Git中描述的东西放到集群中”的问题,但是软件开发的所有其他方面都没有涉及到:

GitOps覆盖范围
GitOps覆盖范围

之所以提到这一点,是因为GitOps工具有时被宣传为万能解决方案,可以解决所有的发行问题,但这是不对的。首先,GitOps要求所有部署构件已经存在,这意味着……

  • 编译代码
  • 运行单元/集成测试
  • 安全扫描
  • 静态分析

……并不是GitOps工具所关心的问题,而是被认为都已经准备好了。

甚至部署问题(例如环境升级、密钥管理、冒烟测试等)也被轻易排除在GitOps范式之外,采用GitOps的团队需要为软件交付的所有方面创建自己的最佳实践。

因此,不能简单“采用GitOps解决方案”并就此结束。GitOps只是整个发展战略的一部分,应该确保所有其他流程和工作流程已经准备好与GitOps合作。

用GitOps分割CI和CD并不容易

GitOps被认为是一种将CI与部署分离的方法。在CI/CD系统的经典应用中,流程中的最后一步是部署。

经典流水线
经典流水线

使用GitOps,可以维持纯粹的CI流程,只要将候选版本通过Git提交,而不需要部署。Git提交触发部署解决方案,该方案监听Git存储库,并通过在集群中拉取变更来处理实际的部署(从而使集群状态与Git状态一致)。

该场景在理论上很完美,也适用于简单的场景,但当涉及到大型组织采用的高级部署时,很快就会崩溃。

混合CI和CD的典型例子是冒烟测试,有时候我们希望在部署完成后运行冒烟测试,并根据测试的结果决定是否回滚变更。

冒烟测试
冒烟测试

正如前面所说,GitOps只处理部署构件,通常不涉及(或不了解)源代码,但是在大多数情况下,为了运行单元测试,我们需要访问应用程序的源代码。

当前的GitOps工具不能运行单元/集成测试,因为这要求能够看到源代码以及测试所需的所有框架和库。这意味着,为了运行冒烟测试,不得不再次回到CI解决方案。

最终的结果是CI-CD-CI-CD组件的混合,这违背了GitOps的主要精神。当然还有一些潜在问题,比如不知道环境何时完成部署以触发测试。

基于传统的CI/CD流水线执行相同的场景会很简单。

GitOps无法将发布部署到不同的环境中

这可能是关于GitOps讨论最多的问题之一,也是当谈到GitOps如何在大型组织中工作时最先讨论的话题之一。

环境升级
环境升级

考虑一个常见场景:在Git中合并(或创建提交),环境X的集群现在部署了新版本,但是如何将这个版本部署到环境Y中呢?

每当有人宣称采用GitOps很简单时,我总会问他们在不同环境之间的部署是如何工作的,但总是得到不同的答案:

  1. “我们用CI系统来做到这一点。”这意味着再次将CI和CD混在一起,并且承认GitOps无法覆盖这一场景。
  2. “我们为其他环境开一个新的pull request。”这意味着必须为每个环境使用不同的Git分支(稍后会详细介绍),并且还需要引入更多的手动步骤,以便部署发布。
  3. “我们只有一个环境。”这可能适合小公司,但在其他情况下不可行。

很令人失望,就连专门为解决GitOps问题而创建的页面[2]都说:

“GitOps并没有提供一个将变更从一个阶段传播到下一个阶段的解决方案。我们建议只使用一个环境,并避免多阶段传播。”

处理不同环境的最流行的方法似乎是使用不同的Git分支,但这个解决方案有几个缺点:

  • 人们有可能会在特定分支上提交特定于某个环境的代码。
  • 项目有可能会耦合到特定的环境(而不是通用的)。
  • 这给CI系统带来了不必要的压力,它必须检查/重建/单元测试每个独立的分支。

此外,如果有很多环境,多分支处理可能会很快失控。

对于多环境配置建模没有标准实践

上一个问题的结论是,如果我们需要多环境部署,那么GitOps在没办法帮助我们。事实上,它会迫使我们采用特定的Git分支模式(每个环境用特定分支),从而让事情变得更糟。

一个典型例子是,每个地理区域(每个大陆或每个国家)都有不同的环境。

假设我们的应用部署在10个国家,我们希望一个国家一个国家的推广版本,GitOps有什么解决方案?

  • 一个有10个分支的存储库,这意味着需要在每次发布时开10个pull request。
  • 10个Git存储库,这意味着需要编写自己的解决方案,在存储库之间复制提交(或者使用Pull Requests)。
  • 一个包含10个子文件夹的Git存储库,同样需要外部解决方案来确保更改同步到所有文件夹。

不管哪种方案,发布过程都非常繁琐,目前的GitOps工具对于哪种才是标准方案还没有给出答案[3]

自动扩容和动态资源管理破坏GitOps

在部署完成后,集群状态与Git存储库中描述的完全相同,这是GitOps的关键点。

在大部分简单的情况下,这没有问题,但一旦我们引入了变量,GitOps工具就会出现冲突,典型例子如下:

  • 配置了autoscaler的集群的副本个数
  • 配置了optimizer的集群的资源限制
  • 外部工具添加的额外属性(特别是带有日期或时间戳的值)

一旦集群状态发生变化,GitOps工具就会尝试从Git同步初始值,但在大多数情况下这违背了我们的期望。

状态差异
状态差异

Argo支持自定义差异[4],Flux也有相关建议[5],但这些变通方法只是简单的hack了GitOps,背离了GitOps的主要承诺,从长期看会产生其他一些问题[6]

对于回滚没有最佳实践

集群历史的所有记录都在Git中,这使得在GitOps中回滚(据说)非常容易。如果想回滚到以前的版本,只需要对以前的某个提交应用同步操作就行了。

然而在实践中,“使用以前的提交”的确切含义并不明确,因此不同的人会使用不同的回滚方式:

  1. 简单的将集群指向一个以前的Git哈希值,然后让GitOps工具同步这个哈希值。这是最快的回滚方式,但根据定义,由于集群不包含上次Git提交中所描述的内容,因此会使集群处于不一致的状态。
  2. 使用标准的Git reset, Git revert命令,然后让GitOps工具像往常一样执行同步操作。这保证了GitOps的承诺(在集群中拥有Git repo中的内容),当然,这需要手工干预。
  3. 可以组合采用上面2种方式,GitOps工具将之前的Git哈希同步到集群,并自动提交(或还原)到Git repo,以保持一致性。这非常复杂,并且不是所有团队都想让部署工具对Git repo有写权限。

不用说,不同的人可能希望使用完全不同的回滚方案。然而,在撰写本文时,目前的GitOps工具对于如何以标准方式执行回滚只有很少的支持和指导。

GitOps(和Git)的可观察性不成熟

GitOps非常适合查看集群状态,并保证与Git状态相匹配。然而,Git的哈希和提交只对开发和运维人员有用,业务相关方对集群中部署了什么Git哈希不感兴趣。

因此,尽管GitOps在技术层面上的可观察性很好,但更重要的是要记住,在软件团队中有一些更有用的问题:

  • 生产环境是否包含特性X?
  • 特性X是否清除了预发环境?
  • bug X、Y只出现在预发环境还是也出现在生产环境?

对于大多数产品所有者和项目经理来说,这类问题非常重要,应该尽快找到答案。

目前,GitOps工具工作在最低级别(即Git哈希值),并且与每个部署的业务价值没有任何关联,开发/运维人员需要找到产品部署和业务价值之间的关联。

在目前的状态下,GitOps工具在技术层面上非常适合观察集群的内容,但是在监控每个部署的业务指标方面却很糟糕。

尽管所有信息都在Git中,审计还是有问题

上一点的推论是,仅仅因为可以以Git提交的形式访问集群的整个部署历史,并不意味着可以轻松审计它的功能。

当前的Git工具非常适合管理Git哈希,但是当涉及到搜索和理解业务价值时,正如Adam在Lack of Visibility[7]中提到的那样,只能提供简单的自由文本搜索功能。

假设我们正在某个特定项目中使用GitOps,并且知道Git历史记录与集群历史记录相匹配,仅仅通过查看Git History,可以多快回答以下问题?

  • 特性X在投入生产之前在预发环境中停留了多长时间?
  • 前两个月的最差、最好和平均交付时间(从开发人员执行提交到实际投入生产的时间)是多少?
  • 对环境X的部署成功百分比是多少,需要回滚的百分比是多少?
  • 有多少特性在环境X中存在但在环境Y中还没有?

这些问题在大型软件团队中很常见,除非在部署平台上有专门的工具,否则仅通过访问Git存储库及其历史来回答这些问题是非常困难的。

大规模运维GitOps很困难

Adam在“Git库的扩展”[7]一文中也提到了这一点。如果在拥有大量环境和应用的大公司中采用GitOps,那么Git存储库的数量会迅速飙升。

这使得跟踪每个环境中正在发生的事情变得非常困难,并且可能很快导致配置冗余或者人们不得不向特定环境提交(而不是使用共享配置)。

例如,如果我们有20个存有Kubernetes manifests的git仓库,当我们需要完成一个重要变更(例如,在每个部署中添加一个新的全公司范围的标签),需要手动完成20个git提交,或者创建一些胶水代码来帮我们完成。

另一方面,可以为所有环境(或集群)提供一个Git存储库,在这些环境中,所有人都与CI/CD系统协作。

但正如Adam所解释的那样,这就产生了Git冲突的问题(Git存储库和很多CI流程交互,由于Git仓库在这一过程中发生了变更,造成推送失败)。

拥有一个巨大的Git存储库会很快成为GitOps流程的瓶颈,这会在扫描存储库进行更改时引入性能问题。

GitOps和Helm并不总是能够很好的合作

Helm是Kubernetes的包管理器,通常被视为在集群中部署第三方应用程序的事实标准,当然也可以用于自己的部署。

Helm的工作方式是将Kubernetes manifests的一组模板及其运行时值结合起来,这些模板被合并从而最终完成集群的部署。

当Helm安装一个发布时,我们可以提供values.yml,最好的做法是将values文件提交给Git,可以为每个环境(例如qa/预发/生产)提供不同的values。

Helm本身并没有规定这3个组件(源代码、manifests、values)应该保存在哪里。可以将它们全部保存在同一个Git存储库中,也可以将它们保存在三个不同的存储库中。然而,普遍接受的做法是,如果我们有不同环境的values,可以将它们与模板保存在不同的位置,模板(chart本身)存储在chart存储库中,每个环境的特定values存储在git存储库中。

这意味着在部署过程中必须完成以下步骤:

  1. 从chart存储库中下载chart
  2. 从Git存储库中获取values
  3. values和chart合并,从而创建一组Kubernetes manifests
  4. 在集群上应用这组manifests

这仅用于初始部署。按照GitOps范例,应该监视包含values文件的Git存储库以及chart存储库中的chart。如果它们的合并结果与集群状态不同,则应该进行新的部署。

在撰写本文时,Flux[8]和ArgoCD都不支持这个基本的Helm场景。如果希望采用基于Helm chart的GitOps,那么必须采用一些变通方法和限制,从而使这一过程变得比需要的复杂得多。

持续部署和GitOps不能混为一谈

GitOps是一个很棒的持续交付解决方案,每个提交都会产生一个发布候选版本,并将其推向生产环境。另一方面,持续部署是直接提交到生产的整个过程,无需任何人工干预[10]

如果作为开发人员,可以在周五下午提交变更,然后立即开始享受周末,那么就是在实践持续部署,提交的变更将在几分钟内通过所有质量检查和测试后投入生产。

根据定义,GitOps是由包含manifests的仓库上的Pull Request驱动的。在大多数情况下,需要人工审查和批准这些Pull Request,这意味着实践GitOps至少需要一些手动步骤来处理这些Pull Request。

理论上,人们可以通过完全自动化的Pull Request来实现持续部署,同时仍然采用GitOps。然而,当前的GitOps(和Git)工具并没有考虑到这种自动化,所以如果你想走这条路,就只能靠自己了。

记住,持续部署并不是圣杯。对于一些组织来说,持续交付已经足够了,在某些情况下,甚至可能受到法律的约束,必须明确禁止完全自动化的部署。

但是如果想要更快的交付时间,GitOps并不是最好的解决方案。

没有管理密钥的标准方案

这是GitOps的一个众所周知的问题,所以为了表述完整,我也把它包括进来。密钥处理是软件部署最重要的方面之一,然而,GitOps并没有解决这一问题。

关于如何管理密钥,目前还没有公认的做法。如果将密钥存储在Git中,则需要进行加密,并且在部署过程中采用自己的工作流。如果不存储在Git中,那么让集群状态与Git相同的想法就不再正确了。

每个公司的密钥管理都有自己的解决方案,所以我希望GitOps工具能够提供一个开箱即用的解决方案。

References:
[1] The pains of GitOps 1.0: https://codefresh.io/about-gitops/pains-gitops-1-0/
[2] GitOps: https://www.gitops.tech/
[3] Document best practises on working with multiple environments: https://github.com/fluxcd/flux/issues/1071
[4] Diffing Customization: https://argoproj.github.io/argo-cd/user-guide/diffing/
[5] How can I prevent flux overriding the replicas when using HPA: https://docs.fluxcd.io/en/1.17.0/faq.html#how-can-i-prevent-flux-overriding-the-replicas-when-using-hpa
[6] Maintain difference in cluster and git values for specific fields: https://github.com/argoproj/argo-cd/issues/2913
[7] Lack of Visibility: https://blog.container-solutions.com/gitops-the-bad-and-the-ugly
[8] Flux helm repository chart updates: https://docs.fluxcd.io/projects/helm-operator/en/latest/helmrelease-guide/automation/#helm-repository-chart-updates
[9] Helm chart + values files from Git: https://github.com/argoproj/argo-cd/issues/2789
[10] What the Heck Is Continuous Integration (CI), Delivery (CD), and Deployment (CDP)? https://codefresh.io/continuous-deployment/heck-continuous-integration-ci-delivery-cd-deployment-cdp/

你好,我是俞凡,在Motorola做过研发,现在在Mavenir做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI等技术始终保持着浓厚的兴趣,平时喜欢阅读、思考,相信持续学习、终身成长,欢迎一起交流学习。
微信公众号:DeepNoMind

- END -

分类:

后端

标签:

运维部署

作者介绍

yuff100
V1

俞凡,公众号DeepNoMind