使用 Golang 语言实现 DevOps Orchestration

2017-09-18 马全一 Go中国 Go中国

一、DevOps 背景

我们先讲一下 DevOps 的概念怎么产生的。2007年时,有一名独立顾问 Patrick Debios,他咨询过程当中遇到研发团队和运维团队在交流障碍。而后他一直在思考这个问题。 在 2008 年的 Agile Conference 会议上,他对一个 “birds of a featcher” 议题特别有兴趣。当他到 Session 的会场时发现会场一个人也没有,即也没有讲师,也没有听众。而后来他在休息室找到了这名讲师 Andrew Clay Shafer 进行交流。他俩对研发和运维测试沟通问题有很多相同的感受和理解,而后他们就为专门讨论了这个话题建立了一个 “Aglie System Administration” 的讨论组。2009年 O'Reily Velcotiy 会议上两名 Flickr 的工程师分享了 Flickr 如何在一天之中完成多次系统迭代更新的议题,这个议题在当时获得了很多关注,Patrick Debios 和他们在 Twitter 上使用 “DevOpsDays” 的标签进行交流。2009 年 10 月份的时候,他们把 “DevOpsDays” 的 Hashtag 缩短为 “DevOps”,第一次有了 “DevOps” 的概念。 2010 年时第一次有了 DevOpsDays 的大型技术会议, 目前这个会议还坚持每年在湾区的计算机博物馆举办。2011年 Grather 象限第一次加入 DevOps 这个概念。 2012 年的 InforQ 对 Patrick Debios 的一个采访中,Patrick 解释说我不是有一个很好的 idea 然后再创造了 DevOps 这样的单词。 DevOps 这个概念是在反复的沟通过程中产生的。原来叫 DevOpsDays,因为 Twitter 140 个字符数限制就去掉 Days ,才产生 DevOps 这个词。整个过程中,DevOps 理念产生也不停在迭代,这个给我们做 DevOps 产品或者在团队中实施 DevoPs 一个提示,不要一开始把目标设定为团队每日迭代多少次,上线更新多少次;应该是从开始一点一滴的改进迭代和积累,最终为整个的商业目标实现带来改进的效益。不要以节约多少人力,节约多少时间去衡量 DevOps 工作,而是以最终的商业成功与否为衡量标准。


二、什么是 DevOps

什么是 DevOps? DevOps is "a portmanteau of 'development' and 'operations'" and is "a software development method that stresses communications, collaboration, integration, automation and measurement of cooperation between software developers and other IT professionals". 这是 Wikipedia 的解释,我反复研究和体会,都觉得这段解释没有实际意义,不能指导我们开发和运维的工作。DevOps is an operational philosophy that promotes better communication between development and operations as more elements of operations become programmable. 这句话最能代表我的观点。 DevOps 更应该是一个处理问题的哲学,一个团队共同追求的理念,它更强调的是团队的沟通和自动化,而不像微服务有12条军规之类的定义。所以要根据团队自己的情况、根据你项目的状况、根据企业的文化等等因素综合一起设计和实施 DevOps 流程。现在市场上有很多 DevOps 产品、服务,要求用户遵循产品的逻辑,我觉得这样反而是违反了 DevOps 初衷,当然这是我个人的看法,仅供参考。

    

三、如何做到 DevOps 终极的目标

DevOps 的终极目标是 The ultimate is break down barriers between developer, QAs and operators.” 就是打破研发、测试和运维之间的界限,提升迭代速度,尽快的实现商业价值。交流的时候,经常有朋友说 DevOps 会降低人力投入、会节约研发费用,在我看来这未必。真实情况往往是增加了更多的研发人员,但是业务的迭代速度变快了,效益比以往更高了。

1、在研发阶段更多的考虑未来运维

研发的过程中应该尽量把在生产环境遇到的状况考虑进去。应用运行的操作系统是什么、依赖的 Library 如何控制管理、应用的环境怎么部署、需要多少 CPU和内存、需要什么样的网络条件、是不是需要对象存储支持和部署 CDN ?这个时候选择使用容器就是最佳的选择,因为它天生把环境一致性的问题解决掉了。 研发阶段使用的容器,构建好以后,它会一直伴随整个服务的生命周期。这也是我选择用容器来构建一个 DevOps 产品的原因。

2、定义开发到产品的 DevOps 流程

构建 DevOps 需要把整个业务流程进行考虑。有很多团队参与到研发、测试和开发的过程,实施 DevOps 流程的时候,要定义清楚。但这个流程不是一成不变的,要根据以上我讲的各种因素不停地改进,DevOps 没有最好,只有最适合

3、尽量自动化

这个是比较极限的一个目标。把所有的东西自动化,是我们每个工程师努力实现的目标。但实际情况是有些 DevOps 场景还是需要有人为的参与。 比如在一些大公司的流程里面,当你要发布的一个产品上线,DevOps 系统必须发一个邮件给一个具有权限的 Boss 。邮件中包含继续 DevOps 流程的一个链接,只有这个链接被具有权限的人点击了,整个 DevOps 流程才可以继续,这个现象在很多大型软件公司或者传统企业还是存在的。


四、为什么推进DevOps如此之难?

推进 DevOps 非常难是我感受最深的。现在各个企业都在推 DevOps ,为什么推进会很困难,实施的效果不好,部门墙很难打破?很多时候推动者是站在管理的角度尝试推进 DevOps 的,突然有个人站在你面前对你指手画脚,告诉你应该如何如何,我想很多工程师都会像我一样非常反感。推进 DevOps 要站在服务者角度,跟团队一起工作,找到业务中遇到的问题,尝试通过 DevOps 解决它;要勇于和团队一起承担后果,不要把责任推给一线的工程师,不要成为大家唾弃的“甩锅侠”;尽量的在原有的业务流程中改进,不要拿出一个工具,讲什么这个工具能够让研发效率提高多少,需要把原来的实现推倒重来,在原来的工作上叠加了一大堆新的任务。一个研发团队肯定是已经能够正常的运行,只不过效率没有达到期待值。这时候给它一个新轮子,代价成本和分享都非常高。所以推进 DevOps 期望改进的时候,首先是不要修改原来的业务流程


我的经验是:

1、不要打破原来的流程;

2、把团队正在使用工具研究明白,不要替换成目前的工具链;

3、将 DevOps  的流程通过编排管理工具编排起来能够按照原先的流程运转;再从工具上尝试替换以提高效率或改进流程。

在我个人观察到的现象,很多团队都在讲 DevOps,其实都在讲如果去做服务或者应用的构建。 如果没有编译器方面的能力,是不可能在编译层面提高它的速度;只能把构建任务变成并行、尽可能的利用构建的中间输出,提供更多的资源让构建的速度更快。这个时候我的观点是,不要在无谓事情投入更多的时间,能通过增加资源让整个 DevOps 流程速度变快的话,就不要投入太多的人力进行优化,还是人力的成本最高,构建 DevOps 的时候一定要考虑成本,不要一味的求快。

五、ContainerOps

基于以上我对 DevOps 的理解,设计和实现了 ContainerOps 项目。第一部分是把封装了 DevOps 业务的容器镜像定义为 Component 。第二部分是针对 DevOps 设计的轻量级 Workflow 。华为公司面对的客户都是大型的电信、银行或者政企客户,业务流程相对互联网公司比较复杂,还有很多管理上的需求。以一个定义文件或配置文件定义整个 DevOps 流程对于我们客户来说都不适用。所以我们做了一个比较轻量级的 Workflow,主要关注定义流程的用户体验。第三部分是我们运行的时候把所有任务都封装为容器,交由 Container OpsOrchestration 引擎来管理。在 ContainerOps 项目中,重度依赖于 Kubernetes 。


  1、DevOps Component 的设计理念

我们讲我们这个 Component 设计的理念,你要把 DevOps 任务封装进去,我们认为这就是一个 Component。你是可以传递一些数据到容器里面,以环境变量方式传递给它,任务在执行过程当中,你可以把参数传给它,它来执行,你程序里面直接读环境变量就可以。我们会有一个环境变量的名字,你自己也可以建更多的环境变量,你输出的时候,你直接把你的结果输出到标准日志里面,标准输出里面,我们会把这个采集出来做一个分析,你输出的格式其实是固定的格式,结果也是我们限定好的关键字,这样我们会把结构采集到。以前版本设计里面,你要在 Component 调引擎的 ATI 结果发给我们,我们跟客户交流,觉得这个方式比较不友好,这样的话大家直接把结果打出去就可以了。最后这个镜像跑起来不占资源,这个镜像里面很容易配置在一个镜像里面做多个应用。还有你还有很多镜像扫描的安全工具,你可以做一个扫描。最后一点,这个镜像默认配好了 ssh,你如果觉得输出打日志方式不友好,就直接 ssh 进去,这种方式可能更符合接触容器比较少的研发人员。这个 Component 所有生命周期都是由我们来维护,它从创建、执行,日志采集出来然后消化,都是由整个平台控制,所以我们叫Component。你已经有的一些工作,比如已经做了一些任务,这个时候可以去调,但是整个生命周期资源管理都在系统之外。第三方是通过 API 调用来实现的功能,Component 是在平台之内生命周期所有管理由整个平台来维护,我们叫 Component。

  

2、为什么要用 DevOps Component  

从使用的角度来说,以 Jenkins 为例,如果你是使用一个 Component,它只要知道它的地址,配置整个工作流的过程中输进去,剩下事就不用管的,运行的时候,把地址发过去,他会自己调动节点下载下来,这个我们觉得使用起来会更方便,见图1。


图1

这里就是我们为什么没有用 pod,如果我们用了 pod 这个概念,这个 Component 就变成绑定性了。如果只是一个容器镜像的话,就便于你使用,你不需要再搞安装。所以我们才选了那个,你原来认为一个 POD 需要多个进程,包括有数据库,这个时候你只要 Component 就可以,见图2。我们觉得这样更简单,更容易分享,这取决于我以前在做社区的感受。一个社区要想成功,它必须有一个能够分享大家沟通交流的对象。我们想做这样一个产品形成一个社区,从产品的角度来说,我们肯定有这样一个 Component 能够产生这样的话题,这种东西才能够合适成为讨论的中心。我们最后没有用 pod 的概念,而是用 Component,而且它比较小,比较容易分享。


图2


六、Component Namespace

我们单独做了一个仓库管理,这里面使用的是 namespace,我们选用这种方式认为最合理。我们会做一个仓库,见图3,这个我们也会做一个运营,它最底层是一个 Container Registry,华为有一个项目,这个项目就是我用 Golang 第一版本的协议,我们在上面做了改动,见图4,它在上面可以什么都存,它作为底层的存储。上面把刚才 namespace 作为实现。这里就是我们的研究,Docker 底层可以传递参数进去,里面读取这些环境变量可以处理任务,处理完了以后把信息通过标准输出打出来,我们就会判断你是成功还是失败。我们把所有日志全部收集出来,发给日志处理模块。整个服务器我们有一个进程,这个进程把信息发到处理中心节点上,还有其他的特性。比如在 Component 添加的时候,可以设定资源,我需要多少 CPU 内存跑它,我们交给执行的时候带进去。有些任务你也不确定跑多长时间,你设一个值,如果在规定时间内这个任务还没有跑完,我们就认为它失败了。


图3


图4


我们的方式就是这样,见图5,node 有一个进程会把日志都拿出来发回到日志处理的那块,现在大规模我们可能推荐用分布式日志处理。它默认的会把里面符合我们要求的格式所有日志信息全部发到引擎管理,你可以自己注册一个我要处理什么样的日志,做一个编排放进去,可以按照你自己的要求去处理,这个是整个日志处理的逻辑。这个就是我们 Workflow 的定义,这边的方块我们叫 actions,这个的划分又你的业务逻辑决定的。你建几个 actions,还有一个特定的暂停,在那一刻我们可以发一个邮件出去来决定是不是继续执行。整个工作流执行的过程中,如果超时了,就认为整个执行失败。我们有咨询的项目有几百条 Workflow,你这一个设计里面,每一个指定的才可以执行。这样你做了一个改动,是你的业务范围,你再做一个改动,它就可以适应另一个场景,你不需要做10个或者20个,而是你可以在上面反复修改。


图5


整个 Workflow 也可以是环境变量,这个环境变量在 Component 也可以读出来。你也可以指定整个Workflow的资源,它会给你计算,你所有执行完了以后需要多少,如果超了保存时候就告诉你这个不能执行不能激活。刚才上面每个方块都是一个 Job,下面就是传递进来的数据,见图6,我们在之前和之后都允许加一些脚本,我们做的 Workflow 跟真正的工作流产品不同的一点,很大的一个区别,我们这里什么都没有,如果你需要这些知识的话,你把它写成脚本放在这里执行。我们会看整个执行的结果,来决定这个任务是正常还是失败。见图7,在 Job 有两种可能,一种你执行一个 Component,我们会把地址进行下发,然后拉下来执行,这套是标准的方案。或者你通过函数来返回回来,这样的话我们也知道执行的结果,最后可以把所有的输出再执行一次脚本判断,在这里你有充分的自由度来控制 Workflow。


图6

图7


七、产品的实际使用实践

    图8,图9是产品的界面,这是第二版界面,这一版就要板上一版更简单易用一些,这个就是执行的结果输出。


    图8


图9


我们讲一个用户实际使用情况,见图10。我们做好第一版本以后,我们拿到内部试用了一下。他们在办公室里面有6台机器,成功以后,它写了一个调度的程序,通知到办公室服务器,然后跑测试,我记得是一千多万测试,跑半个多小时,这些都跑完了以后,他真正要确定一个重要版本的时候,要跑7乘24,这里写模拟用户场景,用户是十个 node 集群,然后不停测试。最后一步,基本就是靠人工来做,因为写了一段时间做完之后,觉得可以发一个版本做一个测试。我们就帮它整个梳理一遍,我们在谷歌的 GCE 上开了十几个虚拟机跑所有的任务,第一把单元测试全部封装为 Component,当每一次 PR 的时候,我们把所有单元测试并行的发到 GCE 的虚拟机上,GCE 虚拟机装好以后我们装了一个集群,第一步把所有单元测试的任务执行,我们最快能够10分钟左右跑完。现在他们挂了两个开源的免费工具。我们把他们一千多万的测试也变成 Component,在第二步来执行。第一步执行完了以后,我们会调 API,这个跑的相对来说比较快一点。我们在整个过程中发现,他们项目是两部分,我们也尝试很多的方案也没有解决这个问题。这是一部分,最后我们还是在 GCE 上,按照用户场景用力重新调谷歌的云调进来,最后再调 API,通知他的团队整个流程这么做。做完以后,效率提高很多,但还有另一个问题,做这个问题的时候,所有资源是我们出的,它就要权衡,项目空间换时间,我需要花那么多钱把 DevOps 提升到这个程度吗,这就是需要一个选择,这还是要看业务场景。DevOps 并不是我最快就是最好的,有时候公司可能会在乎成本,你选择一个比较折中的方式,就比较符合公司的利益。这个就是这些。

  图10

 我们做这个事情,我们觉得现在大家都在向容器转型,大家应用通过编排来上,但是我们在这个之上更抽象一些,你DevOps任务也通过编排方式执行,尽可能利用容器组件的优势来做