公司的以为大神的博客,写的通俗易懂,运维的思路基本上就是目前淘宝java运维发展的过程。
淘宝的运维系统也是这位大神推动构建的bluedavy。
后半部分是facebook的另一位大神的见解,一起保存下来吧。
对于互联网产品或长期运行的产品而言,运维工作非常重要,尤其是在产品复杂了以后,在这篇blog中就来说下Java应用的运维工作(ps:虽然看起来各种语言做的系统的运维工作都差不多,但细节上还是会有很多不同,so本文还是只讲Java的)。
苦逼的码农按照需求开发好了一个全新的Java Web应用,该发布上线给用户用了,要把一个Java Web应用发布上线,首先需要搭建运行的环境,运行的环境需要有JDK、APPServer,在已经装好了os的机器上装上JDK和APPServer,开发好的Java Web应用可以用maven直接打成war或ear,将这个打好的包scp或其他方式到目标机器上,准备妥当,就差启动了。
通常APPServer都带有启动脚本,在做好了上述准备后,直接运行启动脚本,通常就OK了(maybe需要修改appserver的一些配置文件,例如修改监听端口等)。
应用启动后,有一个问题需要解决,就是如何确认应用启动后成功与否,对于Java web应用通常可以放一个jsp文件,在这个文件里做一些必要的检查,以确保应用启动是正常的,例如通常Java web应用会基于spring之类的框架来写,为了确保spring的BeanFactory初始成功,可以在jsp文件里去获取下spring里的bean调用下。
应用完成了启动后,这个阶段的工作就算完成了,这个阶段完成后积累了一个应用启动的脚本,用来启动应用服务器以及在启动完毕了后检验应用是否启动成功。
只要不是一个一次性的应用,就必然会涉及修改的问题,修改完了后就得重新发布,对于Java Web应用,在修改完了后重新打应用的包,然后scp覆盖到目标机器上,重启,就可完成修改,但显然,每次修改完代码后都这么操作实在是痛苦不堪,于是喜欢“偷懒”的同学会专门搞台发布用的机器(或者就是运行应用的那台机器),然后写个脚本,每次需要发布时就通过脚本自动的将发布需要做的所有事情自动做好,:),这下以后发布爽多了。
相应的这个时候通常还要支持回滚,即应用发布失败后自动的回滚到前一版本的功能,也可以折腾到脚本里。
但如果只是为了修改个页面,就得这么折腾,显然还是麻烦了点(主要是经常重启,用户受不了),于是需要支持仅更新页面的发布方式,为了支持这种方式,通常就要求打出的war/ear是解开了的,这样才比较方便直接覆盖页面,继续“偷懒”的本性,写些脚本,支持将需要发布的页面文件从svn或git下载,然后打成一个tgz或其他压缩包,scp到目标服务器,解包覆盖完成页面的更新(当然,对于编译jsp的运行方式这种发布方法就没法玩了)。
经过这个阶段的折腾,就积累了两个脚本,一个用来发布应用包,一个用来发布页面。
每次发布应用包的时候都会有短暂的不可用,这样总折腾下去用户受不了(如果哪天这唯一的一台服务器挂掉的话,就更糟糕了),于是就把应用的机器数从一台变成两台,通常这里对应用是有些技术要求的,这篇blog中只谈运维,通常情况下从部署在一台变成两台后,要做这些事:
1、可能会增加一台机器用来部署nginx/apache来做负载均衡,这个规模的情况下应该很少有会采用lb设备或lvs的(某些国企除外…),于是就得折腾下这台机器的环境等了;
2、增加了一台应用的机器后,就得又来搭建一次应用的运行环境了,通常这个时候“偷懒”的本性会发作,干脆折腾个脚本吧,来支持从另外一台运行的应用机器clone运行环境,做的更好点的话,就会把应用的运行环境记录在某个地方、然后把应用运行需要的软件也放在某个地方(例如yum),这样,当要搭建应用的运行环境时,就可通过记录的信息以及yum源等直接完成运行环境的搭建。
3、但这样还没完,为了避免发布的时候应用完全不能用,除了技术上要做的那点事外,在发布应用时就会多加一步,就是要先发一台,再发另外一台,由于之前有发布脚本,通常这也不是问题,:)。
经过这个阶段,就又积累了两个脚本,一个用来管理负载均衡的那台机器,另一个脚本用来搭建应用的运行环境。
随着应用越来越受欢迎,通常应用的机器就得继续加了,这个时候对运维工作又会带来新的挑战,主要还是在发布上,这个时候应用部署在了一堆的机器上了,每次发布的时候手工输入一堆的机器地址来发布显然很麻烦,于是需要有个地方来记录应用有哪些机器(还记得我们之前搞了个地方来记录应用的运行环境信息吧,可以放一起),除了要解决这个问题外,还需要解决的一个问题是这个时候通常对应用的发布方式会有了多种要求,例如分批发布(先发5台,再发10台,再全部发,或一半一半发等),更高级的话会有beta发布、灰度发布等要求,这个具体可以见facebook的大牛@魏小亮 写的《代码和产品发布的几种方式》,这个时候显然,之前写的那个简单的发布脚本就无法支持了,于是得搞个更为复杂的脚本来支持这多种的发布方式(有些还需要应用架构上做配合改造才能实现),做的更好的就是提供一个web版本的发布系统,来更为方便的进行发布以及发布过程的管理。
经过这个阶段后,通常可以折腾出一个web版的应用运维系统了,包括应用信息的登记(依赖的运行环境、运行的机器地址)、应用的发布、页面的发布、增加应用的机器、下线应用的机器。
通常继续发展下去,会出现一些新的状况,主要是会开始多元化,有了很多个应用,这个时候原来的web版应用运维系统就要支持多应用了,这里主要会带来的问题是为了降低运维的复杂性,需要做一些通用性的工作,例如通用的启动脚本等,这个时候会产生一些要求,例如统一的应用名等,另外会带来的问题就是开发、运维的人多了,这个时候权限上就需要有些控制了,例如某些账号可以操作某些应用等,这个时候通常需要增加一套权限系统,集成到之前的运维系统。
应用多了后,还会带来的问题是应用部署的规范性(同时也是为了降低运维的复杂性),因为每个应用可能都会有一些特殊的配置,这个时候最好是能够通过搭建运行环境以及权限控制来保证一个应用部署后的目录控制。
经过上面的一些发展过程后,Java应用的运维工作基本就可以比较自动的去完成了,但通常其实应用运维除了上面的工作外,还有一件更重要的事,就是保障应用的稳定,应用运维人员需要能够排查大部分的应用故障问题,而不是交由后面的开发人员来解决,通常这个时候需要一套自动的故障处理系统,感兴趣的同学可以参与下facebook的FBAR。
发展到更大后,还可能会碰到例如一个负载设备无法支撑整个网络的请求,需要划分vlan,这个时候在做应用机器的扩容时就需要考虑vlan因素了,还有可能会碰到机器利用率不够高,需要引入虚拟机,更高级的就是引入弹性计算,这些对运维体系都会产生较大的冲击,:)。
从上面对一个Java应用运维的演变描述可以看到,大方向上来说是朝自动化、web化、智能化发展,但最最重要的都是如何和现有运行体系无缝的结合,而且这里谈到的还仅仅是Java应用运维的工作(还没提到开发人员其实也要仔细考虑自己写的产品的可运维性如何),而一个运维的体系还要包括机房、网络、硬件、安全等,就更加的复杂和且需要体系化的考虑,。
----------------------------------------------------------------------------------------------------------
最近有几个朋友提起”灰度发布"这个概念和相关的问题。想解释一下几种具体的发布方式(具体名称中文翻译不一定正确)、他们的优缺点和实现难点。
这几种方式都可以作为快速运营的软件或者web服务公司逐步发布新代码或者新产品,边尝试边改进的方法,这些方法可以避免一次发布里面某个产品/代码的漏洞对网站产生瞬间毁灭性的后果。
这几种方式各有优缺点和难点,根据实际情况一个公司可能使用不同的方法做不同的发布。
- 分步代码发布(multi-phase code push):这是敏捷开发的团队常用的代码发布方式。基本操作是整个团队共用一个代码库,一定频率(比如每天一次,或者每周一次)把整个代码的最新版本做一个新的发布分支(release branch),把发布分支逐步发布到产品线。
- 特点:"逐步选择"的过程不由代码控制(如果代码控制,那新一版本的控制代码有问题就可能让整个代码发布过程崩溃)。“逐步选择”过程由运营团队负责:比如选择每个机柜的第一台机器,或者每个机群的第一个机柜,或者多个数据中心里面选择某一个数据中心⋯⋯关键是选择的时候是均匀分布到各种不同的机器上。如果新代码在某一种配置的机器上有问题,运营团队能够及时发现。另外multi-phase code push的发布周期必须短于敏捷开发的迭代周期,往往一天或者一周之内要把代码发布到所有机器。
- 监控:multi-phase code push一般要做实时的监控:代码逻辑错误的信息按照代码版本(比如svn revision number)来分类,保证新版本的代码不带来新的错误;硬件的信息(CPU内存IO)按照选择的机器、机柜、机群、数据中心分类:保证新的版本不引起更大资源消耗。当以上的信息都确认之后,可以给更大规模的机器安装新代码。
- 难点:
- 如果前端负载均衡器不能保证用户和机器一致的话,一个用户可能在发布过程中看到若干次新版本和若干次旧版本(比如第一个页面是新版本,而AJAX是旧版本),版本不兼容会造成Javascript错误、CSS错位,甚至一些逻辑错误;Javascript体系架构需要做一些安全检测,或者要求程序员开发的时候考虑版本兼容(一般在快节奏的web开发里面不容易);或者用保持用户和机器一致的前端负载均衡器;
- 监控的时候硬件资源消耗信息有可能因为发布过程本身产生很大的扰动,而与代码无关(比如重启之后缓存要重新warmup,增大IO,产生虚报),这需要代码发布经理(pusher)的经验来排除。
- AB测试(AB testing):这是产品发布的常用手段。比起分步代码发布,AB测试往往有更长的周期(比如几个星期甚至几个月)。基本操作是产品的开发者加一个或者多个配置控制(一般每个产品配置应该带有配置的ID),允许通过调节相应的配置来让一个产品发布到“逐步选择”的用户群。
- 特点:“逐步选择”是一个有代码控制的逻辑过程。一般的产品基于用户ID选择;也有基于IP或者其他信息的。
- 监控:AB测试的数据一般按照产品配置ID和打开/关闭状态分类,分析某个产品配置在打开的时候和关闭的时候对用户行为的影响,和对硬件资源的消耗,由此可以预测这个产品在100%发布之后的影响。
- 难点:
-
- 如何做选择:不同的产品有不同的选择方式。一般可以考虑用户ID,但如果跟浏览器的缓存效率关系很大的,可能需要考虑IP(因为一个浏览器可能被多个用户使用);如果对非注册用户做的产品(比如各种注册流程的测试),也可能需要IP,或者实时随机选取;
- 产品效果的评价:有些产品需要有网络效应,如果按照用户ID随机抽取样本,网络效应可能被打破而使产品在AB测试期间失效 (比如一个社交网站的平均用户连接度是50,即一个用户连接其他50个用户,按照1%用户ID随机抽样的AB测试,那被选中的用户子群内部的连接度可能不到1)
- "逐步选择"的逻辑本身是一个代码,如果这个代码写错的话可能带来灾难性后果。
- 灰度上线(dark launch):我想“灰度上线”这个术语可能来源于dark launch。这是产品发布的另一种手段,往往用于需要一次发布的产品。 有一些产品可能因为市场营销策略的原因,或者因为产品本身的特点(比如Facebook的用户名注册,或者可能像火车售票系统)不能进行AB测试那样的逐步上线。同时,我们又需要知道这个产品一次上线的时候带来的影响,在这种情况下我们可以用灰度上线。基本操作是在用户访问网站的时候,打开新功能所需要运行的代码,但把用户可见的输出、互动和写操作都屏蔽掉,按照AB测试的方法逐步把这个去掉用户互动的产品发布出去。
- 特点:外界感觉不到新产品的测试过程
- 监控:和AB测试一样,但主要关注的是系统的负载和资源消耗
- 难点:
-
- 如何屏蔽用户互动:一方面要获得几乎真实的产品负载,另一方面希望不要把代码搞乱导致真正发布的时候需要很大改动;
- 对真实产品发布后负载的预测:产品发布之后可能形成正反馈(比如一个产品发布之后大受欢迎,引起更多的用户注册⋯⋯)灰度上线只能预测第一层效果,不能预测用户行为的改变所引起的连锁反应。