基于Pipeline的DevOps核心实践
本文主要讲述华为从自有研发实践到向外输出的服务——CodeArts流水线Pipeline,以及基于Pipeline的DevOps实践。
本文分为以下四部分,前三部分侧重于理论,第四部分将演示在保障质量的情况下,如何让代码提交快速上线。
DevOps在华为
在华为的研发历程中,IPD(集成产品开发)的影响比较大,从IPD引入华为之后,华为的研发模式才真正走了集团化、正规化的研发模式道路。此后,如测试自动化工厂、DevOps、CI、DevOps大规模敏捷等等,每次研发模式的变更都会带来一部分工具的沉淀,工具本身又会随着模式和技术的变更不断的发展。例如,华为的研发工具部在2003年左右就成立了,最早聚焦在测试自动化工厂方面,包括软件自动化工厂、硬件自动化工厂等等。在CI方面,华为后来引入了持续集成的工具和平台,以及持续交付(CD)的流水线,未来还有随着微服务化、基于无线化的AI流水线,还会继续探索。下图展示了工具和平台发展的过程。
DevOps的核心理念就是CALMS,包括文化和精益的理念、度量和分享。我们有一个自动化的工具平台在支撑。任何先进理念的落地一定离不开最终沉淀下来的自动化工具平台。如果全部靠人完成,那么这个活动将无法快速变现,并且难以高质量地重复执行。所以在核心实践方面,DevOps团队的协作,实现全功能团队,以及领域特性团队显得尤为重要。在架构方面,从原来的单体软件到逐步分层软件,再到现在微服务化架构软件逐步演进。环境方面,从裸金属服务器,后面逐步到虚机再到容器化,倡导基础设施即代码,通过容器化去演进环境的差异,来提升未来环境方面投入的力量和工作。
我们在编程中发现,无论是本地的开发环境还是DTAP四大环境,环境的链条和测试恢复、部署、出问题的定位时间占团队时间30%以上,这是非常大的工作量。在这个过程中,整个交付过程中会实现把所有东西自动化,包括测试自动化、精益看板、构建、部署、发布、测试,以及灰度发布,还有后续到生产环境的监控。
流水线整个调度中,涉及到的动作可能会有几十到几百个。CodeArts几十个团队,这么多人和团队以及不同的服务之间,如果想要进行一次完整的发布流程,并且没有工具,即全部靠人工拷贝、搭建各个环境,把一个环节搭建好的结果流到下一个环节去,无论如何都不可能有多快,特别是在软件规模越大的情况下。假设一个100万行的产品要全部走下来一个发布流程,全人工至少要一个星期或者几个星期才有可能上线。所以流水线把持续交付进行自动化的特性,对DevOps来说是一个最核心的实践,它能够提高持续交付的速度和质量。
下图是一张DevOps的工具链地图,从代码需求的分发到代码的提交以及自动关联需求,代码的提交触发后续的测试,以及线上的环节,一个工具链打通完整的路径,实现端到端的交付和支持。
不同研发模式下流水线的应用与思考
第二部分重点讲述基于华为的流水线支撑的实践,支持三种主流模式:
- 第一种模式:大规模开发
华为起家的核心是交换机,交换机本身业务分成多层架构,例如上面的控制层到下面的数据传送层等。在自我分层的基础上,向上对接网管等各种监控工具,再横向和后端的传送网、骨干网对接,进行数据的传递和核心的传递。在这个基础之上,华为做了一个2012实验室——这是一种大平台战略,几乎所有的产品线产品都会用到的公共平台,而且这个平台的能力是重平台薄产品,平台会达到80%的代码量,可能会越来越大。这时候如果产品要用到另外一个BG产品线的平台就是一个非常大的困难了,在产品线内部也会尽量把一些公共特性下沉为小平台,所以一个核心网络产品从各个角度来看,复杂是最核心的特点。
产品的代码量是非常大的。一般来说,一个产品一百万行或者几百万行是非常正常的,需求一千万行也是非常正常的。这么大规模的代码不可能由一个团队开发,CodeArts是由产品线以及各种周边配合,涉及的团队非常复杂。基于时代背景来看,核心网络产品要交付出来,在以前经常需要一年多以上,基本是一年一个版本,半年的时间做线上验证,包括到客户的机房验证,那个时候升级都需要选访问量最低的时候进行半夜断网升级。
怎么保证迭代的速度加快,迭代的质量逐步变得更好呢?后来我们探索了一套模式是分层分级流水线,进行大规模开发的持续交付模式,建立一个合理的结构,我们通常会分成四层。
个人级流水线:团队一般每天提交几次到数十次不等。这时候在本地会先做代码的增量静态检查、增量单元测试,以及功能测试。如果这些检查都没有问题,全部通过,代码会上传到云端配置库。此时触发一个提交构建,就到了项目级流水线。
项目级流水线包括提交构建、滚动构建和全量构建。提交构建通常需要做的是全量的静态检查和冒烟用例。个人级和项目级流水线提交构建,随时提交就能够触发整个构建和流水线的运行和执行,属于随机发生模式。滚动构建和全量构建属于班车模式。班车是定点定时发,无论是坐地铁还是公交车、火车、飞机,时间是固定的,每个人去赶飞机、地铁、公交,如果迟到就需要等待下一趟。对于一个研发团队或者一个产品,一天内代码提交通常有三个峰值,上午10-11点是一个峰值,下午5点左右是一个峰值,晚上8点多有一个属于加班同事的小高峰。所以一般来说,一个项目级流水线会发三个班车,就是中午12点、下午6点、晚上9点半,这三个班车永远是固定的,团队无论有无提交都会发车,定点执行。
这就实现了一个协同,有多个人提交或者每个人提交都会执行,执行的时候会看结果如何。这种时候一般会做基础用例、拉通用例、滚动用例模式。如果定点发车了,会推送到全量构建,解决他们的协同发布。通常是晚上12点会执行一轮,第二天早上整个项目组的同事就收到了流水线测试的结果。如果成功了,大家欢庆一下;如果不成功,就赶快解决问题,因此所有人一早就要解决持续交付流水线的问题。
滚动构建成功之后,还会把这个包归到版本级流水线的构建里面,在版本级流水线里面滚动构建并测试,当这些全部执行成功后,就会再向下游拉通全链条测试。对于版本级流水线,滚动构建也会投入流水线当中,会比项目级构建晚两个小时,如果自动化率足够高,大部分问题都可以解决。
所以,对于一个典型的大规模敏捷产品,我们有四层分级的流水线,里面有随机发车和定点发车的模式,也有实现这样的高速流转的对应解决方案。
在此过程中,流水线在每一个执行阶段都会有一些门禁的判断,任何一个任务跑完了,都有门禁进行把控。只有通过项目组设定的门禁,才能向下走;如果不通过,流水线就终止了,先把基于静态检查的问题解决完,才能继续向下走。这种方式,不像过去有最终的发布评审,到最后的上线环节再搞发布评审,而是在每一个部分就会把所有的门禁做上去,由此来保证在每一个任务的成功和每一个阶段的成功,以保证最终的成功。
此处与传统的交互模式做一下结构比较,传统交付模式越向后的环节越容易出现问题。例如产品级或者解决方案级一集成,越向后,问题会越来越多、越来越大,再返回来到每一个小团队解决问题,实际上这已经比较晚了,解决的成本也很高。使用了分层分级流水线之后,越向前的环节发现越早,就把问题拦截下来了。
从我们的分析和实践结果来看,假设有四层流水线,个人级提交一定最多,因为每天有几次几十次,每个人提交到代码仓的构建在两次左右,除非BUG多一些。再向后就到了班车模式,一般来说,每天进行三次滚动构建。到最后的全量测试,每天执行一次即可。通常越前端的流水线会越早发现问题,原来是最后进行发布评审,现在如果是有了每个任务的质量门禁,每次执行的时候,越在流水线的前端环节,被执行的次数越多,发现问题的概率也越高,符合分层分级流水线的理念结果。
当然,分层分级持续交付的模式下面,会有很多其他构想。包括如何快速搭建第一条流水线,不同层级的流水线如何快速复制,并且搭建自己对应层级的流水线,以及上一级流水线成功后如何自动触发下一级流水线的执行,这些是最基本的思考。
- 第二种模式:微服务开发
CodeArts团队本是一个微服务化的实践者,有100多个微服务,基本全部微服务化,前端已经全部拆解完。在这个模式下面,首先,架构解耦;其次,结构上最终一定要实现每一个组件足够小,而且尽量没有依赖的。在这个基础上,每一个团队能够比较自由地自我裁决、自我决定。比如发布,以前在传统的模式下,一个团队肯定是要分运维团队、测试团队或者是开发团队、产品团队、QA团队(都是单独的),真正在微服务开发模式下,做成一个全功能团队之后,已经没有非常明显的角色分工,很多功能测试等等都是由开发人员自己去实现的,这个时候对于开发人员来说,既是开发人员也是测试人员、运维人员,会对运维服务的开发、测试、上线、结果,会端到端负责。所以我们针对微服务会做权限的设置,去匹配新的角色模式,一个人可以端到端的执行完整条流水线。上线之前还可以有一个环节,例如和产品经理一起看一下是否满足客户需求等等,如果后续流水线承载的自动化测试的手段都能够完全达到质量发布的条件,届时也不需要评审了。整个过程有40-50%做到不需要人工干预,下一步这个比例还会继续放大。
因为微服务化了,这100多个组件中间还要发一些补丁,一天内都上线是很糟糕的事情,每个微服务中断几分钟,一天下来是几百分钟,整个服务就不可用了。因此微服务模式下一定要实现灰度发布,最基本不能中断业务。
CodeArts团队在最早的时候,每个星期五晚上要12点开放升级,把测试和研发团队、领导都喊起来,整个团队从2点到4点进行测试;测试完成后,4点之前我们决定BUG修复完,再回溯一遍。这个过程每周一次,那时团队成员很痛苦、疲惫,第二天一整天什么也做不了。
后来通过微服务实践和灰度发布实践,才把团队从超级繁忙的、每周一次的超级作战中解放出来,这对团队整体的士气和工作效率的提升都是非常明显的。如果真的发现在灰度测试中出现问题,或者版本有重大的缺陷,要能够触发自动回滚或者手动回滚。这时候运维人员会选前一个包直接启动流水线部署,把灰度测试灰度过的环境全部覆盖掉。
在微服务开发模式下,管理很重要。使用K8S时,如果版本号不增长就不会部署(只要版本不变,就认为是同一个包,因此不会启动部署)。所以版本管理、版本增长等方面都要在流水线上做到位。同时因为这种发布模式时间非常短,我们可以做到小时级、分钟级发布。这种情况下,我们会通过线上A/B、加大测试,在线做各种自动化测试,包括拨号测试、性能测试、压力测试、安全测试等等。在真正大规模灰度之前,就已经把基于真实环境的质量构建好了,其中在线测试、前置测试的比重都要加大。
- 第三种模式:静态资源部署
静态资源部署不需要重新编译构建出包。通过部署流水线,把静态资源拉包,部署到北京、上海、广州、成都等等各个CDN去。在这个过程中,多地区的并行部署以及跨地域的部署是CodeArts重点支持的方向。
流水线关键技术
CodeArts流水线 (CodeArts Pipeline)涉及的关键技术,能够保证用户量增长不会面临宕机。
- 首先,流水线随时可用、高可用
- 其次,通过数据云化存储、高隔离、权限控制来保证数据的安全。
- 执行效率方面,除了并发调度系统,对每个单一任务会做非常多的功能,例如代码预读、分发、增量等等,加速整个流水线的速度。
- 为了用户的使用成本变得越来越低,尽量将配置简化和维护,提供配置模块以及可集成云化的服务去减少开发工作量。
CodeArts是基于Jenkins的流水线来实现master的调度机制。
CodeArts定义了一种CDDL语言(Continuous Deliver Domain Language),它具备非常好的可扩展性,同时CDDL语言解释器和平台无关,因此可以做到在任何平台上的灵活调度。CDDL的设计参考了UML中的状态机模型,更易于控制。
流水线还涉及到其他的关键技术,支持百万级的并发调度。此外关键技术能力还包括“七个一切”,前四个是一切即代码、版本、服务、数据,在“基础设施即代码”的基础上,引入了这些概念,形成一条可视化流水线。基于以上能力,形成了后三个一切。一切自动化:流水线一旦被调用,可能有几十、几百个步骤,如果全部依靠人工操作,则不可能像自动化流水线一样,有这么高的可靠性和可重复性。CodeArts的流水线是可视化的,可以看到每个环节步骤具体进行的情况。未来产品还将更加智能化。
快速交付实战演练
流水线的成功和失败都是一种状态,失败也不用怕,可以帮助我们发现问题。
我们的最终目标还是要提升成功率,尽量检查出由于配置出错导致的构建失败,或者个别服务出现问题。我们可以看到历史构建的软件包,选择一个进行回滚。在代码检查时,可以做一个代码质量门禁,例如这个代码检查任务执行完之后,发现原来有100个问题,现在修复完95个,只剩5个,如果预定的门禁是10个,就可以继续向下跑。API测试也是这样,测试门禁里面生产百分之百,如果有20个没有通过,继续执行就会失败,要修复完所有环节才可以继续执行。
此外,任务还有健康度,可以看到哪个任务失败次数多。对已经关联流水线的代码进行修改提交后,可以重新执行流水线。在此过程中,可以看到构建任务在运行中,也可以直接看到日志并下载全量。构建任务执行成功,流水线会流转到下一个环节,直至部署成功,访问对应页面查看效果,代码修改生效。这就是如何修改一行代码,能够保证让用户满意,真正快速地发布到现网。
在代码仓库中可以设置提交后自动触发流水线,例如同一个代码仓拥有两个分支,可以分别设置是否提交后触发流水线,设置后,不需要手动执行流水线,它会在提交代码后自动执行。这是前文提到的两种模式中的一种,叫做随机发生模式。
还有一种模式是定点发车。每天指定时间执行流水线,如果这是一条版本级流水线,或者是一条项目级流水线,我们可以去设置每天什么时候自动执行一遍,无论是否有提交或者合并,到时间都会执行,如果有,则对已有的结果进行质量的测试和监控,并且最终保证流水线交付的应用质量是没有问题的。
在流水线中,失败率最高的Top3任务被面标识出来,按照执行失败的次数排序,我们可以看到哪些环节是健康的,哪些环节阻碍了任务的继续执行,所以这是一个非常好的实践。
对于健康度,失败率Top3的子任务(流水线每个环节中的任务)会在整条流水线中标识出来,并且可以按照执行失败的次数进行排序。由此,可以发现哪些环节是健康的,哪些环节阻碍了任务的继续执行,这是一个非常好的实践。
此外,在CodeArts的需求规划中,可以看到工作项关联的代码提交记录,并直接跳转查看提交的代码的内容,非常方便查看、追踪基于需求的代码运行情况以及应用发布情况,并且还可以关联bug,真正实现了端到端的双向追溯。这就是整个流水线的演示。
此外,CodeArts还支持移动端的移动运维,如果装了CodeArts APP,就可以在家里,或者咖啡馆、高铁上面随时可以查看流水线执行结果,并且可以再次执行它,查看它的错误码、错误日志,以确保随时随地的移动运维,移动交付。
最后用这一句话结尾:使用尽量多的自动化,并且用更短的交付时间,从代码提交到最终上线的时间来驱动整个团队在DevOps上实践的落地。