写点什么

持续集成之“自动化部署”

  • 2011-07-14
  • 本文字数:4053 字

    阅读完需:约 13 分钟

前文《依赖管理》中,我们讨论了如何在代码变得庞大,组件增多的情况下,做好外部库和内部组件依赖管理,从而提高构建效率。可以应用的实践包括:一次生成,多次复用;建立统一制品库,外部依赖库可以使用像Maven 或Ivy 这样的工具进行统一管理;对架构进行调整,使一个大的代码库分成多个组件;每个组件有自己的持续集成体系;对多个组件做持续集成。然而,解决一个问题后,总会有另一个问题等在那里,需要你来解决。这次Joe 的团队遇到了部署问题。

星期一早上,Alice 一进办公室,就看到一脸倦意的Joe 坐在椅子上,喝着咖啡。

“今天怎么来得这么早?看样子,你没睡好啊?”Alice 问道。

“当然啦,昨天晚上我就来了。”Joe 无精打采地回答道。

“怎么啦?”

“还不是因为新版本上线出了点儿问题”,Joe 说道。“看来我们要把部署这件事好好讨论一下,再这样下去,不只我要来,你们也要和我一样啦!呵呵!”

当天下午,Joe 邀请了运维团队的主要负责人Tom 和Steven,召开了一个关于部署问题的讨论会。

Joe 说道:“先请运维部门的 Tom 介绍一下上周末的新版本上线过程和发现的问题吧。”

Tom 描述了上线部署全过程。

不可重复且不可靠、易出错的手工部署过程

  1. 当新版本开发测试完成后,由开发团队的成员在浏览器上登录运维平台,填写上线申请单。申请单的内容包括新版本的上线部署步骤。
  2. 测试人员为了保证能够升级部署成功,首先要复制生产环境中的程序和数据到本地的测试环境中,然后根据上线申请单中所描述的上线部署步骤进行操作,对上线步骤进行验证。
  3. 运维人员登录到运维平台,收到上线申请单后,确认“已收到”。
  4. 运维人员发现上线部署步骤有问题,生产环境的路径与上线部署步骤中描述的不一致。于是与开发人员进行沟通,让开发人员修改上线部署步骤。
  5. 开发人员修改后,再次通知测试人员和运维人员查看并确认。
  6. 确认无误后,运维人员根据部署计划,登录到生产环境中,依照上线部署步骤,手工操作完成。

“上周末上线部署时出现的情况是:在本次部署之前,我们的集群中,有两台机器因 HotFix,其程序配置被修改过,与其它机器不一致。因此,该机器上的部署失败,导致部分服务不可用。运维人员查了很长时间没有发现问题,星期日打电话把 Joe 叫来帮助我们查问题时,Joe 才回忆起有那么一次 HotFix,但当时负责的运维人员已经离职,没人其它运维人员知道这件事情。”Tom 说道,“我们对问题进行了分析,认为应该加强我们的上线流程管理,对于那种 HotFix 也应该发起一个审批流程,并且在该流程中不但要主要负责人审批,而且要对相关人发出周知通报。另外,我们的运维人员应该对上线单进行严格审核,并对部署中所涉及的机器进行更详细的验证,对生产环境中的任何修改都要进行登记。即使非常紧急,也要在事后补充记录一下。”

“这些方法固然很好,但其实我们可以采用更好的办法来解决。”Joe 接着说到,“假如我们在部署运维工作也能够借鉴持续集成的做法,利用一些最佳实践,那么这次部署事故根本就不会发生。比如(1)将部署操作脚本化;(2)进行持续部署验证测试;(3)部署脚本通用化,环境变量等使用配置方式传入 ;(4)让测试环境尽可能与生产环境一致,至少在成本条件允许的情况下尽量保持相似;(5)对环境配置进行版本控制;(6)任何人不得直接对生产环境进行直接的手工操作,等等。”

将部署操作脚本化,并进行部署验证测试

Bob 说道:“嗯,其实那些上线步骤中所描述的内容都可以进行脚本化,之前也讨论过这一问题。目前上线步骤中的内容基本都可以写成自动化脚本,即使现在不行,也可以通过少量改造,使其可以自动化。但问题是… …”Bob 犹豫了一下,接着说道,“如何来验证这些脚本是正确的呢?”

Joe 说道:“保证运维人员是如何验证上线申请单上的上线步骤是正确的呢?同样,我们也可以做一些部署验证就行了。这些部署的验证也可以通过脚本方式来进行,比如在安装之前验证程序所用端口没有被占用,安装之后验证该端口已被该程序所使用;比如安装之前验证程序日志中记录了该程序已停止运行,在安装之后验证程序日志中刻录该程序已重新启动;等等”。

Alice 问道:“那我们还要调试这些部署脚本呀?没有线上生产环境,我们怎么调试呢?”

各类环境尽可能相似,并使部署脚本通用化

Joe 回答道:“首先我们应该加强基础设施这方面的投入。在力所能及的情况下,让测试环境与生产环境相似。比如,生产环境可能有 100 台机器的集群,那我们至少要找两台机器的集群做测试环境。生产环境中使用 Tomcat,我们的测试环境和开发环境中也应该使用相同的 Tomcat,而不用 Jetty。”

Joe 停下来,喝了一口咖啡,接着说道:“这样一来,我们的部署脚本就可以在开发环境、测试环境进行测试了。当开发人员进行本地测试时,可以使用这个脚本进行单机的部署。当测试人员进行集成测试时,可以使用同样的脚本进行多机部署。与机器数量无关的配置可以统一放在某配置文件中。而与机器数量等相关的配置可以放在另外的配置文件中。由于在真正上线部署之前,开发人员和测试人员已经使用同一个脚本进行多次部署,就是对该脚本进行的测试。当我们上线部署时,只有与机器相关的配置文件会有变化,其它配置基本相同,所以上线部署时脚本出错的几率已经比较小了。而且,这种自动化没有人工干预,也不会发生手工误操作。”

Tom 问道:“那这些脚本由谁来写?由谁维护呢?”

Joe 回答道:“谁最了解情况,就由谁来写。其实,我们也应该像对待产品代码一样,来对待这些脚本和配置文件,把它们放在我们的代码库里,进行版本控制。无论是运维人员还是开发人员,或者测试人员,对这些脚本的修改都应该提交到版本控制库中,除非他所做的修改只是为了测试他自己在本地的程序,那就不用提交了。这样一来,‘谁在什么时候对什么进行了修改,为什么做修改?’这个审计问题就可以直接由版本控制系统来回答,也就做到了所有内容可追踪了。”

对环境管理进行版本控制,杜绝对生产环境的手工直接修改

“听上去,对于配置文件、脚本等进行版本管理的确是解决了运维部署的很多问题。但如何对环境管理进行版本控制呢?”Tom 问道。

Joe 想了想,说道:“环境管理比较复杂。一般来说,环境包括几个层次,包括硬件及网络配置、操作系统、我们的应用程序所依赖的软件堆栈及其配置、以及我们的应用程序运行时所需的数据及其配置。目前对我们来说,对于硬件及网络配置、操作系统这两层来说,有两种方式进行管理。一种是利用一些专用软件进行自动化的远程配置,即只要给机器加电,就可以通过一些技术对一台机器进行系统的安装与配置。另一种是使用虚拟化技术来进行系统配置管理。对我们现在的游戏平台来说, 使用后者即可。只要将基本的环境做成虚拟机镜像文件,并将其作为环境基线进行版本管理。当然,由于镜像通常较大,所以最好不要使用常见的版本控制工具(如 subversion,Git 等)进行,而使用某种简单的机制即可。”

Joe 停了一下,看看大家没有提问的意思,于是接着说道:“至于基于其上的软件堆栈及堆栈中各软件的配置管理完全可以利用类似于 CfEngine,Puppet 或 Chef 的工具进行。这些软件环境管理工具 都提供某种领域专属语言来描述软件堆栈配置,并保存在文本文件中。这些工具一般通过服务器 / 客户端的工作方式运行,客户端向服务器发送请求,验证本机器节点的软件配置是否与服务器中的设置相符,如果不符,就会自动更新。尤其重要的是,这些更新操作都是幂等的,即无论这些配置在该客户机上执行多少遍,每次的结果状态都是相同的。另外,它们通常能与版本控制工具集成。所以,只要将我们的软件堆栈配置管理信息放到版本控制库中,就可以同时管理数台机器。”

“oh, 对不起,Joe,我想打断一下,”Tom 问道:“你能画一个图来解释一下你刚才所说的这种软件环境配置管理工具吗?”

“当然没问题。”Joe 拿起笔在白板上画了一个 Puppet 的工作示意图,如下图所示。

“看上去清楚多啦。”Tom 笑道,“通过这种方式,我们就只需要将版本控制库中保存的配置信息检出到本地,进行相应的修改,再提交到版本控制库中,这种工具就会自动帮我们完成必要的配置更新了。是这样的吗?”

“对,”Joe 点了点头,说道,“如果我们的部署脚本也是通过这种方式来做的,那么我们就根本没有必要登录到生产环境的机器上,进行手工操作了。而且,Puppet 还提供一种 Try Run 功能,可以进行配置变更的模拟,让你能够对比一下变更前后的不同之处。”

Tom 说道:“你说的这些听上去都不错。但并不是所有人都能够修改生产环境的配置信息的。所以我们还是需要一个软件平台来管理上线的申请审批流程。”

“在任何企业中,这种申请审批流程和生产环境变更的授权都是必要的,但这仅仅是审核流程的操作。而真正与软件部署相同的具体操作都不应该在这种审批流程当中。”Joe 回答道。

Tom 接过话来,说道:“嗯,这样的话,我们仍旧能够做到:有权限的人才能真正修改生产环境的配置文件,同时达到了无人真正直接操作生产环境的目的,避免了手工误操作带来的问题。”

参加本次会议的测试人员和运维人员对这种做法产生了浓厚的兴趣,并要求开发人员给予配合,将目前游戏平台的部署自动化。Tom 说道:“这就是我们运维工作的一个方向。让枯燥易出错的重复性手工操作变成受控的自动化,从而解放运维人员,让我们可以关注于更加有价值的运行监控等工作中。”

Alice 说道:“这看上去还是有一定的工作量啊。”

“当然,我们可能需要做一些工作,但我想这些投入是值得的。”Joe 回答道。“同时,还需要各种角色之间更紧密的配合,而不是像之前那样,通过一个代表上个世纪八十年代先进技术的办公自动化平台来描述部署上线步骤这类关键的业务操作信息。”

Tom 也点了点头,说:“嗯,应该使用版本控制方式。但我们还是需要一个上线审批的流程,只不过,这个流程中不再保存上线步骤这类与实际部署相关的业务信息,而只是为了部署人员的资格审核与信息周知的目标。”

经过一番讨论,开发、测试和运维团队在这件事情上达成了一致,并按计划开始实施了。

需要注意的是,他们似乎没有谈到数据管理。他们会遇到相关的问题吗?​

2011-07-14 06:109965
用户头像

发布了 100 篇内容, 共 21.6 次阅读, 收获喜欢 5 次。

关注

评论

发布
暂无评论
发现更多内容

《转》搭建websocket消息推送服务

hasWhere

过滤器、拦截器、监听器

hasWhere

架构实战营模块八作业

技术是伙伴

架构实战营

中秋晴朗夜,我们与星月相见

白洞计划

TCP/IP参考模型与标准协议

Regan Yue

TCP/IP 9月日更

SpringMVC源码分析-HandlerAdapter(2)-RequestMappingHandlerAdapter的初始化

Brave

源码 springmvc 9月日更

端口连接出现大量FIN_WAIT1/CLOSE_WAIT

hasWhere

RabbitMQ的高级特性和消息补偿机制,字节跳动面试真题

Java 程序员 后端

模块四作业

Geek_fc100d

「架构实战营」

写给互联网工程师的5G书 | 7. 云化接入网

俞凡

架构 5G 网络 通信

P8级别的顶级“并发编程”宝典,linux基础入门知识

Java 程序员 后端

Nginx如何支持HTTPS,大厂Java高级多套面试专题整理集合

Java 程序员 后端

架构设计的一些思考

hasWhere

缓存系统设计与实现

hasWhere

Elasticsearch 源码学习(1)源码编译调试

Se7en

JVM启动参数学习笔记三

风翱

JVM 9月日更

Java面试必刷的200道真题,深挖底层原理、啃源码,最终上岸

Java 程序员 后端

P8级别的顶级“并发编程”宝典,面试完腾讯我才发现这些知识点竟然没掌握全

Java 程序员 后端

架构训练营-模块八作业

hello

架构训练营

高可用延迟队列设计与实现

万俊峰Kevin

微服务 延迟队列 microservice Go 语言 定时队列

【LeetCode】最后一个单词的长度Java题解

Albert

算法 LeetCode 9月日更

模块八作业

SAKIN

PDF超过6000页,2021最新Java面试题及答案

Java 程序员 后端

写给互联网工程师的5G书 | 6. 参考实现

俞凡

架构 5G 网络 通信

nginx路径匹配踩坑

hasWhere

古董系统的并发安全改造

hasWhere

Vue进阶(幺贰肆):前端用户体验提升(一)

No Silver Bullet

用户体验 9月日更

技术圈的【多肉小达人】,一篇文章你就能做到

梦想橡皮擦

9月日更

Vue进阶(幺贰叁):v-for 实现一行展示 n 个元素

No Silver Bullet

Vue 9月日更

架构训练营模块八作业

喻高咏        

架构训练营

产品分析:如何给出解决方案?

石云升

产品经理 产品思维 9月日更

持续集成之“自动化部署”_Java_乔梁_InfoQ精选文章