编者按:本文源自阿里云云效团队出品的《阿里巴巴 DevOps 实际指南》,返回:https://developer.aliyun.com/…,下载完整版电子书,理解阿里十年 DevOps 实践经验。
开发一个需要,须要先进行代码的编写和集体验证,验证性能合乎预期之后,再提交代码,并进入到集成环境,进行进一步的验证及验收。而这个编码和验证的过程占据了整个需要交付的大部分工夫,因而进步这部分工作的效率就显得至关重要。
问题
有什么因素升高了开发调试的效率呢?
给定上面一个零碎,其中为了开发某个需要,批改了 A 和 D 这两个利用(这里的利用指的是一个可提供服务的一组独立过程加上可选的负载平衡,比方一个 kubernetes 下的 service 及其后端的 deployment)。
接下来看看为了本地调测这两个利用,会遇到什么问题。
本地难以启动整个零碎
咱们通常都在开发一个简单零碎中的一个利用,这个利用可能在零碎的最前端,也可能在零碎的两头地位,有时候为了端到端验证整个流程,须要把相干的利用都启动起来。
比方上图中的利用 A 为最前端利用,利用 D 处在两头地位,而黑框中局部是为了残缺的测试这个需要而波及到的利用,如果是 Java 利用,开发机上启动这样 5 个过程,就曾经不堪重负了,而很多时候须要残缺启动的利用数量会远大于这个数字。
依赖零碎不稳固
既然不能把整个零碎都在本地启动起来,那么本地就会一部分依赖于公共测试环境。尽管后面提到应该本地测试合乎预期之后再把代码部署到测试环境,但不可避免的还是会呈现一些 bug,导致测试环境不可用(这也是测试环境的价值所在,尽早的发现问题)。一旦依赖零碎不可用,就无奈失常的进行测试。
云原生开发模式下的测试环境的连通性
在基于 Kubernetes 的基础设施下,整个零碎中大部分的利用通常不须要通过 Ingress 裸露到公网。如果你的测试环境是独立的 K8S 集群,那就意味着无奈从本地无法访问到集群内的利用,那么依赖公共测试环境这件事件都无奈进行,比方上图中 A->C,D->E,D->F 的依赖。
还有另外一种依赖,即上游利用对本地利用的依赖,比方 C->D 的依赖。但因为 C 是公共测试环境,不能够将所有的 C 对 D 的申请都打到本地来,这就须要某种机制来保障只有特定规定的申请会路由到开发本地的 D 利用。
内部依赖零碎到开发环境的连通性
有一些测试链路须要承受一些内部依赖零碎的回调,比方微信或者支付宝的回调等。而本地利用通常没有公网地址,这也给调试带来了一些艰难。
中间件的隔离
分布式系统中常常会用到 RocketMQ 等消息中间件,如果应用了公共测试环境,就意味着 MQ 也是共用的,那么 MQ 的音讯到底是应该被测试环境生产,还是某个集体的开发环境生产呢,这也是须要解决的问题。
高效本地开发
为了进行全流程的高效开发,应该尽量应用反馈比拟快的验证形式,并及早发现问题,逐渐进行更加集成,更加实在的测试。
一般来讲,一个开发过程能够通过上面的三个阶段:
1、编码 + 单元测试。在小的逻辑单元的层面保障正确性。
2、针对单个利用的集成测试,可能须要对依赖的利用进行 HTTP 级别的 mock。
3、联合公共测试环境进行残缺的集成测试。
基于下面的三个阶段,能够应用以下的形式来解决后面提到的几个问题。
1、应用各个语言相应的测试工具(比方 JUnit)来进行单元测试。
2、应用 moco 等 HTTP Mock 工具来解决本地隔离验证的问题,实现单个利用的集成测试。
3、应用 kt-connect 和 virtual-environment 等工具来解决云原生基础设施下,本地和测试环境的相互连通性问题,及 http 申请链路的染色和路由。
4、应用 ngrok 等工具解决内部依赖调用本地利用的问题。
5、应用“骨干稳固环境”作为公共测试环境,进步其稳定性。
6、应用中间件的染色隔离能力保障 http 申请之外的其它链路(比方音讯)的染色和路由。
其中第 1、4 是成熟的技术,这里不再赘述。第 5、6 点会在前面的测试环境相干的章节中咱们具体解说。本文次要就第 2、3 点开展解说。
单利用的集成测试计划
比方对利用 D 而言,测试范畴如下图的橙色框所示:
利用自身的长久化等依赖应用实在的(个别应用本地 DB),但内部利用(利用 E、F)应用基于 HTTP 协定的测试替身。这样就能够保障所有的依赖都是稳固的。并且也能够很不便的批改测试替身的行为,以进行特定场景的测试。
利用 D 依赖了两个利用:
1、org-service(利用 F):提供查问组织信息等能力
2、user-service(利用 E):提供查问用户信息等能力
这两个利用的拜访地址配置在利用 D 的配置项中:
...
org-service-host: org-service
user-service-host: user-service
...
咱们应用 docker compose + moco 的计划来解说如何应用本地测试替身。
首先创立如下的目录构造:
├── Dockerfile
├── docker-compose.yml
├── moco-runner.jar
└── services
├── org-service
│ └── config.json
└── user-service
└── config.json
Dockerfile:
FROM openjdk:8-jre-slim
ARG SERVICE
ADD moco-runner.jar moco-runner.jar
COPY services/${SERVICE}/config.json config.json
ENTRYPOINT ["java", "-jar", "moco-runner.jar", "http", "-c", "config.json", "-p", "8080"]
docker-compose.yml:
version: '3.1'
services:
service-f:
ports:
- 8091:8080
build:
context: .
dockerfile: Dockerfile
args:
SERVICE: org-service
service-e:
ports:
- 8092:8080
build:
context: .
dockerfile: Dockerfile
args:
SERVICE: user-service
services/org-service/config.json:
[
{
"request": {"uri": "/"},
"response": {"text": "org service stub"}
},
{
"request": {
"uri": {"match": "/orgs/[a-z0-9]{24}"
}
},
"response": {
"json": {
"name": "some org name",
"logo": "http://xx.assets.com/xxx.jpg"
}
}
}
]
services/user-service/config.json:
[
{
"request": {"uri": "/"},
"response": {"text": "user service stub"}
},
{
"request": {
"uri": {"match": "/users/[a-z0-9]{24}"
}
},
"response": {
"json": {
"name": "somebody",
"email": "somebody@gmail.com"
}
}
}
]
而后应用如下命令来启动两个依赖的利用:docker-compose up --build
验证下本地测试替身的行为:
$ curl http://localhost:8092/users/111111111111111111111111
{"name":"somebody","email":"somebody@gmail.com"}
$ curl http://localhost:8091/orgs/111111111111111111111111
{"name":"some org name","logo":"http://xx.assets.com/xxx.jpg"}
而后再把利用 D 的依赖配置改成本地测试替身即可进行测试:
...
org-service-host: localhost:8091
user-service-host: localhost:8092
...
至此,咱们失去了一个稳固的单利用的集成测试环境。当须要批改依赖的行为时,只须要批改相应利用的 config.json 即可。
应用 docker-componse 和 moco 是一种实现单利用集成测试的形式,你能够依据我的项目的具体情况抉择适合的工具和计划。
本地和公共测试环境的互访及链路隔离
实现单利用的集成测试之后,能够取得单个利用级别的品质信念,但更大范畴的验证还是须要和实在的依赖集成在一起进行。
如上图所示,为了可能在本地按需启动利用(A 和 D),并复用测试环境的其余利用(C),就须要解决两个问题:
1、本地如何调用到公共测试环境的利用,即 A 如何调用到 C
2、公共测试环境如何调用到本地,即 C 如何调用到本地的 D
对于第一点,如果本地环境和测试环境的网络是间接可达的,则间接批改本地利用 A 的配置项即可。如果你应用了云原生的基础设施,那么就须要相似云效 kt-connect 之类的工具来进行买通,这里不再开展,有需要要的能够参看 kt-connect 的 connect 局部。
对于第二点,须要解决三个问题:
1、从测试环境的 A 发动的调用链,应该最终拜访到测试环境的 D,而从本地环境的 A 发动的调用链,应该最终拜访到本地环境的 D,互不影响。为了可能对这两种调用进行辨别,须要对调用链进行“染色”,这里采纳的染色的形式是在申请中退出一个额定的 header。
2、依据这个染色的标记,即“染色标”,进行路由。
3、一个调用链会贯通多个利用,要保障在调用到不同的利用时,染色标要可能主动的传递上来。
对于第一点和第二点,在阿里巴巴外部有一套残缺的计划进行染色和路由,这套计划不仅仅实用于 HTTP 链路,也实用于 RPC,异步音讯等。而在开源畛域,也有基于云原生基础设施的 kt-connect 能够用,应用 kt-connect 的 mesh 性能就能够针对特定染色规定的调用链进行路由。
kt-connect 基于 istio 的 VirtualService 和 DestinationRule 来进行路由。其基本原理是在集群内新建一个影子正本的 service 和 deployment,而后提交一个利用 D 的 DestinationRule 资源,使得蕴含“local-env: true”header 的申请被路由到利用 D 的影子正本,而后利用 D 的影子正本再把申请转发到本地。在这个过程里,除了提交和更新 is t i o 相干资源的操作须要手动进行之外,其余的事件都能够应用 ktctl mesh 命令来实现,详情请参看 mesh 最佳实际。
接下来解决第三点,染色标传递。即须要保障当本地的利用 A 把含有“local-env: true”header 的申请打到测试环境的利用 C 后,利用 C 持续拜访利用 D 时候,申请中也应该蕴含这个 header。
个别的思路是在 Web 层的入口加一个 Interceptor,将染色标记录下来到一个 ThreadLocal 中,而后再进口的 HttpClient 层再从 ThreadLocal 中把这个染色标取出来,并填充到 Request 对象中。这里有一个须要留神的问题,因为染色是放在 ThreadLocal 中的,因而在一个 web 申请的解决中一旦遇到多线程的状况,就须要小心的把这个 ThreadLocal 的值传递到相应的子线程中。所有的利用都正确的将染色标传递上来,就能够保障染色标在全链路进行传递。
应用 kt-connect 的 mesh 计划加上全链路染色标的计划,就能够轻松的在本地按需启动利用,并进行开发调测。
总结
应用单元测试、单利用集成测试、端到端集成测试联合的形式进行本地调测,进步取得反馈的效率。
本地按需启动利用进行端到端集成测试的关键技术是:全链路染色和路由。在不同的基础设施下能够有不同的实现形式。
【对于云效】
云原生时代一站式 DevOps 平台,数十万企业都在用。反对公共云、专有云和混合云多种部署状态,通过云原生新技术和研发新模式,助力翻新守业和数字化转型企业疾速实现研发麻利和组织麻利,打造“双敏”组织,实现多倍效力晋升。
立刻体验