摘要: 生产环境最关注的就是稳定,测试环境更关注的是研发效率,如何从一行代码最快的保证质量发到线上去,这个是我们测试环境最关注的。在全球运维大会上,阿里巴巴研发效能事业部运维中台技术专家——刘湘疆(青冥),从资源稳定性、部署效率,以及业务稳定性方面为大家分享测试环境的那些事儿。
点击查看原文:http://click.aliyun.com/m/40557/
导读:生产环境最关注的就是稳定,测试环境更关注的是研发效率,如何从一行代码最快的保证质量发到线上去,这个是我们测试环境最关注的。在全球运维大会上,阿里巴巴研发效能事业部运维中台技术专家——刘湘疆(青冥),从资源稳定性、部署效率,以及业务稳定性方面为大家分享测试环境的那些事儿。
测试环境特点
资源配置低:测试环境一般来说资源配置都比较低,跟线上不太一样,至少在阿里是这样的。大到网络、机房包括服务器配置都比线上的配置要差,小到虚拟机、容器,虚拟比都比线上的要高。这是不太一样的地方,这些对于我们来说也是一些挑战,后面会讲到。
部署频繁:测试环境下的部署是非常非常频繁的。比如说你开发一个功能,可能线下部署10到20次,线上才发布1次。部署这块对于研发来说是非常关注的,就是怎样能快速看到我部署之后的效果。
业务测试干扰:这块也是很明显,因为当你业务需求越来越多的时候,并行的开发就会很多,而且线下环境类型比较多,比如开发环境、性能环境,各式各样的环境,这些环境之间会有一些干扰。
以下两种情况比较常见,第一种是我要调用你的服务,结果你的服务器挂了。第二种是可能多个环境都在提供某个服务,当你要请求这个服务的时候,就可能会调错了。这些情况都会干扰到我们的测试。
今天的分享主要从资源稳定性,部署效率,以及业务稳定性这三个方面来讲。
资源稳定性
刚才我们提到,测试环境的配置都比较差,绝大多数的机器都是线上淘汰的过保的物理机充当的,虽然过保了实际上还是能用的,为了节省成本基本上都是当测试机来用。这些机器的故障率肯定比线上的要高很多的,那我们要怎么来保证稳定性呢?就是在容器交付和容器运行两个阶段怎么尽量保证能够稳定。
容器交付我们主要是做了资源池的调度策略。上图右侧是一个简化版的容器生产的链路,资源池上面是一层调度,分为资源管理和资源生产,当然,这里还有很多其他的模块,我这里就不细化了,重点是突出中间那块。
最上面有很多的上层系统来调用申请机器,我们线上环境没有资源池动态分配这层的,就是直接上层系统和一层调度做这些事情,因为下层本身是比较稳定的。
对于测试环境来说,我们在中间插了一层,就是资源池的动态分配,这个系统有五个模块,分别是宿主机评分,应用环境分级,资源池管理(就是对资源池怎么进行合理的调度和分配),容量预测,最后一个是分配器。
下面详细说下资源池动态分配系统的模块。
第一块是宿主机评分,我们会对宿主机的机型机房各种维度进行一个综合的考量,对它进行一个打分。比如说机型,最典型的就是磁盘,磁盘类型到底是机械硬盘还是SSD的硬盘,这个可能对IO性能影响会比较大一点。
还有机房,比如说某些机房的网络或者带宽相对配置低一些的,这个分数可能会低一点,还有比如这个机房在计划裁撤,那么分数也会低一点。另外还有过保时间,历史故障次数,可调度状态等。
第二块是应用环境的分级,我们会对应用进行一个重要等级的划分。另外就是对于环境的类型进行分配,在测试环境下,环境类型很多。我们有开发环境,稳定环境,持续集成环境,性能环境等。
比如说像稳定环境,因为是要持续提供服务的,给它分配的资源就要尽量足够稳定。比如说像开发环境、持续集成环境都是属于即用即申请,用完之后立刻就释放了,使用周期很短的,针对这种类型可能会有不同的策略。还有像性能环境,对带宽或者网络的要求,就要尽量的接近线上。
第三块是容量预测,会根据历史数据来预测当天所有应用的所有环境类型大概会申请多少机器,这个基本上来说是比较稳定的数据,会进行一个容器的需求量预测。
第四块是资源池管理,就是动态调整资源池。大家可以看上图右下角,最下面就是资源池物理机打分后的排序,机器分越高越可靠,分数越低越不可靠。
上面三个框是我们对它进行一个逻辑的划分,每一个小的划分段其实就是为某一类环境做准备的,比如说当核心应用来申请稳定环境的时候,会把它分到最可靠的机器上来。
如果是开发环境,或者说是不太重要的应用环境,就会分到不可靠的机器上,因为它们对于不稳定的容忍度相对来说更高。
再比如持续集成环境或者开发环境,他们是用完就释放的,使用时间很短,这种环境我们的分配策略是怎么样的?其实除了分配策略以外,还有一个打标的作用,当这种环境来的时候,会尽量分到比如刚才说到的某个即将要裁撤的机房去,这样的话,等到裁撤的时候,上面的机器可以直接释放掉,而无需在裁撤的时候做大量的迁移操作。
中间的这个划分就是根据容量预测来的,每天去预测某一种类型应用的某种环境大概占多少,会动态的对这根线进行动态的调整,每天会不太一样,保证这个环境的申请尽量落在相应的区间。
这样就可以做到让这些长期占用的环境或者比较重要的应用环境能够尽量落在比较稳定的宿主机上,让它的容器稳定性尽量高。
即使我们做到了上面的资源交付,宿主机还是会有问题的,这个是难以避免的。大多数的做法是自愈,当我发现有问题的时候,怎么样做到自愈,让用户没有感知。我们没有办法避免某个机器不出问题,但是我们可以尽量避免用户感知到。
我们这里有一个自愈系统,看上图右侧,有四个重要的模型:条件模型、原子服务模型、条件处理模型、自愈事件模型。下面结合场景进行说明。
第一个是条件模型,定义触发条件。
第二个是原子服务模型,定义一个一个的原子服务。每一个原子服务应该只做一件事情,这里只是一个规则的定义。以磁盘满为例,磁盘满的原子服务要对应调用某一个清理磁盘的接口API。
第三个是条件处理模型,把上面两个连起来。当满足某个条件的时候,要去做什么样的处理,可能需要把一个或者多个原子服务连起来,比如刚才说的磁盘满调用清理磁盘的API,这是最简单的情况。复杂一点的情况可能就要用到自愈事件模型了。
第四个是自愈事件,这个相对更高一层次,是对于条件处理的一个组合,因为在很多情况下,可能上层系统给到的信息并不是那么的精准,会有些模糊。比如发布失败,这个事件对于自愈系统来说,可以定义多个条件,依次匹配,就像排查一样,先检查磁盘是不是满了,agent是不是正常的,容器是不是ok的等等。
上层系统可以对接很多了,比如刚刚说到的发布系统,还有监控系统,发现磁盘满了清理磁盘,发现应用问题自动把服务拉起来,还有调度系统,刚才提到的底层负责资源管理的调度系统,当它发现某台物理机已经出问题的时候,可以把上面现有的容器迁移走,把这个物理机下线或送修,这些都是可以对接多个上层系统的。总的来说,非业务系统问题相对还是比较好处理的。
而对于业务性的问题处理相对麻烦点,因为业务具有多样性。比如健康检查,发现有问题就需要对应用进行重启,甚至做容器迁移。这个我们尝试在做,从经验上来看,对应用上来说是有一些要求的:
应用需要有比较准确的健康检查,这样监控才能知道应用到底是不是OK的,自愈是不是真的完成了,业务是不是真的恢复了。
优雅上下线比较关键,就是先从负载均衡设备上摘下来,然后再下线服务,这样才能保证客户端的请求都处理完了,并且不会有新的请求在下线的过程中再进来,导致下游失败。
第三点就是无状态,这个也很好理解,我就不多说了。
最后就是快速启停,之前我们有些应用的启动时间非常长,导致自愈不太可控,甚至都超时了,让整个自愈的成功率和时间上不太可控,所以后来就要求应用要能做到快速启停,至少在三分钟之内要完成。
以上实践经验,大家可以参考一下。
部署效率
对于研发同学来说,大家最关注的就是研发效率,写完代码之后,希望能立刻看到效果,并且迅速得到验证。这里面我们认为比较耗时的三个部分是构建、部署和测试,今天主要是对部署和测试重点讲一下。
其实构建这块我们也做了很多事情,在阿里每天的构建量非常大,我们有一个专门的构建集群,这里大概讲一些提效策略。
第一个是Maven的类库缓存,这个缓存其实要考虑到不同的应用在同时构建时,覆盖和冲突的问题,这个我们是采用overlayfs这个方式去做的。
第二个是Docker基础镜像预热。
第三第四个是尽量利用构建的cache和重复利用工作区。这里提一下cache,因为在Docker构建的时候很明显,cache利用越多,Docker构建就越快,但是这个cache跟存储空间需要平衡,怎么样能够保证尽量多缓存的情况下,占用空间尽量少,有一个办法就是尽量把它调度到它上一次构建的那台编译机上。
接下来重点讲讲部署这块,我们认为主要就是能做到快速部署、快速回滚。
对于测试来说,一个是自动化测试,这样的话你才能在整个pipeline里跑的起来;还有一个是业务的稳定性,测试环境的业务稳定性相信是困扰很多开发测试同学的大问题。
其实自动化测试跟业务稳定性这两个东西是相辅相成的,环境越稳定,自动化才能越跑越成熟。反过来说,自动化越成熟,能够为环境的稳定性提供越大的自动化监测保障。
部署效率我们主要是从部署策略介入的。业界用的比较多的策略一般是分批部署,这也是最常见的一种方式。比如说现在有两台机器,如上图右侧,黄色三角表示流量,这两个都有流量进入。开始部署的时候先部署第一台,这时只有第二台在提供服务,等第一台部署好了之后,流量全部打到第一台来了,第二台再部署,最终达到两台都部署完成,流量均衡,这就是分批部署。
在整个部署过程中,总有一台机器提供服务,保证服务不间断。从部署时间和回滚时间上来看,分几批部署,部署总时长就是单台的时间乘以批次。
从整个部署时间上来看,单台部署时间越长,那么部署时间就会拉的很长,所以会尽量减少批次,又要考虑到剩余的机器能不能撑得住线上的流量。
比如说回滚的时候就是这样,部署到倒数第二批的时候发现有问题,换个时候不能把所有部署过的批次一下子回滚掉,因为线上剩余的那一批可能撑不住所有的流量,这个时候你分几批发,就得分几批回滚。
所以回滚总时长这里最好的情况就是在第一批就发现了问题,回滚的时间就等于单台部署的时间。这是线上普遍采用的做法,我们原来在测试环境也是这么做的。
后来我们在想,测试环境有没有办法优化一下,我们考虑用蓝绿部署。这里简单讲一下,还是两台机器,只有一台在提供服务,我们称之为主机,另外一台没有提供服务,我们称之为备机,上面是什么版本我也不关心。
部署开始的时候,先部署备机,部署完之后把流量切到备机来,这时备机就变成新的主机了,原来的主机变成备机了。需要回滚的时候,直接把流量切过来就好了。
这种做法部署时间是最短的,只需要单台的时间就够了,因为另外一台不需要部署。回滚时间其实取决于你切流的时间,我们可以做到在几秒钟之内就切好。
蓝绿部署是用空间来换取时间,我们用新的一批机器来来部署,部署完了之后替换之前的那批机器。
对阿里来说,在线上这样做的话,是不太可取的,因为线上的量实在是太大了,一个应用就上千台服务器,要采用蓝绿部署的话,需要再拿出一千台服务器来先部署然后再下掉,而且我们的应用发布是频率很高的,这对于资源的消耗是非常大,不太可能采取这种方式。
但是对线下来说我们测试环境两台机器就够了,当初在分批部署的时候,采用两台机器,并不是说我们线下流量有多大,必须要两台才能撑得住。
测试环境流量其实很小,一台服务器就够了,之所以用两台是为了保证稳定性。在部署的时候,原来采用分批发布的时候,至少还有一台总是在提供服务的。
我们采用这种方式主要是为了降低部署时间,我们现在正在尝试用这种方式来做。
除了在上节说到的构建策略外,还进行了以下优化。
Dockerfile最佳实践
排除不需要的文件。
减少镜像层数,合并多个指令,体积就会小很多。
还有尽量使用cache,我们统计过数据,大部分变的内容都是应用的代码,对环境的修改很少很少,所以我们会把应用包放在最后。
这些主要是为了构建和拉镜像比较快
镜像分发
基础镜像预热,当有资源进入到资源池的时候,我们就开始对公共的基础镜像做一些预热,提前拉到物理机上。这个又与前面讲到的资源动态调配有关系,在测试环境有动态调配系统,我已经知道哪些机器上会放哪些应用,所以可以提前把那些应用的基础镜像拉到相应的宿主机上,这样的话在部署的时候会很快。因为只需要去拉取应用包那一层就可以了。
部署中的镜像预热,这个在分批部署的时候用处比较大,在第一批部署的时候就开始对第二批第三批进行预热和拉镜像了。
大规模文件分发方案,我们发现不管怎么优化,我们的应用镜像仍然能够达到2-3G,即使除去其他公共的东西,光应用包的镜像也要两三百兆。对于大规模的文件分发有一个成熟的方案叫蜻蜓,这个是我们开发的系统,已经开源了,有兴趣的话大家可以去了解下。现在我们集团里90%以上都是采用蜻蜓来做文件分发的,效果还是比较明显的。蜻蜓对于远距离传输、大文件传输、安全传输等都有针对性的解决方案,对比传统的直接下载方式,尤其是大规模的文件分发,效率能提升至少10倍以上。
业务稳定性
第一,业务方希望我们的环境要足够稳定,所调用的服务要一直正常运行,随时调用都可以,而且是稳定的,最新的,无干扰的。
第二,调用的时候不希望调到干扰服务器上去,包括消息也是一样的,不希望应该发给我的消息,被别人抢了。
第三,高效,比如说建立一个隔离环境的时候,不希望做一大堆的配置来实现。
那怎么做到这几点呢?这里我讲两点,一个是基础环境,另一个是逻辑隔离。
现在比较典型的研发流程是这样的,从开发测试到集成测试,到预发测试,再到上线。在每个研发阶段都有对应的环境做支撑。这里需要注意下开发环境,开发环境之间互相调用的话,肯定会有干扰,并且不稳定。
所以发布上线完之后我们会去部署一个基础环境,开发环境去调用稳定环境的服务。基础环境部署的是主干代码,也就是线上的代码,会自动部署。
这里的隔离是怎么做到的呢?比如,怎么让开发环境调到基础不调到开发呢?甚至还有很多其他的开发环境,为什么可以做到不调用呢?
重点是逻辑隔离。现在阿里的业务非常多,并行需求特别多,一个应用有可能甚至同时会有几十个开发环境存在,当调用服务的时候,很有可能会出现调用错乱。我们是通过隔离组来解决这个问题,原理是根据源头的IP所在隔离组进行路由。
如上图所示,隔离组里面有三个应用,基础环境里有所有的应用,当我发现隔离组里面没有这个应用服务的时候,就会调基础环境,基础环境调完之后还能调回到你的隔离组环境里去。
不管怎样每一次都会先检查隔离组里有没有我要调用的服务。在搭建隔离环境做测试的时候,只需要搭建你要修改的那几个应用就可以了,最大限度的去复用基础环境下的服务。
上图是没有逻辑隔离的情况,上面每个圈代表一个环境,ABCDE代表不同的应用,当A1在调用的时候只要发现有B的服务都会调用到,随机调用的,可能这次是B1下次是B2,乱糟糟的。
这张图里创建了两个隔离组。在隔离组1里,没有B的环境,所以A1会去调用B的基础环境;再看隔离组2,这个隔离组里面有B的环境,所以A2就调B2,没有C了再往基础环境调。这个就是和没做隔离的区别,没有隔离的图上的每个圈上面至少两三条线,而做了隔离只会调一个。
当然这里面还有一个问题,比如说在隔离组2里,B1的服务挂掉了,这时A2是不应该调用基础环境B的,否则会掩盖B1挂掉的事实,这个时候隔离逻辑还是会尝试去调用B1,并报错说调用不到。
这个是最终效果图。大家都在隔离组里,没有散的机器。与上一张图的另一个区别是这条红线,这是一个比较特殊的隔离逻辑的高级用法。
比如A是一个前端应用,B是后端的服务,A调用B的服务,如果我是一个B的开发,通过前端页面方式测试B服务的时候,一般需要再搭一个A的测试环境,这种情况好处理,只要搭一个即可。
如果是多个呢?比如像物流发货这种情况,你需要先要购买,然后付款,整个走完之后才能发货,可能需要把整个交易链路都搭一遍,这时你会发现这个链路很长,要搭建的环境很多,对机器资源也是一种浪费,根本没有必要。
有没有更高效的方式呢?这里面实现了一个逻辑,就是通过用户的请求来精准路由。
比如你是从某个隔离组发起测试,只有你访问的时候会走那条路,而其它人还是走这条路,这个与蓝绿部署里面的备机有点像,备机在下线之后,只是被隔离了,机器并不会被释放掉,一方面这个机器可以用来给开发做debug用,另一方面有问题回滚比较快。
怎样保证还在提供这个服务,但是这个服务又不被别人调用到呢?我们把这个服务单独放在一个隔离组里,就不会被任何人调用到,只是被隔离起来了而已,实际上服务都还在。要切换的时候,只要重新拉回来就行,秒级生效。
讲一下实现的原理,阿里有一个中间件叫做鹰眼,可以查看调用链路。每个用户请求会生成一个ID,这个ID会随着每一次调用一个一个传下去,所以当你把这个ID都统计出来,就能知道这条链路是什么样子的。
我们也是利用了这个东西,把源头的请求IP放在ID里面,每次路由的时候都会看ID有没有跟某个隔离组进行关联。我们有一个web操作界面,想与哪个隔离组关联,界面上点一下就好。
当你服务调用的时候,服务路由会把ID取出来,看看你的IP有没有做关联,如果有的话就到那个隔离组里面去调用。
它的好处就是快速的搭建一整套隔离环境,只要在上面点几下,要哪几个应用一下子就好了,自然而然就呈现一个封闭的隔离组。
这个东西因为是涉及到阿里的中间件产品,所以大家可能不能直接拿去用,但是这里提供一个思路,这套东西目前来看已经能够满足很多场景了。
如果在没有使用鹰眼产品的情况下可以采用这种方式,回过头来看看上一张图。有一个简单点的方法就是直接看标,每台机器打了标之后,这个是隔离组标,这个是基础环境标,比如第一个圈,发现自己的标是隔离组的,调服务的时候,发现这个组里面没有需要的服务,就调基础的了,基础再往下看的时候,就看他自己的标。
这会存在一个问题,就是一旦调出来就调不回去了,所以要保证所有你要测的东西都在这个里面。遇到问题排查也比较简单。
以上就是在业务稳定性这块做的一个逻辑隔离。
嘉宾介绍
刘湘疆(青冥),阿里巴巴运维中台技术专家,2010年加入阿里巴巴,先后从事配置管理、工程效能等相关工作,目前负责阿里巴巴集团测试环境运维及研发效率提升。对于测试环境效率、自动化部署、持续交付有较深刻的认识和实践经验。