近年来,各企业都已在实践 DevOps 的流程、方法和工具,来提升业务价值交付的效率。不同企业的业务团队背景和基础各不相同,因此都在走自身的 DevOps 之路,往往伴随着成功的经验和失败的教训,了解和学习不同背景下的 DevOps 实践大有裨益。
2023 年 QCon 全球软件开发大会上,字节跳动效能领域技术专家姚志坤就「DevOps 交付流程与演进」这一主题分享了相关技术成果及实践经验。
第一部分,概述早期字节跳动 DevOps 发展的背景,整体演进的思路和框架的设计;
第二部分,演进的初期阶段,通过围绕自动化的建设,字节快速构建起可以落地的 DevOps 流水线平台;
第三部分,深入业务后,我们探索了协同式的交付流程,并如何实施落地;
第四部分,经验回顾及我们近期探索方向。
首先我先简介一下软件交付的痛点,我相信这是很多公司都会面临的问题。
我们常见的交付痛点很多,在需求阶段有易变的问题,在开发阶段有上下文交接损耗的问题,集成阶段有环境的问题,发布阶段有阻塞的问题,总之有各种各样的坑。这些痛点往往浪费了时间、浪费了资源、也降低了交付质量。
一般来说我们会将结论归于一些交付流程的问题,譬如流程不规范、流程冗长、缺少必要的流程等等,但往往又不知道如何改进,尝试改了这个又会引入新的痛点。
而实际上,交付流程来源于一系列的现实复杂性:
第一,业务的复杂性。
软件业务本身都有探索的部分,行业方向、公司策略的变化经常发生,不可避免造成需求的易变,同时质量速度平衡的天平也是在变化中。因此我们期望交付流程适应业务变化。
第二,团队的复杂性。
我们知道,大小团队都有各式各样的人组成,能力水平和成熟度是一个变量,团队角色和分工协作方式也各不相同,因此我们期望交付流程适应团队的情况。
第三,技术的复杂性。
大部分公司是有存量技术架构、工具基础,技术栈的优劣需要考虑, Python 还是 Go 、微服务架构还是单体架构,对交付的模式都有很大的影响。因此交付流程要适配现有技术架构。
总之,交付流程需要匹配所服务的公司的现实复杂度,这是一个需要动态调整的过程。
同样,我们在字节也会面临类似的问题痛点,除此之外还有一些特点:
第一,业务规模问题。
受团队规模和业务规模影响,不同微服务应用量级到数十万 ,技术栈和服务框架种类繁多。
第二,安全问题。
技术规模大之后,安全问题会被放大,很小的疏漏能造成不小的影响。
第三,多元的业务和团队。
不同业务和团队对于交付的诉求是不一样的,公司角度也是期望业务跑得更快。
第四,工具基础。
和很多公司初期发展阶段类似,基础设施不统一,每个业务自建了定制化的工具和平台,影响使用效率。
总之,很多规模化和多样性的问题摆在面前,如何设计匹配字节跳动这样的规模化场景的交付流程就是我们的重点解决的问题。
谈一下我们主要的破解方法,就是通过开放共建的流水线体系为底座,打造业务可自定义的自动化和协同流程。
第一个关键词是开放共建,为什么需要开放共建?
一,人力有限的情况下,开放可以最大限度地利用已有工具;
二,通用的工具和关键链路的工具可以集中力量优化,比如部署工具;
三,业务可以基于自身情况定制工具,不依赖平台方排期,快速达到业务目标。
第二个关键词是业务自定义,作为架构平台要支持所有的业务,我们的定位是赋能业务,只有一线的业务自身才能定义好优实践,而自动化和可视化协同是可以将交付过程中的一些复杂问题简单化。
因此从解决方案的概念图来看,自下而上有三层,对应着不同的目标层级。最下层的点代表原子能力,比如需求、开发、测试领域,做到单点的自动化提效;中层通过流水线链路做串联,做到全链路提效;上层在流水线之上进行进一步的整合,通过统一的视图管理,做到协同提效。
在最上层的典型业务场景中,有一些交付特点,比如 ToC 业务,要敢于试错,常用 A/B 实验小流量,保障基本的质量,能快速地交付需求。而商业类业务如广告,则需要控制资损,需要更严格的交付验收标准。
中台业务支持很多上层业务,需要有统一的升级节奏支持不同的业务线。也就是说,各业务接口人会根据自身情况,在标准的交付流程框架下定义研发流程和规范,并落地。
而在业务场景之下,我们提供了 3 套基础的交付流程框架:自动化为特点的单服务流水线、协同视图为特点的需求交付模式和版本火车模式。三者分别提供从灵活多样到协调一致的方案。除此之外,效能度量、安全管控、开放共建的方案也需要支撑业务落地。
第三层就是平台层了,我们自建了一个以流水线为核心的效能平台,通过标准的对接系统,流水线的标准化、原子服务的标准化、变量参数的标准化,对接了字节的各种基建能力,包含需求、开发、测试、发布等领域的工具和平台。
这是今天主要介绍的,三套主流交付流程解决方案,介绍一部分特点和业务逻辑,单应用的流水线交付,协同的两套模式。
当然,这样一套体系也不是一次性完成的。这是我们实际的演进节奏,主要分成 3 个阶段。
第一阶段,我们在 2019 年及之前是在做流水线基建部分,建设了标准的环境系统、版本和发布的能力,也就是说早期是在做标准化的工作,基本完成了有能够支持业务使用的最基础的 CICD 流程。在那个阶段,字节业务需要的是基本的流水线能力,替换自建的一些开源工具,提升稳定性和可用性。
第二阶段,从 2020 年开始,我们发力提升开放能力,因此业务的自建工具纷纷接入进来,随着生态的扩大,越来越多的业务在流水线系统上固化自动化的流程。在那个阶段,字节业务高速发展并直面大规模的用户增长, QA 团队纷纷建立起来,也急需开放式的工具链使得业务跑得更快。
第三阶段,就是 2021 年后,随着字节业务进入质量效率并重的阶段,业务的角色分工进一步加强,加上一些 ToB 业务的启动需要更规范的流程。协同场景被提出探索和构建起来,开始满足团队管理的一些诉求。
自动化在软件开发中是属于入手不难,做好却不容易的主题。
我这样分析自动化和交付流程之间的底层逻辑:我们做软件工程最基础的要求,无非就是效率、质量、可持续维护和迭代。而现在流行的持续集成、持续交付、精益 DevOps 这些理念都有一个共同的关键词,那就是自动化,比如说持续集成,强调频繁的自动化,持续交付强调可靠可重复的流水线,精益关注过程损耗、流动效率,人工和自动化能有机结合。
所以自动化提效的典型路径方案有 3 种:
一、提升单点的自动化效率,尽量让人工的过程自动化,频繁地跑起来。
二、建设自动化工具链,单点的自动化能串起来,形成规模效应。
三,更进一步,因为软件开发还是有很多人工决策的部分,现在还没有办法自动化的,需要和我们的自动化流程结合起来,避免一会儿线下操作,一会儿再跑流水线,也就是说,让我们人工活动和自动化的协作更加顺畅丝滑。
最后的落地就是通过这样一个能力丰富的、高可用的、交互顺畅的流水线了。
这是字节最常用流水线交付类型,以单个微服务为维度,用两条流水线分别对应需求开发分支和发布主干分支。分支策略其实就是分支开发、主干发布的双分支模式。
为什么双分支模式会成为字节的主流?因为字节大部分微服务拆的比较细,维护人员不多,通常在 2 到 7 人。双分支并不需要特别复杂的设计,特性分支和发布分支各承担一部分的测试职责。当然比较极限的主干开发主干发布也是存在的,毕竟在字节跳动,个人维护的微服务不少,也能通过流程编辑出来,而三分支或者 GitFlow 之类在字节的服务端并不常见,分支的复杂性需要额外的管理成本来处理。
如图上的三套流程,需求过程通常不在流水线上,需求开始、环境、拉分支等活动仍然是由线下完成。从研发提交特性分支开始,实际进入了持续集成的流水线,也就是特性分支流水线,下面都是比较常规的代码检测、个人环境、提测等过程。而到了发布阶段,是由研发发起主干 MR 开始,然后执行回归测试,直到线上小流量,灰度,线上监控阶段。
下面说到流水线原子服务,我们的方案特点就是自定义。流水线需要保证自动化的“车同轨”,有三大功能点支持自定义。
第一个就是 API 和 Hook 能力。
在此之上,我们的业务可以支持这些场景,将流水线集成到聊天机器人中,定制一些便捷的自动触发和自动通知的能力。
第二个是原子服务的市场。
通过这个开放性的市场,我们的业务可以定制自己的自动化能力,调用自己专用的工具和平台,通过这个市场,也可以找到类似的原子能力,多个业务共建,同时,将一个内部平台的能力抽象为一个原子,在其他业务或整个公司推广。我们的原子服务覆盖了多个阶段,比如能在需求领域流转状态,在代码领域自动做合码做 rebase 等等。
第三个是模版市场。
通过模版市场,自动化的流程可以在多个业务复制,也可以在一个业务线起到规范团队流程的作用,也就是说,虽然流水线是业务线自由编排的,但针对某个业务团队来说,流程编排仍然是可以统一的,做到规范落地。
再说一下变量系统,如果说流水线是“车同轨”,那么,强大的变量系统是“书同文”。原子服务、流水线和团队的模式就是靠变量系统做的数据交换。首先变量系统是标准化的,其次,也提供了一些扩展的能力。
变量系统主要有 3 种相对标准化的数据交换范围:原子服务的变量,流水线的变量,团队流程的变量。它们分别可以实现分支信息、版本信息、产物传递、团队分支规则、门禁参数等等。
同时我们也赋予变量一定可编程的能力,就像我们我们日常生活中的各种转接插头,很方便地将数据格式做一些转换,适配不同的原子能力的输入输出。
再介绍一下决策节点,流水线和变量是车同轨书同文,那么决策节点是必要的关卡和中转站。众所周知,软件研发的生产过程还有相当的手工活,没有人参与的自动化是不完整的,我们这边有很多对决策能力的支持。
分为两种类型,第一种是各种人工卡点和门禁,第二种是选择器和逻辑决策。
人工卡点比较好理解,诸如审批、提测节点之类,用于批准后续的执行;而选择器是一种逻辑判断,在流水线中实现逻辑分叉。
当我们需要根据变更代码的覆盖率,走不同的决策流程时,比如覆盖率低时,我们认为需要人工介入,覆盖率高时能自动通过,那么可以在选择器中编辑一行 if 逻辑,系统会自动决策下一步的流程。
而在另一个场景下,我们传统流水线设计中可能有常规流程和紧急流程,常规流程和紧急流程可能在不同的流程中,中间必然存在着一些沟通成本、维护成本,而通过选择器可以合二为一,我们通过流程发起时就可以在每个阶段固化哪些原子必须做哪些是可以跳过的。
有了决策节点后,流水线看上去会变长,但实际的效率却是更高了。省去了很多线下沟通的成本,清晰地刻画流程了。而有了一些开放能力的加持,字节的流水线可以做非常丰富的功能性的扩展。
一方面,自动化建设得好,强大的自定义能力,覆盖几乎所有的交付场景,复杂场景有上百节点。另一方面,繁荣生态,覆盖了需求到监控的能力,上千的开发者和原子。
然后进化为自动化不是免费的,自由也有代价。前期我们对标准化的投入,对于自动化的维护,稳定性、治理都是必要的。从稳定性来说,流水线的稳定性逻辑是这个样子的, 10 个节点的可用性 99.9%, 原子能力串在一起,可用性就成 99%,何况并不是所有的原子都能到 99.9%,实际落地的时候自动化流水线就需要确保这些基础投入,才能保证持续可用。
三、以价值流为主线的协同模式
关于协同,不得不先提一下价值流的概念。
我画的这个图叫价值的漏斗过程,揭示了人力在流程阶段中一步一步增值和损耗的过程。人力投入像一个水龙头一样流入到价值漏斗,我们有很多过程阶段,我们在每个阶段都有价值产出,要从用户的诉求出发提炼出用户痛点,设计方案,代码编译,验证,让业务流量;人不是机器,一定会有损耗,错误的决策容易造成很大的资源浪费。这些在每个阶段的损耗看似很小,但是整体会做乘法,就难以忽视了。甚至所有人的努力可能在一个阶段完全流尽,完全体现不出价值,比如在产品需求和用户诉求南辕北辙的情况下。
在这个过程中,有很多人、很多的事、很多的产出物,我们在研发过程中就是面对这些,也容易迷失掉重点,每个人都在忙,以为很高效,实际并没有产出价值。因此我们认为过程管理需要识别软件开发到底交付的价值是什么,以什么为中心进行交付活动的组织。
面向我们交付的内容,可以形成一个中心的价值产出,那么围绕这个价值产出,我们做协同是有效的。比如开发测试阶段,我们围绕的价值产出是代码。而如果是开发到运维,代码是不够的,就需要变更我们围绕着的这个价值产出。更大的协同中心就是业务需求,或者 ToB 领域的直接交付。
因此以价值流为主线的协同,我们需要识别出协同的核心是什么,并且协同的范围越大,我们越需要一致的节奏和模式,人越多,越需要劲往一处使,这是我们的理念和底层逻辑。
实际实践角度来看,我们的协同层次有代码、需求、版本,就好比单车、拼车、火车,单车就是研发灵活交付一个代码,没有什么复杂的协作,完成交付。拼车就是业务需求,需要很多角色共同参与推进。火车就是版本式的交付,限定了一个时间或者交付需求的范围,大家赶车,交付最终的价值产出。
下面讲解我们的需求全流程模式和火车模式。
这是我们以需求为中心的协作方案概念图。
这种协作也是分支开发主干发布,特点是提供一个需求的视图,来管理一个完整需求端到端的研发活动,并提供聚合能力,方便业务使用。
在需求模式下,交付物的核心是业务需求,流转的建模有这么几个部分:多个微服务的变更版本记录、多配置的变更、过程质量的数据、发布依赖的编排。说一下发布依赖的概念,在微服务场景下,单个需求并不是对应单个的微服务,为了上线某个业务需求,需要多个微服务发布,但微服务的依赖在实操过程中很难做到完全绝对的前后兼容,所以研发在上线过程中期望能够按照既定的顺序发布多个微服务。
略微不同于我们日常在需求管理平台如 Jira 管理的需求工作流程,传统的需求管理平台,主力用户其实是产品经理,并不是研发的工程师,而且很多时候,要求工程师操作需求流转状态其实并没有那么好落地、或者说达到预定的效果。这个需求协同研发模式则更倾向于工程开发角色的流程视角,从开始开发时,这么多角色如何在一个协同流程里协作,研发人员真正花在这个需求上的活动将被整合进来,并与需求平台能打通信息。
对于业务团队来说,他们的需求是并行交付的,研发人员工作在需求的流程中,每个需求分为固定的几个阶段,阶段间提供一些准入准出条件,阶段内业务可以自定义一些流水线或者工具来使用,可视化过程。
在字节落地时,适合很多 ToC 业务和一些全职能特性团队,比较契合持续交付的理念,对于这些业务来说,大部分情况下,需要根据市场变化,持续地交付一个个独立的功能点,这些功能点同时涉及到多个后端的微服务,随时并行开发、独立地测试和上线。通过协同也降低同一个需求下的多个开发独立上线测试带来的协调成本,敏捷程度刚刚好。
上图是一个业务的需求全流程的典型设计方案,需求会被划分为几类,不同的需求等级定了了不同的流程,通过这样,复杂的需求可以准备地更充分,而简单的需求可以更快地交付。
再介绍一下火车模式,如果说需求全流程的协同范围仅仅限于单个业务需求。那么火车模式就是版本式多个需求的协同。借鉴了火车的概念,简单来说,普通研发人员的研发活动类比为一个凭票上车的动作,而 QA 、安全、发布人员更像检票员,或者是自动化的门禁。大量微服务的开发、测试、发布在发布火车中被分开了,大量的变更统一地进行提测、集成和发布,做到有节奏的按批次交付。
通常来说,火车主要适配的是分支开发分支发布的模式,在需求模式的基础上,扩展了多需求的维度。因此,在交付物建模时,引入了一个车票的概念,定义一个不可分割的交付物,一个车票可以绑定了多个需求、多个应用版本、多个配置类型的变更。一个车票下的交付内容统一上车,如果出问题统一下车。
在概念图中,火车发布在一个团队通常是串行的,每趟火车分为集成、冻结和部署阶段,分别对应一些研发活动。
火车模式比较适合较大协作范围,或者影响面较大的团队。在一些 ToB 类型的业务线,或者整体业务模式比较重的团队适用,比如中台类型的业务。火车并不是说很慢的模式,它是一个更高效的模式,按单个需求交付的不确定性被约束了,对于大团队和重要业务来说,不可控的交付才真正可怕。
这是我们主要的分支策略,是一个比较常规的三分支模型。分支合码的操作和火车的阶段做了映射。版本火车的好处是,通过一致性的规范提升整体的效率。同样,火车更需要多个微服务的依赖发布,并且在需要合规审查的发布中,大量微服务一起发布的优势更明显。
字节跳动的协同模式介绍完了,说一下做落地。为什么协作模式是需要落地对接的呢?其实像这样的协作流程,相对于自动化流水线来说,难度是比较高的。两者都定义了很多角色和规范,只提供了有限的灵活度,对业务的现有流程大概率是有影响的,那么不匹配的流程和工具设计怎么适配,是业务流程改还是平台支持?就需要专家角色来判断,因此落地需要有对接有策略。
而我们在字节的落地推广方式是一个分工分步的模式,在组织层面,有 4 个角色,一线研发 QA 、业务 POC 接口人、平台解决方案 SA 、平台产研,这是一个典型的 ToB 的对接方式。业务 POC 是负责定义业务的流程,对一线人员负责,解决方案 SA 则解决业务 POC 的诉求、提供方案建议,并和 POC 一起做到一线的落地推广工作。
在流程层面,流程改进遵循这 4 步:
第一,平台产研一定要自己吃狗粮,解决基础的问题;
第二,树立试点和标杆,通常选择收益最大的小组或难度最高的小组,快速突破业务痛点,产出有信服力的最佳实践;
第三,大业务推广,和大型业务接口人立项推进,做好培训、配置、尤其注意流程和体验优化等工作;
第四,长尾业务运营,例行解决一些推进过程中的定制化需求。
当然好的理念和工具并不一定能够落地,还需要考虑“天时”、“地利”、“人和”。
天时讲的是时机,例如一些内部事件如事故复盘,或者团队外部的事件和流程要求,还有业务架构或组织架构的变动,需要确定一套新交付流程和协作模式的时候。
地利讲的是收益,要有一定的势能,确保实施的自动化基础都已经具备,另外随着协同交付流程的落地,业务的收益大于实施成本。
人和讲的是各个角色的支持,整体要有信心,自上而下,影响团队决策层,明确收益和计划,自下而上,做好一线的布道和培训。
在内部落地,最讲究的是天时,作为业务和技术负责人来说,识别出团队交付流程的问题是一方面,更重要的是抓住合适的时机,在正确的时候做正确的事,达到事半功倍的效果。
四、总结与探索方向
看一下成果的总结,字节目前来看服务端交付流程的比例,基于应用的流水线模式占大头,需求交付的模式次之,版本模式最小,属于金字塔式的分布,从这点来看是比较符合现状,近 50% 的团队完成了存量 CICD 流水线的模式,已经进入强协同的模式。从单个团队的分析来看,从流水线的模式切换到强协同的模式,一些主要指标都有改善,包括交付周期、回滚率、测试左移等等。
不管是自动化,还是协同等方面,字节 DevOps 解决方案还有很多领域要探索,例如通过 GitOps 声明式的方式管理大团队的研发模式、客户端和服务端的协同方案等等,仍然有很多实际的问题要解决。
最后这里有几个经验沉淀点做一下总结。
一是自动化优先,如果你不知道如何改善交付流程,做自动化是永远考虑的选项之一,单点的自动化、自动化链路、人工过程线上化等等。
二是梳理团队价值流做协同流程改进,尤其团队业务复杂时有助于理清主线,当然有条件的可以平台化。
三是推广落地讲究天时、地利、人和,不管是自动化、协同流程落地,均需考虑好时机、收益和团队角色的配合。