大型组织利用 GitOps 难免会遇到在多环境中部署的问题,本文剖析了应用环境分支策略会遇到到问题,介绍了利用文件夹策略解决这些问题的计划。原文:Stop Using Branches for Deploying to Different GitOps Environments[1], How to Model Your Gitops Environments and Promote Releases between Them[2]
在对于 GitOps 问题的指南中,咱们简要解释了 (参见第 3 和第 4 点) 以后 GitOps 工具在反对不同环境部署以及多集群配置建模时的问题。
“如何将公布部署到下一个环境?”的问题在心愿采纳 GitOps 的组织中越来越受到重视[4],并且有几种可能的答案。但在这篇文章中,咱们将重点探讨在这一过程中不应该做什么。
咱们不应该应用 Git 分支来建模不同的环境。如果保留配置的 Git 存储库 (在 Kubernetes 的例子中是 manifests/templates) 有名为“预发”、“QA”、“生产”等分支,那就掉进了陷阱。
重要的事件说三遍:\
应用 Git 分支来建模不同的环境是一种反模式,不要这样做!\
应用 Git 分支来建模不同的环境是一种反模式,不要这样做!\
应用 Git 分支来建模不同的环境是一种反模式,不要这样做!
咱们将从以下几点探讨为什么这个实际是反模式:
- 在部署环境中应用不同的 Git 分支是过来的遗留问题。
- 不同分支之间的 pull request 和合并是有问题的。
- 人们偏向于蕴含特定于环境的代码并创立不同的配置。
- 一旦环境数量增多,环境的保护就会变得难以管制。
- 每个环境的分支模型违反了现有的 Kubernetes 生态系统。
在不同环境中采纳分支应该只利用于遗留应用程序。
当问到为什么抉择 Git 分支来建模不同的环境时,答复简直总是“咱们始终都是这样做的”,“感觉很天然”,“这是开发人员晓得的”等等。
这没有错,大多数人都相熟在不同环境中应用分支。这一实际是由古老的 Git-Flow 模型 [3] 大力推广的。但自从引入这种模式以来,状况产生了很大的变动,甚至最后的作者也从宏观角度收回了重大正告,倡议人们不要在不理解结果的状况下采纳这种模式。
事实上,Git-flow 模型……
- 专一于应用程序源代码,而不是环境配置(更不用说 Kubernetes manifest 了)。
- 如果须要在生产环境中反对多个利用版本,这一模型很适合,通常没有这种场景,但也时有发生。
因为本文是对于 GitOps 环境而不是应用程序源代码的,因而不打算在这里过多探讨 Git-flow 及其毛病,总而言之,如果须要为不同的环境反对不同的个性,那么应该遵循基于骨干的开发 [5] 并应用个性标记[6]。
在 GitOps 上下文中,应用程序源代码和配置也应该在不同的 Git 存储库中(一个存储库只有利用程序代码,一个存储库有 Kubernetes manifests/templates)。这意味着应用程序源代码分支不应该影响环境存储库中的分支。
当咱们在我的项目中采纳 GitOps 时,应用程序开发人员能够为源代码抉择想要的任何分支策略 (甚至应用 Git-flow),然而环境配置 Git 存储库(蕴含所有 Kubernetes manifests/templates) 不应该遵循每个环境一个分支的模型。
部署降级绝不是简略的 Git 合并
既然咱们曾经理解了在部署中应用按环境辨别分支的办法的历史,就能够探讨其毛病了。
这种办法的次要长处是“部署降级是一个简略的 git 合并”。实践上,如果想要将一个版本从 QA 环境降级部署到预发环境,只需将 QA 分支合并到预发分支即可。当咱们筹备好生产环境时,再次将预发分支合并到生产分支,就能够确定来自预发的所有变更曾经部署到了生产环境中。
想晓得生产环境和预发环境之间有什么不同吗?只须要在两个分支之间做一个规范的 git diff[7]就能够了。想要将配置变更从预发环境反向移植到 QA 环境?从预发分支到 QA 分支的一个简略的 Git 合并就能够做到这一点。
如果想对部署降级施加额定的限度,能够应用 Pull Requests。一方面任何人都能够触发从 QA 到预发的合并,另一方面如果想在生产分支中合入一些货色,能够触发 Pull Request 并要求所有利益相关者手动批准。
这在实践上听起来很棒,一些琐碎的场景实际上能够像这样工作。但在实践中,状况并非如此。通过 Git 合并来降级一个版本可能会遇到合并抵触、引入不想要的变更,甚至触发谬误的变更程序。
上面咱们以 Kubernetes 部署为例看一个简略的例子,以后部署位于预发分支中:
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-deployment
spec:
replicas: 15
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: backend
image: my-app:2.2
ports:
- containerPort: 80
QA 团队曾经告诉咱们说版本 2.3(位于 QA 分支中)看起来曾经筹备好了,能够转移到交付阶段。咱们将 QA 分支合并到预发分支,部署应用程序,并认为所有都很好。
但咱们不晓得,因为某些资源限度,有人将 QA 分支中的正本数量更改为 2。应用 Git 合并,不仅将 2.3 部署到了预发环境,而且还将正本改成了 2 个(而不是 15 个),这可能并不是咱们想要的。
你可能会说,在合并之前查看正本个数很容易,但请记住,在理论场景中,有大量的应用程序,其中有大量的 manifests 被模板化(通过 Helm 或 Kustomize)。因而,了解想要带来什么变动,留下什么变动并不是一件小事件。
即便咱们的确发现了不应该被合并的变更,也须要应用 git cherry-pick[8]或其余非标准办法手动抉择“好的”局部,这与最后的“简略的”git 合并相去甚远。
然而,即便咱们晓得了所有能够合并的变更,也会呈现合并的程序与提交的程序不同的状况。例如,QA 环境上有以下 4 个更改。
- 更新了利用 ingress[9]的主机名。
- 版本 2.5 被部署到 QA 环境,所有 QA 人员开始测试。
- 在 2.5 版本中发现了一个问题,并修复了 Kubernetes 的 configmap。
- 资源限度 [10] 进行了微调,并提交到 QA 分支。
而后咱们决定 ingress 设置和资源限度应该部署到下一个环境(预发),然而 QA 团队还没有实现 2.5 版本的测试。
如果咱们自觉的将 QA 分支合并到预发分支,就将同时合并所有 4 个变更,包含 2.5 的降级。
为了解决这个问题,须要再次应用 git cherry-pick 或其余手动办法。
在更简单的状况下,提交之间存在依赖关系,因而即便是 cherry-pick 也帮不上忙。
在下面的示例中,版本 1.24 必须部署到生产环境。问题是其中一个提交 (hotfix) 蕴含了大量的变更,而其中某些变更又依赖于另一个提交(ingress 配置变更),而后者自身无奈部署到生产环境(因为只实用于预发环境)。因而,即便是精心筛选,也不可能只将所需的变更从筹备阶段引入到生产阶段。
最终的后果是,部署降级绝不是简略的 Git 合并。大多数组织还领有大量利用,这些利用位于大量集群中,由大量 manifests 组成,手动抉择变更将是一场失败的战斗。
特定于环境的变更更容易造成配置漂移
实践上,配置漂移不应该成为 Git 合并的问题。如果在预发环境中进行了变更,而后将该分支合并到生产环境,那么所有变更都应该迁徙到新环境中。
然而在实践中,事件是不一样的,因为大多数组织只向一个方向合并,团队成员很容易扭转上游环境,而从不将这些扭转迁徙到上游环境。
在 QA、预发和生产三个环境的经典例子中,Git 合并的方向只有一个。人们将 QA 分支合并到预发,将预发分支合并到生产,这意味着变动只会向上流动。
QA -> 预发(Staging)-> 生产(Production).
典型场景是,在生产环境中须要对配置进行疾速变更(一个 hotfix),而后有人部署了该修复程序。在 Kubernetes 的状况下,这个修补程序能够是任何货色,比方对现有 manifest 的更改,甚至是一个全新的 manifest。
当初生产环境有了一个与预发齐全不同的配置。下次一个版本从长期版本升级到生产版本时,Git 只会告诉咱们将从长期版本升级到生产版本。生产上的长期变更永远不会呈现在 Pull Request 中的任何中央。
因为当初生产中有一个没有文档化的变更,这意味着所有后续部署都可能失败,而这个变更永远不会被任何后续降级检测到。
实践上,咱们能够反向迁徙这些变更,并周期性的将所有提交从生产阶段合并到交付阶段(以及交付阶段合并到 QA 阶段)。实际上,因为后面提到的起因,这种状况从未产生过。
能够设想,如果有很多环境,就会进一步放大这个问题。
总而言之,通过 Git 合并来部署公布版本并不能解决配置漂移问题,而且实际上团队会试图做出一些不按程序合并的非凡变更,因而会使问题更加重大。
在大量环境中治理不同的 Git 分支是一场注定失败的战斗
在后面的所有示例中,我只应用了 3 个环境 (QA 环境 -> 预发环境 -> 生产环境) 来阐明基于分支的环境部署的毛病。
依据组织的大小,兴许有更多的环境,如果思考地理位置等其余因素,那么环境的数量就会迅速减少。
咱们以某个公司为例,它有 5 个工作环境:
- 负载测试
- 集成测试
- QA
- 预发
- 生产
咱们假如最初 3 个环境也部署在欧洲、美国和亚洲,而前 2 个环境也有 GPU 和非 GPU 变体,这意味着该公司共有 13 个环境,而这只是针对单个利用的。
如果应用基于分支的办法:
- 在任何时候都须要有 13 个长期 Git 分支。
- 须要 13 个 pull requests 能力跨所有环境部署一个变更。
- 有一个二维的部署降级矩阵,纵向 5 步,横向 2 - 3 步。
- 谬误合并、配置漂移和特地变更的可能性在所有环境组合中都有可能呈现。
在这个示例组织的上下文中,所有以前的问题当初都更加广泛了。
branch-per-environment 模型与 Helm/Kustomize 南辕北辙
形容应用程序的两个最风行的 Kubernetes 工具是 Helm 和 Kustomize,咱们看看这两种工具如何对不同环境进行建模。
对于 Helm,须要创立一个通用 chart,该 chart 自身承受 values.yaml 模式的参数,如果心愿领有不同的环境,则须要多个 values 文件[11]。
对于 Kustomize,须要创立一个“base”配置,而后每个环境被建模为一个 overlay,有本人的文件夹:
在这两种状况下,不同的环境应用不同的文件夹 / 文件进行建模。Helm 和 Kustomize 对 Git 分支、Git merge 或 Pull Requests 无所不知,只应用一般文件。
再反复一遍:Helm 和 Kustomize 在不同的环境下应用一般文件,而不是 Git 分支。这是一个很好的提醒,阐明如何应用这两种工具建模不同的 Kubernetes 配置。
如果引入 Git 分支,不仅会引入额定的复杂性,还会违反本人的工具。
在 GitOps 环境中部署公布的举荐办法
建模不同的 Kubernetes 环境,并在环境之间部署公布,对于所有采纳 GitOps 的团队来说都是十分广泛的问题。只管十分风行的办法是在每个环境中应用 Git 分支,并假如每次部署都是一个“简略的”Git 合并,但在本文中曾经看到,这是一个反模式。
上面咱们将介绍一种更好的办法来为不同的环境建模,从而在不同的 Kubernetes 集群上部署公布,之前的介绍 (对于 Helm/Kustomize) 应该曾经给了你一点对于这种计划的提醒。
上面我会解释如何在同一个 Git 分支上应用不同的文件夹对 GitOps 环境进行建模,以及如何通过简略的文件复制操作来解决环境降级(简略的和简单的)。
首先理解应用程序
在创立文件夹构造之前,须要先做一些钻研,理解应用程序的“设置”。只管一些人以通用的形式探讨应用程序配置,但实际上并不是所有的配置设置都同样重要。
在 Kubernetes 利用的上下文中,咱们有以下几类“环境配置”:
- 容器 tag 模式的 利用版本。这可能是 Kubernetes manifest 中最重要的设置(就环境降级而言)。依据不同的用例,只需更改容器镜像版本即可。不过有可能源代码中的新变更也须要更改部署环境。
- 利用相干的Kubernetes 特定配置。包含利用的正本和其余 Kubernetes 相干信息,如资源限度、运行状况查看、长久卷、亲和性规定等。
- 根本 动态业务配置。这是一组与 Kubernetes 无关的设置,但与利用业务无关。可能是内部 url、外部队列大小、UI 默认值、身份验证配置文件等。所谓“根本动态”,我指的是为每个环境定义一次的设置,而后永远不会更改。例如,咱们总是心愿生产环境应用 production.paypal.com,而非生产环境应用 staging.paypal.com。在不同的环境中,这是一个咱们永远不心愿迁徙的设置。
- 非动态业务配置。和上一点一样,但蕴含了心愿在不同环境之间迁徙的设置,能够是寰球 VAT 设置、举荐引擎参数、可用的比特率编码,以及任何其余特定于业务的配置。
必须理解所有不同的设置是什么,更重要的是,哪些属于第 4 类,因为这些是咱们心愿随应用程序版本一起推广的设置。
这样就能够笼罩所有可能的部署场景:
- 利用在 QA 中从版本 1.34 降级到 1.35,这是一个简略的源代码变更,因而只须要在 QA 环境中更改容器镜像属性。
- 利用在预发环境中从版本 3.23 降级到 3.24,这不是一个简略的源代码变更,岂但须要更新容器镜像属性,而且从 QA 环境带来了新的设置“recommender.batch_size”。
我看到很多团队不了解不同配置参数之间的区别,而只应用一个配置文件 (或机制) 来设置不同域的值(即运行时和利用业务配置)。
有了配置列表以及所属区域之后,就能够创立环境构造并优化须要常常变更并且须要在不同环境之间迁徙的文件复制操作。
5 个 GitOps 环境及其变更示例
咱们来看一个理论的例子。
咱们将对之前提到的环境进行建模,该公司有 5 个不同的环境:
- 负载测试
- 集成测试
- QA
- 预发
- 生产
咱们假如最初两个环境也部署在欧洲、美国和亚洲,而前两个环境也有 GPU 和非 GPU 变体,这意味着该公司共有 11 个环境。
能够在 https://github.com/kostis-codefresh/gitops-environment-promotion 找到倡议的文件夹构造,所有环境都是同一分支中的不同文件夹,对于不同的环境没有分支。如果想晓得在一个环境中部署了什么,只需查看 repo 的主分支中的 envs/。
在解释构造之前,有一些免责申明:
免责申明 1: 写这篇文章花了我很长时间,因为不确定应该探讨 Kustomize[12]、Helm[13]还是一般的 manifests。我抉择了 Kustomize,因为它更简略(在文章的最初我也提到了 Helm)。然而请留神,示例 repo 中的 Kustomize 模板只是为了演示目标。本文不是 Kustomize 教程。在理论利用中,你可能有 Configmap 生成器[14]、定制补丁[15],并采纳和这里展现的齐全不同的“组件”构造。如果你不相熟 Kustomize,请先花些工夫了解它的性能,而后再回来。
免责申明 2: 我用于部署的利用 [16] 齐全只是为了演示,它的配置因为简洁和简略的起因而疏忽了几个最佳实际。例如,某些部署短少运行状况查看[17],所有部署都短少资源限度[18]。同样,本文不会探讨如何创立 Kubernetes 部署,你应该曾经晓得正确的部署 manifests 是什么样子的。如果想理解更多对于生产级最佳实际的信息,请参阅另一篇文章 https://codefresh.io/kubernetes-tutorial/kubernetes-antipatte…
抛开免责申明不说,上面是存储库的构造:
base 目录保留对所有环境通用的配置,不会常常扭转。如果同时对多个环境进行更改,最好应用“variants”文件夹。
variants 文件夹 (或者叫 mixins、components) 保留不同环境之间的独特特色。在钻研上一节探讨的应用程序之后,能够自行定义你认为的环境之间的“共同之处”。
在示例利用中,咱们为所有 prod 和非 prod 环境以及地区提供了 variants。上面是一个实用于所有生产环境的 prod variant[19]示例。
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: simple-deployment
spec:
template:
spec:
containers:
- name: webserver-simple
env:
- name: ENV_TYPE
value: "production"
- name: PAYPAL_URL
value: "production.paypal.com"
- name: DB_USER
value: "prod_username"
- name: DB_PASSWORD
value: "prod_password"
livenessProbe:
httpGet:
path: /health
port: 8080
在下面的示例中,咱们确保所有生产环境都应用了生产 DB 凭证、生产领取网关和流动探针(这是一个精心设计的示例,请参阅本节结尾的免责申明 2)。这些设置属于咱们不心愿在不同环境之间迁徙的配置集,咱们假如在整个利用生命周期中这些都是动态的。
筹备好 base 和 variants 之后,能够用这些属性的组合来定义每个最终环境。
上面是一个 ASIA 环境的示例[20]:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: staging
namePrefix: staging-asia-
resources:
- ../../base
components:
- ../../variants/non-prod
- ../../variants/asia
patchesStrategicMerge:
- deployment.yml
- version.yml
- replicas.yml
- settings.yml
首先定义一些公共属性,从 base 环境、非 prod 环境和 asia 的所有环境中继承所有配置。
这里的关键点是咱们利用的补丁。version.yml[21]和 replicas.yml[22]是自解释的,只定义本人的镜像和正本,其余什么都没有。
version.yml 文件 (这是环境间最重要的货色) 只定义了利用的镜像,其余什么都没有。
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: simple-deployment
spec:
template:
spec:
containers:
- name: webserver-simple
image: docker.io/kostiscodefresh/simple-env-app:2.0
咱们心愿在不同环境之间部署的每个版本的相干设置也在 settings.yml[23]中定义。
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: simple-deployment
spec:
template:
spec:
containers:
- name: webserver-simple
env:
- name: UI_THEME
value: "dark"
- name: CACHE_SIZE
value: "1024kb"
- name: PAGE_LIMIT
value: "25"
- name: SORTING
value: "ascending"
- name: N_BUCKETS
value: "42"
请随便查看整个存储库[16],以了解所有 kustomizations 的结构形式。
通过 GitOps 执行初始部署
要将应用程序部署到相干的环境中,只需将 GitOps 控制器指向相应的“env”文件夹,kustomize 将创立残缺的 settings 和 values 层次结构。
上面是在 Staging/Asia 中运行的示例应用程序[16]。
能够在命令行上应用 Kustomize 预览将为每个环境部署的内容,例如:
kustomize build envs/staging-asia
kustomize build envs/qa
kustomize build envs/integration-gpu
当然,也能够将上述命令的输入通过管道输入到 kubectl 来部署每个环境,但在 GitOps 的上下文中,应该始终让 GitOps 控制器部署环境,防止手动 kubectl 操作。
比拟两个环境的配置
对于软件团队来说,一个十分广泛的需要是了解两个环境之间的不同之处。我看到一些团队有这样的误会,他们认为只有应用分支能力很容易的发现不同环境之间的差别。
这与事实相去甚远。通过比拟文件和文件夹,能够很容易的应用成熟的文件 diff 工具来查找环境之间的不同之处。
最简略的办法是只辨别对应用程序至关重要的设置。
vimdiff envs/integration-gpu/settings.yml envs/integration-non-gpu/settings.yml
在 kustomize 的帮忙下,还能够比拟任意数量的环境,获取整体的概念:
kustomize build envs/qa/> /tmp/qa.yml
kustomize build envs/staging-us/ > /tmp/staging-us.yml
kustomize build envs/prod-us/ > /tmp/prod-us.yml
vimdiff /tmp/staging-us.yml /tmp/qa.yml /tmp/prod-us.yml
集体认为这种形式和在环境分支之间执行“git diff”没有什么差异。
如何在 GitOps 环境中进行部署降级
当初文件构造曾经很分明了,终于能够答复这个古老的问题:“我如何用 GitOps 部署公布”?
让咱们看看上面一些部署场景。如果你有关注文件构造,应该曾经理解到所有部署降级都能够解析为简略的文件复制操作。
场景: 在美国将利用版本从 QA 降级到预发环境:
- cp envs/qa/version.yml envs/staging-us/version.yml
- commit/push 变更
场景: 从 GPU 集成测试到 GPU 负载测试,再到 QA 的利用版本升级。这是一个两步的过程:
- cp envs/integration-gpu/version.yml envs/load-gpu/version.yml
- commit/push 变更
- cp envs/load-gpu/version.yml envs/qa/version.yml
- commit/push 变更
场景: 通过额定配置,将应用程序从 prod-eu 降级到 prod-us。这里咱们还复制了 settings 文件。
- cp envs/prod-eu/version.yml envs/prod-us/version.yml
- cp envs/prod-eu/settings.yml envs/prod-us/settings.yml
- commit/push 变更
场景: 确保 QA 领有与 staging-asia 雷同的正本数量
- cp envs/staging-asia/replicas.yml envs/qa/replicas.yml
- commit/push 变更
场景: 从 QA 到移植所有配置到集成测试(非 gpu 版本)
- cp envs/qa/settings.yml envs/integration-non-gpu/settings.yml
- commit/push 变更
场景: 一次性对所有非 prod 环境进行全局更改(但请参阅下一节,以理解对于此操作的一些探讨)
- 在 variants/non-prod/non-prod.yml 中做出变更
- commit/push 变更
场景: 向所有美国环境 (包含生产环境和预发环境) 增加新的配置文件。
- 在 variants/us 文件夹中增加新的 manifest
- 批改 variants/us/kustomization.yml 引入新的 manifest
- commit/push 变更
一般来说,所有的部署降级只是复制操作。与 branch-per-environment 办法不同,当初能够自在的将任何货色从任何环境推广到其余环境,不用放心进行谬误的变更。特地是当波及到反向移植配置时,environment-per-folder 的确很杰出,因为能够简略地“向上”或“向后”挪动配置,甚至能够在不相干的环境之间挪动配置。
留神,我应用 cp 操作只是为了演示。在理论的应用程序中,此操作将由 CI 零碎或其余编排工具主动执行。依据环境的不同,你可能想先创立一个 Pull Request,而不是间接在主分支中编辑文件夹。
一次对多个环境进行更改
首先,咱们须要定义“多重”环境的确切含意,假如以下两种状况。
- 同时更改同一“级别”上的多个环境。例如,想要同时变更“prod-us”、“prod-eu”和“prod-asia”。
- 同时更改不在同一级别上的多个环境。例如,想同时更改“integration”和“staging-eu”。
第一种状况是无效场景,咱们将在上面探讨。然而,我认为第二个场景是反模式,领有不同环境的关键在于可能以一种渐进的形式公布内容,并推动从一个环境到下一个环境的变动。因而,如果你发现自己在不同的环境中部署了雷同的变动,问问本人是否真的须要这样做以及为什么。
对于部署单个更改到多个“相似”环境的无效场景,有两种策略:
- 如果你确定更改是相对“平安的”,并且心愿立刻利用到所有环境,那么能够在适当的 variant(或各自的文件夹)中进行更改。例如,如果你在 variants/non-prod 文件夹中提交 / 推送一个更改,那么所有非生产环境都会同时利用这个更改。我集体 拥护 这种办法,因为有些更改在实践上看起来是“平安的”,但在实践中可能会有问题。
- 更可取的办法是将更改利用于每个独自的文件夹,而后将其挪动到“父”variant(当它在所有环境中都存在时)。
让咱们举个例子。咱们想做一个影响所有 EU 环境的扭转(例如 GDPR 性能[24])。简略的办法是将配置更改间接提交 / 推送到 variants/eu 文件夹。这的确会影响到所有的 EU 环境(prod-eu 和 staging-eu)。然而,这有一点危险,因为如果部署失败,就会导致生产环境解体。
倡议采纳如下办法:
- 首先在 envs/staging-eu 中做出变更
- 而后对 envs/prod-eu 做同样的批改
- 最初,从两个环境中删除更改,并将其增加到 variants/eu 中(通过一个 commit/push 操作)。
你能够从渐进的数据库重构 [25] 中意识到这种模式。最初的提交是“过渡性的”,不会以任何形式影响任何环境。Kustomize 将在这两种状况下创立完全相同的定义,GitOps 控制器应该不会发现任何差别。
这种办法的长处是,当咱们在环境中挪动更改时,能够轻松回滚 / 复原更改。毛病是须要减少工作 (和提交) 将更改推广到所有环境,然而我置信这些工作的益处大于危险。
如果采纳这种办法,意味着 永远 不会间接对 base 文件夹利用新的更改。如果心愿对所有环境进行更改,则首先将更改利用于单个环境和 / 或 variants,而后将其反向移植到 base 文件夹,同时将其从所有上游文件夹中删除。
“environment-per-folder”办法的长处
既然咱们曾经剖析了“environment-per-folder”办法的所有外部工作原理,当初就该解释为什么它比“branch-per-environment”办法更好了。如果你曾经看过后面的局部,那么应该曾经了解了“environment-per-folder”办法是如何防止之前剖析的所有问题的。
环境分支最突出的问题是提交的程序,以及从一个环境合并到另一个环境时带来不必要更改的危险。应用文件夹办法,这个问题就齐全打消了:
- 提交程序当初曾经无关紧要了。当你将一个文件从一个文件夹复制到下一个文件夹时,不须要关怀它的提交历史,只须要关怀它的内容。
- 通过只复制四周的文件,只拿须要的货色,而不拿其余货色。当你复制 envs/qa/version.yml 到 env/staging-asia/version.yml 中,能够确定只降级了容器镜像,没有其余货色。如果其他人在 QA 环境中扭转了正本,并不会影响降级流程。
- 不须要应用 git cherry-picks 或任何其余高级的 git 办法来降级版本,只须要复制文件,并且能够拜访用于文件解决的实用程序的成熟生态系统。
- 能够自在的从任何环境对上游或上游环境进行任何更改,而不受环境正确“程序”的任何限度。例如,如果想将设置从 prod-us 反向移植到 staging-us,能够简略的将 env/prod-us/settings.yml 拷贝到 env/staging-us/settings.yml,而不必放心可能会无心中部署了不相干的只应在生产环境中利用的修补程序。
- 能够容易的应用文件 diff 操作来理解各个环境之间的不同之处(源环境和指标环境,反之亦然)
我认为这些劣势对于任何重要的应用程序都是十分重要的,我敢打赌大型组织中总会有几个“失败的部署”能够间接或间接归因于有问题的 environment-per-branch 模型。
之前咱们提到的第二个问题是,将一个分支合并到下一个环境时,会呈现配置漂移。这样做的起因是,当你执行“git merge”时,git 只会告诉你它将带来的更改,而不会通知你指标分支中曾经产生了什么更改。
同样,文件夹计划齐全打消了这个问题。正如后面说的,文件 diff 操作没有“方向”的概念,能够从任何环境向上或向下复制任何设置,如果对文件执行 diff 操作,能够看到环境之间的所有更改,而不论它们的上游 / 上游地位如何。
对于环境分支的最初一点是随着环境数量的增长,分支复杂性将会线性减少。对于 5 个环境,须要在 5 个分支之间切换更改,而对于 20 个环境,须要解决 20 个分支。在大量的分支之间正确迁徙公布版本是一个繁琐的过程,在生产环境中,这是一场劫难。
应用文件夹办法,分支的数量不仅是动态的,而且只有一个。如果有 5 个环境,能够用“主”分支来治理,如果须要更多的环境,你只须要增加额定的文件夹。如果 20 个环境,依然只须要一个 Git 分支。当只有一个分支时,取得部署的集中视图是很简略的。
在 GitOps 环境中应用 Helm
如果你不应用 Kustomize 而是更喜爱 Helm,也能够创立一个文件夹层次结构,其中蕴含所有环境的“通用”设置,特定的个性 /mixins/ 组件,以及特定于每个环境的最终文件夹。
上面是文件夹构造的样子:
chart/
[...chart files here..]
common/
values-common.yml
variants/
prod/
values-prod.yml
non-prod/
Values-non-prod.yml
[...other variants…]
envs/
prod-eu/
values-env-default.yaml
values-replicas.yaml
values-version.yaml
values-settings.yaml
[..other environments…]
同样,你须要花一些工夫来查看利用属性,并决定如何将它们宰割成不同的 values 文件,以获得最佳的降级速度。
除此之外,在环境降级方面,大多数过程都是一样的。
场景: 在 US 将利用版本从 QA 晋升到预发环境:
- cp envs/qa/values-version.yml envs/staging-us/values-version.yml
- commit/push 变更
场景: 从 GPU 集成测试到 GPU 负载测试,再到 QA 的利用版本升级。这是一个两步的过程:
- cp envs/integration-gpu/values-version.yml envs/load-gpu/values-version.yml
- commit/push 变更
- cp envs/load-gpu/values-version.yml envs/qa/values-version.yml
- commit/push 变更
场景: 通过额定配置,将利用从 prod-eu 晋升到 prod-us。这里咱们还复制了 settings 文件。
- cp envs/prod-eu/values-version.yml envs/prod-us/values-version.yml
- cp envs/prod-eu/values-settings.yml envs/prod-us/values-settings.yml
- commit/push 变更
了解 Helm(或者你的 GitOps 代理解决 Helm)如何解决多个 values 文件以及它们互相笼罩的程序也是十分重要的。
如果心愿预览某个环境,能够应用以下命令,而不是“kustomize build”:
helm template chart/ --values common/values-common.yaml --values variants/prod/values-prod.yaml –values envs/prod-eu/values-env-default.yml –values envs/prod-eu/values-replicas.yml –values envs/prod-eu/values-version.yml –values envs/prod-eu/values-settings.yml
能够看到,如果在每个环境文件夹中都有大量的 variants 或文件,那么 Helm 比 Kustomize 更麻烦一些。
environment-per-git-repo 办法
当我与大型组织探讨文件夹办法时,听到的第一个拥护意见是,人们 (尤其是平安团队) 不喜爱看到单个 Git 存储库中的单个分支同时蕴含产品化和非产品化环境。
这是一个能够了解的拥护意见,能够说是文件夹办法绝对于“environment-per-branch”范式的惟一弱点。毕竟,在 Git 存储库中爱护各个分支比在单个分支中爱护文件夹要容易得多。
这个问题能够很容易的通过自动化、验证查看甚至手工批准 (如果这对你的组织至关重要的话) 来解决。我想再次强调,在文件操作中应用“cp”来降级公布版本,只是为了演示的目标,并不意味着当降级产生时,须要在交互式终端中手动运行 cp。
现实状况下,应该有一个自动化零碎来复制文件并 commit/push 它们,能够是继续集成 (CI) 零碎或处理软件生命周期的其余平台。如果依然有人本人做出扭转,不应该间接 commit“main”目录,而是应该发动一个 Pull Request,而后通过适当的流程,在合并之前查看 Pull Request。
然而,我意识到有些组织对平安问题特地敏感,当波及到 Git 爱护时,他们更喜爱齐全隔离的办法。对于这些组织,能够应用 2 个 Git 存储库,一个保留 base 配置、所有生产 variants 和所有生产环境(以及所有与生产相干的货色),而第二个 Git 存储库保留所有非生产的货色。
这种办法让降级变得有点艰难,因为当初须要在做任何降级之前签出 2 个 git 仓库。另一方面,它容许平安团队向“生产”Git 存储库搁置额定的平安束缚,并且无论部署到多少环境中,依然领有动态数量的 Git 存储库(只有 2 个)。
集体认为这种办法有些过分,至多在我看来,它显示出开发和运维不足信赖。对于人们是否应该间接拜访生产环境的探讨是一个简单的问题,可能须要独自探讨。
拥抱文件夹,遗记分支
心愿通过这篇文章,能够解决在多环境中部署的问题,当初你曾经很好的了解了文件夹办法的益处以及应该应用它的起因。
GitOps 部署高兴!
References: \
[1] Stop Using Branches for Deploying to Different GitOps Environments: https://medium.com/containers-101/stop-using-branches-for-dep… \
[2] How to Model Your Gitops Environments and Promote Releases between Them: https://codefresh.io/about-gitops/how-to-model-your-gitops-en… \
[3] Multiple environments(dev, stage, ..,. prod) example: https://github.com/argoproj/argocd-example-apps/issues/57 \
[4] A successful git branching model: https://nvie.com/posts/a-successful-git-branching-model/ \
[5] Trunk based development: https://trunkbaseddevelopment.com/ \
[6] Feature flags: https://trunkbaseddevelopment.com/feature-flags/ \
[7] git diff: https://git-scm.com/docs/git-diff \
[8] git cherry-pick: https://git-scm.com/docs/git-cherry-pick \
[9] Ingress: https://kubernetes.io/docs/concepts/services-networking/ingress/ \
[10] Manage resources containers: https://kubernetes.io/docs/concepts/configuration/manage-reso… \
[11] Helm deployment evnironments: https://codefresh.io/helm-tutorial/helm-deployment-environments/ \
[12] Kustomize: https://kustomize.io/ \
[13] Helm: https://helm.sh/ \
[14] Configmap generator: https://kubectl.docs.kubernetes.io/references/kustomize/kusto… \
[15] Patches strategic merge: https://kubectl.docs.kubernetes.io/references/kustomize/kusto… \
[16] GitOps promotion source code: https://github.com/kostis-codefresh/gitops-promotion-source-code \
[17] Configure liveness readiness startup probes: https://kubernetes.io/docs/tasks/configure-pod-container/conf… \
[18] Manage resources containers: https://kubernetes.io/docs/concepts/configuration/manage-reso… \
[19] Sample prod: https://raw.githubusercontent.com/kostis-codefresh/gitops-env… \
[20] Sample staging-asia: https://github.com/kostis-codefresh/gitops-environment-promotion/tree/main/envs/staging-asia \
[21] Sample version.yml: https://github.com/kostis-codefresh/gitops-environment-promotion/blob/main/envs/staging-asia/version.yml \
[22] Sample replicas.yml: https://github.com/kostis-codefresh/gitops-environment-promotion/blob/main/envs/staging-asia/replicas.yml \
[23] Sample settings.yml: https://github.com/kostis-codefresh/gitops-environment-promotion/blob/main/envs/staging-asia/settings.yml \
[24] GDPR: https://gdpr-info.eu/ \
[25] Database refactoring: https://databaserefactoring.com/你好,我是俞凡,在 Motorola 做过研发,当初在 Mavenir 做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI 等技术始终保持着浓重的趣味,平时喜爱浏览、思考,置信继续学习、一生成长,欢送一起交流学习。\
微信公众号:DeepNoMind
本文由 mdnice 多平台公布