基于 ANSIBLE 自动化运维实践
运维这个话题很痛苦,你做任何的产品都离不开运维。不管你用什么语言、什么平台、什么技术,真正能够决定你产品成熟度的很有可能就是你运维的能力。
云时代的运维
以前的运维那么痛苦,大家却并未做多大的努力去改变这个现状,为什么?
因为原来你要自己去建机房、自己去采购、去调研机房、采购服务器、采购带宽,中间出了任何问题很大可能都是机房的问题。
在云时代,尤其是在AWS出现之后,很多美国团队的运维方式发生了极大的变化。
为什么云时代的运维跟原来的运维不一样?
首先是从云主机。云主机的出现以前跟机房有模糊地带的争议话题全部都抹平了。有了一个供应商专门提供云主机的服务,被上亿的用户验证。提供的服务其实很简单,包括一些基本的监控,一个部署服务的入口。如果说一个云主机的服务商一大半的用户使用情况都是很稳定的,基本上你碰到不稳定的概率是比较低的。其可靠性是比自己建机房、找带宽供应商要高。
一个专业的云主机服务提供商能够更专业的去解决基础设施的问题。你不会再面临那么多很痛苦的问题了。
另一个本质转变就是弹性运算。点击一个按纽就能申请到一台机器,或者是运行API就能拿到一台机器,在两分钟之内就能完成整个申请、测试应用甚至上线的过程。以前需要几个月完成的事情,现在可以在调用一个API的时间内完成。
我个人感觉,是因为基础设施的变化造成了云时代的运维跟原来的运维有本质的区别。
原来你做的其实是很烂的事情,大家也不会太在意,因为就算哪个环节做的不好,你的老板也感觉不到。实际上它带来的变化就是弹性运算。
当基础设施是弹性运算之后,实际上所有的服务都可以变成弹性运算。
弹性扩容
初始做产品时你只需要做最小的容量。用户增长后再去扩容。
灰度上线
过去前置条件很长,长达几个月甚至无法做。因为前置条件的成本非常低。我们几乎每做完一个小的特性马上就去做灰度上线,因为灰度上线的时间越早、发现问题的时间点也会越早,这个系统稳定的时间也会越短。
每天部署
扩容、灰度上线,实际上是一个开发的过程。整个集群的部署我们每天都会做一些微调,比如增加一个新机房,在早期几乎是不敢想象的。但是在现在这个年代,这都会越来越轻量。
新增加机房
现在云主机服务商群雄并起,在增加机房之前,你需要做充分的验证。验证过程包括部署一整套的东西,在上面跑一些测试用户。新增加一个机房的成本其实也是很低的。
扩容
扩容的过程包括:
- 申请机器
- 配置DNS
- 部署应用
- 部署监控
- 自动测试
- 分配流量
整个过程实际上在自动化运维里面就是一个脚本。通常扩容就是3台到5台,即小粒度的方式扩容。3台到5台耗时20多分钟,实际上你也可以认为这个扩容过程就是一个灰度上线或者是灰度测试。每完成一个特性就去做扩容,先灰度上线,上线成功之后就把它切到生产的流量并扩容,同时把老版本的流量或者是容量下掉,就完成了一次扩容过程。
廉价的分配过程
我理解的弹性运算主要是两点,一是需要的时候才分配。另一个是分配过程很廉价。
这是什么概念呢?过去,分配过程成本高昂,需要有很多的前置条件。现在的分配过程很廉价,运行一个API,调用他们的一个API接口,整个分配过程就完成了,包括主机的分配、流量的分配、IP的绑定、域名解析的配置。或者还包括其他的服务,比如存储服务、load balancer等。
我最早用的服务是AWS,但是现在国内的云主机服务商真的做到那么成体系、配套、完整的应该还是没有的。有一些已经有API了,但是跟周边一些自动部署模块的整合做的不够好。
所谓的分配过程很廉价,弹性、扩容、灰度非常方便,两者结合就形成了弹性预算的重点。
用户随时在线扩容
上文提到一个结论:就没有自动运维就没有弹性运算。
而用户随时在线扩容是另一个概念。不同之处在于,上文是以运维者的角度,或者是以产品服务提供者的角度来讲这个问题。
我们服务的目标用户,早期用户量非常少只是做验证没有付费,用户量越来越多发现延迟变大了,查监控数据发现每天的在线用户很多、每天交互次数很多,现在的通道不足以支撑那么多的用户,你的通道可能需要去扩展。
以前的做法是打电话给商务说“再多交一点钱,你们再给我多开一台服务器”。
我觉得那个还是传统服务,云服务就应该是纯自助的。
发现通道不够用了就去申请,考虑到主要用户是在上海、南京,或者在西藏、西安,就需要去根据具体情况在不同区域扩张通道。这个事情应该是用户自己去做,但是他需要我们去帮助他,那我们帮助他的办法是什么?实际上很简单,我们会看到哪里有资源、哪里缺资源,实际上买资源的过程就是自动扩容的过程。
自动扩容不是由运维来驱动,而是由使用我们服务的开发者来驱动。
自动扩容
每天,我们会通过监控系统查看各个模块、各个组件的情况,一旦到了一定的基准线之后,以前需要人工干预,现在各个环节成熟度足够,实际上不需要人工去干预。例如,做一个配置,每天晚上12点到早上8点,哪一部分东西容量出问题了,就启动自动扩容过程。
相比之下,就算每天有运维人员盯着,后半夜的工作能力是很弱的。其实很多运维做的事情都可以被标准化,尤其是像扩容这种事情,很多事情可以简单的通过扩容让他撑过很长的时间。不管你中间运维程序写的不好、代码不好,甚至因为你选的 Erlang 没有用 Go 的原因,无论什么原因,只要把那条路加宽就可以让车全部通起来。
举云巴自己的例子:有一次,系统突然出现瓶颈,超时、丢包,其中有一个集群的压力很大。我只做了一件事情:把它的容量扩充了一次,从原来是20台机迅速扩成40台机。压力马上就下去了。
我就跟团队说“就是这个模块里面有瓶颈”,我们没有去看日志、也没有去抓包。得出这个结论的原因就是发现入口数据一直在增长、出口的数据没有变,调用依赖模块的请求数量没有增加,入口一直在堆积、出口上不去。很简单,就说明他这个管子太细了。
云计算跟企业服务不同之处
企业服务出现了问题立马停下来,但是云服务是不行的,现在可能有成千上万的用户正在用你的服务,停一分钟就会有人开始骂人了,你的电话、你的QQ群、你的邮箱都会爆满。正确做法是离线的去找问题在哪里。
我们将来想做的事情
把任何模块的出入口都做很详细的监控,每一次请求甚至是每一个协议包都做监控,我们会找出一些范式。应该进去多少、出去多少,出去哪些到模块A、哪些到模块B、哪些到模块C,我们都有预测。一旦发现哪个模块有压力,马上做自动扩容。上文提到,有压力不仅仅是因为容量的问题,也可能是程序有BUG。不管为何,应该先让它通起来,这是我一直在构思的事情。我们后面也会在这方面投入很大的精力,希望尽快和大家分享。
团队分工和运维工程师的角色
我们以前开发团队的分工是什么呢?就是写代码的人写代码、测试的人测试、运维的人去敲各种命令去部署。
有一个很有趣的悖论,其实写代码的人比测试的人更清楚要怎么去测试,写代码的人比运维的人更清楚应该怎么去配置和部署。
实际上我们有过惨痛的教训,以前做极光推送的时候,每次做升级,我会在办公室放两个很大的白板,准备出3支笔,黑色、蓝色和红色,把开发人员、运维人员、测试人员全部叫在一起,一起写一个很复杂的作战地图。如果一次迁移涉及到9个模块,就把9个模块全部画出来,那个过程有点像话剧的脚本。我先说A你要先做什么事情,做完之后你要运行什么确认之后通知B同时要告诉我,B要干什么事情,B干完之后也要告诉我通知下一个C,然后你要干什么事情,可能有一个事情需要我们几个人同时做。
相信大家应该明白,A做了之后B应该在几秒钟之内立即完成另外的动作,要不然就会出问题。这样的事情我们每个月会做一次,凌晨2点干到早上5点钟,还要盯到早上8点才能回去睡觉,非常辛苦。每次运维要问,这个东西到底怎么配置、配置文件到底在哪里、每个项目是什么意思,等等。最后发现在强求每一个人写文档。所有的文档写完之后我都要去重写。
写文档的代价很高,所以它的时间成本是很高的,大部分的情况大家未能写出很好的文档。就算写出很好的文档,运维和测试也需要要花很长时间去看。这三个角色之间沟通的成本非常高,沟通的效果也是非常差的。
怎么去解决这个问题?
答案:Devops
让运营来参与开发。
实际上我们的做法是没有测试和运维。所有的事情都是由开发来做。大家的分工会有一点交叉,可能5个人写一个模块,这个模块可能又被分成5个小模块,可能是做特性A的人可能做了特性A的30%测试,测试用例的代码和部署的代码。做特性B的人做70%的特性A测试和一部分特性A的部署,这样大家可以互相交叉。
你在写测试和部署程序的时候,你实际上对那个东西的理解可能比写代码的人更深刻。因为在整个产品开发里面写代码是最简单的事情。一个好的产品,写的东西大概只能占30%,还有其他的因素占到70%。
一个好的产品是由你运维水平来决定的,而不是由你写代码的水平来决定的。
实际上部署脚本的代码量有可能是远远超过特性的代码,写一个代码会造成一大片测试脚本/运维脚本和部署脚本的调整。DevOps 并非我们首创,在国外一些团队已经实践了几年。他们没有测试也没有运维,所有的人又是开发、又是运维、又是测试。
我们开发团队每一个成员,除了写好特性以外也会写测试用例、写部署脚本。我们招人的时候会招全栈工程师,因为你在写测试脚本和部署脚本的时候会反思你原来写的东西。
过去经常遇到的问题是,运维的人会抱怨“你这个程序怎么写的、这个配置怎么这么复杂,根本就不知道你在搞什么”。测试的人也会说“你的接口怎么定义的?我完全看不懂,我怎么写你的测试用例?”
现在,每一个工程师就会经常反思,原来写是否合理、是否考虑到以后将来的维护,这样反过来又可以提高你编码过程的质量。
理想的部署方式
早期有很多传统部署工具,比如Puppet,有一个很大的悖论。他们是用一套集群或者是用一套已经部署好的东西,在去管理另外一些没有部署好的东西。这就有一个鸡生蛋和蛋生鸡的问题。
Puppet本身的集群你怎么去做?原来的做法是运维把机器弄好了、把所有的机器都装好、配置全都配好,这个过程成本非常高。
我个人比较喜欢的做法是,新机器只需要支持SSH。标准的做法是申请好之后,把key导到部署机器上,调用一个API主机申请,用sudo权限即部署完成。配置新机的方式其实好多都申请好了。这和Puppet一类的部署系统比照,它的成本更低,没有Master,没有Agent,只要在这台机器上可以连上目标机就可以了。
Ansible概述
找到 Ansible 的过程
最早我们用 SSH 写很多脚本,要用 SSH 连过去,也是在某一台机器上执行,不用在目标机上登陆。这种做法在相当一段时间内是我们实际使用的手段,它实际上比 Puppet 有效。但是它有一些问题:管理成本高、脚本会越来越多。部署的过程有很多的基础部件需要反复部署,几乎是没法管理。
后来我们用了 RunDeck,它有界面、有一定的管理能力。我们还用过 Fabric,即批量执行命令,能做到类似部署的事情。但是,目标机规模大了之后仅有管理的能力是不够的。后来我们又调研过 Salt,不认为有太大的差别。
选择 Ansible 主要因为丰富的相关支持,包括很多现有的组件和模块和开源的 Ansible 部署和脚本。我们的团队不喜欢纠结。我们发现 Ansible 没有太本质的区别,就开始用起来。如果没有明确理由,我们就凭感觉选一个用。
Ansible 是通过 SSH 连接到目标服务器,上面这两个东西是我想了很久,我希望去拥有的特性。一个是完整的集群,只需要有一个 inventory 去定义,写出清单就可以定义集群。每一个角色的具体功能有若干 playbooks 来定义。
Ansible Inventory
这是一个 Inventory 的虚拟例子。这定义了两个角色,一个是 web-group,一个是 db-group。
Playbook的详细案例。我们用的是 Ubuntu 12.04,所以先用 Nginx 的 PPA。
Ansible 的开发过程是写大量 Playbooks。现在 Ansible 支持的有251个模块,特别是对于云服务的支持。像 AWS、Docker、Rackspace、OpenStack,部署脚本都放在一个子目录下。这就意味着把别人写的脚本拿过来,或者把别人写定义的Playbook拿过来非常容易。现在关于 Ansible 的开源脚本数量庞大,有 3000 多个项目,我相信这数字会越来越大。因为它的分享方式真的很简单,只要把目录拷过去即可。
关于作者
张虎