# 基于Sermant+Istio的无代理服务网格
本文主要介绍基于Sermant+Istio无代理服务网格的概念、部署形态和具体能力。该能力基于Sermant框架层的xDS核心服务 (opens new window)实现。
# Istio下的Sermant服务治理
服务网格(Service Mesh)是处理服务间通信的基础设施层。它负责构成现代云原生应用程序的复杂服务拓扑来可靠地交付请求。在实践中,Service Mesh 通常以轻量级网络代理阵列的形式实现,这些代理与应用程序代码部署在一起,对应用程序来说无需感知代理的存在。
Istio是目前应用最广泛的Service Mesh产品,由控制平面和数据平面组成。Istio的数据平面一般会启动独立的Sidecar容器代理业务微服务的网络,因此会增加额外的性能损耗,包括CPU占用增加和服务调用时延的大幅增长,除此之外,系统的复杂性和运维的难度也在增加。
Sermant xDS服务使微服务可以在Kubenetes场景下接入Istio。Sermant基于xDS协议和Istio的控制平面直接进行通信,获取服务发现、路由、负载均衡等配置信息,从而可以替代Envoy作为Istio的数据平面完成服务治理能力。
Sermant是基于Java Agent的云原生无代理服务网格,业务微服务挂载Sermant同进程运行,无需启动额外的Sidecar容器进行网络代理,可以大幅度降低应用的性能损耗和服务之间的调用时延。
# 支持版本和限制
# 版本支持
Istio版本(已验证支持):1.6 - 1.23
xDS版本:v3
Kubenetes版本和Istio的版本适配请参考Istio版本支持 (opens new window)。
# 限制
Sermant是基于Java Agent的云原生无代理服务网格,仅支持Java语言。
# Istio+Sermant的Sidecar无代理模式部署形态
- 混合部署模式:Sidecar代理模式和Sidecar无代理模式共存。业务微服务通过混合部署模式接入Istio并实现服务治理能力,该模式可以避免修改已有的部署架构,仅新增服务使用Sidecar无代理模式。
- 无代理部署模式:所有的业务微服务均使用Sermant作为Istio的数据平面实现服务发现、路由、负载均衡等能力。Sermant可以替代Envoy提供的能力,当前已经支持了服务发现,未来功能将持续演进。
# Istio环境下使用Sermant的优势
- Sermant和业务微服务同进程运行,无需启动额外的Sidecar容器,大幅减少网络调用时延和CPU损耗。
- 基于Sermant框架开发插件可以实现比Envoy更丰富的治理能力,可扩展性更强。
- 更低的架构复杂度可以带来更低的部署成本。
# 基于xDS服务的服务发现能力
Kubenetes环境中,用户可以通过Deployment (opens new window)和Service (opens new window)自定义资源文件创建Pod和Service。Sermant框架层基于xDS协议实现了服务发现能力,插件可以调用xDS服务发现接口获取Kubenetes Service的服务实例,具体开发指导请参考基于xDS服务的服务发现开发指导。
# Kubenetes创建Pod和Service模版
Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-test
labels:
app: spring-test
spec:
replicas: 1
selector:
matchLabels:
app: spring-test
template:
metadata:
labels:
app: spring-test
spec:
containers:
- name: spring-test
image: spring-test:1.0.0
ports:
- containerPort: 8003
Service:
apiVersion: v1
kind: Service
metadata:
name: spring-test
spec:
type: ClusterIP
ports:
- name: test
port: 8003
targetPort: 8003
protocol: TCP
selector:
app: spring-test
说明:Sermant使用Service资源文件的
metadata.name
作为服务名称进行服务发现
# 支持xDS服务发现能力的Sermant插件
# 基于xDS服务的路由能力
Sermant框架层基于xDS协议实现了路由配置的获取能力,插件可以调用xDS路由配置服务接口获取Kubenetes Service的路由配置。具体开发指导请参考基于xDS服务的路由配置服务开发指导。
# Istio路由配置字段支持
Istio通过下发DestinationRule (opens new window)和VirtualService (opens new window) 自定义资源文件下发路由配置。Sermant基于xDS协议和Istio的控制平面协议进行通信获取路由配置,具体支持的路由配置字段如下所示:
VirtualService:
支持字段 | 描述 |
---|---|
spec.hosts | 路由的服务域名 |
spec.hosts.http | http路由配置 |
spec.hosts.http.match | http路由匹配规则 |
spec.hosts.http.match.headers | http路由header匹配规则,支持精确、前缀和正则匹配 |
spec.hosts.http.match.uri | http路由路径匹配规则,支持精确、前缀和正则匹配 |
spec.hosts.http.match.ignoreUriCase | http路由路径匹配忽略大小写 |
spec.hosts.http.route | http路由 |
spec.hosts.http.route.destination | http路由目标服务 |
spec.hosts.http.route.destination.host | http路由目标服务域名 |
spec.hosts.http.route.destination.subset | http路由目标服务子集 |
spec.hosts.http.route.destination.port.number | http路由目标服务端口 |
spec.hosts.http.route.weight | http路由目标权重 |
DestinationRule:
支持字段 | 描述 |
---|---|
spec.trafficPolicy | 流量策略 |
spec.trafficPolicy.loadBalancer | 负载均衡策略 |
spec.trafficPolicy.loadBalancer.localityLbSetting.enabled | 同AZ路由配置 |
# Istio路由配置模版
VirtualService:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: spring-test-virtualservice
spec:
hosts:
- spring-test
http:
- name: "v1-routes"
match:
- headers:
version:
exact: v1
uri:
exact: /test
ignoreUriCase: false
route:
- destination:
host: spring-test
subset: v1
port:
number: 8003
weight: 100
- name: "base-route"
route:
- destination:
host: spring-test
subset: base
port:
number: 8003
描述:对于访问服务名称为spring-test的上游服务,存在如下路由规则:
- http请求中存在version:v1的header,并且访问路径为/test,路由到spring-test的v1子集的8003端口
- 其他http请求路由到spring-test的base子集的8003端口
DestinationRule:
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: spring-test-destinationrule
spec:
host: spring-test.default.svc.cluster.local
subsets:
- name: v1
labels:
version: v1
trafficPolicy:
loadBalancer:
localityLbSetting:
enabled: true
subsets:
- name: base
labels:
version: base
trafficPolicy:
loadBalancer:
localityLbSetting:
enabled: false
描述:spring-test服务根据version标签划分为两个子集,即服务的两个Cluster集群。v1集群使用同AZ路由策略,v2集群不使用同AZ路由策略。
# 支持xDS路由配置能力的Sermant插件
# 基于xDS服务的负载均衡能力
Sermant框架层基于xDS协议实现了负载均衡配置的获取能力,插件可以调用xDS负载均衡配置服务接口获取Kubenetes Service的负载均衡配置。具体开发指导请参考基于xDS服务的负载均衡配置服务开发指导。
# Istio负载均衡配置字段支持
Istio通过下发DestinationRule (opens new window)自定义资源文件下发负载均衡配置。Sermant基于xDS协议和Istio的控制平面协议进行通信获取负载均衡配置,具体支持的负载均衡配置字段和负载均衡规则如下所示:
支持字段 | 描述 |
---|---|
spec.trafficPolicy | 流量策略 |
spec.trafficPolicy.loadBalancer | 负载均衡策略 |
spec.trafficPolicy.loadBalancer.simple | 简单负载均衡策略,支持ROUND_ROBIN、RANDOM、LEAST_REQUEST |
# Istio负载均衡配置模版
DestinationRule:
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: spring-test-destinationrule
spec:
host: spring-test.default.svc.cluster.local
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
subsets:
- name: v1
labels:
version: v1
trafficPolicy:
loadBalancer:
simple: RANDOM
subsets:
- name: base
labels:
version: base
描述:spring-test服务根据version标签划分为两个子集,即服务的两个Cluster集群。v1集群使用随机负载均衡策略,v2集群使用轮训负载均衡策略。
# 支持xDS负载均衡配置能力的Sermant插件
# 基于xDS服务的流控能力
Sermant框架层基于xDS协议实现了流控配置的获取能力,插件可以调用xDS流控服务接口获取Kubenetes Service的流控配置。具体开发指导请参考基于xDS服务的流控服务开发指导。
# Istio流控配置字段支持和模板
Istio流控配置包含熔断、重试、错误注入、限流四种配置。Istio可以通过下发DestinationRule (opens new window)自定义资源文件来下发熔断配置,通过下发VirtualService (opens new window)自定义资源文件来下发重试配置和错误注入配置,通过下发EnvoyFilter (opens new window) 自定义资源文件下发限流配置。Sermant基于xDS协议和Istio的控制平面协议进行通信获取流控配置,具体支持的流控配置字段如下所示:
# 熔断配置
- 熔断配置支持的字段:
支持字段 | 描述 |
---|---|
spec.trafficPolicy | 流量策略 |
spec.trafficPolicy.connectionPool | 连接池配置 |
spec.trafficPolicy.connectionPool.http | http的连接池配置 |
spec.trafficPolicy.connectionPool.http.http2MaxRequests | 最大活跃请求数,活跃请求数超过阈值则触发熔断 |
spec.trafficPolicy.outlierDetection | 实例熔断策略,实例失败次数达到阈值则会触发熔断而被驱逐 |
spec.trafficPolicy.outlierDetection.splitExternalLocalOriginErrors | 是否区分本地来源错误和外部错误,设置为true,将使用consecutiveLocalOriginFailures来检测实例的失败次数是否达到阈值 |
spec.trafficPolicy.outlierDetection.consecutive5xxErrors | 实例被熔断之前可以发生的5xx错误次数,连接超时、连接错误/失败和请求失败均被视为 5xx 错误 |
spec.trafficPolicy.outlierDetection.consecutiveLocalOriginFailures | 实例被熔断之前可以发生的本地来源错误次数 |
spec.trafficPolicy.outlierDetection.consecutiveGatewayErrors | 实例被熔断之前可以发生的网关错误次数,响应码为502、503、504时视为网关错误 |
spec.trafficPolicy.outlierDetection.interval | 检测的时间间隔,在时间间隔内错误次数达到阈值则会触发实例熔断 |
spec.trafficPolicy.outlierDetection.baseEjectionTime | 实例的最小熔断时间,实例保持熔断状态的时间等于熔断次数*最小熔断时间 |
spec.trafficPolicy.outlierDetection.maxEjectionPercent | 驱逐的实例占可选实例的最大百分比 |
spec.trafficPolicy.outlierDetection.minHealthPercent | 至少有minHealthPercent的实例处于健康状态,才会进行驱逐 |
注意:发送字节给服务端前发生的错误视为本地来源错误,发送字节给服务端后发生的错误视为外部错误
- 熔断配置的模板(DestinationRule):
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: spring-test-destinationrule
spec:
host: spring-test.default.svc.cluster.local
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
subsets:
- name: v1
labels:
version: 1.0.1
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
localityLbSetting:
enabled: true
connectionPool:
http:
http2MaxRequests: 1000
outlierDetection:
consecutive5xxErrors: 8
splitExternalLocalOriginErrors: true
consecutiveLocalOriginFailures: 7
consecutiveGatewayErrors: 9
interval: 5m
baseEjectionTime: 15m
maxEjectionPercent: 15
minHealthPercent: 10
描述:spring-test服务根据version标签划为Cluster集群。对于v1集群有以下流控规则:
- 客户端调用v1集群时支持的最大活跃请求数为1000,活跃实例数超过1000时客户端会直接返回失败,不会调用服务端,不同的客户端实例不会相互响应。
- 客户端调用v1集群进行负载均衡时,如果可选实例中存在满足5分钟内本地来源错误次数达到7、5xx错误次数达到8、网关错误次数达到9任一条件的实例,则标记为熔断实例,当可选的实例中标记为熔断的实例数量小于10%则驱逐熔断的实例,驱逐的实例将不会被调用。如果驱逐熔断实例之后剩余实例数小于15%则返回全部实例。熔断的实例会在熔断时间结束之后取消熔断标记。
# 重试配置
- 重试配置支持的字段:
支持字段 | 描述 |
---|---|
spec.retries | 重试策略配置 |
spec.retries.attempts | 允许的最大重试次数 |
spec.retries.perTryTimeout | 重试的时间间隔 |
spec.retries.retryOn | 重试条件,支持5xx、gateway-error、connect-failure、retriable-4xx、 retriable-status-codes、retriable-headers |
- 重试配置的模板(VirtualService):
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: spring-test-virtualservice
spec:
hosts:
- spring-test
http:
- name: "base-route"
match:
- uri:
exact: /test
route:
- destination:
host: spring-test
port:
number: 8003
retries:
attempts: 4
perTryTimeout: 2s
retryOn: "gateway-error"
描述:对于访问服务名称为spring-test的上游服务且访问路径为/test的请求,存在如下流控规则:
- 如果上游服务返回的响应状态码为502、503、504的其中一种,则客户端会进行重试,最多重试4次,每次重试间隔2秒。
# 错误注入配置
- 错误注入配置支持的字段:
支持字段 | 描述 |
---|---|
spec.fault | 错误注入配置 |
spec.fault.delay | 请求延时配置 |
spec.fault.delay.percentage | 请求延时的触发概率 |
spec.fault.delay.fixedDelay | 延迟时间 |
spec.fault.abort | 请求终止配置 |
spec.fault.abort.httpStatus | 请求终止时返回错误响应的响应码 |
spec.fault.abort.percentage | 请求终止的触发概率 |
- 重试和错误注入支持的模板(VirtualService):
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: spring-test-virtualservice
spec:
hosts:
- spring-test
http:
- name: "base-route"
match:
- uri:
exact: /test
route:
- destination:
host: spring-test
port:
number: 8003
fault:
delay:
percentage:
value: 20
fixedDelay: 5s
abort:
percentage:
value: 10
httpStatus: 400
描述:对于访问服务名称为spring-test的上游服务且访问路径为/test的请求,存在如下流控规则:
- 请求会有10%的概率触发请求中止,触发请求中止时客户端会直接返回状态码为400的响应。
- 请求会有20%的概率触发请求延时,触发请求延时客户端会延时5秒再调用上游服务。
# 限流配置
- 限流配置支持的字段:
支持字段 | 描述 |
---|---|
spec.configPatches.applyTo | 应用位置,支持HTTP_ROUTE:应用于路由配置中指定的虚拟主机内的路由对象 |
spec.configPatches.match | 匹配条件 |
spec.configPatches.match.routeConfiguration | 路由配置,通过路由配置进行匹配,匹配成功后应用补丁 |
spec.configPatches.match.routeConfiguration.vhost | 路由配置中的主机信息 |
spec.configPatches.match.routeConfiguration.vhost.name | 路由配置中的主机名称,服务的全限定名称+端口,如:spring-test.default.svc.cluster.local:8003 |
spec.configPatches.match.routeConfiguration.vhost.route | 路由信息 |
spec.configPatches.match.routeConfiguration.vhost.route.name | 路由名称 (opens new window),对应VirtualService中的spec.http.name |
spec.configPatches.patch.value.typed_per_filter_config.envoy.filters.http.local_ratelimit | 限流配置 |
spec.configPatches.patch.value.typed_per_filter_config.envoy.filters.http.local_ratelimit.value | 限流配置的具体配置信息 |
spec.configPatches.patch.value.typed_per_filter_config.envoy.filters.http.local_ratelimit.token_bucket | 限流配置的令牌桶信息 |
spec.configPatches.patch.value.typed_per_filter_config.envoy.filters.http.local_ratelimit.token_bucket.max_tokens | 最大令牌数量 |
spec.configPatches.patch.value.typed_per_filter_config.envoy.filters.http.local_ratelimit.token_bucket.tokens_per_fill | 每次填充的令牌数量 |
spec.configPatches.patch.value.typed_per_filter_config.envoy.filters.http.local_ratelimit.filter_enabled | 限流配置的启用配置 |
spec.configPatches.patch.value.typed_per_filter_config.envoy.filters.http.local_ratelimit.filter_enabled.default_value | 请求触发限流配置的概率 |
spec.configPatches.patch.value.typed_per_filter_config.envoy.filters.http.local_ratelimit.filter_enabled.default_value.numerator | 触发概率的分子 |
spec.configPatches.patch.value.typed_per_filter_config.envoy.filters.http.local_ratelimit.filter_enabled.default_value.denominator | 触发概率的分母 |
spec.configPatches.patch.value.typed_per_filter_config.envoy.filters.http.local_ratelimit.response_headers_to_add | 触发限流时响应信息需要添加的响应头 |
注意:
- 如果只配路由名称,限流规则会对所有服务实例下路由名称匹配的路由生效。
- 如果只匹配主机名称,则限流规则会对主机下所有的路由生效。
- 如果路由名称和主机名称都配置,限流规则只会对指定服务实例(名称和端口必须全部匹配)下路由名称匹配的路由生效。
- 限流配置支持的模板(EnvoyFilter):
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: filter-local-ratelimit-svc
namespace: istio-system
spec:
configPatches:
- applyTo: HTTP_ROUTE
match:
routeConfiguration:
vhost:
name: spring-test.default.svc.cluster.local:8003
route:
name: base-route
patch:
operation: MERGE
value:
typed_per_filter_config:
envoy.filters.http.local_ratelimit:
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
type_url: type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
value:
token_bucket:
max_tokens: 2
tokens_per_fill: 2
fill_interval: 90s
filter_enabled:
default_value:
numerator: 50
denominator: HUNDRED
response_headers_to_add:
header:
key: x-local-rate-limit
value: 'true'
描述:对于调用服务spring-test且路由名称为base-route的请求,会触发以下流控规则:
- 每个触发服务端限流规则的请求有50%的概率消耗令牌。基于令牌桶限流策略,令牌桶的最大容量为2,每90秒会重新填充2个令牌,因此每90秒,最多允许2个消耗令牌的请求通过限流规则。当请求被限流时,服务端会在响应头中添加 x-local-rate-limit: true。
- 限流规则对同一服务的不同实例单独生效,不会相互影响。
# 支持xDS流控能力的Sermant插件
# 启动和结果验证
# 基于xds服务的路由示例
本教程使用Sermant-examples (opens new window)仓库中的xds-demo演示Sermant 基于xDS服务的路由能力。本Demo中包括spring-client微服务、spring-server微服务。spring-client微服务挂载Sermant的路由插件启动,并开启基于xDS的路由能力,Sermant路由插件在spring-client调用上游服务时,根据上游服务的路由规则进行路由,并选择符合规则的服务实例进行调用。
# 1 准备工作
- 下载 (opens new window) Demo二进制产物压缩包
- 下载 (opens new window) Sermant二进制产物压缩包
- 准备 (opens new window) Kubenetes环境
- 安装Istio (opens new window)并启动
# 2 获取Demo二进制产物
解压Demo二进制产物压缩包,即可得到product/
目录文件。
# 3 获取和移动Sermant二进制产物
解压Sermant二进制产物压缩包,即可得到sermant-agent/
目录文件。
执行如下命令,将Sermant二进制产物移动至spring-client目录,用于打包spring-client镜像:
cp -r ${sermant-path}/sermant-agent/agent ${demo-path}/product/spring-client
说明:${sermant-path}为Sermant二进制产物所在路径,${demo-path}为Demo二进制产物所在路径。
# 4 启动spring-server
进入product/spring-server目录:
执行以下命令打包spring-server镜像:
sh build-server.sh
执行以下命令运行spring-server Pod和Service
kubectl apply -f ../script/spring-server.yaml
# 5 启动spring-client
进入product/spring-client目录:
执行以下命令打包spring-client镜像:
sh build-client.sh
执行以下命令运行spring-client Pod和Service
kubectl apply -f ../script/spring-client.yaml
# 6 下发路由规则
进入product/script目录,执行如下命令下发路由规则:
kubectl apply -f spring-server-destination.yaml
kubectl apply -f spring-server-virtureservice.yaml
路由规则说明:
DestinationRule: 根据Deployment的version标签将Pod划分为v1和v2两个子集, spring-server集群使用ROUND_ROBIN负载均衡规则。
VirtualService: 对于访问spring-server服务的http请求,如果存在version:v1的header,并且请求路径为/router,则将请求路由到spring-server的v1子集。
# 7 验证
通过网页访问spring-client微服务,入参host设置为spring-server,version为v1,验证spring-client服务是否能成功调用v1版本的上游服务spring-server:
http://127.0.0.1:30110/router/httpClient?host=spring-server&version=v1
网页收到如下显示,说明spring-client成功调用了v1版本的spring-server服务
spring-server version: v1
# 基于xds服务的流控示例
本教程使用Sermant-examples (opens new window)仓库中的xds-demo演示Sermant基于xDS服务的错误注入能力。本Demo中包括spring-client微服务、spring-server微服务。spring-client微服务挂载Sermant的流控插件启动,并开启基于xDS的流控能力,Sermant流控插件在spring-client调用上游服务时,根据上游服务的错误注入规则进行请求中止。
# 1 准备工作
- 下载 (opens new window) Demo二进制产物压缩包
- 下载 (opens new window) Sermant二进制产物压缩包
- 准备 (opens new window) Kubenetes环境
- 安装Istio (opens new window)并启动
# 2 获取Demo二进制产物
解压Demo二进制产物压缩包,即可得到product/
目录文件。
# 3 获取和移动Sermant二进制产物
解压Sermant二进制产物压缩包,即可得到sermant-agent/
目录文件。
执行如下命令,将Sermant二进制产物移动至spring-client目录,用于打包spring-client镜像:
cp -r ${sermant-path}/sermant-agent/agent ${demo-path}/product/spring-client
说明:${sermant-path}为Sermant二进制产物所在路径,${demo-path}为Demo二进制产物所在路径。
# 4 启动spring-server
进入product/spring-server目录:
执行以下命令打包spring-server镜像:
sh build-server.sh
执行以下命令运行spring-server Pod和Service
kubectl apply -f ../script/spring-server.yaml
# 5 启动spring-client
进入product/spring-client目录:
执行以下命令打包spring-client镜像:
sh build-client.sh
执行以下命令运行spring-client Pod和Service
kubectl apply -f ../script/spring-client-flowcontrol.yaml
# 6 下发流控规则
进入product/script目录,执行如下命令下发错误注入的请求中止规则:
kubectl apply -f spring-server-destination.yaml
kubectl apply -f spring-server-virtureservice-flowcontrol.yaml
规则说明:
DestinationRule: 根据Deployment的version标签将Pod划分为v1和v2两个子集, spring-server集群使用ROUND_ROBIN负载均衡规则。
VirtualService: 对于访问spring-server服务的http请求,如果存在version:v1的header,并且请求路径为/router,则将请求路由到spring-server的v1子集,对于请求到v1子集的请求100%触发请求中止
# 7 验证
通过网页访问spring-client微服务,入参host设置为spring-server,version为v1, 调用验证spring-client服务是否触发请求中止:
http://127.0.0.1:30110/router/httpClient?host=spring-server&version=v1
网页收到如下显示,说明spring-client触发了请求中止
The request has been aborted due to triggering fault injection
← 动态配置中心使用手册 插件介绍 →