发布工程:软件构建、自动化发布与持续部署实践
发布工程是软件工程领域中一个相对较新、且发展迅速的分支,核心关注软件的构建、交付、自动化发布和持续部署,可以简要概括为:软件的构建与交付[McN14a]。发布工程师通常需要对源代码管理、编译器、构建配置语言、自动化构建工具、包管理器和安装程序有扎实的理解,虽然他们未必在每个领域都达到专家水平。他们的能力横跨多个方向,包括软件开发、配置管理、测试集成、系统管理和客户支持。
可靠的服务离不开可靠的发布流程。站点可靠性工程师(SRE)需要确保所使用的二进制文件和配置,能够以可复现、自动化的方式构建出来。这样才能保证发布流程可重复,避免出现依赖人工经验或临时操作的“特殊个案”。发布流程中任何环节的变化,都应该是经过审慎设计的结果,而不是偶然发生的副作用。SRE 关注的是从源代码到部署上线的完整链路。

发布工程:软件构建、自动化发布与持续部署实践
在海外某大型科技公司,发布工程是一个明确的岗位职能。发布工程师会与产品研发团队中的软件工程师(SWE)以及站点可靠性工程师(SRE)协作,定义软件发布所需的全部步骤:从软件在源代码库中的组织方式,到编译所需的构建规则,再到测试、打包和部署的执行方式。
发布工程师的角色
海外某些大型科技公司高度重视数据驱动,发布工程也遵循这一理念。发布工程团队会建设多种工具,用于报告一系列关键指标,例如代码变更部署到生产环境所需的时间,也就是发布速度;以及构建配置文件中各类功能的使用情况[Ada15]。这些工具大多由发布工程师构思并开发。
发布工程师还会制定工具使用的最佳实践,确保各项目能够以一致、可重复的方式完成软件发布。这些最佳实践覆盖发布流程的各个环节,例如编译器参数、构建标识标签的格式,以及构建过程中必须执行的步骤。确保工具默认可用、行为可靠且文档完善,可以让团队专注于功能和用户,而不是在软件发布过程中低效地重复造轮子。
在大型技术组织中,SRE 通常负责安全地部署产品,并保障线上服务稳定运行。为了确保发布流程能够满足业务需求,发布工程师和 SRE 会共同制定策略,用于分阶段发布变更、在不中断服务的情况下推送新版本,以及在功能出现问题时快速回滚。
发布工程的核心理念
发布工程遵循工程化和服务导向的理念。这一理念主要体现在以下四项原则中。
自助服务模式
为了实现规模化运作,各团队必须具备足够的自主性。发布工程团队会开发最佳实践和配套工具,使产品研发团队能够控制并运行自己的发布流程。即使组织内有数千名工程师和众多产品,只要各团队能够自行决定新版本的发布频率和发布时间,整体发布效率仍然可以保持在很高水平。
发布流程可以实现高度自动化,工程师只需极少介入。许多项目会通过自动化构建系统与部署工具的组合,实现自动构建和自动发布。真正成熟的自动化发布意味着:在流程正常运行时不需要人工干预,只有在出现异常时才需要工程师介入处理。
高速度发布
面向用户的软件通常会频繁发布,因为团队希望尽快将新功能交付给用户。频繁发布的核心理念是:缩短版本间隔,减少相邻版本之间的变更量。这样可以降低测试和故障排查的复杂度。
一些团队会每小时构建一次,然后从生成的构建版本池中选择最终部署到生产环境的版本。选择依据包括测试结果,以及该构建版本中包含的功能。另一些团队则采用“绿色推送”模式,也就是部署所有通过测试的构建版本[Kle14]。
封闭式构建
构建工具必须确保一致性和可重复性。如果两名工程师在不同机器上,使用源代码库中相同的版本号构建同一个产品,那么预期结果应当完全一致。
为实现这一点,构建过程应当是封闭式的。也就是说,构建结果不应受到构建机器上已安装库文件或其他软件的影响。构建过程应依赖已知版本的构建工具,例如编译器;以及已知版本的依赖项,例如基础库。构建过程本身应当自包含,不能依赖构建环境之外的外部服务。
当需要修复生产环境中正在运行的软件缺陷时,重新构建旧版本可能会很有挑战。常见做法是基于与原始版本相同的版本号重新构建,并纳入之后提交的特定变更。这种策略通常称为 cherry-pick,即选择性合入。
构建工具本身也会根据被构建项目源代码库中的版本号进行版本控制。因此,如果需要进行 cherry-pick,上个月构建的项目不会使用本月版本的编译器,因为新版本编译器可能包含不兼容或并不需要的功能。
策略与流程执行
多层安全防护和访问控制机制决定了在项目发布过程中,哪些用户可以执行哪些特定操作。受限制的操作包括:
批准源代码变更。此类操作通常通过分布在代码库各处的配置文件进行管理。
明确发布过程中需要执行的操作。
创建新版本。
批准初始集成请求,即请求基于源代码库中的特定修订号执行构建。
批准后续的 cherry-pick 请求。
部署新版本。
修改项目的构建配置。
几乎所有代码库变更都需要经过代码审查。代码审查已经被整合进日常开发工作流,是一项轻量但必要的流程。自动化发布系统会生成一份报告,列出某个版本中包含的全部变更,并将该报告与其他构建产物一起归档。
这份报告能够让 SRE 清楚了解项目新版本中包含了哪些变更。一旦版本出现问题,SRE 可以据此更快定位故障原因,加快排查和恢复速度。
持续构建与持续部署
海外某些大型技术组织会建设自动化发布系统,用于支撑大规模软件发布。这类系统通常会结合多项内部技术,提供可扩展、封闭且可靠的发布框架。以下内容将介绍一种典型的软件生命周期,以及如何使用自动化发布系统和相关工具来管理这一生命周期。
构建
内部构建系统通常是大型技术组织首选的构建工具。它可以支持多种语言的二进制文件构建,包括 C++、Java、Python、Go 和 JavaScript 等常见语言。工程师会使用构建系统定义构建目标,例如构建输出结果,如 JAR 文件,并为每个目标指定依赖项[Kem11]。执行构建时,构建系统会自动构建其依赖目标。
自动化发布系统的项目配置文件会定义二进制文件和单元测试的构建目标。发布系统会将项目特定的参数传递给构建系统,例如唯一的构建标识符。所有二进制文件都应支持显示构建日期、修订号和构建标识符的参数,这样就可以轻松地将某个二进制文件与其构建记录关联起来。
分支
所有代码都会提交到源代码树的主分支,也就是主线。然而,大多数大型项目并不会直接从主线发布。相反,团队会基于主线上的某个特定版本创建发布分支,并且不会将该发布分支中的变更再合并回主线。
缺陷修复会先提交到主线,然后再从主线 cherry-pick 到发布分支,纳入发布版本。这样可以避免无意中引入自上次构建以来提交到主线的无关变更。通过这种分支与 cherry-pick 相结合的方式,团队可以准确知道每个版本中究竟包含了哪些内容。
测试
持续测试系统会在每次提交变更时,对主线代码运行单元测试,使团队能够快速发现构建失败和测试失败。发布工程团队通常建议,持续构建中的测试目标应当与项目发布门禁中的测试目标保持一致。
同时,团队也会建议使用最近一次成功完成所有测试的持续测试构建所对应的修订号来创建版本。这些措施可以降低后续主线代码变更导致发布时构建失败的概率。
在发布过程中,团队会基于发布分支重新运行单元测试,并创建审计轨迹,以证明所有测试均已通过。这一步非常关键,因为如果发布涉及 cherry-pick,发布分支中可能包含主线中不存在的代码版本。团队需要确保测试是在实际将要发布的版本环境中通过的。
为了补充持续测试系统,团队还会使用独立测试环境,对打包后的构建产物运行系统级测试。这些测试既可以手动启动,也可以通过自动化发布系统启动。
打包
软件通常会通过内部包管理器分发到生产机器。包管理器会根据构建规则组装软件包,这些规则会列出应包含的构建产物,以及相应的所有者和权限。软件包会被命名,例如 service/example/frontend,使用唯一哈希值进行版本控制,并通过签名来确保真实性。
包管理器通常支持为特定版本的软件包添加标签。自动化发布系统会添加一个包含构建 ID 的标签,从而确保可以通过软件包名称和该标签唯一地引用某个软件包。
也可以为软件包添加标签,以标识该包在发布流程中的位置,例如 dev、canary 或 production。如果将已有标签应用到新包上,该标签会自动从旧包移动到新包。例如,如果某个包带有 canary 标签,那么之后安装该包 canary 版本的用户,将自动获得带有 canary 标签的最新版本。
自动化发布系统
自动化发布系统通常通过“蓝图”一类的配置文件进行配置。蓝图使用内部配置语言编写,用于定义构建和测试目标、部署规则,以及项目所有者等管理信息。基于角色的访问控制列表决定了哪些用户可以对某个发布项目执行特定操作。

发布工程:软件构建、自动化发布与持续部署实践
图 8-1. Rapid 架构的简化视图,展示了系统的主要组件
每个发布项目都包含工作流,用于定义发布过程中要执行的操作。工作流中的操作可以串行执行,也可以并行执行;一个工作流也可以启动其他工作流。自动化发布系统会将工作请求分派给任务执行器,这些执行器运行在生产基础设施之上。由于发布系统使用的是大规模生产基础设施,因此它可以同时处理数千个发布请求。
一个典型的自动化发布流程如下:
自动化发布系统使用请求的集成修订号创建发布分支。该修订号通常会从持续测试系统中自动获得。
发布系统调用构建工具编译所有二进制文件,并执行单元测试。通常,这两个步骤会并行执行。编译和测试运行在专用环境中,而不是运行在发布工作流本身所在的任务环境中。这种分离使相关工作更容易并行化。
构建产物随后可用于系统测试和金丝雀部署。典型的金丝雀部署会在系统测试完成后,在生产环境中启动少量作业。
流程中每个步骤的结果都会被记录。系统会生成一份报告,列出自上次发布以来的所有变更。
自动化发布系统还允许团队管理发布分支和 cherry-pick。每个 cherry-pick 请求都可以被批准或拒绝,以决定其是否应纳入发布版本。
部署
自动化发布系统通常可以直接驱动相对简单的部署。它会根据蓝图文件中的部署定义,以及专门的任务执行器,更新线上作业,使其使用新构建的软件包。
对于更复杂的部署,团队通常会使用通用部署自动化框架。部署可以被视为一个逻辑工作单元,由一个或多个独立任务组成。部署自动化框架通常会提供一组可扩展的类或接口,用于支持各种部署流程。它还会提供仪表盘,以便更精细地控制部署执行方式,并监控部署进度。
在典型的集成过程中,自动化发布系统会在一个长时间运行的部署作业中创建部署计划。发布系统知道与其创建的软件包相关联的构建标签,并且可以在创建部署计划时指定该构建标签。部署框架会使用该构建标签来确定应部署哪个版本的软件包。
借助部署自动化框架,部署过程可以根据需要设计得非常简单,也可以非常复杂。例如,它既可以立即更新所有相关作业,也可以在数小时内将新的二进制文件逐步部署到各个集群。
发布流程的目标,是根据特定服务的风险状况调整部署策略。在开发或预生产环境中,团队可能每小时构建一次,并在所有测试通过后自动推送版本。对于大型面向用户的服务,团队可能先从一个集群开始推送,然后以指数级方式逐步扩大范围,直到所有集群都完成更新。对于敏感基础设施,团队可能会将部署过程延长到数天,并在不同地理区域的实例之间交错推进。
配置管理
配置管理是发布工程师与 SRE 密切协作的领域之一。虽然配置管理乍看之下似乎很简单,但配置变更同样可能导致系统不稳定。因此,系统和服务配置的发布与管理方式,通常会随着组织规模和系统复杂度的提升而不断演进。
如今,大型技术组织通常会使用多种模型来分发配置文件。无论采用哪种方案,配置通常都会存储在主源代码库中,并严格执行代码审查要求。
使用主线管理配置
这是许多早期系统最初用于服务配置的方法。在这种方案下,开发人员和 SRE 会在主分支的头部修改配置文件。修改后的配置文件经过审查后,会被应用到运行中的系统。因此,二进制版本发布与配置变更是解耦的。
虽然这种方法在概念和流程上都很简单,但由于线上作业必须更新后才能应用这些变更,因此它经常导致代码库中已提交的配置文件版本,与实际运行中的配置文件版本不一致。
将配置文件和二进制文件放入同一个软件包
对于配置文件较少的项目,或者配置文件及其部分内容会随每个发布周期变化的项目,可以将配置文件与二进制文件一起包含在同一个软件包中。虽然这种策略将二进制文件和配置文件紧密绑定在一起,限制了一定的灵活性,但它简化了部署流程,因为只需要安装一个包。
将配置文件打包成配置包
封闭式构建的原则同样可以应用到配置管理中。二进制配置通常与特定版本的二进制文件紧密绑定,因此可以利用构建和打包系统对配置文件进行快照,并将其与二进制文件一起发布。与处理二进制文件的方式类似,团队可以使用构建 ID 来重建某一特定时间点的配置。
例如,实现新功能的变更可以通过配置中的功能标志来发布。通过生成两个软件包,一个用于二进制文件,另一个用于配置,团队可以分别独立修改它们。也就是说,如果某个功能最初发布时,配置标志 first_folio 被设置为 true,但后来发现它应当被设置为 false,则可以将该变更 cherry-pick 到发布分支,重新构建配置包,然后进行部署。这种方法的优势在于不需要重新构建二进制文件。
团队还可以利用包管理器的标签功能来指定哪些版本的软件包应当一起安装。例如,可以为上一段中描述的两个软件包添加同一个标签 much_ado,这样就可以通过该标签同时获取这两个包。当项目构建新版本时,much_ado 标签会被应用到新的包上。由于这些标签在软件包命名空间内是唯一的,因此系统只会使用带有该标签的最新包。
从外部存储读取配置文件
有些项目的配置文件需要频繁变更,甚至需要动态变更,例如在二进制文件运行期间变更。这些文件可以存储在分布式协调服务、结构化存储系统,或基于源代码的文件系统中[Yor11]。
总而言之,项目所有者需要综合考虑配置文件分发和管理的不同选项,并根据具体情况决定哪种方案最合适。
结论:为什么企业需要重视发布工程
虽然本文重点介绍的是海外某大型技术组织的发布工程实践,以及发布工程师与 SRE 的协作方式,但这些方法也具有更广泛的适用性。
发布工程并非大型公司专属
只要具备合适的工具、适当的自动化流程和完善的策略,开发人员和 SRE 就不必为软件发布而焦虑。理想情况下,发布可以像按下一个按钮一样简单可靠。
大多数公司无论规模大小、使用何种工具,都会面临相似的发布工程问题:如何管理软件包版本?应该采用持续构建和持续部署模型,还是采用定期构建模型?发布频率应当多高?应该使用哪些配置管理策略?哪些发布指标值得关注?
对于希望把需求、开发、测试、发布上线和知识沉淀串联起来的团队,PingCode 这类智能化研发管理工具可以作为发布工程落地的重要支撑,帮助企业将研发管理流程自动化、数据化,并打通研发过程中使用的各类工具,使数据在不同环节之间顺畅流转。
由于开源工具或供应商提供的工具往往难以满足超大规模场景下的需求,一些海外大型技术组织会根据自身需要开发定制化工具。定制化工具使团队能够添加相应功能,以支持甚至强制执行发布流程策略。然而,必须先定义这些策略,才能在工具中加入相应功能。所有公司都应该努力定义自己的发布流程,无论这些流程是否能够被自动化或强制执行。
从一开始就重视发布工程
发布工程常常被忽视。随着平台和服务规模不断扩大、复杂度不断增加,这种观念必须改变。
团队应当在产品开发周期的早期就为发布工程资源预留预算。尽早建立良好的实践和流程,比后期再对系统进行改造要经济得多。
开发人员、SRE 和发布工程师之间的协作至关重要。发布工程师需要理解代码预期如何构建、如何部署。开发人员不应该在构建完成后,就把结果简单地“扔给发布工程师”处理。
在跨团队协作场景中,Worktile 这类通用项目协作系统也可以帮助团队承载任务、项目、文档、日历、审批等协作事项,让发布工程相关的计划、执行和沟通更容易被统一管理。
各个项目团队会自行决定何时将发布工程引入项目。由于发布工程仍是一门相对新兴的学科,管理者往往不会在项目早期就对发布工程进行规划和预算。因此,在考虑如何融入发布工程实践时,务必将发布工程的作用放在产品或服务的整个生命周期中审视,尤其要重视早期阶段。
