返回 登录
0

关乎DevOps成败的三个因素

阅读2886

本文梗概:

刚开始接触持续部署、微服务(MS)和容器,你可能觉得这三个东西毫无关联。因为DevOps并没有规定持续部署中需要使用微服务,也没有要求微服务必须打包集成到容器中。但是,当我们发现这三样东西相互结合的时候,新世界的大门就这样打开了。容器领域的发展以及不可变部署的理念指导我们克服了很多以前微服务出现的问题。同时使系统变得更加灵活,部署变得更加快捷,进而实现持续部署并提高成本效率。

持续集成

想要了解持续部署,我们必须先了解它的前身,也就是持续集成(CI)和持续交付(CD)。

在软件研发生命周期中,集成阶段往往是最让人煎熬的。每个不同的小组可能负责不同的应用和服务,耗费数周、数月甚至是数年时间进行开发。每个小组的需求各不相同,开发人员也尽力实现需求。虽然我们对各个分散的应用和服务进行了周期性的核查(这并不难),但当负责人决定开始将这些应用和服务集成并交付时,大家还是很担心。

从以往的项目经验来看,集成阶段一定会漏洞百出。我们清楚的知道,集成时肯定会出现问题,依赖性错误、各种彼此无法正常通讯的接口等等。集成阶段可能持续数周甚至数月的时间,大家对此也司空见惯。最糟糕的是,如果此时发现了一个漏洞,那么我们可能要回过头来重做数日甚至数周的工作。那个时候与现在不一样,我们当时都认为那就是研发应用的“正确”方法。

极限编程(XP)和其他敏捷方法的出现改变了很多东西,我们越来越多的采用自动测试,持续集成也逐渐发展成熟。今天,软件行业已经有了长足的发展,我们也知道当初研发软件的方式是错误的。

持续集成(CI)通常在开发环境中进行代码的集成、构建和测试。它要求开发人员频繁的将代码集成到一个共享的库里。具体进行集成的频率取决于研发小组的规模、项目的规模以及编写代码所用的时间。大部分情况下,程序员要么直接推送(push)到共享库,要么将代码合并(merge)到库中。

不论是哪一种方式,每天我们都要这样做好几次。但仅仅将代码整合到一个共享库里并不够,我们还需要一个流水线(pipeline),起码要对代码进行检查,同时运行所有与库内代码相关的测试。这个流水线执行的结果是红色或者绿色。如果出现问题显示红色,如果没有问题则显示绿色。当出现红色的时候,我们就会通知提交代码的程序员进行相应的修改。

与持续交付不同的是,CI并没有一个明确的目标。也就是说,当我们将一个应用和其他应用集成到一起的时候,我们并不知道它是否可以用于生产(是否做好生产准备production readiness)。在代码被最终应用于生产之前,我们不知道还要做多少工作。

我们做了这么多,只是希望确保新提交的代码能够通过现有的任何测试。但不论如何,使用得当的话CI还是有很大作用的。许多情况下,实施CI十分困难,但只要大家都熟悉并适应它,CI能够带来的好处也十分巨大。

CI 的前提条件不只是测试,还有一条很重要的规则就是,当流水线中出现问题的时候,修复问题的优先级要高于其他任务。如果推迟修复问题,流水线中的下一步处理工作也会出现问题。

如果我们忽视流水线中的故障通知,那么CI就会逐渐失去其作用,因此越早修复问题越好。立刻采取纠正措施,可以很清楚的知道造成问题的原因(毕竟提交代码和错误提示之间只有几分钟的时差),那么修复工作就小菜一碟。

那么CI到底是如何运作的?具体的工作要靠工具、编程语言、项目等等。大致的流程是这样的:

代码入库(pushing to thecode repository)

静态分析

部署前测试

打包并部署至测试环境 

部署后测试

图片描述

持续交付(CD)和持续部署

持续交付和持续集成很相似,主要的区别就是我们对于流水线处理的信心以及处理后是否需要继续进行操作。在CI中,我们知道流水线结束后还会有其他的审查(大部分是人工审核),而完整的CD处理后,生成的程序包或工件就可直接部署在生产中。换句话说,只要某样东西在CD流水线中顺利进行,那么毫无疑问,它就可以部署在生产环境中。而最终是否会应用于生产,很大程度上取决于公司的决策而非技术决策。

比如市场部可能希望在某个具体的时间点进行发布,或者是希望与其他功能特性一同部署到生产中。不论最终决定什么时候将哪一部分应用到生产中,从技术的角度来说,经过流水线处理的所有东西都已经完全完成了。持续集成和持续交付的唯一区别就在于,程序包一旦通过流水线处理,在持续交付中就不会再有其他人工测试阶段。

简单来说,持续交付流水线中的审查已经足以确保程序包万无一失,因此不需要再进行人工审查。具体哪一部分会部署到生环境、什么时间发布以及程序包中包含多少功能特性,这些问题都取决于公司经营和市场的决策了。

图片描述

Developer:研发人员

Code repository:代码库

CI pipeline:CI 流水线

CI工具时刻监控着代码库,当检测到变化的时候,运行CI流水线

研发人员将其编写的代码提交到主干或特性分支(特性分支也需要整合到主干)中。

流水线会启动不同的任务集,保证代码按预期运行。最终结果还需经过人工测试。

如果任何一步出现问题,流水线就会中止并向研发人员和有关各方发送错误报告。

如果整个流水线没有任何问题,那么所提交的代码就会成为发布候选,进行下一步人工审核。

就流水线处理来说,CI和CD并没有本质上的差别。两种方式都经历类似的阶段。真正的差别就是我们对于流水线处理的信赖程度。CD流水线中并没有人工的QA(质量保证)阶段。某个程序包是否能够部署到生产环境中,完全在于我们的决定。

相比之下,持续部署流程则更进一步,它会将所有通过审查的部分自动部署到的生产中。持续部署的整个过程都是自动化的,从向代码库提交代码,一直到形成应用或把服务部署到生产中。没有人为干涉,也无需做决定,研发人员需要做的就是在上一阶段的工作成果到达用户端时,开始进行下一个功能特性的开发。

我们需要格外注意数据库(尤其当与数据库有关的时候)并确保我们对原有产品做的改变是回溯兼容的,同时也兼容其他产品(至少在一段时间内)。

持续部署中另外一个十分重要的技术就是功能切换(feature toggles)。因为所有构件(build)都要部署到生产环境中,我们可以暂时分离一些功能。例如,我们可能已经完成了登入界面的开发工作,但还没有完成注册的相关开发工作,此时就没有必要对访问者开放登入界面。持续交付通过人工选择部署和等待的方式处解决了这类问题。

而在持续部署中是没有决策的,因此就必须使用功能切换,否则的话我们就要等到所有相关的功能都完成开发后才能进行整合,造成了不必要的延迟。在此之前,我们已经讨论过了经常合并到主干的重要性,也知道了这种延迟是违背CI/CD 的流程逻辑的。当然,也有其他的解决方案,但我认为只要是选择使用了持续部署,功能切换就是不可或缺的。

大部分的研发团队都是从持续集成开始,慢慢发展到持续交付和持续部署,因为这三者一一关联,循序渐进。

需要注意的是,我们讨论的流水线都是按照一定的顺序进行的。这种顺序不仅是符合逻辑的(例如,我们不能先部署再编译),而且也是符合执行时间的。执行时间较短的部分往往最先开始。例如,通常来说,部署前测试的运行时间要远远小于部署后测试的运行时间。同样的规则也适用于流程中的每个阶段。

比方说,如果在部署前阶段你需要做不同类型的测试,那么就先做那些速度比较快的。因为这会影响到我们收到反馈的时间,越早发现代码中的问题越好。理想情况是,在进行下一项测试任务的时候我们已经得到了上一项的测试反馈。提交代码,忙里偷闲喝杯咖啡,查看收件箱,如果没有的恼人的问题报告,那就可以开始下一项任务了。

随着本书的讲解,在我们了解微服务和容器的优势后,你可能会发现,这里所讲的流程阶段和细节有些不一样。例如,打包会由不可变容器完成,部署到测试环境可能完全没必要,我们可能会选择直接在生产环境进行测试(在蓝/绿技术的帮助下)等等。但请不要操之过急,该来的总会来的。

微服务

在持续部署中,我们早已经讲到了速度问题。这里的速度是指从产生新功能的想法到开发完成并部署生产的时间。我们当然希望能够提高速度,尽早投放到市场。如果某个新功能可以在几个小时或者几天的时间里就完成交付,而不是几周或者数月,那么企业也就能够更快的获得收益。

提高速度有很多方法,例如,我们希望流程处理速度越快越好,这样才能在出现问题的时候尽快发回反馈,同时释放资源供队列中的其他任务使用。我们应当努力减少从检查代码到部署生产的时间,这就用到了微服务技术。为一个巨大的巨石应用运行整个流程,进行测试、打包和部署通常十分缓慢。另一方面,因为微服务很小,所以速度自然也就很快。单次测试的代码量很少,需要打包的代码也少,当然需要部署的代码也就很少。

如果不是出于速度的考虑,我们也不会采用微服务。本书后面会有一大章专门分析微服务。现在我们需要注意的是,今天我们在软件行业面临的商业竞争无非就是时间的竞争,因此微服务可能是我们能够使用的最佳的架构。

容器

在容器流行之前,微服务的部署工作十分困难。巨石应用相对来说比较容易处理,我们只要创建了一个构件(JAR、WAR、DLL等),将其部署在服务器上并确保所需的可执行文档和库(例如JDK文件)都已经准备好就可以了。整个过程基本上已经实现了标准化,需要考虑的东西很少。

当然,一个微服务也同样十分简单,但当微服务的数量剧增,达到几十个,数百个甚至上千个的时候,事情就变得复杂了。这些微服务可能要使用不同的依赖(dependency)、不同的框架、不同的应用服务器等等。我们需要考虑的东西呈指数型爆炸增长。

不论如何,我们选择微服务的原因之一就是因为它能够为任务选择最佳的工具。某项任务可能最好用GoLang编写,而另一项任务则最好用NodeJS;某项任务可能使用JDK7,而另一项可能需要使用JDK8。管理服务器的人员需要对所有微服务任务进行安装和维护,这很快就会使服务器变成杂乱的垃圾箱,也很快会让他疯掉的。

那么以前的解决方案是什么呢?尽可能的标准化。规定后端只能使用JDK7,前端只能使用JSP。公共代码应当存放在共享库中等等。换句话说,人们解决与微服务部署相关问题所使用的方法,正是他们多年以来在开发、维护以及部署巨石应用中学到的方法。为了标准化,砍掉了创新,但这当然不能怪他们。

唯一的方法就是不可变虚拟机,而这也仅仅只能解决一部分问题。直到容器开始流行并能够被大众所使用,Docker使得容器的使用更加简单,不用考虑复杂的过程,所有人都能够很轻易的使用容器。

什么是容器?容器的定义是“存放或输送某样东西的物品”。

大部分人会把容器和海运集装箱联系起来,认为容器应当能够用来装货、存储并进行处理。你可以看到人们用各种方式在运输容器,最常见的就是海运。在较大的船厂,你可以找到成百上千的容器堆叠在一起。几乎所有货品的运输都是通过容器,而原因也十分简单,容器是标准化的。

标准化的容器就意味着它们可以很容易的堆叠在一起,而且难以破坏。大部分进行装运的人并不清楚容器内部是什么,也没有人(除了客户)在乎。其实容器内部是什么无所谓,最重要的是我们知道从哪里提取容器,然后运到何处。各司其职,我们在知道如何在外部处理容器,而容器内部是什么也只有一开始装货的人知道了。

“软件”容器的理念也是一样的。它们是各自独立并且不可改变的镜像,能够提供所需的各种功能,并且大部分情况下只能通过其各自的API接口连接使用。软件容器的出现是为了使我们的软件能够在任何环境(几乎)都可靠的运行。不论这些软件容器在哪里运行(研发人员的笔记本、测试或生产服务器、数据中心等等),其运行结果都是保持不变的。终于,我们可以不再有以下这种对话。

质控:登入界面有个问题。

开发人员:在我电脑上好好的啊!

之所以容器能够解决这类的问题,就是因为无论容器的运行环境怎么变化,其运行结果都是相同的。

容器的这种能力来源于其自给自足和不可改变两大特性。传统的部署方式会将工件(artifact)放在现有的节点处,同时保证应用服务器、配置文件和依赖等其他东西都已经准备好。而容器本身就已经包含了软件所需的所有东西,各种镜像文件一开始就会堆叠到容器中,这样容器中就包含了包括库、应用服务器、配置文件、运行时依赖(runtime dependency)和操作系统包在内的所有东西。到目前为止我们对容器的描述也都符合虚拟机的定义,那么容器和虚拟机有什么区别呢?

例如,一台服务器上正运行着5个虚拟机,除了虚拟机监控器(耗费的资源比lxc(linux container容器)还多),每个虚拟机上都有各自的操作系统。而如果是五个容器,那么这些容器就可以共享服务器的操作系统,甚至在适当的时候还可使用服务器的库和二进制文件。因此,容器的体量更小。

但这样看来,和巨石应用的差别好像也不大,尤其是当一个应用占用整个服务器的时候。微服务就不一样了,考虑到我们会有数十个甚至上百个微服务部署在同一台服务器上,那么我们所得的资源利用率就大大的提高了。换句话说,一台物理服务器可支持的容器数量要多于虚拟机数量。

图片描述

三个火枪手:持续部署、微服务和容器的协同效应

持续部署、微服务和容器之间相辅相成。它们就像三个火枪手一样,每个都身怀绝技,但只有当它们协同合作的时候,才会彰显出真正的力量。

持续部署能够自动化的不断为我们提供应用开发状态的反馈信息,同时自动的持续将应用部署到生产环境。这样一来就提高了我们交付的应用质量,并且缩短了投放市场的时间。

微服务使得我们更加自由的做出更优的决策、更快的研发、以及不久之后我们将会看到的更简便的服务扩展。

最后,容器的出现解决了许多部署中存在的问题,尤其解决了和微服务一同使用时出现的问题。由于容器的不可变性,系统的可靠性也得到了一定程度的提高。

持续部署、微服务和容器协同使用,我们可以做到更多。本书中我们将一同探索,寻找方法实现频繁快速部署、全自动化、零宕机时间、回滚能力、跨环境可靠性、简易扩展以及创建自愈型系统(在发生故障后能够快速恢复)等能力。

每一种能力的实现都需要我们付出很多,那么我们最终是否能够实现所有能力呢?当然可以!目前存在的方法和工具已经可以为我们提供这些能力,我们只需要正确的将这些能力整合为一体。路漫漫其修远兮,吾将上下而求索。我们需要从源头开始,既然要创建一个系统,那么首先我们就需要讨论系统的架构。

学需致用,思需有为(知道还不够,你必须实践;想法还不够,你必须去做)。

——歌德

普元云计算专区:http://primeton.csdn.net/m/zone/primeton/index#

普元公众号:

图片描述

评论