行业动态

行业动态、技术资源、时事资讯、评估报告

帮助您深入了解实践、洞见、和本质。

测试环境的稳定性:Kubernetes 在喜马拉雅的实践
来源:分布式实验室 | 作者:宋银涛 | 发布时间: 2020-03-02 | 1099 次浏览 | 分享到:
在容器化背景之下,如何保证测试环境开发效率的同时兼顾环境的稳定性。


喜马拉雅是专业的音频分享平台,汇聚了有声小说、有声读物、相声小品等数亿条音频。目前平台激活用户超6亿,主播用户超过1000万,大V用户超过100万,活跃用户日均收听时常170分钟,5年时间估值增长1000倍。作为一个产品驱动的互联网公司,公司的业务线扩展迅猛,需求的迭代十分迅速。前期开发在测试环境的发布也相当的频繁,那么所带来的稳定性问题十分突出。因此,我们在2016年底启动了容器化项目,将公司的应用进行容器化改造。在容器化背景之下,如何保证测试环境开发效率的同时兼顾环境的稳定性。

容器化的价值:资源+效率==>效果
即使用更少的资源+更高的开发效率,产生更好的效果。在测试环境当中,更高的开发效率显得更加重要。


容器化的实践


这里我们先来简单介绍一下容器化在喜马拉雅的实践。

给喜马拉雅带来的好处

容器化之前

在进行容器化之前,开发在测试环境会碰到这些问题:

1
、新项目创建
开发在接到新的需求之后,创建的新项目,本地开发完成之后,需要部署到测试环境。他们将不得不面对这些问题:

  • 代码开发完成
  • 找运维人员分配机器
  • 自己在Jenkins上创建Task


这一套流程下来,至少半个工作日过去了。时间都耗费在这些没有意义的事情上,开发效率相当低下。

2
、物理机冲突
由于历史原因,作为一个创业公司,业务发展迅速,开发团队人员极速上升。运维团队相对精简,没有足够的精力运维管理测试环境。导致测试环境的管理相当混乱,主要问题如下:

§  共享账号

§  端口冲突

§  进程莫名被kill

由于大家共享物理机SSH账号,权限管理混乱。大家都可以随意的配置自己的服务,为了确保自己的服务正常上线,随意占用别人服务的端口,kill掉被人的服务。这也是导致测试环境不稳定的一个重要因素。

3
、断电重启
由于历史原因,老园区每12个月都会进行停电检修。机房服务器断电,导致整个测试环境不可用,电力恢复之后,需要开发手动拉起自己负责的业务。整个流程下来,半个工作日就过去了。当然,现在公司有钱了,有了自己的办公大楼,新建了UPS机房,保证24小时不断电,这些问题是不存在的。

4
、小结
针对这些问题,容器化的进程势在必行。因此,2016年底我们引入了容器技术进行容器化改造,来解决这些问题。

容器化之后


对测试环境进行容器化之后,我们取得了以下收益:

§  分钟级创建、发布新项目;

§  每个应用独享"物理机",不会存在端口冲突、服务莫名被销毁;

§  断电服务自动拉起。


容器化现状

整体架构

在架构层面,CMDB-DOCKER(容器化管理平台)如何能与上层业务和底层Kubernetes平台更好地分层和解耦,是我们在设计之初就优先考虑的问题。我们期望它既要能对业务使用友好,又能最大限度地发挥Kubernetes的调度能力,使得业务层和使用方毋需关注资源关系细节,所求即所得。同时使发布、配置、负载等逻辑层与底层的Kubernetes平台解耦分层,并保持兼容原生Kubernetes API来访问Kubernetes集群。
从而可以借助于统一的、主流的、符合业界规范的标准,来解决基础架构面临的复杂、多样、不统一的管理需求。




自上而下来看,集群管理与调度平台面向全公司服务,有各个主要业务线、统一的OPS平台,CMDB-DOCKER不可能针对每个平台定制化接口和解决方案,所以需要将多样的业务和需求抽象收敛,最终统一通过CMDB-DOCKER API来屏蔽CMDB-DOCKER系统的细节,做到CMDB-DOCKER与上层业务方的解耦。「CMDB-DOCKER API是对业务层和资源需求的抽象,是外界访问「CMDB-DOCKER」的唯一途径。


解决了上层的问题后,我们再来看与下层Kubernetes平台的解耦。CMDB-DOCKER接到上层资源请求后,首先要进行一系列的初始化工作,包括参数校验、资源余量、IPHostname的分配等等,之后向Kubernetes平台实际申请分配机器资源,最终将资源交付给用户,Kubernetes API进一步将资源需求收敛和转换,让我们可以借助于Kubernetes的资源管理优势。此外,因为完全兼容Kubernetes API,可以让我们借助社区和生态的力量,共同建设和探索。可以看到,CMDB-DOCKER APIKubernetes API将我们整个系统分为三层,这样可以让每一层都专注于各自的模块。


支持的功能
喜马拉雅在容器化工作上主要支持了以下功能:

1
、多语言支持
目前支持JavaPythonNodejsPHP

2
、现有技术栈兼容
为了无缝迁移传统服务,我们需要做一些兼容性的工作,整合现有的技术栈。

下面对喜马拉雅主要的中间件做一些简单的介绍:

§  Mainstay:微服务治理框架,采用自定义Thrift协议的RPC框架(类似Dubbo);

§  southgate:API Gateway公司自研的网关系统,管理公司所有客户端请求;

§  cmdb-docker:容器化管理平台。


主要解决的问题:
由于容器化以后,实例在重调度、扩缩容、重发布之后,IP是不断变化的,这些中间件要动态的感知到这些事件,对这些实例进行一些实例销毁、新增,服务上下线的操作。为此,我们开发了Nile Agent组件用于同步这些信息。


主要流程如下:

§  服务发布时,注入nile sidecar容器和应用容器共同发布;

§  nile sidecar启动之后,通过liveness、readiness接口,检查App Service的状态;

§  App Service可用之后,将其实例信息(IP、port、应用名称等)注册到ZooKeeper,同时,调用网关、注册中心的上线接口,上线服务;

§  中间件通过监听ZooKeeper的事件,来对这些中间件的实例信息进行更新、服务发现。


3
、无损发布
无损发布主要解决的就是应用重新发布时,保证旧实例在新发布的实例就绪时再销毁。在销毁的过程中,需要先进行服务的下线操作,再进行零流量检测,最终销毁容器。


这里主要是用了Kubernetes提供的LivenessReadiness探针功能,在新的发布ready之后,在进行旧实例的销毁。同时,还使用了容器生命周期管理的Hook preStop功能,在容器销毁之前,下线服务进行零流量检测,没有流量之后销毁旧的实例。

4
、静态IP支持
由于RPC微服务框架只提供了Java版本的SDKC/C++应用通过IP直连的方式访问服务,因此需要提供固定IP的功能。

5
、隔离组支持
测试环境频繁的发布、发布代码存在bug,经常导致测试环境不稳定,开发效率受到严重的影响。因此,我们提供了隔离组的功能,用来确保测试环境的稳定性。这部分具体内容在下面将会详细说明。

6
、监控、告警
我们提供了应用实例的健康监测功能,在服务不可用时将会发送钉钉消息给相关负责人。


测试环境稳定性解决方案

 

存在的问题

导致测试环境不稳定的原因主要有以下几个方面:

§  开发发布问题代码,导致服务调用异常;

§  开发开启了远程debug的调试功能,导致服务短时间内不可用。

导致服务不可用。间接导致开发人员在定位问题时浪费较长。

具体场景如下:
测试人员发现某个服务接口返回500,其调用链路为A->B->C->D。如下图所示:


沟通+排查问题时间太长。比如,测试发现A服务提供的接口有问题,找AA花了五分钟发现是B返回的结果存在问题,B又花了5min,以此类推,最终发现是D的问题。这样花费了这么多的5min,开发效率可想而知。经常会出现开发5分钟,联调两小时的状况。

隔离组


概念


我们在了解隔离组的概念之前,先回顾一下Git的功能分支模型。

Git
功能分支模型:

§  当有多个需求需要同时进行开发的时候,从master分支中拉取feature分支进行开发;

§  多个需求开发完成之后,合并多个feature分支,通过集成测试之后,发布到线上的环境;

§  测试确认无误之后,分支合并到master分支。


在开发测试的过程中,可能会存在的问题:多个feature分支测试代码发布到测试环境麻烦,开发之间会存在冲突。比如,A在测试feature1的功能时,B发布了他的最新代码feature2。两者之间,如果没有有效的沟通或者约定,频繁发布会导致互相影响、降低开发效率。那么,测试环境急需对这些分支的部署进行隔离,确保多人协作开发的效率,各自在开发、测试的时候不会互相影响。为此,我们提出了隔离组的概念,实现了测试环境的逻辑上的"软隔离"

隔离组的定义与划分:
隔离组:逻辑上的一组服务实例的集合,由中间件的路由策略来保证它的隔离性。

关于隔离组的划分如下:

§  stable隔离组:永久存在的、稳定的实例集合,提供稳定的基础环境。和线上的release分支代码一致,该隔离组和线上部署的服务一致,永久保证高可用,任何时候都能请求成功;该隔离组保证了测试环境的稳定性。

§  feature隔离组:一组临时的实例集合。和产品提的需求有关,调用链路上相关的项目都会从master分支上拉取新建feature分支用于该需求的开发,项目发布时指定隔离组信息,重新部署一个实例;和stable隔离组的实例共存。


stable
隔离组的特点:

§  和线上发布实例一致;

§  线上发布成功后,自动触发测试环境的stable实例的发布;

§  测试环境,开发人员不能发布该隔离组实例;

§  关闭远程debug功能。


feature
隔离组的特点:

§  临时实例,生命周期开发可控,到期自动回收;

§  feature分支发布;

§  提供远程debug功能。


隔离组与Git功能分支模式的关系:

示例:



如上图,正常请求的调用链路如下:
AppClient—>southgate
API Gateway—>A1—>B1—>C1


新的迭代周期,产品提出了两个需求,开发分别在feature1feature2进行开发。同时,在发布实例时指定不同的隔离组信息。确保三个隔离组的实例同时存在(该特点直接采用Kubernetes中的service可以多版本发布deployment的特点)。

主要的路由规则如下:
§  App Client在没有指明隔离组的请求中,该请求流量全部打到stable隔离组的实例上面;
§  App Client的请求头中指定了隔离组的信息,则选择相应的隔离组实例,如不存在转发到stable隔离组中,同时,请求中注入隔离组信息用于后续调用链的路由。如上图中的feature1隔离组,网关收到请求后,发现A服务存在对应的隔离组feature1的A2实例。则请求达到该实例上面,再往下调用B服务的实例时,发现并不存在隔离组实例,则将请求转发到Stable隔离组实例B1中。由于B1收到的请求中携带feature1的隔离组标识,在调用C服务时,发现存在feature1隔离组的实例C2,将请求路由到C2实例中。


从上面可以看出,隔离组的实现主要依赖流量的路由功能,具体的实现方案如下。

实现方案
隔离组,即多版本实例的发布主要依赖Kubernetes多版本发布功能,这里不再赘述。路由转发实现软隔离,主要是对我们公司中间件的改造,支持根据隔离组标签的路由。

具体的实现如下:

§  在现有技术栈兼容的部分,我们提过通过ZooKeeper同步实例的信息,其中就包含了隔离组的信息。这样中间件(微服务注册中心、API网关)中,包含了对应实例的isolation信息。

§  API Gateway:当收到App客户端请求时,解析请求头里面的isolation字段,选取对应隔离组的实例,请求路由到该实例中。如果没有isolation字段,则转发到stable隔离组的实例当中,同时将隔离组信息注入到后续的请求调用中,用于进一步的路由;

§  RPC client:在filter中注入isolation信息到RPC请求消息的body的attachment中。同时,根据isolation信息选择对应的实例,将请求发送到该实例当中。如果不存在对应的隔离组实例,则转发到stable隔离组实例里面;

§  RPC server:获取到RPC请求消息之后,消息解包,从attachment中获取isolation信息,缓存在本地,用于后续RPC请求的路由,用于选择下一跳服务的实例,以此类推。


优化
为了提升开发效率,迅速定位调用链的问题服务,解决调用链路黑盒问题。我们做了以下优化:

§  服务依赖拓扑查询:方便开发、测试迅速定位问题服务;

§  隔离组可视化:轻松定位上下游关系,方便调试。


依赖拓扑查询:
隔离组可视化:

鼠标悬浮到实例上,可获取实例详细信息(IP、端口、健康状态、负责人等)。


测试环境开发效率提升

测试环境除了稳定性之外,还需要注重开发效率。为此,我们开发了一款测试环境的发布工具,用于本地代码的编译、镜像构建、服务发布。借鉴了Kubernetes的配置即发布的思想,可以让开发迅速将自己的服务根据自己的配置文件,发布到容器环境(非stable隔离组),大大提升开发效率。

该工具提供了以下功能:

§  一键构建、打包服务,并发布到容器化环境(测试);

§  测试环境实例状态查看;

§  测试环境实例快捷登录,查看日志;

§  多语言支持。


工具相关命令详情:

1.     NAME:

2.        barge - build code, build image, and push image to harbor

3.    

4.     USAGE:

5.        barge [global options] command [command options] [arguments...]

6.    

7.     VERSION:

8.        0.0.16-beta

9.     COMMANDS:

10.      doc       open browser to show barge help document

11.      cmdb      open browser to show cmdb-docker page

12.      show      show mvn command

13.      build     build image and push to harbor

14.      deploy    build and publish

15.      info      list instance command

16.      debug     open browser to show barge debug document

17.      upgrade   open browser to show barge upgrade document

18.      exec      ssh instance

19.      conflict  list maven pom dependency conflicts

20.      help, h   Shows a list of commands or help for one command

21.  

22.   GLOBAL OPTIONS:

23.      --publishURL value  the url for the publish endpoint

24.      --help, -h          show help

25.      --version, -v       print the version


发布配置文件(以Spring Maven项目为例):

1.     apiVersion: 0.0.1

2.     apps:

3.     - appName: demo-web

4.       alias: web

5.       build:

6.         language: java

7.         maven:

8.           prepareCommand: mvn clean install -am -Dmaven.test.skip=true -Ptest-out

9.           pom:demo-web/pom.xml

10.         war: demo-web/target/demo-web.war

11.     deploy:

12.       isolation: feature1

13.       expire: 1h

14.       instances: 2


该配置文件指定了项目的构建、隔离组、实例数等信息,在项目的根目录下执行:

1.     barge deploy -m demo-web


该项目的代码将会在本地进行编译、构建、镜像打包、push到镜像仓库、部署到测试环境的Kubernetes集群中。成功部署之后,将会给用户发布信息。
执行barge info -m demo-web命令将会显示项目的实例信息,及其健康状态。barge exec -m demo-web 命令将远程登录到容器里面。该工具简化了测试环境的发布流程,实例状态的查看。以类似于mvn命令的方式,进行测试环境服务的发布、管理,避免浏览器的频繁切换。(当然,线上环境并不支持该操作)


总结



今天主要和大家分享了喜马拉雅在容器化方面进行的一些工作。介绍了容器化给我们带来的收益:减少维护成本、提升开发效率。其次,列举了导致我们测试环境不稳定的一些因素:1. 多分支并行开发的频繁发布;2. 开发远程debug,服务短暂不可用;3. 开发发布问题代码导致服务不可用。为此,我们提出了隔离组的概念,实现测试环境的"软隔离"。在隔离组中:首先,我们保证一整套和线上发布一致的stable隔离组,该隔离组保证测试环境稳定。当需要,开发新的分支代码时,创建新的隔离组,保证stable隔离组的实例不受影响,确保了环境的稳定性。其实现原理比较简单,主要是打通了中间件之间的流量管理,通过隔离组信息来进行请求路由。最后,介绍了我们在测试环境使用的小工具,通过简单的命令行方式进行服务的快速发布、日志查看,提升开发效率。

Q&A


Q:测试环境只是开发自己用吗?开发和测试共用一个测试环境吧?
A:开发测试好了之后,钉钉沟通,哪个隔离组信息的,同步一下这个信息就好了。

Q
:隔离组和Git分支是一一对应吗,如何关联Git分支、隔离组和Pod版本?
A:隔离组和Git分支是一一对应吗?不一定,大部分场景下面是一致的。Git分支、隔离组和Pod版本这些信息会以环境变量的形式注入到容器里面,我们这有一个Agent容器,将这些信息同步。

Q
Kubernetes集群的容器网络是怎么组成的呢?
A:测试环境使用的是最简单的MacvlanUAT、线上用的Calico

Q
:请问下喜马拉雅现在运维团队大概是多少人,Kubernetes规模能大概说一下吗,Kubernetes环境是自建的还是选择公有云的,是基于什么样的考虑?
A:运维团队比较精简,人数在个位数。之前都是只有一个大佬维护。Kubernetes在测试环境和UAT自建,线上公有云。基于什么样的考虑?这个得运维大佬来解释了。

Q
:您的隔离组访问的分配是用的什么技术,Istio还是Nginx Ingress或者其他?还有cmdb-dockercmdb的一部分吗?
AIstioNginx Ingress暂时还没有使用,路由的方式比较传统,使用网关+微服务框架的路由功能。请求直接打到Nginx,转发给网关。将隔离组信息注入到请求里面。cmdb-dockercmdb的一部分吗?和cmdb并行的关系,用于容器服务的发布,说成cmdb也没啥错。Service Mesh已经在落地中,后面的化应该会对Mesh化的环境进行隔离策略的调整,主要的还是路由配置相关的。

Q
:请问微服务监控具体怎么实施?
A:微服务的监控这个就涉及到了微服务方面的问题了,具体的实现可以参考Hystrix里面的统计实现,本人没参与过这方面的开发哈。