6 月 10 日,优维科技与数人云、中生代联合举办了 DevOps&SRE 超越传统运维之道(北京站)。DevOps&SRE 上海站将于 7 月 15 日举办,敬请期待!
任发科老师在此次活动中分享了《如何打造易用的 DevOps 工具链》,以下为演讲实录。
任发科 网名常新居士。曾任职于唯品会、会唐网、亚马逊和 ThoughtWorks,是 QCON,TOP100Summit,TiD 优秀讲师,近年主要关注和从事 DevOps 工具链设计与实现,并长期从事和关注高效研发团队的组件和管理,译作有《 DevOps,软件架构师行动指南》等
为什么讲这个主题
之前很多分享都是集中在 DevOps 工具的设计领域,讲方法论的比较少。最近看到一些程序员做的东西,缺乏逻辑性,做出来的东西效果不好,感觉很违和,所以今天我们讲讲工具的易用性。
目前,我们公司 DevOps 的整个理念是传承自亚马逊,跟当前的 DevOps 并不是 100%重合。不过,DevOps 只是一种理念,一种描述,并没有明确的定义,从 2011 年到 2016 年,DevOps 相关的很多东西一直在不断变化。
就传统运维来说,DevOps 有两个最大的不同点:一是把开发运维结合在一起,开发和运维应该交付一个运行系统。运行系统承载了运行的业务,因此,系统是从一个运行的版本进入到另一个运行的版本。
在此基础上,区别于 DevOps 所强调的相关团队协同合作,我们更强调开发承担运维、测试的工作,也就是谁构建谁运维,让开发做上线部署、运行监控、系统稳定性优化等等工作,而运营人员应该更专业化,提供专业性的支持和相关运维工具。
隐藏在问题背后的共性
就是今天要讲的如何打造易用的 DevOps 工具链,而这个工具链是多个工具组成的生态。
实际上“易”字代表了两个意思:一是简易,工具的设计架构本身要足够简单,方便拓展配合。二是易用,对于使用者来说,不会造成过多负担,能快速解决问题。
简易、易用为什么重要
我们在 DevOps 工具链的研发过程中,分析了许多相关工具,遇到一些问题,这里是一些思考,希望能对大家有所借鉴。
如果从产品用户体验去看,当讲到易用性大家都是知道的。
作为用户,我们每天都在使用各种互联网工具。但 2C 和 2B 的应用是不一样的,易用性表现的也有所不同,2C 强调的是让用户减少思考和决策,保持粘性,解决痛点。而 2B 要解决效率,如怎样更快、低成本地解决相关业务的问题,直接影响业务效率。
易用性是如何对我们产生影响的?能够影响简单易用的有两种方面:业务和用户。而影响方面则具体在架构、设计和交付等方面。
全生命周期管理
现在大家都关注软件研发和运营的全生命周期管理。从这个角度看,现阶段多数 DevOps 工具链其实都不算完善。基于工具链去分析公司的能力,可以分为三个梯队,第一梯队是亚马逊、谷歌、脸书、推特等公司。
第一梯队已经在业务发展的过程中构建起完善的 IT 支持生态,但它们从来不讲自己做 DevOps。只有第二和第三梯队在以概念为抓手来实施 DevOps,而第二梯队已经基于自动化运维或者开源工具构筑了一定的持续交付能力。
举例来说,2006 年初亚马逊的整个工具链其实就已经基本就位,当时尚未有 DevOps 的概念,而今天,围绕研发和运维已经有近 50 个独立的工具或系统。
由于今天演讲的时间限制,我们不可能涵盖 DevOps 的方方面面,因此,我们将主要关注在部署流水线相关的工具上,这是我们探讨的主要业务关注点。
部署流水线
关于部署流水线,有一些关键的点需要注意:
每一次只生成一次二进包,接下来的阶段无论是手工、验收、部署等所有测试,都是前面生成的二进制包做的。
即便在如今十分完善的部署流水线设置上,还需要引入人工的节点。尤其是关键业务系统,因为有一个升级的过程,所有要卡一个人工审核的节点,甚至人工审核的阶段,在测试时需要让运维和测试人员能够手动选取版本。
当操作人员看到构建版本时,需要看到连同构建的全部状态,如版本之前的活动的进展状态。
技术人员
这是我们工具的主要用户。当分析技术人员时,会发现他们都自视甚高,尤其是研发人员,在自己眼中什么都能做,比如项目管理时,研发人员的预估永远是乐观的:没问题,都可以完成。
在一些事情上,技术人员的标准很低,当交代给研发人员一些任务时,若没有产品把关,研发出来的东西很难用。如果经常和技术人员打交道会发现他们的一句口头禅是:能用就行。
研发人员特别强的一点是可以并行处理大量的信息,在工作中非常适合进行批量处理。但有一些事情会极度较真。如大括号的问题:在 PHP 或者 Java 时是放下面还是放在后面。
经验十则之如何打造灵活易用的 DevOps 工具链
经验一:实现上分离,服务上整合
做工具链要实现“实现上分离,服务上整合” 。如上图,构建、环境管理以及其他一些工作,如果在实践时运用 Jenkins,Jenkins 本身非常强大,可以将所有的事情都放在一个工具里去做。但当系统或者公司规模庞大时,就应该摒弃 Jenkins 了。因为系统需要做到关注点分离,需要独立发展每一个工作。
部署流水线最重要的事情是:构建和部署。流水线只是将其串起来的工作流和在此之上加一些决策逻辑。但超出流水线之外的仍有很多工具,比如环境定义,能够做到配置化的环境管理。
每次推送的都是运行中的系统而不是可工作的软件,所以就可以看到有些系统有自己的复杂性。
如构建,构建绝不是依托于 Jenkins 的脚本,其本身可能非常复杂,尤其是当系统分模块后,一个系统有 2 — 3 个模块之间的依赖,而这些模块又依赖于一些第三方库,就会发现非常麻烦,构建的过程非常复杂,本就可以作为一个独立系统去做。
**当需要整合时有两种方式:
1、视图的方式整合,如上图,此视图非常适合。Git,Jenkins 可以将它放到一个图形界面上使用。
2、各个工具对外提供 API,通过服务化的方式进行相互调度。
**总结下第一个经验带来了什么:
在做系统时,要记住每个系统只做一件事,并且将它做好,如做构建就只做构建,做发布部署就只做部署。10 个系统做 10 件事,远比 1 个系统做 10 件事更好,因为复杂程度更低,更好配置,前提是业务真的达到 10 个系统的需求。
从工作到现在,所遇到的大部分一站式系统都是反人类的,因为功能太多,只有在真正需要的时候才给出一站式解决方案,需要合的时候再合才是正确的。
在亚马逊做供应商集成工作时,经常提到 One stop shop 即一站式。一个系统解决某个方面的所有问题,但只解决一个层面的问题,整个流程工作很多,绝对不拿到一个系统里做,因为太复杂。
经验二:设计上的关注点分离
设计上需要关注点分离,这个部署系统可以把所有部署逻辑集中在一起,明确往哪个机器上做哪些任务、工作,然后中间进行管控,这很容易实现。缺点是不适合扩展。
更好的办法是中间控制的部分只管任务和状态,至于如何部署让 Agent 去做,挂载另一台机器就可以自动运行,不需要控制,如此整个系统设计就相对简单。
经验三:简单性>完美性
案例:要记住简单性要胜过完美性,在设计层面包括架构,如上图,这是一个在线系统的轮值排班功能,现需要进行减员。
研发人员给出的第一个方案:人员的详细信息是放在 IPD 里面,有一个查询的 API,每次创建排版系统表时,系统里只保存人员的 ID,展示时调用 API 提出信息,好处是人员的信息可以修改,如名字,也能实时的反应出来。
这个方案不考虑历史信息,比如说之前排版如何,人员执行状况如何,这些都不考虑。排班情况是在现有的时间和规则基础上,来推演后面排班是什么样的计划,这个方案是可以接受的,但是有一个巨大的问题,当去做考核时,因为没有历史信息以及值班情况都无法查询。
研发人员给出的方案二无法接受,因为当删掉一个人时,所有东西都会被删除。如不要实时查询,缓存数据即可。历史数据按照跑批沉淀,如按照周去沉淀,在生成排班表时将信息抓取,落到历史库里,基本信息常用信息也会同步保存。
但有个问题是沉淀阶段数据仍可能不一致,比如说一天的排班,ABC 三个人在当天或者当周都有排版,此时将 B 删除后,前阶段还没有沉淀的仍会被修改,数据仍然不精确。
研发人员给出的方案三:解决方案是在修改的瞬间发起一次沉淀,把开头到现在跑批,沉淀进去,剩下的东西不去管,进行更新,因为更新属于未来,再跑批时再进行沉淀,这是非常精确的方案,但方案复杂度会比较大。
研发人员给出的方案四:数据精确性是否真的重要,是否可以允许有一些误差,比如拉历史数据只是为了看报告,一个轮值的性能指标,错一天或者错一周对结果绩效没有根本性的影响。
所以接下来想能不能在一个有限的范围内让数据变得更精确,如缩短沉淀周期,从一周跑一次沉淀变成一天跑一次,一天跑一次变成一个小时跑一次,如此随时都可以沉淀,不准确的范围和可能性就相对较小了。
方案三和方案四孰优孰劣?需要根据自身的诉求去看,看是否需要非常精确的数据,一般的情况下选择方案四就可以了,因为一是没有必要,二是越精确的方案,它的复杂性就越大。
再举一例:简单性要比完美性、一致性更优先考虑
如果 IPD 删了一个人(因离职等),因为想要省事,让 IPD 调用排班系统的 API,然后将其删除,但这不是一个好方法,因为耦合性太强。IPD 系统根本不知道是否有排班这件事。让 IPD 关注不相关的排班业务,这是错误的方式。
方案二是轮询信息,让排班系统调用 API 轮询查看人员是否发生变化。但这也不是一个好的方法,对 IPD 压力过大,也没有必要。
实际在技术上有更好的办法——加消息队列,在两个系统间加消息队列,把人员修改变成更新事件,如此两个系统都不知道对方的存在。这个方案的好处是,如果人员信息是非常重要的数据,那么发出来的事件消费者可以是任何系统,排班系统可以、其他系统也可以
研发人员给出的方案四:就是根本不去做通知,如果一个人离职,他的经理一定会修改排班计划,虽然修改可能会有时间滞后性,也有一定的影响但影响不大,因为可以替换其他人去进行值班。而且整个排班系统里面如果一个人在规定时间内没有响应,随时上报经理,立刻进行修改,所以人肉修改的方式其实是最简单的方式。
此时再考究方案三和方案四哪个更好?依然是方案四,即便名字变了,也可以在系统里增加一个名字修改功能,甚至可以不把名字纳入数据系统,因为邮件地址和工号是不会变更的。
#经验四:关注高级使用的情况
有 80%的情况,用简单的工具就可以解决,比如说部署,点一下按钮就可以部署了。但有 20%的情况是需要批量处理的,甚至在程序的层次上,有时候要进行逻辑验证或二次开发,而这个时候图形化的解决方案是无法胜任的。
所以核心系统服务化是非常重要的,核心系统服务化后,Web Site 可以交给日常处理,不需要太多业务知识,同时要有基于 DSL 的命令行工具,它的特点是扩展性很好,可以再此之上再做编程,执行效率高,比如一百台的机器到界面上如何部署,在命令行敲几个命令筛选出来,这样更快。
防止来自脚本的诱惑
当提供命令行时,一定要防止脚本过多,如果脚本处理的业务不是单一的,不要作为系统的一部分,只作为一个帮助性的工具,因为 DevOps 强调的是整个工具链需要状态上报和数据沉淀,如里面有 4 部分的事,不知对错,效率依然很低,若是单一的,就能知道模型在做什么,就可以成为系统的一部分被调用。
#经验五:Just Work
Just Work 经常被提到,如当使用电视、遥控器、手机时,只关注怎么用,背后的实现逻辑和电子电路的原理,不需要关心。
从这个层面上去看,两个界面对比,上面的界面暴露了太多的实现细节,而下面只关注重点阶段,这是两个工作流的实现方式。
上面界面另外一个巨大的问题是,测试通过和不通过跟人工测试是属于两个抽象层面的。人工测试和容量测试、单元测试及部署才是一个层面的,但是测试通过与否是一个逻辑上的判断,不应该在此界面上显示出来。
在这个层面上,不需要了解太多的细节,关键在于发生了什么事,上报 BETA 版,上报自动化测试环境,至于中间是不是人肉测试过,不需要关心。到 Promotion 的角度,才是应该呈现出来的。
#经验六:重在当下
拿优维科技的产品界面举例,重在当下是说如果做部署的话,昨天发生的部署不在关心范畴之内,只关心当前发生了什么,最近一次发生什么,是六个小时前发生还是一个月前发生即可,业务系统是否正常。历史列表则不需要告诉我,可以将其隐藏当需要时才出现。
#经验七:隐藏非关键信息
当一个工具链从一个阶段到另一个阶段 Promotion 时通常也不关心中间逻辑。只有在出错的情况下才去关注细节,如在测试时测试失败,这时去看到底是哪一个阶段失败了。
所以隐藏关键信息就是出错时才进行提醒细节性信息,如果没有出错,就不需要,因为整个工具链的信息是非常丰富的。
#经验八:业务为线,不要堆砌功能
整个工具的设计,不仅仅适用于工具链,只要是 2B 的功能,那么就不要堆砌功能,上图里会发现里面有将近 200 个菜单,业务人员使用时非常痛苦,处理一个业务需要在不同的菜单重复进行切换和设置。
所以需要写文档来帮助业务人员,如第一步到哪些菜单等等,这就是堆砌功能。可以发一个图把所有的功能罗列出来,自然而然变成一个树状图,反映到左侧的列表里头。
这是一个展现方式比较好的页面编辑器,一个业务界面。在逻辑层面上,左边的菜单可以收起来,这是需要关注的几个概念。
在 workspace 里,创建出来进入到工程开发以后,收起来以后就进入到 Workspace 内部了,以项目为单元,以文件为基本的组成部分去调试、开发、编辑,是非常实用,没有过多功能堆砌,一眼就能看到要用的。
上图是专题编辑界面,也是一个比较好的界面,抛弃了那种树状图功能的导航,在主要业务上,把不必要的信息隐藏,把必要的信息漏出,自然而然关注到业务问题的解决。
#经验九:突出使用、分离配置
当使用一个工具链的时候,包括 Jenkins,会发现会发现 10%的时间在配置流程,一旦配置好 90%的时间是不变的只是在使用,工具要有这样的特点——用的多变的少,把使用的部分重点突出。
把配置的部分做的简单一些,把功能隐藏起来,因为只有 10%的时间去配置,而且配置完就基本无需理会了,使用即可。
#经验十:简化操作
整个 2B 工具链的研发,无论是 DevOps 或者其他,都是在解决效率和业务问题。每一次构建一个版本,之后基于这个版本做测试就需要整个生命周期的变化,因此要建立一个全流程追踪的体系。
在追踪体系里,同源的问题很重要,同源不仅仅是提供相同的制品叫同源,如构建一个 1.01 版本部署到生产环境时,还是这个制品,这不叫同源,是伪同源,因为构建时是基于 2.2.1 版本,所以生产时上的公共库是 2.1 版本,不是 2.3.0 版本。所以依赖处理时,要把信息保留,用什么构建,就上什么版本,如此才叫同源。
想的更多一些,将来运行所用的目标系统可能是 32 位,可能是 64 位,需要在构建的时候明确未来要在哪个平台上运行,当从平台上去拉制品的时候,应该拉过来的是对应的 32 位或者 64 位。
如此带来一个问题,系统要记录这些信息并且把它锁住,否则你应该再去选择,锁住这个情况,当去部署,上面提到,有一些情况下需要手动去选择什么部到某一环境,如测试、生产。
如果有此情况,选产品 1.0 版跟其他相关构建,产生关联时必须锁定不能被选择,这是简化操作。可能有 100 个包都不能编辑,整个部署只有一个或者两个可以变,其他不变。
#总 结
DevOps 其实没有大家想象的那么复杂,当然,也并不简单。亚马逊的整个体系工具链大概是 50 个左右工具。
最近数人云和其他一些互联网企业都在学习谷歌的 SRE,而我本人也关注 DevOps 很长时间。无论是亚马逊的经验,还是谷歌的经验,在落地时都需要一些改进,要进行调整。
比如谷歌的单根代码库,谷歌曾经有人去其它公司推单根代码库,但是没有做起来,因为其从最开始就选择了一个非常有挑战性的方法,在这个基础上做了很多很牛的事情。
但根本上,想解决的问题实际上就是代码共享和依赖处理等等问题,但是其实有更简单的解决方案,在那个年代,亚马逊用另外一套解决方案做的也很好。
谷歌的流程很强,亚马逊也同样如此,但在借鉴的背后我们需要思考与变更。