你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

微服务体系结构的 CI/CD

Azure

更快的发布周期是微服务体系结构的主要优势之一。 但是,如果没有良好的 CI/CD 过程,你将无法实现微服务承诺的敏捷性。 本文介绍挑战,并建议一些解决问题的方法。

什么是 CI/CD?

当我们谈到 CI/CD 时,我们实际上正在讨论几个相关流程:持续集成、持续交付和持续部署。

  • 持续集成。 代码更改经常合并到主分支中。 自动化生成和测试过程可确保主分支中的代码始终具有生产质量。

  • 持续交付。 通过 CI 过程的任何代码更改都会自动发布到类似于生产的环境。 部署到实时生产环境可能需要手动批准,但本来是自动化的。 目标是,代码应始终 准备好 部署到生产环境。

  • 持续部署。 通过上述两个步骤的代码更改会自动部署到生产

下面是微服务体系结构可靠 CI/CD 过程的一些目标:

  • 每个团队都可以独立构建和部署它拥有的服务,而不会影响或中断其他团队。

  • 在将新版本的服务部署到生产环境之前,它会部署到开发/测试/QA 环境进行验证。 每个阶段都强制实施质量入口。

  • 新版本的服务可以与以前的版本并排部署。

  • 已准备好足够的访问控制策略。

  • 对于容器化工作负荷,可以信任部署到生产环境的容器映像。

为什么可靠的 CI/CD 管道很重要

在传统的整体应用程序中,有一个生成管道,其输出是应用程序可执行文件。 所有开发工作都馈送到此管道中。 如果发现高优先级 bug,则必须集成、测试和发布修补程序,这可能会延迟新功能的发布。 你可以通过具有适当因素的模块并使用功能分支来最大程度地减少代码更改的影响来缓解这些问题。 但是,随着应用程序变得越来越复杂,并且添加了更多功能,整体式的发布过程往往变得更加脆弱,并且可能会中断。

遵循微服务的理念,永远不应该有很长的发布培训,每个团队都必须排队。 构建服务“A”的团队可以随时发布更新,而无需等待服务“B”中的更改合并、测试和部署。

CI/CD 整体 的 关系图

若要实现高发布速度,发布管道必须自动化且高度可靠,以最大程度地降低风险。 如果每天发布一次或多次生产,则回归或服务中断必须很少见。 同时,如果部署了错误的更新,则必须有一种可靠的方法来快速回滚或回滚到以前版本的服务。

挑战

  • 许多小型独立代码库。 每个团队都负责生成自己的服务,并创建自己的生成管道。 在某些组织中,团队可以使用单独的代码存储库。 单独的存储库可能会导致了解如何构建系统的情况分散在团队中,并且组织中的任何人都不知道如何部署整个应用程序。 例如,如果需要快速部署到新群集,灾难恢复方案中会发生什么情况?

    缓解:具有统一和自动化的管道来生成和部署服务,以便每个团队中不会“隐藏”此知识。

  • 多种语言和框架。 由于每个团队都使用自己的技术组合,因此很难创建一个跨组织工作的生成过程。 生成过程必须足够灵活,每个团队都可以根据所选的语言或框架对其进行调整。

    缓解:容器化每个服务的生成过程。 这样,生成系统只需能够运行容器。

  • 集成和负载测试。 随着团队以自己的速度发布更新,设计可靠的端到端测试可能会很困难,尤其是在服务依赖于其他服务时。 此外,运行完整生产群集可能非常昂贵,因此,每个团队不太可能在生产规模上运行自己的完整群集,只是为了进行测试。

  • 发布管理。 每个团队都应能够将更新部署到生产环境。 这并不意味着每个团队成员都有权这样做。 但是,拥有集中式发布管理器角色可以降低部署速度。

    缓解:CI/CD 过程自动化且可靠越多,中心机构需要的就越少。 也就是说,你可能有不同的策略来发布主要功能更新与次要 bug 修复。 分散并不意味着零治理。

  • 服务更新。 将服务更新到新版本时,它不应中断依赖于它的其他服务。

    缓解:使用蓝绿或金丝雀发布等部署技术进行非重大更改。 对于中断性 API 更改,请与以前的版本并排部署新版本。 这样,可以使用以前的 API 的服务可以针对新 API 进行更新和测试。 请参阅下面的 更新服务

Monorepo 与多存储库

在创建 CI/CD 工作流之前,必须了解代码库的结构和管理方式。

  • 团队是否在单独的存储库或 monorepo(单存储库)中工作?
  • 分支策略是什么?
  • 谁可以将代码推送到生产环境? 是否有发布管理器角色?

单一存储库方法一直在获得青睐,但两者都有优点和缺点。

  Monorepo 多个存储库
优势 代码共享
更轻松地标准化代码和工具
更轻松地重构代码
可发现性 - 代码的单个视图
清除每个团队的所有权
合并冲突可能更少
帮助强制分离微服务
挑战 对共享代码的更改可能会影响多个微服务
合并冲突的可能性更大
工具必须扩展到大型代码库
存取控制
更复杂的部署过程
更难共享代码
更难强制实施编码标准
依赖项管理
漫射代码库,可发现性差
缺少共享基础结构

更新服务

有各种策略可用于更新已在生产中的服务。 在这里,我们将讨论三个常见选项:滚动更新、蓝绿部署和 Canary 发布。

滚动更新

在滚动更新中,部署服务的新实例,新实例会立即开始接收请求。 当新实例出现时,将删除以前的实例。

例。 在 Kubernetes 中,在更新 部署的 pod 规范时,滚动更新是默认行为。 部署控制器为更新的 Pod 创建新的 ReplicaSet。 然后,它会纵向扩展新的 ReplicaSet,同时纵向缩减旧副本集,以保持所需的副本计数。 在新 Pod 准备就绪之前,它不会删除旧 Pod。 Kubernetes 会保留更新的历史记录,因此如果需要,可以回滚更新。

例。 默认情况下,Azure Service Fabric 使用滚动更新策略。 此策略最适合在不更改现有 API 的情况下部署具有新功能的服务版本。 Service Fabric 通过将应用程序类型更新到节点或更新域的子集来启动升级部署。 然后,它会前滚到下一个更新域,直到升级所有域。 如果升级域无法更新,应用程序类型将回滚到所有域的以前版本。 请注意,具有多个服务的应用程序类型(如果所有服务作为一个升级部署的一部分进行更新)很容易失败。 如果一个服务无法更新,整个应用程序将回滚到以前的版本,其他服务不会更新。

滚动更新的一个难题是,在更新过程中,旧版本和新版本的混合运行和接收流量。 在此期间,任何请求都可以路由到这两个版本之一。

对于中断性 API 更改,最佳做法是并行支持这两个版本,直到更新以前版本的所有客户端。 请参阅 API 版本控制

蓝绿部署

在蓝绿部署中,将新版本与以前的版本一起部署。 验证新版本后,将所有流量一次性从以前的版本切换到新版本。 切换后,可以监视应用程序是否存在任何问题。 如果出现问题,可以交换回旧版本。 假设没有任何问题,可以删除旧版本。

使用更传统的整体式或 N 层应用程序,蓝绿部署通常意味着预配两个相同的环境。 将新版本部署到过渡环境,然后通过交换 VIP 地址将客户端流量重定向到过渡环境。 在微服务体系结构中,更新发生在微服务级别,因此通常会将更新部署到同一环境中,并使用服务发现机制进行交换。

示例。 在 Kubernetes 中,无需预配单独的群集来执行蓝绿部署。 相反,你可以利用选择器。 使用新的 Pod 规格和一组不同的标签创建新的 部署 资源。 创建此部署,而无需删除以前的部署或修改指向它的服务。 运行新 Pod 后,可以更新服务的选择器以匹配新部署。

蓝绿部署的一个缺点是,在更新期间,运行的 Pod 数是服务的两倍(当前和下一个)。 如果 Pod 需要大量 CPU 或内存资源,可能需要暂时横向扩展群集来处理资源消耗。

Canary 发布

在 Canary 版本中,向少量客户端推出更新的版本。 然后,在向所有客户端推出新服务之前监视新服务的行为。 这样,便可以以受控方式进行缓慢的推出,观察实际数据,并在所有客户受到影响之前发现问题。

Canary 版本比蓝绿或滚动更新要复杂得多,因为必须将请求动态路由到不同版本的服务。

示例。 在 Kubernetes 中,可以将 服务 配置为跨两个副本集(每个版本一个),并手动调整副本计数。 但是,这种方法相当粗略,因为 Kubernetes 跨 Pod 进行负载均衡的方式。 例如,如果总共有 10 个副本,则只能以 10% 增量转移流量。 如果使用服务网格,可以使用服务网格路由规则来实现更复杂的 Canary 发布策略。

后续步骤