1.1 软件工程发展概述
“软件工程”这一学科出现于1968年,当时正值第一次软件危机。第一次软件危机是落后的软件生产方式无法满足迅速增长的计算机软件需求,从而导致软件开发与维护过程中出现一系列严重问题的现象。人们试图借鉴建筑工程领域的工程方法来解决这一问题,以实现“按预算准时交付所需功能的软件项目”的愿望。
1.1.1 瀑布软件开发方法
瀑布软件开发模型由Dr. Winston W. Rovce在1970年发表的“Managing the development of large software systems”一文首次提出,如图1-1所示。它将软件开发过程定义为多个阶段,每个阶段均有严格的输入和输出标准,项目管理者希望通过这种重计划、重流程、重文档的方式来解决软件危机。很多人将具有以上3个特征的软件开发方法统称为“重型软件开发方法”。
图1-1 瀑布软件开发模型
在20世纪,瀑布软件开发模型的每个阶段都需要花费数月的时间。在写出第一行产品代码之前,甲乙双方需要花费大量精力确定需求范围,审核比《新华字典》厚得多的软件需求规格说明书。即便如此,双方还是要为“是否发生了需求范围的变更”“是否准时交付了软件”“交付的软件是否满足了预先设定的业务需求”而纠缠不清。
1.1.2 敏捷软件开发方法
从20世纪80年代开始,微型计算机快速普及。20世纪90年代,人们对软件的需求迅速扩大。然而,Standish Group的Chaos Report 1994显示,软件交付项目的失败或交付困难的比率仍旧很高(成功率只有16.2%,受到挑战的项目占比52.7%,而失败率为31.1%)。此时,很多优秀的软件工作者不满意瀑布软件开发方法的交付成果,并在各自工作实践中总结了各种新的软件开发方法,例如我们现在经常听说的Scrum和极限编程,都是在那个时代涌现出来的软件开发方法。
2001年,17位软件大师齐聚美国犹他州的一个小镇——雪鸟(Snowbird),总结了当时涌现的这些轻量级软件开发方法所具备的特点,共同发表了“敏捷宣言”,提出敏捷软件开发方法应该遵循的十二原则,见附录A。与会人员一致同意,凡符合这一宣言所倡导的价值观且遵循十二开发原则的方法均可被认为是“敏捷软件开发方法”。因此,“敏捷软件开发方法”这一说法从其诞生开始就是一簇软件开发方法的代名词,而不是特指某一种软件开发方法。
敏捷软件开发方法强调发挥人的主观能动性,提倡面对面沟通、拥抱变化、通过迭代和增量开发尽早交付有价值的软件。此时,很多团队已认识到,软件开发实际上是一个不断迭代学习的过程,即软件工程师需要快速学习并理解领域知识,并将其转化成数字世界的表达形式,通过与业务专家的交流讨论来学习并持续迭代这个过程。一个软件交付计划被划分成多个迭代,强调在每个迭代结束时应该得到可运行的软件,如图1-2所示。
图1-2 迭代开发,多批次部署发布
与瀑布软件开发方法只在项目交付后期才能看到可运行的软件相比,敏捷软件开发方法在这方面有很大的进步。“持续集成”作为敏捷开发方法中的一个工程实践,率先被更广泛的IT组织所接受,即便那些没有采纳敏捷开发方法的团队也会使用它,因为其强调的频繁自动化构建和自动化测试减少了质量保障团队的重复工作量,也排除了开发团队与质量保障团队之间的沟通障碍。
当时的主流软件需求仍旧是来自企业级定制软件开发。虽然敏捷开发方法使用的是迭代模型,但两次软件发布之间的间隔时间仍旧较长(通常是数月,甚至一年以上)。因此,业务人员与研发团队之间关于需求变更和研发效率的矛盾仍旧是主要矛盾。系统的部署发布工作在整个发布周期中所占用的时间和成本相对较小,部署和运维工作还不是突出矛盾。另外,部署活动通常由专门的技术运维团队执行,产品研发团队对其无感。
在这一时期,无论瀑布开发还是敏捷开发,在软件行业中最重要的关注点都是可交付的软件包本身,即如何快速地将软件需求变为可交付的软件包。
1.1.3 DevOps运动
DevOps的萌芽源于2008年8月敏捷大会多伦多站的一个临时话题“敏捷基础设施”(Agile Infrastructure)。当时比利时独立IT咨询师Patrick Debois非常感兴趣,并且分享了关于“将敏捷实践应用于运维领域”。2009年10月,Patrick在比利时的根特组织了一个“DevOpsDays”社区会议,并正式启用了“DevOps”这个术语。
2010年“The Agile Admin”网站上发表的一篇题为“What is DevOps”(什么是DevOps)的文章中指出:“DevOps是一组概念集合的代称,虽然并非全部都是新概念,但它们已经催化为一种运动,并迅速在整个技术社区中传播。”同时,该文章也给出了其原始定义,即“DevOps是运维工程师和开发工程师参与整个服务生命周期(从设计到开发再到生产支持)的一组实践”,并提出,DevOps应该倡导运维人员更多地使用和开发人员使用的相同技术来进行系统运维工作。
DevOps在维基百科上的定义也在随着时间的推进而不断变化着。截止到2017年,其定义为:
DevOps是一种软件工程文化和实践,旨在统一整合软件开发和软件运维。DevOps运动的主要特点是强烈倡导对构建软件的所有环节(从集成、测试、发布到部署和基础架构管理)进行全面的自动化和监控。DevOps的目标是缩短开发周期,提高部署频率和更可靠地发布,与业务目标保持一致。
事实上,到写作本书之时,业界对DevOps并没有统一的标准定义。正如“敏捷”一样,每一位从业者、每一个企业都有自己所理解的DevOps。有些人认为DevOps是敏捷的一个子集,有些人认为“敏捷做对了,就是DevOps”,还有人则将DevOps看作围绕自动化实施的一套实践,或多或少与敏捷有些关联性。
从历史时间点上来看,DevOps源于敏捷思想和实践在运维领域的应用,但当时的实操指导性不足,而近乎同期出版的《持续交付》一书使其更加具象化,在企业实践方面更具有可操作性。书中给出了一系列的原则、方法与实践,使DevOps运动的参与者有线索可循,例如持续交付中强烈倡导的“一切皆代码,自动化一切,部署流水线尽早反馈”等。
DevOps并非一个标准、一种模式或者一套固定方法,而是一种IT组织管理的发展趋势,也就是说,通过多种方式打破IT职能部门之间的隔阂,改变IT组织内部的原有合作模式,使之更紧密结合,从而促进业务迭代速度更快。这种发展趋势将会引起IT组织内部原有角色与分工的变化,甚至范围更大,会影响到相关的业务组织。对互联网公司来说,其软件产品对业务发展起到极其关键的作用,业务结果与IT效能强关联,因此顺应这一发展趋势的动力更加明显和迫切。
既然DevOps是一种组织管理的发展趋势,那么它就是IT领域普适的。对于不同行业、不同企业中的IT组织,需要根据其所在行业的行业特点以及企业实际状况进行一系列的管理定制。
1.1.4 持续交付1.0
2006年,Jez Humble、Chris Read和Dan North共同发表了一篇题为“The Deployment Production Line”(部署生产线)的文章。文中讨论了软件部署带来的生产效率问题,并首次提出“部署生产线”模式:
……测试和部署是软件开发过程中最困难且耗时的阶段。即使团队已经使用自动构建完成了代码的测试工作,也需要几天时间做生产部署的情况仍旧很常见……我们描述的原则和实践使你可以一键创建、配置和部署新的环境。
……通过多阶段自动化工作流程,测试和部署过程可以完全自动化。利用这种“部署生产线”,可以将已经过验证的代码快速部署到生产环境中,并且一旦发生问题,就可以轻松地回退到以前的版本。
该文章的3位作者当年均就职于ThoughtWorks公司,而该公司是敏捷软件开发方法的践行者、倡导者和推广者。该公司使用的软件开发方法也源自极限编程方法。作者通过各自的实践总结出了“部署流水线”模式的雏形,并且对如何使自动化部署活动更轻松给出了以下4条指导原则。
(1)每个构建阶段都应该交付可工作的软件,即中间产物的生成(例如搭建软件框架)不应该是一个单独的阶段。
(2)用同一个制品(artifact)向不同类型的环境部署,即将其与运行时配置分开管理。
(3)自动化测试和部署,即根据测试目的,分成几个独立的质量关卡。
(4)这个部署生产线设计也应该随着你的应用程序的发展而不断演进。
2007年,ThoughtWorks公司的Dave Farley也发表了一篇题为“The Deployment Pipeline- Extending the range of Continuous Integration”(部署流水线——持续集成的延伸)的文章。文中指出,部署流水线就是通过自动化方式将多个质量验证关卡及其中的验证内容联系在一起,如图1-3所示。
图1-3 Dave Farley在2007年定义的部署流水线
同年12月,ThoughtWorks在北京正式组建了产品研发团队,启动了以这种部署流水线为指导思想的软件持续发布管理工具的研发。当时Jez Humble是产品经理,我负责该产品的交付与业务分析。该产品的第一个版本发布于2008年7月,取名为Cruise(现名为GoCD)。其最重要的一个功能特性就是“部署流水线”(deployment pipeline)。而且很多特性设计(包括内置的制品仓库、多阶段之间的制品引用)也体现了“一次构建,多次使用”的原则。
在开发GoCD期间,Jez Humble和Dave Farley合著了《持续交付》一书,英文版于2010年正式出版,该书于2011年获得Jolt杰出大奖,中文版也于2011年在国内上市,这标志着“持续交付”这一术语的正式诞生。Jez Humble说:“持续交付是一种能力,也就是说,能够以可持续方式,安全快速地把代码变更(包括特性、配置、缺陷和试验)部署到生产环境上,让用户使用。”本书将这一定义称为“持续交付1.0”。
在《持续交付》一书中,讲述了持续交付1.0所遵循的理念、原则和众多方法与实践,并在该书最后一章指出:“它不仅仅是一种新的软件交付方法论,而且对依赖软件的业务来说,是一个全新的范式[1]。”
持续交付1.0提供的很多原则及方法是DevOps运动的具体实操指引,它们可以为企业的IT团队将DevOps运动落地实施提供非常具体的指导,如图1-4所示。
图1-4 持续交付将发布权交还给业务方
从所涉及的协作角色来看,敏捷开发更多地涉及产品需求方、软件开发工程师和软件测试工程师。在历史上,DevOps更多地涉及软件研发团队(包括开发工程师和测试工程师)与运维工程师,而持续交付1.0涉及产品需求方、软件研发团队和运维工程师,如图1-5所示。
图1-5 相关概念在组织角色的主要触达点
持续部署与持续交付
很多人认为,持续部署(continuous deployment)是持续交付(continuous delivery)的进阶状态,是指代码提交后一旦成功通过所有质量验证,就立即自动部署到生产环境中,不需要任何人的审批。事实上,“部署”与“交付”这两个主干词的意义并不相同。
“部署”是一种技术领域的操作,也就是说,从某处获取软件包,并按照预先设计的方案将其安装到计算节点上,并确保系统可以正常启动,但它并不一定意味着“必须包含业务功能的发布或交付”。“交付”则是一个业务决策活动,通常也被称为“发布”,也就是说,如果将新构建的特性交付到客户(用户)手中,用户就可以看到并使用它们。
之所以“部署”与“发布”几乎成为等价词,是历史原因造成的。很久以前,软件的发布周期较长,每次新功能部署之后就会立即发布。久而久之,“部署”就成了“发布”的代名词。为了保证软件质量,IT部门通常不允许无关代码(即与本次发布新特性集合无关,例如未开发完成的功能、不完善的功能集)被带到生产环境中。因此,每次部署就一定是重大功能的发布。
随着互联网软件的出现,“部署”和“发布”内容与频率不同的情况也是很常见的。我们可以向环境多次部署,但只有当业务需要时才向用户发布,如图1-4中的箭头所示。例如,Facebook公司的发布工程师Chuck Rossi在2011年该公司举办的技术分享会上指出:“我们现在每天会对Facebook网站进行一次部署操作……Facebook公司在半年后将要主推的功能特性,现在已经上线了,只是用户看不到而已。”