将原则从实践中分离出来,这是一种很有用的做法。当然了,这两个词分别有着不同的含义,因而需要先对其定义达成一致。说到原则,我们是指DevOps所基于的关键思想,如果未被采纳或应用,DevOps就没有多大的意义。说到实践,我们是指为了取得预期成果而实施的与原则相匹配的活动。原则对任何应用DevOps的组织来说都是不变的,而实践的采纳和调整很可能取决于具体的上下文。
“对于方法,数量可能多达成千上万,但原则只有少数几条。把握原则的人,能够成功选择自己的方法。只尝试方法但忽略原则的人,肯定会碰到麻烦。”——哈灵顿·爱默生(Harrington Emerson)
本章将给出全球DevOps专家所描述的核心原则。在第4章中,我们将讨论实践部分。
DevOps的关键概念之一是价值流,借自精益生产。这个概念本身已经使用了很长一段时间,但随着实际应用的扩展,出现许多新的出版物从实践角度充分探讨这个话题。
我们可以从创造价值以响应客户请求的角度来考虑组织中的工作。完成请求所需要实施的相关行动,可以按顺序排列起来,这称为“价值流”。通常,组织处理着多样化的不同请求。同时,传统组织多半工作在多个产品或服务上。这样一来,公司中就存在很多个价值流。
价值流可视化的工作,称为“价值流映射”。它开始于对拟分析产品的选择:有时是有着最大优化机会的地方;有时是承诺作出最快速重大改变并为方法的研究提供资源的地方。价值流映射可以通过两个步骤来完成:
研究未来图景之所以也很重要,有两个原因。
实际上,价值流映射的活动很简单,需要识别处理请求的关键步骤,记录每个步骤中实施的工作,将这些步骤组织为一个创造预期结果的活动序列。困难之一是过度细化,这时最终的映射图在一页纸中容纳不下。有些作者建议,图中区块的数量限定在15个以下,使基于这个图的进一步工作更容易开展。第二个困难是对到底有哪些步骤、这些步骤是如何执行及由谁执行达成一致。有些组织对流程并没有共同的理解,这会导致长时间的争论。
一旦价值流图创建出来,就可以往里面填充进一步的重要细节。写上负责的角色或人员名字,比较有用。明确标出待处理队列出现堆积的步骤或者由于等待一个预先计划的事件而产生延迟的步骤(例如,月度CAB会议或者季度预算审批会),也是一个好主意。最终,最有价值的信息是流中每个步骤的3个度量数据,即前置时间(Lead Time,LT)、处理时间(Process Time,PT)及完整度与准确度百分比(the Percent Complete and Accurate,%C/A)。实践中,计算这些度量的数值,对于没有部署相关的工具与实践的组织来说,是一个很大的挑战。进行价值流映射的人,易于低估LT和PT指标的值;有时则相反,人们诉诸极端的案例,如被处理过长时间的请求,这样会过高估计前置时间(Lead Time)的值。对于%C/A情况就更糟,因为每个步骤的这个值人们是经常不知道的,因而只能靠猜测。重要的是记住一点,为了绘制当前价值流图,需要研究真实的实践,而不要指望不同指南中记录的文档信息,这些信息也许只存在于管理者的幻想中,或者仅仅用于极少数特殊的案例。
图3.1是一个价值流图的例子
我们为什么需要价值流映射?为什么流的概念对DevOps如此重要?
在进行价值流映射之后,通常可以提出以下几个问题。
值得注意的是,优化工作不应该只限制在分析当前(as-is)价值流图以尝试改进指标。相反,有必要绘制未来(to-be)价值流图,这也许会显然不同于当前工作的实践。这正是DevOps工具和实践能够发挥作用帮助改变IT工作方式的地方。最后,对价值流的了解,有助于实现DevOps的关键思想:构建一个顺畅、一致流经各个步骤的价值流,使得我们能够持续地、有节奏地、没有非必要的延迟并以最优的资源使用方式来交付成果。
理解价值流是通往DevOps的路上必经且重要的一步,但是在”纸上”与价值流一起工作,是不足够的。1.1节中描述的因素可以帮助我们采取接下来的重要步骤:构建部署流水线。构建类似于流水线的需要,可以用下面的例子来清晰阐释:尝试关注应用中一行新代码在生产环境中生效所需的时间。如果这个结果是用天、周或者月来度量,就说明价值流的确需要进行一些优化。部署流水线用以帮助实施这个优化,这意味着,变更尽可能自动化传递流经价值流上的所有步骤,起始于“开发完成”这个结点,然后一直到“完成部署进入运维”。
部署流水线的操作可以通过图3.2来阐释。
开发人员在版本控制系统中放入新的代码之后,流水线就自动开始运行;同时,变更的信息被记录下来:谁进行变更?什么时候变更?变更什么内容?基于这条新的变更记录,一套所需的临时测试环境就被自动化创建出来,然后预先开发好的测试有序地启动运行。排列测试顺序的逻辑很简单:能侦测到大部分错误的测试,放在流水线的起端;所有需要手工进行(如果有)的测试,放到流水线的末端。未能通过的任何测试,会造成这个变更的流水线中断,并提供反馈给开发人员。要想重启流水线,开发人员必须修复程序代码。除了测试环境外,也可以自动创建流水线所需要的其他环境。这些环境占用的资源,在使用完毕后自动释放掉。当然了,如果测试逻辑允许且没有流水线的前序步骤本可拦截的变更(而导致后续测试资源无效使用),几个测试并行执行也是可能的。
因此,流水线有助于处理几个重要的DevOps任务。
实施部署流水线带来了以下挑战:
还有三个与部署流水线有关的对DevOps很重要的概念:持续集成、持续交付和持续部署。它们的含义各不相同,下面的描述来自提出这些概念的专家的观点。
很多人认为,“流水线(pipeline)”这个名称是类比自生产线,例如来自汽车制造工厂的装配线。还有些人认为“流水线”这个词引用自流经水管的液体或其他物质,而部署流水线应该是参照了这些类比。这两种观点都不太准确。
作为这个术语的作者,Jez Humble(韩捷)和David Farley(大卫·法利)曾解释,这个想法源自于现代处理器的传输管道,在这里性能改进无法通过单纯的增加时钟频率来做到。被应用的架构解决方案是并行执行原本串行到达的指令。为了做到这一点,处理器必须“猜测”并行流中的处理结果,“假定”它们会在当前的流中按照预期来执行计算。否则,计算的结果会被丢弃。虽然“运气不佳的猜测”会导致时间损失,但由于那些“猜测”正确而获得加速的好处,会获得比损失更多的补偿。
因而,一个被正确实施的部署流水线,允许开发和测试在时间上彼此独立,这假定测试能够成功,并进入下一个批次的处理中。同样的逻辑也被应用到并行测试上。
持续集成通常理解为持续地集成程序代码的过程;持续意味着每当开发人员把变更的内容放到版本控制系统之后,就会触发集成动作。软件开发实践一般涉及许多独立的代码分支,不同的程序员和团队都要工作很长时间(几日、几周或几月)才能创建新的功能。在每一部分开发完成时,甚至在等待工作在同一个产品上的所有团队完成各自的开发后,把所有变更集成到一个构建中的痛苦过程,就开始了。因为有很多程序员,他/她们大体上异步地工作,每个人都长时间工作在一个很大的变更上,集成的过程本身变成一个很消耗时间的任务,也许需要花几个星期。确实,要全盘考虑所有的变更,将它们与其他变更做比对,更新测试以覆盖变更及对比,重写部分或全部早前开发的功能,然后重复以上所有工作,直到新代码被流转进入运维状态。集成是软件开发中的重要环节,并且集成事实上就是最初的测试。进一步的工作,严重依赖于集成是否成功。
持续集成最初的描述,出现在1999年Ken Beck(肯特·贝克)的书《极限编程解析》中。他提出简化集成,并将其变成例行的工作。期望程序员工作在最小数目的分支,理想情况下使用一个共用的统一代码库。这也假设开发人员可以做出最小的改变,将工作分解成小片,每个小片带一点风险,但可以立即启动集成过程;同时,每个程序员至少每日一次将其代码放到版本控制系统中,每次集成自动执行,并允许立即识别与更正错误,这意味着系统总能保持可工作状态。
持续交付,由作者在同名书中进行了详细描述,它扩展了持续集成的想法:每次变更的代码在版本控制系统中的保存动作,将触发集成过程以及整个部署流水线。因此,所有尚未被完整及成功测试的变更,不会被验收通过,并需要立即进行修正。所有没有差错的变更,进入对生产环境部署完全就绪的状态。
持续部署意味着从“当所有变更就绪时,系统随时可以部署”的状态,转化到“任何变更都被立即部署到生产环境”的状态。这个转化需要重新定义“发布”(release)这个术语:它不再是IT的事情,而是一个关于某个特定的新功能何时可用的业务决定。技术上,在部署与测试完成之后,功能已经在生产环境中,但当业务例如市场部门需要时,可以通过额外的程序设置来激活它。这个实践称为“影子发布”(shadow release)或“暗发布”(dark launch)。
在任何一个案例中,所有这些实践都基于上述同样的部署流水线原则。
现代软件开发人员已经习惯于使用图3.3所示的版本控制系统。最初这种类型的工具出现在20世纪70年代,称为“源码存储系统”。如今,已经很难找到一个不熟悉Git,Subversion或Mercurial的程序员。而且不光只是程序员,有很多的网站不光使用这些系统来存储源代码,也存储生产环境的拷贝,比如解析后的互联网系统或网站。
DevOps扩展了这些系统的使用,如同它扩展很多其他领域一般。存储的不光是源代码,还有与IT系统有关联的几乎所有一切:测试、创建及修改数据库的脚本、构建脚本、环境创建脚本(包括开发环境)、部署脚本、工件、类库、文档、配置文件、甚至开发工具如编译器与IDE等。在前面列出的所有元素前面加上“所有”两个字,也是合适的:所有测试、所有脚本,等等。唯一的例外是编译后的二进制代码,因为它通常会占用很大的空间(尤其是在每次变更之后重新创建),而且如果其他一切都在存储系统中,就可以重新生成。
这个原则实现了对运行中的系统各组成部分的空前级别的控制,这些组成部分不可以通过其他工具来获取。当然了,这个原则的应用,需要改变如何与信息及配置一起工作的文化。
应用这个原则的一个结果,是有能力确定变更了什么内容、何时变更以及谁做的变更。另外一个重要的特征是有能力将系统重置到过去任何一个时间点,包括以最小的代价回滚出错的系统到一个有保证的工作状态。还有一个涌现出来的不是特别重要的特征,是允许团队的任何成员自由删除不再需要的文件和文档,而无需承担意外损失重要信息或产品的风险。我们都知道,随着产品的不断开发,引入的变更越来越多,伴随的文件数量也不断增长,清理这些碎片的风险很大,除非有能够持续创建的受控的拷贝。
对上述原则进行拓展,DevOps完全重构了对生产环境(以及任何其他环境)的管理。许多组织的传统实践是这样的:通过一个预先定义的镜像,创建出一个新的服务器,然后管理员手工设置、安装及配置附加的软件包,这些软件包既有系统层面的,也有应用层面的。如果需要变更这些软件包或者它们的配置,管理员用自己的账号连接到服务器上,然后手工进行必要的设置。
这样的实践,在DevOps的世界里完全不可能,因为任何环境的任何变更都只能通过存储在版本控制系统里的脚本来实施。例如,如果需要明天在测试环境增加一个新的类库,管理员应该更新创建测试环境的脚本,测试并放到版本控制系统中。当部署流水线运行时,环境的创建就自动完成了。
前面提到的DevOps与日常实践之间的很多差别主要影响到开发和测试,偶尔才会引发运维的兴趣。这个原则需要完全重构IT支持和运维的工作。确实,这个时候管理员再也没有权限用其以往的方式挪动生产环境中的任何东西。
有一个常见的误解是,当开发人员得到生产环境的管理员权限时,DevOps就彻底实现了,这混淆了职责,也削弱了系统的可靠性。
实际上可以认为,就算是管理员,现在也被剥夺了生产环境中的权限,因为从现在开始不再允许他/她们改变任何东西,除非通过完全受控的脚本。
DevOps配置管理提供的收益如同从全面版本控制中获得的收益,只不过主要的受益者运维人员。现在,所有的变更都是受控的,系统可以被快速重置到稳定状态。如果关键成员离开,知识也不会遗失。
有些DevOps信徒狂热地捍卫这个实践,他/她们建议使用全面IT基础设施审计系统来检测在任何地方发生的任何非授权变更,然后立即解雇试图手工配置服务器或网络的员工。假如有几千台服务器和几百个工程师,为了确保可持续性、质量和速度,除了这样做,可能也没有其他更好的选择。
有些团队走得更远,不同环境的管理员密码会定期自动重置,并且不会把新的密码告诉IT员工。这防止了对生产环境的未授权变更,这个实践也应用到所有环境中,包括开发环境、测试环境以及其他环境。
普通员工对工作的日常态度,可以大致定义为下面两个阶段:我在工作和我完成工作了。的确,员工由于他/她们所完成的工作而获得报酬。分析师定义功能需求就算是工作完成了。开发人员写出程序代码就算是实现了整个业务中其所负责的部分。测试人员进行了测试也完成了其所负责的部分,以此类推。然而,在DevOps中,这一切截然不同。
有一个关键原则,不是说当有人干完了他/她们那个部分的工作,就可以算是“完成”,而是要等到客户接收到或者开始接收其所预期的价值。如图3.4所示,这意味着整个价值流已经被完整地流经,一直到生产环境;只有这时,工作才会被标记为“完成”。
虽然这个原则看起来显而易见,但对原则的遵循并不会自然发生,而需要一些管理上的努力。这些努力可以获得如下的收益。
有些想法激进的DevOps信徒,坚持一个更加严格的完成定义(Definition of Done)。他/她们建议,只有当应用运行在生产环境上并且所有的集成、测试和部署活动都自动化完成时,新功能的创建才可以被视为“完成”。
总结这一章,我们先回顾一下本章开始时给出的对原则的定义。我们说过,原则是指DevOps所基于的关键思想,如果未被采纳或应用,DevOps就没有多大的意义。
的确,如果不理解、接受及应用价值流、部署流水线、全面版本控制系统、自动化配置管理以及完成的定义这些重要原则,我们也许仍然可以玩各种DevOps实践,想玩多久就玩多久,但永远无法取得显著的成效。