本章内容包括:
- 将生成的制品部署到各个环境中
- 使用环境流水线和 GitOps 管理环境
- 使用 Argo CD 与 Helm 高效交付软件
本章介绍了环境流水线的概念。我们将讲解如何将服务流水线生成的制品部署到具体的运行环境,直到生产环境。我们还将介绍云原生领域的一种常见实践——GitOps,它允许我们使用 Git 仓库定义和配置环境。最后,我们将介绍 Argo CD 项目,它实现了在 Kubernetes 上管理应用的 GitOps 方法。本章分为三个主要部分:
- 环境流水线
- 使用 Argo CD 实践环境流水线
- 服务流水线 + 环境流水线协同工作
4.1 环境流水线
我们可以构建任意数量的服务并生成新版本,但如果这些版本不能自由流转到不同环境中进行测试,最终被客户使用,组织就难以实现顺畅的端到端软件交付实践。环境流水线负责配置和维护我们的各类环境。
公司通常会为不同用途设置不同环境,例如:
- 预发布环境(Staging) :开发者可以部署服务的最新版本
- 质量保证环境(QA) :进行人工测试
- 生产环境(Production) :真实用户与应用交互
这只是示例,环境数量不应有硬性限制。图 4.1 展示了单次发布如何流转经过不同环境,最终到达生产环境,并在用户面前上线。
每个环境(开发、预发布、QA 和生产)都会有一条环境流水线。这些流水线负责保持环境配置与运行该环境的实际硬件同步。环境流水线使用一个包含环境配置的仓库作为事实来源(source of truth),其中包括需要部署的服务以及每个服务的版本(见图 4.2)。
如果你采用这种方法,每个环境都会有自己的配置仓库。发布新版本意味着需要修改环境配置仓库,要么添加新服务,要么更新配置以指向新发布的版本。有些组织会将所有敏感的环境配置集中在一个仓库中,这有助于统一管理访问这些配置所需的凭证。
这些配置变更可以自动化处理,也可以需要人工干预。对于一些敏感环境,例如生产环境,你可能需要不同的利益相关方在添加或更新服务之前进行批准。
但是,环境流水线从何而来?为什么你以前可能没听说过它们?在深入了解环境流水线的具体形态之前,我们需要先了解一下它的重要性和背景。
4.1.1 过去的做法以及近期的变化
传统上,创建新环境既困难又昂贵。按需创建新环境并不可行,主要有两个原因。首先,开发人员用来创建应用的环境与应用实际运行给终端用户的环境完全不同。这些差异不仅体现在计算能力上,也给负责运行这些应用的运维团队带来了巨大的压力。根据环境的能力,他们需要对应用配置进行精细调整(而这些配置并非他们设计的)。其次,用于自动化复杂环境配置和部署的工具已经成为主流。在容器和 Kubernetes 的帮助下,这些工具的设计和跨云提供商的工作方式已经标准化。开发人员可以使用自己熟悉的编程语言编写基础设施定义,也可以依赖 Kubernetes API 来创建这些定义。
在云原生应用兴起之前,部署新应用或新版本的流程通常是:关闭服务器、运行一些脚本、复制二进制文件,然后重新启动服务器并运行新版本。服务器重启后,应用可能无法启动,因此还需要进一步调整配置。大多数配置都是在服务器上手动完成的,这使得记录和跟踪变更内容及原因变得困难。
为了自动化这些流程,像 Jenkins(www.jenkins.io/,非常流行的流水线引擎)或脚本工具被用来简化二进制文件的部署。操作人员无需手动停止服务器或复制二进制文件,只需运行一个 Jenkins Job,指定要部署的工件版本,Jenkins 会执行任务并通知操作人员结果。这种方法有两个主要优势:
- Jenkins 等工具可以访问环境凭证,避免操作人员手动访问服务器。
- Jenkins 会记录每次作业的执行情况和参数,便于追踪执行内容及结果。
虽然使用 Jenkins 自动化比手动部署有了很大改善,但仍存在一些问题,例如固定环境与软件开发和测试环境差异很大。我们需要指定环境的创建和配置方式,包括操作系统版本以及机器或虚拟机中安装的软件,以减少不同环境之间的差异。虚拟机在这方面提供了极大便利,因为我们可以轻松创建两个或多个配置相似的虚拟机。
我们甚至可以把这些虚拟机提供给开发人员使用。但这也带来了新问题:我们需要新工具来管理、运行、维护和存储虚拟机。如果我们有多台物理机器需要运行虚拟机,不希望运维团队手动在每台服务器上启动虚拟机,因此我们需要一个管理虚拟机的虚拟机监控器(Hypervisor)来在物理机集群中运行和监控虚拟机。
使用 Jenkins 和虚拟机(配合 Hypervisor)已经是巨大的进步。通过实现一定的自动化,操作人员无需手动访问服务器或虚拟机更改配置,环境可以通过预定义的固定虚拟机配置来创建。像 Ansible(www.ansible.com/)和Puppet(www.puppet.com/)这样的工具就是建立在这些概念之上的。
图 4.3 展示了配置 Jenkins Job 来创建承载应用的虚拟机。但需要注意,这些虚拟机承载了完整的操作系统,操作系统中捆绑的所有工具都会与应用一起运行!
虽然这种方法在业界仍然常见,但仍有很多改进空间,例如以下几个方面:
- Jenkins Jobs 和脚本本质上是命令式的,意味着它们逐步指定需要执行的操作。这有一个很大的缺点:如果环境发生变化——比如某台服务器不再可用,或者需要更多数据才能进行服务认证——流水线逻辑就会失败,需要手动更新。
- 虚拟机很“重” 。每次启动虚拟机时,都会启动一个完整的操作系统实例。运行操作系统进程并没有增加业务价值;集群越大,操作系统的开销就越大。由于虚拟机的要求,在开发人员环境中运行虚拟机可能不可行。
- 环境配置是隐藏的且不可版本化。大多数环境配置以及部署流程都被编码在 Jenkins 等工具中,而复杂的流水线容易失控,使得修改风险很高,迁移到新的工具和技术栈也非常困难。
- 每个云提供商创建虚拟机的方式不标准。这可能导致供应商锁定问题。如果我们为 AWS 创建了虚拟机,就无法直接在 Google Cloud 或 Microsoft Azure 上运行这些虚拟机。
那么,现代团队如何通过新工具解决这些问题呢?答案很简单:现在有了 Kubernetes 和容器,它们旨在通过容器和广泛采用的 Kubernetes API 来解决虚拟机造成的开销以及云提供商间的可移植性问题。Kubernetes 还提供了构建模块,确保我们无需关闭服务器即可部署新应用或修改配置。如果按 Kubernetes 的方式操作,应用不应出现停机。
但仅靠 Kubernetes 并不能解决集群本身的配置过程问题。我们如何应用配置更改,或者部署应用到集群的流程和工具也很关键,这就是为什么你可能听说过 GitOps。
4.1.2 什么是 GitOps,它与环境流水线有什么关系?
如果我们不想把所有运维知识都编码在像 Jenkins 这样的工具中(难以维护、修改和追踪),就需要另一种方法。
GitOps 这一术语由 CNCF 的 GitOps 工作组定义(opengitops.dev/),它描述了使用 Git 作为单一可信源,以声明式方式创建、维护和应用环境及应用配置的过程。OpenGitOps 定义了四个核心原则:
- 声明式(Declarative) :由 GitOps 管理的系统必须以声明式方式表达期望状态。如果使用 Kubernetes 清单(manifest),我们就满足了这一点,因为我们使用声明式资源定义了需要部署的内容及配置方式,Kubernetes 会进行调和(reconcile)。
- 版本化且不可变(Versioned and immutable) :期望状态应以不可变和版本化的方式存储,并保留完整的版本历史。OpenGitOps 并不强制使用 Git,只要定义可存储、版本化且不可变,就可视为 GitOps。这也可以扩展到例如 S3 桶中存储文件,只要它们支持版本控制和不可变性。
- 自动拉取(Pulled automatically) :软件代理自动从源中拉取期望状态声明。GitOps 软件会定期自动拉取源的变更,用户无需关心拉取的时间。
- 持续调和(Continuously reconciled) :软件代理持续监控系统状态,并尝试应用期望状态。持续调和增强了环境和交付过程的韧性,因为有组件负责应用期望状态并监控配置漂移。如果调和失败,GitOps 工具会通知问题并持续尝试,直到实现期望状态。
通过将环境和应用配置存储在 Git 仓库中,我们可以跟踪和版本化所做的更改。依赖 Git 可以轻松回滚不符合预期的变更。GitOps 涵盖了配置存储以及这些配置如何应用到运行应用的计算资源上。
GitOps 虽然在 Kubernetes 背景下提出,但这一方法并非全新,因为配置管理工具已存在很久。GitOps 是对这些成熟方法的改进,可应用于任何软件操作,而不仅仅是 Kubernetes。随着云提供商的基础设施即代码工具流行,Chef、Ansible、Terraform 和 Pulumi 等工具深受运维团队喜爱,因为它们允许团队以可复现的方式定义和配置云资源。如果需要新环境,只需运行 Terraform 脚本或 Pulumi 应用,即可快速搭建环境。这些工具还可以与云提供商 API 通信,自动创建 Kubernetes 集群。
使用 GitOps 时,我们管理配置并依赖 Kubernetes API 作为向 Kubernetes 集群部署应用的标准方式。我们使用 Git 仓库作为环境内部配置(Kubernetes YAML 文件)的可信源,同时无需手动操作集群,从而避免配置漂移和安全问题。GitOps 工具会定期从源(Git 仓库)拉取,并持续监控环境,实现持续调和循环,确保仓库中定义的期望状态在实际环境中得到体现。
通过运行环境流水线,我们可以将任意 Kubernetes 集群重新配置为与 Git 仓库中存储的配置一致。图 4.4 展示了这些组件如何协同工作:左侧为基础设施即代码工具,可创建云资源,包括 Kubernetes 集群和应用基础设施;环境配置完成后,使用 GitOps 方法的环境流水线可以将环境配置同步到目标 Kubernetes 集群,并定期检查 Git 中的配置是否与集群一致。
通过将基础设施和应用程序关注点分离,我们的环境流水线能够确保环境易于复现,并在需要时轻松更新。依赖 Git 作为唯一可信源,我们可以根据需要回滚基础设施和应用程序的变更。同样重要的是要理解,由于我们使用的是 Kubernetes API,环境定义现在以声明式方式表达,支持在配置应用的上下文中进行更改,并让 Kubernetes 负责实现这些配置所表达的期望状态。
图 4.5 展示了这些交互:运维团队只需对存放环境配置的 Git 仓库进行修改,然后流水线(即一系列步骤)会执行,确保该配置与目标环境保持同步。
当你开始使用环境流水线时,你的目标是停止手动交互、修改或更改环境配置,所有操作都由这些流水线独家完成。举一个具体的例子:操作员不再直接执行 kubectl apply -f 或 helm install 到 Kubernetes 集群中,而是根据存放了集群所需安装定义和配置的 Git 仓库内容来运行这些命令。
理论上,一个监控 Git 仓库并对变更作出反应的操作员就足够了,但实际上,我们仍需要一系列步骤来确保对部署到环境中的内容有完全控制。因此,将 GitOps 理解为一种流水线方式,有助于我们明白在某些场景下,每当环境配置发生变化时,需要向这些流水线中添加额外步骤。
让我们使用一些在实际场景中常见的工具,来看这些步骤。
4.1.3 环境流水线涉及的步骤
无论你将何种应用部署到不同环境中,环境流水线通常都包括一组预定义步骤。图 4.6 展示了这些步骤的顺序,因为大多数情况下,这些步骤是定义在脚本中或编码在负责检查每个步骤是否正确执行的工具里。下面详细说明这些步骤:
对配置变更作出反应:这可以通过轮询(Polling)或推送(Push)实现:
- 轮询变更:组件可以定期拉取仓库,检查自上次检查以来是否有新的提交。如果检测到新变更,就会创建新的环境流水线实例。
- 使用 Webhook 推送变更:如果仓库支持 Webhook,仓库可以通知环境流水线有新的变更需要同步。需要记住,GitOps 原则中提到“自动拉取(pulled automatically)”,这意味着我们可以使用 Webhook,但不应完全依赖它们来获取配置变更更新。
从包含我们环境期望状态的仓库中克隆源代码:这一步从远程 Git 仓库中获取环境配置。像 Git 这样的工具只会拉取远程仓库与本地之间的差异(delta)。
将期望状态应用到运行中的环境:通常包括执行 kubectl apply -f 或 helm install 命令来安装新版本的工件。需要注意的是,无论是使用 kubectl 还是 Helm,Kubernetes 都足够智能,可以识别出变更的部分,只应用差异。一旦流水线在本地获取了所有配置,它会使用一组凭证将这些变更应用到 Kubernetes 集群中。可以微调流水线对集群的访问权限,从安全角度确保不会被滥用,同时也可以撤销单个团队成员对部署服务的集群访问权限。
验证变更是否已应用且状态与 Git 仓库描述一致(处理配置漂移) :一旦变更应用到运行的集群,需要检查服务的新版本是否正常运行,以判断是否需要回滚到先前版本。如果需要回滚很简单,因为所有历史都保存在 Git 中。回滚只需查看仓库中的前一次提交并应用即可。
验证工作负载是否按预期运行:配置正确应用后,需要确认部署的应用正常工作,功能符合预期。
为了让环境流水线工作,需要一个能够将变更应用到环境的组件,并配置正确的访问凭证。这个组件的核心思想是确保没有人可以通过手动操作集群来更改环境配置。它是唯一允许更改环境配置、部署新服务、升级版本或从环境中移除服务的组件。
要使环境流水线生效,需要满足以下两个条件:
- 存放环境期望状态的仓库必须包含成功创建和配置环境所需的全部配置。
- 环境运行的 Kubernetes 集群需要配置正确的凭证,以允许流水线更改状态。
环境流水线一词指每个环境都会有一个与之关联的流水线。由于通常需要多个环境(开发、预发布、生产)来交付应用,每个环境都有负责部署和升级其中组件的流水线。通过这种方式,在不同环境之间推广服务,只需向环境仓库发送 Pull Request 或 Change Request,流水线会在目标集群中反映这些变更。
4.1.4 环境流水线的要求及不同方法
那么,这些环境仓库的内容是什么?如图 4.7 所示,环境仓库的内容只是定义哪些服务需要出现在该环境中。环境流水线随后只需将这些 Kubernetes 清单应用到目标集群即可。
第一种方案(简单布局)是将所有 Kubernetes YAML 文件存放在一个 Git 仓库中,然后环境流水线只需对配置好的集群执行 kubectl apply -f * 即可。虽然这种方法很简单,但有一个明显缺点:如果每个服务的 Kubernetes YAML 文件都在服务仓库中,那么环境仓库中将会有这些文件的重复副本,容易出现不同步的情况。想象一下,如果你有多个环境,就必须保持所有副本同步,这可能会变得很有挑战性。
第二种方案(使用 Helm Charts)则更复杂一些,现在我们使用 Helm 来定义集群的状态。你可以使用 Helm 依赖关系创建一个父 Chart,其中包含环境中应该存在的所有服务作为依赖项。如果采用这种方式,环境流水线可以通过 helm update . 将 Chart 应用到集群中。我不太喜欢这种方法的原因是,每次变更会创建一个 Helm Release,而不会为每个服务创建单独的 Release。这种方法通过 Helm 依赖来获取每个服务的定义,因此前提是每个服务都必须打包成 Helm Chart。
第三种方案是使用一个专门项目 helmfile(github.com/helmfile/he…)来定义环境配置。helmfile 允许你以声明式方式定义集群中需要存在的 Helm Release。当我们运行 helmfile sync 时,helmfile 会根据定义好的 helmfile 创建所需的 Helm Release。
无论使用上述哪种方法或其他工具,核心思想是清晰的:你需要有一个存放配置的仓库(每个环境一个仓库,或每个环境一个目录),并由流水线负责获取配置并使用工具将其应用到集群中。
通常会有多个环境(staging、QA、生产),甚至允许团队按需创建环境来进行测试或日常开发任务。如果采用“一环境对应一个 Namespace”的方法,如图 4.8 所示,通常每个环境会有单独的 Git 仓库,因为这样可以保持对环境的访问隔离和安全性。这种方法简单,但在 Kubernetes 集群上隔离性不足,因为 Kubernetes Namespace 主要用于逻辑分区。在这种情况下,staging 环境将与生产环境共享集群资源。
另一种方法是为每个环境使用全新的集群。其主要区别在于隔离性和访问控制。通过为每个环境配置独立的集群,你可以更严格地定义谁以及哪些组件可以在这些环境中部署和升级,同时可以为每个集群设置不同的硬件配置,例如多区域部署和其他可能不适合在 staging 或测试环境中使用的可扩展性设置。使用不同的集群,你还可以实现多云架构,让不同的云服务提供商承载不同的环境。
图 4.9 展示了如何对开发环境使用 Namespace 方法,由不同团队创建,然后为 staging 和生产环境配置独立的集群。这里的核心思想是尽可能让 staging 和生产集群的配置一致,从而确保部署到不同环境的应用表现一致。
好的,那么我们如何实现这些环境流水线呢?我们应该使用 Tekton 来实现这些流水线吗?在下一节中,我们将介绍 Argo CD(argo-cd.readthedocs.io/en/stable/),这是一款将环境流水线逻辑和最佳实践编码到一个非常具体的持续部署工具中的工具。
4.2 环境流水线实战
你可以像上一节描述的那样使用 Tekton 或 Dagger 来实现环境流水线。这在 Jenkins X(jenkins-x.io)等项目中已经有所应用,但如今,环境流水线的各个步骤通常被编码在专门的持续部署工具中,例如 Argo CD(argo-cd.readthedocs.io/en/stable/)。
与服务流水线不同,服务流水线可能需要依赖特定技术栈的构建工具来生成制品,而 Kubernetes 的环境流水线在 GitOps 的框架下已经高度标准化。考虑到我们的所有制品都由服务流水线构建并发布,我们首先需要创建环境的 Git 仓库,其中包含环境配置,包括部署到该环境的服务。
Argo CD 提供了一个非常有指导性但灵活的 GitOps 实现。我们可以将部署软件到环境所需的所有步骤委托给 Argo CD。Argo CD 可以开箱即用地监控包含环境配置的 Git 仓库,并定期将配置应用到运行中的集群。这使我们无需手动操作目标集群,从而减少了配置漂移,因为 Git 成为了我们的“事实源”。
使用像 Argo CD 这样的工具,我们可以以声明式方式定义希望在环境中安装的内容,而 Argo CD 负责在出现问题或集群配置不同步时通知我们。Argo CD 不局限于单个集群,这意味着我们的环境可以存在于不同的集群,甚至是不同的云服务提供商中。图 4.10 展示了 Argo CD 管理不同集群上的不同环境,使用不同的 Git 仓库作为事实源来维护每个环境的配置。
就像我们现在为每个服务建立了独立的服务流水线一样,我们也可以为环境配置建立独立的仓库、分支或目录。Argo CD 可以监控这些仓库或仓库内的目录的变化,以同步我们的环境配置。
在这个示例中,我们将在 Kubernetes 集群中安装 Argo CD,并使用 GitOps 方法配置我们的预发布(staging)环境。为此,我们需要一个 Git 仓库作为我们的“事实源”。你可以参考位于 github.com/salaboy/pla… 的逐步教程。
关于 Argo CD 的安装,我建议查看他们的 Getting Started 指南。该指南会安装 Argo CD 正常运行所需的所有组件,因此完成指南后,我们应该具备启动预发布环境的一切条件。指南还会引导你安装 argocd CLI(命令行工具),有时候这个工具非常方便。在接下来的章节中,我们将重点使用 Argo CD 的用户界面,但你也可以使用 CLI 来访问相同的功能。Argo CD 提供了一个非常实用的用户界面,可以让你监控环境和应用的状态,并快速发现潜在问题。
本节的主要目标是复现第 2 章 2.1.3 节中我们对应用进行安装和交互的操作,但这里的目标是将整个流程完全自动化,环境配置将通过 Git 仓库管理。我们仍将使用 Helm 来定义环境配置,因为 Argo CD 提供了开箱即用的 Helm 集成。
注意:Argo CD 使用的命名与我们之前使用的不同。在 Argo CD 中,你配置的是“应用(Applications)”,而不是环境。在接下来的截图中,你将看到我们将配置一个 Argo CD 应用来表示我们的预发布环境。由于 Helm Chart 可以包含任意内容,我们将使用 Helm Chart 将 Conference 应用配置到该环境中。
4.2.1 创建 Argo CD 应用
访问 Argo CD 用户界面后,你会在屏幕左上角看到一个 + New App 按钮(图 4.11)。
点击该按钮,就可以看到应用创建表单。除了为 Argo CD 应用添加名称并选择应用所属的项目(我们将选择默认项目)之外,我们还会勾选 Auto-Create Namespace 选项,如图 4.12 所示。
通过将我们的环境与集群中的新命名空间关联,我们可以仅使用 Kubernetes 的 RBAC 机制来允许管理员修改该命名空间内的 Kubernetes 资源。请记住,使用 Argo CD 的目的是确保开发人员不会意外更改应用配置或手动将配置更改应用到集群中。Argo CD 会同步 Git 仓库中定义的资源。那么,这个 Git 仓库在哪里呢?这正是我们接下来需要配置的内容(图 4.13)。
如前所述,我们将使用 github.com/salaboy/pla… 仓库中的一个目录来定义我们的暂存环境(staging environment)。你应该先 fork 该仓库(然后使用你自己的 fork URL)来对环境配置进行任何修改。包含环境配置的目录位于 chapter-4/argo-cd/staging/ 下。如图 4.14 所示,你还可以在不同的分支和标签之间进行选择,从而可以精细控制配置的来源以及配置如何演变。
下一步是定义 Argo CD 将在哪里应用该环境配置。我们可以使用 Argo CD 在不同的集群中安装和同步环境,但在本示例中,我们将使用安装了 Argo CD 的同一个 Kubernetes 集群以及暂存(staging)命名空间。Argo CD 提供了自动创建该命名空间的选项,或者你也可以在设置集群和不同命名空间的权限时手动创建它。
最后,由于重用相似环境的相同配置是合理的,Argo CD 允许我们配置针对本次安装的不同参数。由于我们使用的是 Helm,并且 Argo CD 的用户界面足够智能,可以扫描我们输入的仓库路径内容,它能够识别正在处理的是 Helm Chart。如果我们没有使用 Helm Chart,Argo CD 也允许我们将环境变量作为参数传递给配置脚本(见图 4.15)。
如你在前一张图片中看到的,Argo CD 还识别出了我们提供的仓库路径中的一个空的 values.yaml 文件。如果 values.yaml 文件中包含任何参数,用户界面会解析这些参数并显示出来,供你验证。我们还可以在 VALUES 文本框中添加更多参数,以覆盖任何其他 chart(或子 chart)的配置。
在提供了所有这些配置后,我们就可以点击表单顶部的 Create 按钮了。Argo CD 将创建该应用程序,并根据我们选择的 Automatic Sync 选项自动同步更改(见图 4.16)。
如果你点击进入该应用程序,你将看到应用程序的完整视图,其中显示了与该应用程序关联的所有资源的状态,如图 4.17 所示。
如果你在本地集群或真实的 Kubernetes 集群中创建环境,你应该访问应用程序并与其进行交互。让我们回顾一下我们已经完成的工作:
- 我们已经将 Argo CD 安装到 Kubernetes 集群中。通过提供的 Argo CD 仪表板(用户界面),我们为我们的暂存环境(staging environment)创建了一个新的 Argo CD 应用程序。
- 我们在 GitHub 托管的 Git 仓库中创建了暂存环境的配置,该配置使用 Helm Chart 定义来配置我们的 Conference 应用服务及其依赖项(Redis、PostgreSQL 和 Kafka)。
- 我们已将配置同步到安装了 Argo CD 的同一集群中的一个命名空间(staging)。
- 最重要的是,我们消除了对目标集群的手动操作需求。理论上,不再需要对 staging 命名空间执行 kubectl 命令。
为了使此设置正常工作,我们需要确保 Helm Chart(以及其中的 Kubernetes 资源)所需的制品(artifacts)可以供目标集群拉取。我强烈建议你按照分步教程(链接)操作 Argo CD,亲身体验它的工作方式以及如何帮助团队将应用程序持续部署到多个环境。
4.2.2 以 GitOps 方式处理变更
现在假设负责开发用户界面(前端)的团队决定引入一个新功能。他们在前端仓库中创建了一个 pull request。一旦该 pull request 与主分支合并,团队可以决定为服务创建一个新的发布版本。发布过程应包括使用发布号创建带标签的制品(tagged artifacts)。这些制品的创建由服务流水线负责,正如我们在前面的章节中看到的。图 4.18 展示了 Argo CD 在此情况下如何从暂存环境配置仓库同步配置变更。
一旦我们有了发布的制品(artifacts),就可以更新环境了。我们可以通过向 GitHub 仓库提交 pull request 来更新暂存环境(staging environment),在合并到主分支(我们用来配置 Argo CD 应用的分支)之前进行审核。环境配置仓库中的变更通常包括:
- 提升或回退服务版本:以本例为例,这只是简单地修改一个或多个服务的 Helm Chart 版本。将某个服务回退到之前的版本也同样简单,只需在环境 Chart 中回退版本号,或者直接回退最初增加版本号的提交(commit)。需要注意的是,回退提交总是推荐的做法,因为回退到旧版本可能还涉及服务的配置更改,如果这些更改未应用,旧版本可能无法正常工作。
- 添加或移除服务:添加新服务稍微复杂一些,因为你需要同时添加 Helm Chart 引用和服务配置参数。为了使其生效,Chart 定义必须可被 Argo CD 安装访问。假设服务的 Chart 可用且配置参数有效,那么下一次同步 Argo CD 应用时,新服务就会部署到环境中。移除服务则更简单,只需从环境 Helm Chart 中删除依赖,该服务就会从环境中移除。
- 调整 Chart 参数:有时我们不希望更改服务版本,而是希望微调应用参数以满足性能或可扩展性需求、监控配置,或调整一组服务的日志级别。这类变更同样会进行版本管理,应被视为新功能或 bug 修复。
如果将此方式与手动使用 Helm 安装应用到集群进行对比,会很快发现差异。首先,开发者可能只在自己的笔记本上拥有环境配置,这会导致环境难以在其他位置复现。未使用版本控制系统跟踪的环境配置更改可能会丢失,并且无法验证这些更改在真实集群中是否有效。配置漂移(configuration drift)也会变得更难跟踪和排查。
通过 Argo CD 的自动化方法,我们可以实现更高级的场景。例如,我们可以为 pull request 创建预览环境(preview environments,如图 4.19 所示),在合并代码和发布制品之前先测试更改。
使用预览环境可以帮助团队更快地迭代,并在将更改合并到项目主分支之前进行验证。预览环境还可以在 pull request 被合并时收到通知,从而使自动化清理机制的实现变得简单直接。
注意:在使用 Argo CD 和 Helm 时,还有一个重要细节需要说明:与手动使用 Helm Charts 不同,手动方式每次在集群中更新 Chart 时,Helm 都会创建一个 release 资源,而 Argo CD 并不使用这一 Helm 特性。Argo CD 的做法是先使用 Helm 模板渲染 Kubernetes 资源的 YAML 文件,然后通过 kubectl apply 应用输出。这种方式依赖于 Git 中的一切都有版本控制,同时允许统一不同的 YAML 模板引擎。除了提供一些安全性好处之外,这也是 Argo CD 差异比较(diffing)功能的关键,它让我们能够指定哪些资源由 Argo CD 管理,哪些元素可以由其他控制器管理。
最后,为了把整个流程串联起来,我们来看一下服务管道(service pipelines)和环境管道(environment pipelines)如何交互,以实现从代码变更到将新版本部署到多个环境的端到端自动化。
4.3 服务管道 + 环境管道
让我们看看服务管道和环境管道如何连接。这两者的连接通过对 Git 仓库的 pull request 或 change request 实现,因为当提交和合并更改时,管道就会被触发(图 4.20)。
开发人员在完成一个新功能后,会向仓库的主分支创建一个 pull/change request(拉取/变更请求)。这个 pull/change request 可以由专门的服务管道进行审查和构建。当新功能被合并到仓库的主分支时,会触发服务管道的新实例。该实例会创建一个新的发布版本以及部署服务新版本到 Kubernetes 集群所需的所有工件。正如我们在第 3 章中看到的,这包括编译后的二进制文件、容器镜像以及可以使用 Helm 等工具打包的 Kubernetes Manifest 文件。
作为服务管道的最后一步,可以加入通知步骤,通知相关环境有服务的新版本可用。这个通知通常是向环境仓库自动创建的 pull/change request。或者,你可以监控(或订阅通知)你的工件仓库,当检测到新版本时,自动向配置好的环境创建 pull/change request。
发送到环境仓库的 pull/change request 可以由专门的环境管道自动测试。就像我们对服务管道所做的那样,对于低风险环境,这些 pull/change request 可以在无需人工干预的情况下自动合并。
通过实现这一流程,我们可以让开发人员专注于修复 bug 和创建新功能,而这些功能会被自动发布并推广到低风险环境。一旦在如 staging 这样的环境中测试了新版本,并确认这些新版本或配置没有引发问题,就可以为包含生产环境配置的仓库创建 pull/change request。
环境越敏感,所需的检查和验证就越多。在这种情况下,如图 4.21 所示,为了将新服务版本推广到生产环境,会创建一个新的测试环境来验证并测试 pull/change request 中引入的更改。完成这些验证后,需要人工签核以合并 pull request 并触发环境管道的同步。
环境管道是用来编码组织对软件发布和在不同环境中推广的需求的机制。本章中,我们已经了解了像 Argo CD 这样的工具可以为我们做什么。接下来,我们需要评估:单个 Argo CD 安装是否足够?谁来管理它并保持其安全?是否需要通过自定义 hook 扩展 Argo CD?是否需要将其与其他工具集成?我们将在第 6 章探讨这些问题。在结束本章之前,让我们看看环境管道和像 Argo CD 这样的工具如何融入平台工程的整体故事中。
4.4 回到平台工程视角
从平台工程的角度来看,为团队提供 GitOps 方法配置不同环境正变得越来越流行。随着 Argo CD 等工具的普及,越来越多的人愿意在版本控制系统(如 Git)中存储和操作环境配置。作为平台工程团队,你可以让你的团队使用这种方法,而不必强迫他们学习如何安装、维护和配置这些工具。
平台可以自动创建环境仓库,并确保正确的团队能够访问这些仓库以读取和写入配置,从而推动服务的发布。平台的使用者只需要知道如何与他们的环境交互,而不需要了解平台提供的工具如何工作或如何配置。例如,在开发环境中,有些开发团队可能希望直接访问集群,这时使用 GitOps 方法可能行不通,你的平台应当足够灵活,以便在需要时允许这种访问。
如第 4.3 节所述,服务管道和环境管道协同工作,用于生成软件工件并在不同环境间迁移。服务管道和环境管道都是实现所谓 “黄金路径(golden paths)” 的关键机制。随着平台的成熟,环境管道之间的协调变得至关重要,以自动化新软件从源代码到生产环境的发布流程,并由最终用户(客户)进行验证。这些黄金路径是自动化工作流,用于将团队产生的变更迁移到客户可访问的生产环境。图 4.22 从高层次展示了我们应用的黄金路径示意。
想一想,要将开发环境中生成的软件推广到生产集群,让客户能够访问某个服务的发布版本,需要执行多少服务管道和环境管道。这些管道是如何协调和连接的,以确保部署按预期工作?在整个过程中需要多少人工验证?最重要的是,你可以自动化哪些环节,让团队无需担心这些复杂的交互?
到目前为止,我们已经介绍了如何将应用安装到 Kubernetes 集群中、如何将应用服务打包成容器、以及如何打包和分发部署这些服务所需的配置文件。本章进一步展示了如何使用 GitOps 方法管理应用运行的不同环境。图 4.23 展示了所有组件的整体关系。
在深入探讨“黄金路径”(第 6 章)之前,我们必须先了解在将应用部署到不同环境时面临的另一个挑战:应用基础设施,这将在下一章讨论。
总结
环境管道负责将软件工件部署到运行环境中。环境管道避免团队直接与运行应用的集群进行交互,从而减少错误和配置失误。环境管道应在更新配置后检查环境是否完全可用。
使用像 Argo CD 这样的工具,你可以将每个环境的内容定义到 Git 仓库中,作为环境配置应如何呈现的真实来源。Argo CD 会跟踪环境所在集群的状态,并确保集群中应用的配置不会发生偏移。
团队可以通过向存储环境配置的仓库提交拉取/变更请求来升级或降级环境中运行的服务版本。团队或自动化流程可以验证这些变更,一旦获批并合并,这些变更将反映到运行环境中。如果出现问题,可以通过回滚 Git 仓库中的提交来恢复先前版本。
如果你跟随了分步教程,就可以实际操作,体验如何使用 Argo CD 按照 GitOps 方法部署应用工作负载。