GitOps and Kubernetes Continuous Deployment with Argo CD, Jenkins X, and Flux

持续集成 (CI) 是一种软件开发实践,所有开发人员都将代码更改合并到中央存储库 (Git) 中。使用 CI,每个代码更改(提交)都会触发给定存储库的自动构建和测试阶段,并向进行更改的开发人员提供反馈。 GitOps 与传统 CI 的主要区别在于,使用 GitOps,CI 管道还会在成功完成构建和测试阶段后使用新的映像版本更新应用程序清单。

持续交付(CD)是自动化整个软件发布过程的实践。除了部署之外,CD 还包括基础设施配置。 GitOps CD 与传统 CD 的不同之处在于使用 GitOps operator 来监视清单更改并编排部署。只要 CI 构建完成并且清单更新,GitOps operator 就会负责最终的部署。

1、GitOps CI

1.1、预构建阶段

以下阶段也称为静态分析阶段。它们是在构建代码并将其打包到 Docker 映像之前对代码进行手动和自动扫描的组合

  • Pull request/code review

所有 CI/CD 管道应始终以拉取请求开始,这允许代码审查以确保设计和实现之间的一致性并捕获其他潜在错误。代码审查还有助于共享最佳实践、编码标准和团队凝聚力。

  • Vulnerability scan

开源库无需定制开发即可提供许多功能,但这些库也可能存在漏洞、缺陷和许可问题。集成开源库扫描工具(例如 Nexus Vulnerability Scanner)可以在开发周期的早期检测已知漏洞和许可问题,并通过升级库或使用替代库来修复问题。

  • Code analysis

虽然手动代码审查对于设计和实现一致性非常有效,但编码标准、重复代码和代码复杂性问题更适合自动 linting 或代码分析工具(例如 SonarQube)。这些工具并不能替代代码审查,但它们可以更有效地发现普通问题。

1.2、构建阶段

静态分析之后,就可以构建代码了。除了构建和创建可部署文件(也称为 Docker 映像)之外,单元(模块)测试和单元测试的有效性(代码覆盖率)也是构建过程中不可或缺的部分。

  • Build

构建阶段通常从下载依赖库开始,然后再实际编译项目源代码。 (Python 和 Node.js 等脚本语言不需要编译。)对于 Java、Ruby 和 Go 等编译语言,代码将使用相应的编译器编译为字节码/机器二进制文件。另外,生成的二进制文件及其依赖库需要打包成可部署单元(例如Java中的jar或war)进行部署。

  • Unit test

单元测试用于验证一小段代码是否执行了预期的操作。单元测试不应该依赖于被测试单元之外的代码。单元测试主要侧重于测试各个单元的功能,并不能揭示不同模块相互交互时出现的问题。在单元测试期间,通常会“模拟”外部调用,以消除依赖性问题并减少测试执行时间。

  • Code coverage

代码覆盖率衡量自动化单元测试覆盖的代码的百分比。代码覆盖率测量只是确定代码体中的哪些语句已通过测试运行执行,哪些语句尚未执行。一般来说,代码覆盖率系统会检测源代码并收集运行时信息以生成有关测试套件代码覆盖率的报告。

代码覆盖率是开发过程中反馈循环的关键部分。随着测试的开发,代码覆盖率会突出显示代码中可能未充分测试并需要额外测试的方面。此循环将持续下去,直到覆盖范围达到某个指定的目标。覆盖范围应遵循 80-20 规则,因为增加覆盖范围值变得困难且回报减少。

仅提高代码覆盖率可能会导致错误的行为,并且实际上可能会降低质量。代码覆盖率衡量正在执行的行的百分比,但不衡量代码的正确性。带有部分断言的 100% 代码覆盖率将无法实现单元测试的质量目标。我们的建议是随着时间的推移专注于增加单元测试的数量和代码覆盖率,而不是专注于绝对代码覆盖率数字。

  • Docker build

Docker 镜像是 Kubernetes 的可部署单元。构建代码后,您可以通过创建 Dockerfile 并执行 docker build 命令,为构建工件创建具有唯一镜像 ID 的 Docker 映像。 Docker 镜像应该有其独特的命名约定,并且每个版本都应该标有唯一的版本号。此外,您还可以在此阶段运行 Docker 映像扫描工具来检测基础映像和依赖项的潜在漏洞问题。

由于 Git 为每次提交创建唯一的哈希值,因此建议使用 Git 哈希值来标记 Docker 映像,而不是创建任意版本号。除了唯一性之外,每个 Docker 镜像还可以使用 Git 哈希轻松追溯到 Git 存储库历史记录,以确定 Docker 镜像中的确切代码。

  • Docker push

新构建的 Docker 镜像需要发布到 Docker 注册表,以便 Kubernetes 协调最终的部署。 Docker 注册表是一种无状态、高度可扩展的服务器端应用程序,用于存储并允许您分发 Docker 映像。对于内部开发,最佳实践是托管一个私有注册表,以严格控制图像的存储位置。

1.3、GitOps CI 阶段

对于传统 CI,管道将在构建阶段后结束。使用 GitOps,需要额外的 GitOps 特定阶段来更新最终部署的清单。

  • Git clone config repo

假设 Kubernetes 配置存储在单独的存储库中,此阶段将执行 Git 克隆,将 Kubernetes 配置克隆到构建环境,以便后续阶段更新清单。

  • Update manifests

在构建环境中拥有清单后,您可以使用 Kustomize 等配置管理工具使用新创建的镜像 ID 更新清单。根据您的部署策略,一个或多个特定于环境的清单将使用新的镜像 ID 进行更新。

  • Git commit and push

使用新的镜像 ID 更新清单后,最后一步是将清单提交回 Git 存储库。至此,CI pipeline 已完成。您的 GitOps operator 会检测清单中的更改并将更改部署到 Kubernetes 集群。

1.4、构建后阶段

GitOps CI 的一切完成后,需要额外的阶段来收集持续改进和审计报告的指标,并通知团队构建状态。

  • Publish CI metrics

CI 指标应存​​储在单独的数据存储中:

构建问题:开发团队需要相关数据来对构建失败或单元测试失败的问题进行分类。

CI构建时间过长:会影响工程团队的行为和生产力。代码覆盖率的减少可能会导致更多的生产缺陷。拥有历史构建时间和代码覆盖率指标使团队能够监控趋势、减少构建时间并提高代码覆盖率。

合规性要求:对于 SOC2 或 PCI 要求,构建信息(例如测试结果、发布者以及发布的内容)需要维护 14 个月到 7 年不等。

  • Build notification

对于 CI/CD 部署,大多数团队更喜欢“没有消息就是好消息”模型,这意味着如果所有阶段都成功,他们不需要担心构建状态。如果出现构建问题,应立即通知团队,以便他们获得反馈并纠正问题。此阶段通常使用团队消息传递或电子邮件系统来实现,因此只要 CI/CD 管道完成,团队就可以收到通知。

2、GitOps CD

2.1、GitOps CD 阶段

这些是 GitOps operator 根据清单更改执行部署的逻辑阶段:

  • Git clone config repo

GitOps operator 检测您的存储库中的更改并执行 Git 克隆以获取 Git 存储库的最新清单。

  • Discover manifests

GitOps Operator 还确定 Kubernetes 中的清单与 Git 存储库中的最新清单之间的任何差异。如果没有差异,GitOps Operator 将在此时停止。

  • Kubectl apply

如果 GitOps Operator 确定 Kubernetes 清单和 Git 存储库清单之间存在差异,GitOps Operator 将使用 kubectl apply 命令将新清单应用到 Kubernetes。

2.2、部署后阶段

部署镜像后,我们可以针对依赖项和运行时漏洞对新代码进行端到端测试。

  • Integration tests

集成测试是一种检查不同模块是否正常工作的测试。将镜像部署到 QA 环境中后,集成测试可以跨多个模块和其他外部系统(例如数据库和服务)进行测试。集成测试的目的是发现当不同模块交互执行单元测试无法涵盖的高级功能时出现的问题。

  • Run-time vulnerability

传统上,运行时漏洞是通过渗透测试来检测的。渗透测试,也称为笔测试或道德黑客,是测试计算机系统、网络或 Web 应用程序以查找攻击者可能利用的安全漏洞的做法。典型的运行时漏洞是 SQL 注入、命令注入或发出不安全的 cookie。可以使用 Contrast4 等代理工具来对 QA 环境进行检测,同时执行集成测试以在开发周期的早期检测任何运行时漏洞,而不是在生产系统中进行渗透测试(成本高昂且事后进行)。

  • Publish CD metrics

CD 指标应存​​储在单独的数据存储中:

运行时问题:开发团队需要相关数据来对部署、集成测试失败或运行时漏洞等问题进行分类。

合规性要求:对于 SOC2 或 PCI 要求,构建信息(例如测试结果、发布者以及发布的内容)需要维护 14 个月到 7 年不等。

3、合规管道

对于合规性管道,有一个与 preprod CI/CD 管道分开的生产管道。在 CI/CD 管道的末尾,有一个阶段会使用新映像 ID 生成新 PR 到生产清单存储库。任何获得批准的 PR 都将部署到生产中。