# 基于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无代理模式部署形态

  1. 混合部署模式:Sidecar代理模式和Sidecar无代理模式共存。业务微服务通过混合部署模式接入Istio并实现服务治理能力,该模式可以避免修改已有的部署架构,仅新增服务使用Sidecar无代理模式。
  2. 无代理部署模式:所有的业务微服务均使用Sermant作为Istio的数据平面实现服务发现、路由、负载均衡等能力。Sermant可以替代Envoy提供的能力,当前已经支持了服务发现,未来功能将持续演进。

# Istio环境下使用Sermant的优势

  1. Sermant和业务微服务同进程运行,无需启动额外的Sidecar容器,大幅减少网络调用时延和CPU损耗。
  2. 基于Sermant框架开发插件可以实现比Envoy更丰富的治理能力,可扩展性更强。
  3. 更低的架构复杂度可以带来更低的部署成本。

# 基于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的上游服务,存在如下路由规则:

  1. http请求中存在version:v1的header,并且访问路径为/test,路由到spring-test的v1子集的8003端口
  2. 其他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集群有以下流控规则:

  1. 客户端调用v1集群时支持的最大活跃请求数为1000,活跃实例数超过1000时客户端会直接返回失败,不会调用服务端,不同的客户端实例不会相互响应。
  2. 客户端调用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的请求,存在如下流控规则:

  1. 如果上游服务返回的响应状态码为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的请求,存在如下流控规则:

  1. 请求会有10%的概率触发请求中止,触发请求中止时客户端会直接返回状态码为400的响应。
  2. 请求会有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 触发限流时响应信息需要添加的响应头

注意:

  1. 如果只配路由名称,限流规则会对所有服务实例下路由名称匹配的路由生效。
  2. 如果只匹配主机名称,则限流规则会对主机下所有的路由生效。
  3. 如果路由名称和主机名称都配置,限流规则只会对指定服务实例(名称和端口必须全部匹配)下路由名称匹配的路由生效。
  • 限流配置支持的模板(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的请求,会触发以下流控规则:

  1. 每个触发服务端限流规则的请求有50%的概率消耗令牌。基于令牌桶限流策略,令牌桶的最大容量为2,每90秒会重新填充2个令牌,因此每90秒,最多允许2个消耗令牌的请求通过限流规则。当请求被限流时,服务端会在响应头中添加 x-local-rate-limit: true。
  2. 限流规则对同一服务的不同实例单独生效,不会相互影响。

# 支持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 准备工作

# 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目录:

  1. 执行以下命令打包spring-server镜像:

    sh build-server.sh
    
  2. 执行以下命令运行spring-server Pod和Service

    kubectl apply -f ../script/spring-server.yaml
    

# 5 启动spring-client

进入product/spring-client目录:

  1. 执行以下命令打包spring-client镜像:

    sh build-client.sh
    
  2. 执行以下命令运行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 准备工作

# 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目录:

  1. 执行以下命令打包spring-server镜像:

    sh build-server.sh
    
  2. 执行以下命令运行spring-server Pod和Service

    kubectl apply -f ../script/spring-server.yaml
    

# 5 启动spring-client

进入product/spring-client目录:

  1. 执行以下命令打包spring-client镜像:

    sh build-client.sh
    
  2. 执行以下命令运行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
上次更新: 2025/5/30 08:10:02