在阿里,咱们如何管理测试环境

前言

阿里的许多实践看似简单,背后却蕴涵着许多思考,譬如测试环境的管理。数据库

互联网产品的服务一般是由Web应用、中间件、数据库和许多后台业务程序组成的,一套运行环境就是一个自成一体的小生态。最基本的运行环境是线上环境,部署产品的正式发布版本,为用户提供持续可靠的服务。缓存

除此之外,还有许多不对外部用户开放的运行环境,用于产品团队平常的开发和验证,统称为测试环境。正式环境的稳定性,除去软件自身的质量因素,主要与运行的主机、网络等基础设施相关,而测试环境的稳定性则更多受到人为因素影响。因为频繁的版本变动,以及部署未经充分验证的代码,测试环境出故障的状况家常便饭。服务器

良好的代码提交习惯、适当的变动前检查有助于减小故障的发生,但没法完全杜绝后患。增长多套测试环境副本可以有效控制故障的影响范围,然而企业的资源终归有限,下降测试环境成本和提升测试环境稳定性成为了矛盾的两面。网络

在这个领域里,独具匠心的阿里研发效能团队设计了一种服务级复用的虚拟化技术,称为“特性环境”,其巧妙的思路使人赞叹。本文将围绕测试环境管理的话题,聊聊这种具备阿里特点的工做方式。运维

测试环境管理的困局

测试环境的用途很普遍,常见的测试环境譬如系统集成测试环境、用户验收测试环境、预发布测试环境、灰度测试环境等,它们体现了产品的交付生命周期,也间接反映出整个团队的组织结构。分布式

小做坊型产品团队的测试环境管理起来十分简单,每一个工程师本地就能启动全套软件组件进行调试,假若不放心,再加上一个公共的集成测试环境也就足够。工具

随着产品规模扩大,本地启动全部服务组件逐渐变得既费时又费事,工程师们只能在本地运行一部分待调试的组件,而后利用公共测试环境上的其他组件组成完整系统。性能

与此同时,团队规模的扩张,使得每一个团队成员的职责进一步细分,新的子团队被划分出来,这意味着项目的沟通成本增长,公共测试环境的稳定性开始变得难以控制。在这个过程当中,测试环境管理复杂性带来的影响,不只体如今服务联调变得繁琐,更直接反映在交付流程和资源成本的变化上。测试

在交付流程方面,一个显著的变化是测试环境种类增多。出于不一样的用途和目的,工程师们设计出了各式各样的专用测试环境。这些测试环境的组合造成了各个企业独具特点的交付流程。下图展现了一种用于大型项目的复杂交付流程。spa

从单独服务的角度来看,环境与环境之间是由流水线相连的,再加上自动化测试或手工审批操做组成关卡,实现环境之间的传递。一般越高级别环境的部署频率越低,所以相对稳定性也越高。与之相反,在级别较低的环境上,就随时可能存在新的部署,会打扰正在使用该环境的其余人。有时为了复现某些特殊的问题场景,一些开发者不得不直接登陆到服务器上面去“搞事情”,进一步影响环境的稳定性和可用性。

面对随时可能崩溃的测试环境,小企业会试着去“堵”:约束服务变动时间、设立严格的变动规范,大企业则善于用“疏”:增长测试环境副本,隔离故障影响范围。显然,不堪重负的测试环境必定越“堵”越“漏”,千年之前大禹治水的故事早就揭示了的道理,刻意的管控拯救不了脆弱的测试环境。

近年来,DevOps文化的兴起,端到端解放了开发者的双手,这对于测试环境的管理而言倒是一把双刃剑。一方面,DevOps鼓励开发人员参与运维,了解产品的完整生命周期,有助于减小没必要要的低级运维事故;另外一方面,DevOps让更多的手伸向测试环境,更多的变动、更多的Hotfix出现了。这些实践从全局来看利大于弊,然而并不能缓解测试环境的动荡。单纯的流程疏通一样拯救不了脆弱的测试环境。

那么该投入的还得投入。将不一样团队所用的低级别测试环境各自独立,此时每一个团队看到的都是线性流水线,从总体上观察,则会程现出河流汇聚的形状。

由此推广,理想状况下,每位开发者都应该获得独占且稳定的测试环境,各自不受干扰的完成工做。然而因为成本因素,现实中在团队内每每只能共享有限的测试资源,不一样成员在测试环境相互干扰成为影响软件开发质量的隐患。增长测试环境副本数本质上是一种提升成本换取效率的方法,然而许多试图在成本和效率之间寻找最优平衡的探索者们,彷佛都在同一条不归路上越行越远。

因为客观的规模和体量,上述这些测试环境管理的麻烦事儿,阿里的产品团队都没法幸免。

首先是测试环境种类的管理。

在阿里内部,一样有十分丰富的测试环境区分。各类测试环境的命名与其做用息息相关,虽然业界有些经常使用的名称,但都未造成权威的标准。实际上,环境的名称只是一种形式,关键还在于各类测试环境应当分别适配于特定应用场景,且场景之间应当或多或少存在一些差别。

这种差别有些在于运行的服务种类,譬如性能测试环境极可能只须要运行与压力测试相关的那部分访问量最大的关键业务服务,其余服务运行了也是浪费资源。有些差别在于接入数据的来源,譬如开发自测的环境的数据源与正式环境确定不同,这样测试使用的假数据就不会污染线上用户的请求;预发布环境(或用户验收测试环境)会用与正式环境一致的数据源(或正式数据源的拷贝),以便反映新功能在真实数据上运行的状况;自动化测试相关的环境会有单独的一套测试数据库,以避测试运行过程当中受到其余人为操做的干扰。

还有些差别在于使用者的不一样,譬如灰度和预发布环境都使用正式的数据源,但灰度环境的使用者是一小撮真实的外部用户,而预发布环境的使用者都是内部人员。总之,不必为一个不存在业务特殊性的测试场景专门发明一种测试环境。

在集团层面,阿里对流水线形式的约束相对宽松。客观的讲,只有在一线的开发团队知道最适合团队的交付流程应该是什么样子。阿里的开发平台只是规范了一些推荐的流水线模板,开发者可在此基础上进行发挥。列举几个典型的模板例子:

这里出现了几种外界不太常见的环境类型名称,稍后会详细介绍。

其次是测试环境成本的管理。

成本管理的问题十分棘手且十分值得深究。与测试环境相关的成本主要包括管理环境所需的“人工成本”和购买基础设施所需的“资产成本”。经过自动化以及自服务化的工具能够有效下降人工相关的成本,自动化又是个很大的话题,宜另起一篇文章讨论,此处暂且收住。

资产购买成本的下降依赖技术的改良和进步(排除规模化采购带来的价格变化因素),而基础设施技术的发展史包括两大领域:硬件和软件。硬件发展带来的成本大幅降低,一般来自于新的材料、新的生产工艺、以及新的硬件设计思路;软件发展带来的基础设施成本大幅降低,目前看来,大多来自于虚拟化(即资源隔离复用)技术的突破。

最先的虚拟化技术是虚拟机,早在20世纪50年代,IBM就开始利用这种硬件级的虚拟化方法得到成倍的资源利用率提高。虚拟机上的不一样隔离环境之间各自运行完整操做系统,具备很好的隔离性,通用性强,但对于运行业务服务的场景,显得略为笨重。2000年后,KVM、XEN等开源项目使得硬件级虚拟化普遍普及。

与此同时,另外一种更轻量的虚拟化技术出现了,以OpenVZ、LXC为表明的早期容器技术,实现了创建于操做系统内核之上的运行环境虚拟化,减小了独立操做系统的资源消耗,以牺牲必定隔离性为代价,得到更高的资源利用率。

以后诞生的Docker以其镜像封装和单进程容器的理念,将这种内核级虚拟化技术推上百万人追捧的高度。阿里紧随技术前进的步伐,早早的就用上了虚拟机和容器,在2017年双十一时,在线业务服务的容器化比例已经达到100%。然而,接下来的挑战是,基础设施资源利用率还能作得更高吗?

甩掉了虚拟机的硬件指令转换和操做系统开销,运行在容器中的程序与普通程序之间只有一层薄薄的内核Namespace隔离,彻底没有运行时性能损耗,虚拟化在这个方向上彷佛已经发展到了极限。惟一的多是,抛开通用场景,专一到测试环境管理的特定场景上,继续寻找突破。终于,阿里在这个领域里发现了新的宝藏:服务级虚拟化。

所谓服务级虚拟化,本质上是基于消息路由的控制,实现集群中部分服务的复用。在服务级虚拟化方式下,许多外表庞大的独立测试环境实际只须要消耗极小的额外基础设施资源,即便给每一个开发者配备一套专用的测试环境集群都再也不是吹牛。

具体来讲,在阿里的交付流程上,包含两种特殊类型的测试环境:“公共基础环境”和“特性环境”,它们造成了具备阿里特点的测试环境使用方法。公共基础环境是一个全套的服务运行环境,它一般运行一个相对稳定的服务版本,也有些团队将始终部署各服务的最新版本的低级别环境(称为“平常环境”)做为公共基础环境。

特性环境是这套方法中最有意思的地方,它是虚拟的环境。从表面上看,每一个特性环境都是一套独立完整的测试环境,由一系列服务组成集群,而实际上,除了个别当前使用者想要测试的服务,其他服务都是经过路由系统和消息中间件虚拟出来的,指向公共基础环境的相应服务。因为在阿里一般的开发流程中,开发任务须要通过特性分支、发布分支和诸多相关环节最后发布上线,大多数环境都从发布分支部署,惟独这种开发者自用的虚拟环境部署来自代码特性分支的版本,故可称为“特性环境”(阿里内部叫“项目环境”)。

举个具体例子,某交易系统的完整部署须要由鉴权服务、交易服务、订单服务、结算服务等十几种小系统以及相应的数据库、缓存池、消息中间件等组成,那么它的公共基础环境就是这样一套具有全部服务和周边组件的完整环境。假设此时有两套特性环境在运行,一套只启动了交易服务,另外一套启动了交易服务、订单服务和结算服务。对于第一套特性环境的使用者而言,虽然除交易服务外的全部服务实际上都由公共基础环境代理,但在使用时就像是本身独占一整套完整环境:能够随意部署和更新环境中交易服务的版本,并对它进行调试,不用担忧会影响其余用户。对于第二套特性环境的使用者,则能够对部署在该环境中的三个服务进行联调和验证,假若在场景中使用到了鉴权服务,则由公共基础环境的鉴权服务来响应。

咋看起来,这不就是动态修改域名对应的路由地址、或者消息主题对应的投递地址么?实事并没那么简单,由于不能为了某个特性环境而修改公共基础环境的路由,因此单靠正统路由机制只能实现单向目标控制,即特性环境里的服务主动发起调用可以正确路由,若请求的发起方在公共基础环境上,就没法知道该将请求发给哪一个特性环境了。对于HTTP类型的请求甚至很难处理回调的状况,当处于公共基础环境的服务进行回调时,域名解析会将目标指向公共基础环境上的同名服务。

如何才能实现数据双向的正确路由和投递呢?不妨先回到这个问题的本质上来:请求应该进入哪一个特性环境,是与请求的发起人相关的。所以实现双向绑定的关键在于,识别请求发起人所处的特性环境和进行端到端的路由控制。这个过程与“灰度发布”颇有几分类似,可采用相似的思路解决。

得益于阿里在中间件领域的技术积累,和鹰眼等路由追踪工具的普遍使用,识别请求发起人和追溯回调链路都不算难事。如此一来,路由控制也就水到渠成了。当使用特性环境时,用户须要“加入”到该环境,这个操做会将用户标识(如IP地址或用户ID)与指定的特性环境关联起来,每一个用户只能同时属于一个特性环境。当数据请求通过路由中间件(消息队列、消息网关、HTTP网关等),一旦识别到请求的发起人当前处在特性环境中,就会尝试把请求路由给该环境中的服务,若该环境没有与目标一致的服务,才路由或投递到公共基础环境上。

特性环境并非孤立存在的,它能够创建在容器技术之上,从而得到更大的灵活性。正如将容器创建在虚拟机之上获得基础设施获取的便利性同样,在特性环境中,经过容器快速而动态的部署服务,意味着用户能够随时向特性环境中增长一个须要修改或调试的服务,也能够将环境中的某个服务随时销毁,让公共基础环境的自动接替它。

还有一个问题是服务集群调试。

配合AoneFlow的特性分支工做方式,假若将几个服务的不一样特性分支部署到同一个特性环境,就能够进行多特性的即时联调,从而将特性环境用于集成测试。不过,即便特性环境的建立成本很低,毕竟服务是部署在测试集群上的。这意味着每次修改代码都须要等待流水线的构建和部署,节约了空间开销,却没有缩短期开销。

为了进一步的下降成本、提升效率,阿里团队又捣鼓出了一种开脑洞的玩法:将本地开发机加入特性环境。在集团内部,因为开发机和测试环境都使用内网IP地址,稍加变通其实不难将特定的测试环境请求直接路由到开发机。这意味着,在特性环境的用户即便访问一个实际来自公共基础环境的服务,在后续处理链路上的一部分服务也能够来自特性环境,甚至来自本地环境。如今,调试集群中的服务变得很是简单,不再用等待漫长的流水线构建,就像整个测试环境都运行在本地同样。

DIY体验特性环境

以为服务级虚拟化过小众,离普通开发者很远?实事并不是如此,咱们如今就能够动手DIY个体验版的特性环境来玩。

阿里的特性环境实现了包括HTTP调用、RPC调用、消息队列、消息通知等各种经常使用服务通讯方式的双向路由服务级虚拟化。要完成这样的功能齐全的测试环境有点费劲,从通用性角度考虑,咱不妨从最符合大众口味的HTTP协议开始,作个支持单向路由的简易款。

为了便于管理环境,最好得有一个能跑容器的集群,在开源社区里,功能齐全的Kubernetes是个不错的选择。在Kubernetes中有些与路由控制有关的概念,它们都以资源对象的形式展示给用户。

简单介绍一下,Namespace对象能隔离服务的路由域(与容器隔离使用的内核Namespace不是一个东西,勿混淆),Service对象用来指定服务的路由目标和名称,Deployment对象对应真实部署的服务。类型是ClusterIP(以及NodePort和LoadBalancer类型,暂且忽略它们)的Service对象可路由相同Namespace内的一个真实服务,类型是ExternalName的Service对象则可做为外部服务在当前Namespace的路由代理。这些资源对象的管理均可以使用YAML格式的文件来描述,大体了解完这些,就能够开始动工了。

基础设施和Kubernetes集群搭建的过程略过,下面直接进正题。先得准备路由兜底的公共基础环境,这是一个全量测试环境,包括被测系统里的全部服务和其余基础设施。暂不考虑对外访问,公共基础环境中的全部服务相应的Service对象均可以使用ClusterIP类型,假设它们对应的Namespace名称为pub-base-env。这样一来,Kubernetes会为此环境中的每一个服务自动赋予Namespace内可用的域名“服务名.svc.cluster”和集群全局域名“服务名.pub-base-env.svc.cluster”。有了兜底的保障后,就能够开始建立特性环境了,最简单的特性环境能够只包含一个真实服务(例如trade-service),其他服务所有用ExternalName类型的Service对象代理到公共基础环境上。假设它使用名称为feature-env-1的Namespace,其描述的YAML以下(省略了非关键字段的信息):

kind: Namespace

metadata:

name: feature-env-1

    • *

kind: Service

metadata:

name: trade-service

namespace: feature-env-1

spec:

type: ClusterIP

...

    • *

kind: Deployment

metadata:

name: trade-service

namespace: feature-env-1

spec:

...

    • *

kind: Service

metadata:

name: order-service

namespace: feature-env-1

spec:

type: ExternalName

externalName: order-service.pub-base-env.svc.cluster

...

    • *

kind: Service

...

注意其中的order-service服务,它在当前特性环境Namespace中可使用局部域名order-service.svc.cluster访问,请求会路由到它配置的全局域名order-service.pub-base-env.svc.cluster,即公共基础环境的同名服务上处理。处于该Namespace中的其它服务感知不到这个差别,而是会以为这个Namespace中部署了全部相关的服务。

若在特性的开发过程当中,开发者对order-service服务也进行了修改,此时应该将修改过的服务版本添加到环境里来。只需修改order-service的Service对象属性(使用Kubernetes的patch操做),将其改成ClusterIP类型,同时在当前Namespace中建立一个Deployment对象与之关联便可。

因为修改Service对象只对相应Namespace(即相应的特性环境)内的服务有效,没法影响从公共基础环境回调的请求,所以路由是单向的。在这种状况下,特性环境中必须包含待测调用链路的入口服务和包含回调操做的服务。例如待测的特性是由界面操做发起的,提供用户界面的服务就是入口服务。即便该服务没有修改,也应该在特性环境中部署它的主线版本。

经过这种机制也不难实现把集群服务局部替换成本地服务进行调试开发的功能,假若集群和本地主机都在内网,将ExternalName类型的Service对象指向本地的IP地址和服务端口就能够了。不然须要为本地服务增长公网路由,经过动态域名解析来实现。

与此同时,云效也正在逐步完善基于Kubernetes的特性环境解决方案,届时将会提供更加全面的路由隔离支持。值得一提的是,因为公有云的特殊性,在联调时将本地主机加入云上集群是个必须克服的难题。为此云效实现了经过隧道网络+kube-proxy自身路由能力,将本地局域网主机(无需公网IP地址)加入到不在同一内网Kubernetes集群进行联调的方式。其中的技术细节也将在近期的云效公众号向你们揭晓,敬请留意。

小结

当许多人还在等待,在虚拟机和容器以后,下一轮虚拟化技术的风口什么时候到来的时候,阿里已经给出了一种答案。创业者的心态让阿里人懂得,能省必须省。其实,限制创新的每每不是技术而是想象力,服务级虚拟化的理念突破了人们对环境副本的传统认知,以独特的角度化解了测试环境成本与稳定性的矛盾。

做为一种颇具特点的技术载体,特性环境的价值不只仅在于轻量的测试环境管理体验,更在于为每位开发人员带来流畅的工做方式,实则是“简约而不简单”。

实践出真知,阿里巴巴云效平台致力于解决大型项目协做、敏捷高速迭代、海量代码托管、高效测试工具、分布式秒级构建、大规模集群部署发布等世界级业务和技术难题,为阿里巴巴集团内部、生态伙伴以及云上开发者服务。诚挚欢迎业界同行与咱们探讨交流。

相关阅读:

在阿里,咱们如何管理代码分支

当kubernetes应用遇到阿里分批发布模式



本文做者:云效鼓励师

阅读原文

本文为云栖社区原创内容,未经容许不得转载。

相关文章
相关标签/搜索