内容来自《软件研发效能提升之美》一书
- 上一节偏思想方面,这次更多偏实操相关
DevOps落地精要
核心解读
DevOps字面上是Development和Operations的组合,即开发、运维⼀体化,测试作为质量保障角色也会融合其中,⼀体化最大的优势就是打破壁垒、拉通职能,最终体现在效能的提升上,同时把握产品质量与业务增长的平衡点
DevOps的六大武器
- 标准化作业
- 好处是能够固化流程,将人力从重复劳动中解放出来,同时有效地减少错误的发生
- 快速失败
- DevOps实践中,通常会引入CI/CD技术,通过高频集成和高频交付,将问题暴露在早期
- 快速反应
- 消除角色壁垒,尽可能用工具替代人力,并用流水线方式加速流程
- 高质量与高效率
- DevOps通过持续部署、快速反馈的方式,更好地将不确定性逐步转变为确定性,从而保障⾼质量交付
- 降低成本
- 当DevOps模式运作成熟,尤其是快速迭代的流水线初成规模后,软件研发的效率将大大提升,研发成本将明显降低,应对变化的能力也将大幅提高
- 团队协作
- DevOps依靠⼯具和自动化(尤其是流程自动化)来弥补开发与运维之间的技能鸿沟和沟通鸿沟,将软件研发中的三大角色(研发、运维、测试)有效地黏合在⼀起
- 标准化作业
DevOps是⼀种软件研发管理模式和思想,是⼀种文化实践,并不是具体的⼯具或技术,所有在保证质量的前提下提升效能的方法都属于DevOps的范畴
DevOps生命周期
- 持续开发
- 持续开发包含计划和编码⼯作,伴随这⼀阶段的工具主要有代码仓库、版本控制⼯具、包管理⼯具,以及⼀些计划可视化方法,如⽢特图、燃尽图等,也会涉及分⽀管理、单元测试等⼯作
- 持续集成
- 持续集成是DevOps中最核⼼的组成部分,通过频繁地提交代码、频繁地编译代码、频繁地构建项⽬、频繁地执⾏单元测试等,不断高频集成,贯彻快速失败的原则,尽可能早地收敛问题
- 持续测试
- 持续测试需要确保软件代码的每⼀次提交都能够被及时验证,并输出完整的质量反馈。纯⼈⼯的⽅式很难做到持续测试,⾃动化测试和容器化手段是这⼀阶段的标配
- 持续监控
- 这⾥的监控不仅是指对软件运行状况的监控,还包括对DevOps各项⼯作执⾏的监控,以便我们及时做出处理和纠正。持续监控还可以提供历史趋势信息,帮助我们更好地提供决策依据
- 持续反馈
- 整个软件研发周期的每⼀项⼯作都对外暴露了大量的信息,持续反馈在每个关键节点介入,以各种形式总结输出反馈建议,并不断改进,从而推动项目良性循环
- 持续部署
- 持续部署同样基于高频的理念,尽可能早地让软件产品接受生产的检验,快速发现并收敛问题,保证软件产品及早与用户见面
- 持续运营
- 运营阶段涉及对事件和变更的管理,如配置管理、容量管理、高可用管理、用户体验管理等,是DevOps的最后⼀个阶段。运营也是⼀项连续性的⼯作,是需要持续不断进行的。相对于其他周期项,持续运营更需要具备全局视角,才能做到目无全牛
- 持续开发
不适合DevOps的场景
- 在⼀些传统行业或政府机关,软件需求较为固定,甚至会采用外包的方式,且研发周期较长,对质量要求很高
- 传统金融领域或⼀些从事机密行业的商业机构
代码,分支与流水线
代码是软件产品的原始生产产物,经过编译、构建、测试、发布、部署后,形成可用的产品对象
三个重要因素对研发效能起关键作用
代码,软件产品究其本质都是由代码构成的,代码的质量直接决定了整个研发周期的投入和产出
分支,我们探讨的不是小作坊式的单⼈开发模式,软件开发团队需要在同⼀代码基础上并发进行多个功能的代码编写活动,显然不可能让员工在相同文件上作业,这时候就需要分支和配套的⼯作流。分⽀的实质是能够让研发活动并发起来,提升效率
流水线,在上述例子中,无论是软件代码的集成和交付过程,还是厨师生产美味佳肴的过程,都是⼀道道⼯序不断向前推进的 持续过程。流水线⼀方面可以使流程固化,避免⼈为因素造成的不确定性;另⼀方面能够透明地将每道⼯序的结果呈现出来,便于及早识别问题
代码质量
代码质量不是研发单体的责任,发现质量问题也不是测试单体的责任。我们所谈到的代码质量是需要由软件项⽬中涉 及的所有角色共同保证的,⽬的就是尽可能早地将质量问题消除在源头。绝⼤部分缺陷都是在编码阶段“写”出来的,⽽问题发现得越早,修复的成本就越低
测试驱动开发
- 从实践⾓度来讲,狭义的TDD,即UTDD(Unit Test DrivenDevelopment,单元测试驱动开发)对研发模式的侵⼊性是⽐较⼤的,现实中很多研发⼈员会不习惯,导致推⼴困难。我们可以考虑将测试⼯作上升至业务层,推行ATDD(Acceptance Test DrivenDevelopment,验收测试驱动开发),先定义质量标准和验收细则,再通过⾃动化测试的⽅式进行验收,这样就能够在代码编写和交付阶段预防缺陷
静态扫描
- ⼀种成本较低的质量保障手段,它最⼤的好处是不需要运行代码,仅仅通过分析静态代码结构和抽象语法树,就能发现潜 在的风险和违反规约的地方。目前市面上比较流行的静态扫描⼯具有阿里的代码规约扫描⼯具、Sonar、Findbugs、PMD等
代码评审
代码评审对质量保障的重要性不言而喻,但实际⼯作中就是另⼀番景象了,做得好的团队经常能够主动发现代码问题,做得不好的团队则流于形式,草草签字、画押了事
并至主干分支前进行,评审完成后再进行正式的测试⼯作
分支与工作流
- 分⽀的存在允许多人同时⼯作而互不干扰,不过我们总要在某个时机,将多分⽀代码合并成⼀份,以便⽣成最终交付的代码版本,这个过程涉及版本管理、发布管理、缺陷修复等⼀系列环节
- 工作流:
- Git Flow⼀共设计了5种分支,其中包含两种长期存在的“主要分支”(master和develop)和三种暂时存在的“协助分 支”(feature、release和hotfix)
- master分支上保持的版本必须是时刻可以发布运行的,因此不允许在master上直接进行修改和提交,只有其他分支的代码经过⼀系列的流程验证后,才能合⼊master。
- develop分支是代码开发的基准分支,也不允许直接修改和提交。
- feature分⽀是开发者编码的主要⼯作分支,在develop分支建⽴完成后,通过PR的⽅式合并回develop分支。
- release分⽀是⽤来进⾏版本发布的分支,发布成功后分别合⼊develop和master分⽀。
- hotfix分⽀⽤于处理紧急bug修复,在master分支建⽴、修复完成后,hotfix分支将合⼊develop和master分支
流水线
- 从DevOps的视⾓看,流⽔线是持续交付的载体,通过构建⾃动化、集成⾃动化、验证⾃动化、部署⾃动化等⼯作,完成从开发到上线的持续交付过程。其中,每个环节相当于CPU中的各个电路单元,在整体上形成了并⾏的效果,通过持续向团队提供及时的反馈,让交付过程⾼效、顺畅
- 精益理论强调了价值流动的重要性,⽽价值流动需要通过⼀定的⽅式来体现,DevOps流⽔线从某种程度上说就是⼀个很好的 载体,因为它⼏乎覆盖了整个端到端的流程。在流⽔线上,各环节流转得越快,价值流动就越快,这间接体现了公司的研发效能
- Jenkins Pipeline⽀持声明式和脚本式两种脚本编写⽅式,前者语法限制多,偏模板化,⽽后者则更⾃由,善于应对复杂的需求。下⾯我们以声明式的⽅式尝试编写⼀个流⽔线脚本,覆盖代码拉取、构建、部署、测试等⼀系列环节
持续集成与持续交付
集成,指部分向整体合并的过程,⽐如,某⼯程师研发的代码,将其合并到主干的过程就是集成,这其中涉及编译、打包、构建、单 元测试等工作。
交付,指将软件产品移交给质量团队或⽤户评审的过程,评审完成的下⼀步就是部署至生产环境。
持续,这个概念可以参照我们之前谈到的流水线作业,通过将任务化整为零,由单个节点完成⼀部分⼯作后,交由下⼀个节点执行,当任何⼀个节点出现问题时就执行快速失败(Fast Fail),这⼀方⾯加快了周转速度,另⼀方面能够及早暴露问题。正如软件大师Martin Fowler所说:“持续集成并不能消除缺陷,但可以让它们非常容易地被发现和改正”
持续集成与持续交付的实施
- 如果你的项目基于GitLab作为代码仓库,那么就可以使⽤GitLab默认提供的CI/CD⼯具,贯穿CI/CD的整个⽣命周期。由于其本⾝就是GitLab的⼀部分,所以省去了不少对接代价,对中⼩型项⽬来说,这是⼀种成本非常低的搭建⽅式,未来若有需求也可以切换到Jenkins等平台。
- GitLab CI/CD的核心集中在⼀个被称为.gitlab-ci.yml的YAML⽂件中,在这个文件中,我们可以定义⼀系列Job,以及它们的执⾏内容。
- 通过image和services关键字指定了使⽤的Docker镜像和服务。before_script和after_script提供了在所有Job执行前后进行准备和收尾⼯作的功能
- stage标注了运行的阶段,比较难以理解,可以认为stage就是顺序性的标记
持续集成与持续交付误区
- 误区⼀:DevOps就是CI/CD
- 持续集成和持续交付是DevOps的重要组成部分,但不是全部,不能视为充要关系
- 误区⼆:坚守固定的CI/CD流程
- 在⼀些⼩规模的初创团队,我们会看到⼤量项⽬采⽤了主⼲开发的⼯作模式,发布和部署的对象可能就只是⼀个Jenkins Job,通过⾯对⾯沟通的⽅式串联流⽔线,⼀样能够运作得很好,⽽且效率很⾼。但是,当公司业务发展到⼀定的规模,尤其是业务复杂度剧增之后,为了使代码复杂度不⾄于呈指数级上升,同时尽可能让研发⼯作能够并发进行,就需要拆分领域,⾛微服务路线。这时原有的CI/CD流程就需要针对各种变化(新的分⽀策略、服务数量的增加、发布和部署的管控措施等)及时做出相应的调整,⾃动化流⽔线也成为重点,因为⾯对⾯的沟通模式已经遇到瓶颈。如果服务的数量进⼀步增加,那么我们还需要在CI/CD流程中引⼊容器化技术,以降低资源消耗
- 误区三:盲从权威
- 比如,对源代码的管理,⼀些知名的国外IT公司采用了Mono-Repo的方式,即所有的代码都放在⼀整个“大库”里,这种做法可以简化依赖项管理,集合分散知识库,新手也能快速上手搭建系统。听起来很美好,那么我们是不是可以立刻将项目全部都切换至这种模式呢? 在实施Mono-Repo的背后,这些国外知名公司都对代码仓库进行了大量的个性化改造,以支撑单⼀构建和无依赖管理,继而形成了更轻松的代码协作与共享模式,这非常符合这些公司开放自由的企业文化,但代价也是巨大的。
- 误区⼀:DevOps就是CI/CD
容器技术在DevOps中应用
容器技术凭借其操作系统级的虚拟化技术,能够轻量级的开箱即用,可移植性强且成本较低,可以快速弹性伸缩等
无容器管理
小规模业务场景,现有的CI/CD开源技术和简单部署,已经能够满足绝大部分需求
整条流水线,通过Jenkins作为调度枢纽,连接代码仓库和各种环境,执行单元测试等验证

持续集成容器化
服务规模的扩大,集成和构建的服务越来越多,集成的密度和频率也会大大增加
Jenkins分布式模式是解决这⼀问题的法宝,部署大量Slave节点来执行构建、单元测试等⼯作,Master节点作为调度中枢,可以⼀定程度上满足需求。但这种方式的成本较高,因为集成的密度⼀般都是不均匀的
传统的分布式模式,若要保证高可用,就必须以最大使用量为基准,始终冗余大量的资源,即便是在低峰期也是如此,显然成本很高

考虑引入容器技术,可先在Jenkins中配置多个Slave节点,这些Slave节点在需要执行具体任务时才创建容器镜像,执行完毕后立即销毁,实现资源的合理利用
持续交付容器化
持续交付方面,会考虑通过引入容器技术提升环境部署方面的效率
例如下图:使用Docker结合Registry可以应对比较简单的场景,在集成阶段将服务镜像打包并推送至Docker Registry,在交付阶段则选取相应的镜像拉起容器,并完成部署工作

以上只适用于小规模场景,若服务规模逐步扩张,随之而来的就是环境数量的快速增长、集群化能力的要求、服务发现和调度能力的支持
需要引入诸如Kubernetes等技术对容器进行管理,包括服务发现、负载均衡、弹性伸缩等工作,提升容器的使用效率

测试环境容器化