Spring Cloud Alibaba 在 Proxyless Mesh 上的探索
-
2016 年作为 Service Mesh 的元年,Buoyant 公司 CEO William Morgan 率先发布 Linkerd [ 1] ,成为业界首个 Service Mesh 项目,同年 Lyft 发布 Envoy [ 2] ,成为第二个 Service Mesh 项目。 -
2017年,Google、IBM、Lyft 联手发布了 Istio [ 3] ,它与 Linkerd / Envoy 等项目相比,它首次给大家增加了控制平面的概念,提供了强大的流量控制能力。经过多年的发展 Istio,已经逐步成为服务网格领域控制平面的事实标准。 -
2018年7月, Istio 1.0版本 发布 [ 4] ,标志着其进入了可以生产可用的时代,逐渐也有越来越多的企业开始考虑和尝试将服务网格应用于生产中。
Proxyless Mesh 全称是 Proxyless Service Mesh,其是近几年在 Service Mesh 基础上发展而来的一种新型软件架构。Service Mesh 理想很丰满,但现实很骨感!通过一层代理虽然做到了对应用无侵入, 但增加的网络代理开销对很多性能要求很高的互联网业务落地造成不少挑战。 因此 Proxyless Mesh 作为一种在传统侵入式微服务框架与 Service Mesh 之间的折中方案,通过取众家之所长,为大量的非 Service Mesh 应用在云原生时代,拥抱云原生基础设施,解决流量治理等痛点提供了一种有效的解决方案。Service Mesh 和 Proxyless Mesh 架构区别如下图所示:
要通过Mesh化方案解决微服务治理需求,一个能给应用动态下发规则的控制面不可或缺,社区本着不重复造轮子,拥抱业界主流解决方案的原则,通过支持xDS协议不仅为用户提供通过主流的Istio控制面来对 Spring Cloud Alibaba 应用进行服务治理以外,用户也可以使用阿里巴巴开源的 OpenSergo 微服务治理控制面所提供的差异化治理能力进行应用治理。相关提供 Mesh 技术方案社区在最近发布的 2.2.10-RC 版本 [ 6] 中进行了提供。作为提供微服治理能力的第一个版本,社区当前已经部分兼容了 Istio VirtualService & DestinationRule 的标签路由和服务鉴权能力,用户可以通过 Istio 控制面给应用下发相关规则,对应用进行流量治理。
1. Istio 控制面
-
安装 K8s 环境,请参考 K8s 的 安装工具 [ 7] 小节 -
在 K8s 上安装并启用 Istio,请参考 Istio 官方文档的 安装小节 [ 8]
2. OpenSergo 控制面
- 安装 K8s 环境,请参考 K8s 的安装工具小节
-
在 K8s 上安装并启用 OpenSergo Control Plane,请参考 OpenSergo 官方提供的 OpenSergo 控制面安装文档 [ 9]
应用背景
- 多版本开发测试
- 应用流量隔离
- A/B Testing
使用方式
1. 导入依赖并配置应用
首先,创建服务消费者应用,推荐通过云原生应用脚手架进行项目构建,修改完配置即可便捷试用:云原生应用脚手架 :
https://start.aliyun.com/
<dependencyManagement><dependencies><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2.2.10-RC1</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-xds-adapter</artifactId></dependency></dependencies>
server:port: 18084spring:main:allow-bean-definition-overriding: trueapplication:name: service-consumercloud:nacos:discovery:server-addr: 127.0.0.1:8848fail-fast: trueusername: nacospassword: nacosistio:config:# 是否开启Istio配置转换enabled: ${ISTIO_CONFIG_ENABLE:true}# Istiod IPhost: ${ISTIOD_ADDR:127.0.0.1}# Istiod 端口port: ${ISTIOD_PORT:15010}# 轮询Istio线程池大小polling-pool-size: ${POLLING_POOL_SIZE:10}# 轮询Istio时间间隔polling-time: ${POLLING_TIME:10}# Istiod鉴权token(访问Istiod 15012端口时可用)istiod-token: ${ISTIOD_TOKEN:}# 是否打印xds相关日志log-xds: ${LOG_XDS:true}
# 第一个生产者,版本为v1spring.cloud.nacos.discovery.metadata.version=v1# 第二个生产者,版本为v2spring.cloud.nacos.discovery.metadata.version=v2
启动两个生产者应用和一个消费者应用,并将这些应用都注册到本地的 Nacos 注册中心里,消费者在调用生产者时,会根据控制面下发的标签路由规则来调用不同的生产者实例。启动消费者和两个生产者后,可以在 Nacos 注册中心里看到这几个已注册的服务:2. 运行应用程序
3. 通过 Istio 控制面下发标签路由规则
通过 Istio 控制面下发标签路由规则,首先下发 DestinationRule 规则:此规则将后端服务拆分为两个版本,label 为 v1 的 pod 被分到 v1 版本,label 为 v2 的 pod 被分到 v2 版本。 之后,下发 VirtualService 规则:kubectl apply -f - << EOFapiVersion: networking.istio.io/v1alpha3kind: DestinationRulemetadata:name: my-destination-rulespec:host: sca-virtual-servicesubsets:- name: v1labels:version: v1- name: v2labels:version: v2EOF
这条 VirtualService 指定了一条最简单的标签路由规则,将请求头 tag 为 gray,请求路径为 /istio-label-routing 的 HTTP 请求路由到 v2 版本,其余的流量都路由到 v1 版本。 发送若干条不带请求头的 HTTP 请求至 IstioConsumerApplicationkubectl apply -f - << EOFapiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata:name: sca-virtual-servicespec:hosts:- service-providerhttp:- match:- headers:tag:exact: grayuri:exact: /istio-label-routingroute:- destination:host: service-providersubset: v2- route:- destination:host: service-providersubset: v1EOF
因为请求头不为 gray,所以请求将会被路由到 v1 版本,返回如下while true;do curl localhost:18084/istio-label-routing;sleep 0.1;echo "";done;
因为满足路由规则,所以请求会被路由至 v2 版本while true;do curl localhost:18084/istio-label-routing -H "tag: gray";sleep 0.1;echo "";done;
4. 通过 OpenSergo 控制面下发标签路由规则
通过 OpenSergo 控制面也定义了特定的流量路由规则 TrafficRouter,如下是一个 OpenSergo 控制面对应的流量路由规则:这条 TrafficRouter 指定了一条最简单的流量路由规则,将请求头 tag 为 v2 的 HTTP 请求路由到 v2 版本,其余的流量都路由到 v1 版本。如果 v2 版本没有对应的节点,则将流量 fallback 至 v1 版本。 停止 v2 版本的 ProviderApplication 后,继续发送一条请求头 tag 为 v2 的 HTTP 请求kubectl apply -f - << EOFapiVersion: traffic.opensergo.io/v1alpha1kind: TrafficRoutermetadata:name: service-providernamespace: defaultlabels:app: service-providerspec:hosts:- service-providerhttp:- match:- headers:tag:exact: v2route:- destination:host: service-providersubset: v2fallback:host: service-providersubset: v1- route:- destination:host: service-providersubset: v1EOF
curl --location --request GET '127.0.0.1:18083/router-test' --header 'tag: v2'
Route in 30.221.132.228: 18081,version is v1.
上述详细示例代码可以在社区 Github 上 示例代码 [ 10] 中获取。
使用方式
1. 导入依赖并配置应用
首先,创建应用,推荐通过云原生应用脚手架进行项目构建,修改完配置即可便捷试用:云原生应用脚手架 :
https://start.aliyun.com/
在应用的 application.yml 配置文件中配置 Istio 相关元数据:<dependencyManagement><dependencies><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2.2.10-RC1</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-governance-auth</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-xds-adapter</artifactId></dependency></dependencies>
server:port: ${SERVER_PORT:80}spring:cloud:governance:auth:# 是否开启鉴权enabled: ${ISTIO_AUTH_ENABLE:true}istio:config:# 是否开启Istio配置转换enabled: ${ISTIO_CONFIG_ENABLE:true}# Istiod IPhost: ${ISTIOD_ADDR:127.0.0.1}# Istiod 端口port: ${ISTIOD_PORT:15010}# 轮询Istio线程池大小polling-pool-size: ${POLLING_POOL_SIZE:10}# 轮询Istio时间间隔polling-time: ${POLLING_TIMEOUT:10}# Istiod鉴权token(访问Istiod 15012端口时可用)istiod-token: ${ISTIOD_TOKEN:}# 是否打印xds相关日志log-xds: ${LOG_XDS:true}
在导入好以上的依赖并且在 application.yml 文件中配置了相关配置之后,可以将此应用程序运行起来,启动一个简单的 Spring Boot 应用,其中只含有一个简单的接口,此接口将会把本次请求的详细信息返回给客户端。 启动应用后,控制台上会打印出以下信息,说明此应用正在监听 Istio 控制面下发的配置:2. 运行应用程序
3. 通过 Istio 控制面下发鉴权配置
在使用如下命令通过 Istio 下发一条鉴权规则至 demo 应用,这条规则的限制了访问该应用的请求 header:kubectl apply -f - << EOFapiVersion: security.istio.io/v1beta1kind: AuthorizationPolicymetadata:name: http-headers-allownamespace: ${namespace_name}spec:selector:matchLabels:app: ${app_name}action: ALLOWrules:- when:- key: request.headers[User-Agent]values: ["PostmanRuntime/*"]EOF
由于此请求由于携带了正确的 HTTP Header 信息,将会返回:while true;do curl localhost/auth -H "User-Agent: PostmanRuntime/7.29.2";sleep 0.1;echo "";done;
由于此请求没有携带正确的 HTTP Header 信息,将会返回:while true;do curl localhost/auth;sleep 0.1;echo "";done;
上述详细示例代码可以在社区 Github 上 示例代码 [ 11] 中获取。
[2] Envoy https://www.envoyproxy.io/
[3] Istio https://github.com/istio/istio
[4] Istio 1.0版本 https://istio.io/latest/news/releases/1.0.x/announcing-1.0/
[5] 架构形式 https://istio.io/v1.12/blog/2021/proxyless-grpc/
[6] 2.2.10-RC 版本 https://github.com/alibaba/spring-cloud-alibaba/releases
[7] 安装工具 https://kubernetes.io/zh-cn/docs/tasks/tools/
[8] 安装小节 https://istio.io/latest/zh/docs/setup/install/
[9] OpenSergo 控制面安装文档 https://opensergo.io/zh-cn/docs/quick-start/opensergo-control-plane/
[11] 示例代码 https://github.com/alibaba/spring-cloud-alibaba/tree/2.2.x/spring-cloud-alibaba-examples/governance-example/authentication-example