引言

在云原生应用负载均衡系列第一篇文章《云原生应用负载均衡选型指南》介绍了云原生容器环境的入口流量管理使用场景与解决方案,用 Envoy 作为数据面代理,搭配 Istio 作控制面的 Istio Ingress Gateway,在多集群流量分发、安全、可观测性、异构平台支持等方面的综合对比中,是云原生应用流量管理的最佳方案。

在接入层,我们需要配置路由规则、流量行为(超时、重定向、重写、重试等)、负载均衡算法、断路器、跨域等流量管理规则,本文将基于 Istio Ingress Gateway 面向入口流量分发、容错与高可用调度介绍上述功能的原理与演示。

组件介绍

Istio Ingress Gateway,可作为 Kubernetes 的一种 Ingress Controller 12,由数据面(Envoy 网络代理 1)与控制面 Istiod 13组成,默认以 Deployment 的形式部署 Pod 至 Kubernetes 集群。

服务发现

Istio Ingress Gateway 控制面 Istiod 可联通各种服务发现系统(Kubernetes,Consul,DNS)获取 endpoints 信息,或者我们可以使用 ServiceEntry 手动添加服务与 endpoints 对应信息。以常见的 Kubernentes 集群为例,Istiod 从 API Server 获取 Kubernetes Services 及其对应的 endpoints,对应关系实时监测与自动更新。获取服务发现信息后,Istiod 会将其转化为数据面标准数据格式,以 Envoy xDS API 的形式 push 到实际执行流量转发操作的数据面 Envoy 网络代理。

值得注意的是,Istio Ingress Gateway 的数据面 Envoy 是以单独 Pods 的形式部署于 Kubernetes 集群,只需使用 Istio 南北向流量管理能力时,无需在业务 Pods 注入 Istio 数据面 sidecar,Ingress Gateway 的 Envoy Pods 即可承载全部入口流量管理的能力,因为 Istio 入口流量管理的功能大部分是在 Envoy 网络代理的 client 端(Istio Ingress Gateway)实现的。

云原生应用负载均衡系列 (2): 入口流量分发、容错与高可用调度-LMLPHP

Istio 流量管理模型及 API 介绍

Istio 设计了自己的流量管理 API,主要通过 Gateway,VirtualService,DestinationRule 这几个 CR(Kubernetes Custom Resource 2)实现。

  • Gateway:配置监听规则。包括端口、协议、host、SSL 等。
  • VirtualService:配置路由规则。包括匹配条件、流量行为、路由目的服务/版本等。
  • DestinationRule:配置服务版本负载均衡连接池健康检查策略

云原生应用负载均衡系列 (2): 入口流量分发、容错与高可用调度-LMLPHP

我们可以与安装 Istio 控制面所在集群的 API Server 交互,提交上述 CR,配置流量管理规则。Istiod 会与该集群的 API Server 交互,获取 Istio API,将这些配置转化为 Envoy 数据面标准数据格式,通过 xDS 接口 push 至数据面(Istio Ingress Gateway),数据面即可根据相应规则转发流量。

入口流量管理实践

下面以 Istio Ingress Gateway 为例介绍入口流量分发、容错与高可用调度的实践。

环境准备

我们准备一个 Istio Ingress Gateway(使用腾讯云服务网格 TCM 演示) + Kubernetes 集群(使用腾讯云容器服务 TKE 演示)环境,首先创建一个服务网格实例,并在网格实例中创建 Istio Ingress Gateway,然后添加一个 TKE 集群作为网格的服务发现集群。

在集群部署 TCM demo 3,无需为业务 Pod 注入 envoy sidecar。该 demo 是一个电商网站,由不同语言开发的 6 个微服务组成,以下是 demo 的完整结构:

云原生应用负载均衡系列 (2): 入口流量分发、容错与高可用调度-LMLPHP

本次演示入口流量管理会使用 demo 中的 user、product、cart 三个应用,将其提供的 API 通过 istio- ingressgateway 暴露供客户端调用。

云原生应用负载均衡系列 (2): 入口流量分发、容错与高可用调度-LMLPHP

入口流量分发

应用发布

TCM demo 中,product 服务提供 /product 接口,可获取在售商品列表;user 服务提供 /user 接口,可获取用户信息;cart 服务提供 /cart 接口,可获取购物车商品列表。下面我们配置 Istio Ingress Gateway 按照请求的路径暴露上述接口。

1. 首先我们通过 kubectl 获取 Ingress Gateway 的 IP,并配置为变量 $INGRESS_HOST,方便后续直接引用。

$ export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')

2. 使用 Gateway 配置 Ingress Gateway 监听规则,开启 80 端口,HTTP 协议,暂不配置 SSL。

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: apis-gw
spec:
  servers:
    - port:
        number: 80
        name: HTTP-80-6wsk
        protocol: HTTP
      hosts:
        - '*'
  selector:
    app: istio-ingressgateway
    istio: ingressgateway

3. 使用 VirtualService 配置路由规则,gateway 参数填写上一步创建的 default/apis-gw,http 路由匹配规则将 /product 的请求路由至 product 服务,/user 路由至 user 服务,/cart 路由至 cart 服务。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: apis-vs
spec:
  hosts:
    - '*'
  gateways:
    - default/apis-gw
  http:
    - match:
        - uri:
            exact: /product
      route:
        - destination:
            host: product.base.svc.cluster.local
    - match:
        - uri:
            exact: /user
      route:
        - destination:
            host: user.base.svc.cluster.local
    - match:
        - uri:
            exact: /cart
      route:
        - destination:
            host: cart.base.svc.cluster.local

4. 使用 curl 验证上述配置,请求 API 返回的 JSON 字串使用 jq 解析,提取出返回的 service 信息。请求已按照预设方式按路径路由至不同服务。

$ curl http://$INGRESS_HOST/product | jq '.info[0].Service'
...
"product-v1"
$ curl http://$INGRESS_HOST/cart | jq '.Info[1].Service'
...
"cart-v1"
$ curl http://$INGRESS_HOST/user | jq '.Info[0].Service'
...
"user-v1"

灰度发布

以下是配置 Ingress Gateway 做 product 服务灰度发布的流程:

1. 使用 DestinationRule 定义 product 服务的版本(subsets),用标签键值匹配即可定义一个服务内 subsets 与 endpoints 对应关系。

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: product
  namespace: base
spec:
  host: product
  subsets:
    - name: v1
      labels:
        version: v1
    - name: v2
      labels:
        version: v2
  exportTo:
    - '*'

2. 使用 VirtualService 配置灰度发布路由策略,发布最初 v2 版本为 0% 的流量,v1 版本 100%。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: apis-vs
spec:
  hosts:
    - '*'
  gateways:
    - default/apis-gw
  http:
    - match:
        - uri:
            exact: /product
      route:
        - destination:
            host: product.base.svc.cluster.local
            subset: v2
          weight: 0
        - destination:
            host: product.base.svc.cluster.local
            subset: v1
          weight: 100
    - match:
        - uri:
            exact: /user
      route:
        - destination:
            host: user.base.svc.cluster.local
    - match:
        - uri:
            exact: /cart
      route:
        - destination:
            host: cart.base.svc.cluster.local

3. 我们把 TCM demo product 服务的 v2 版本(deployment)5 部署到集群,然后模拟发起 10 次请求调用 product 服务,返回结果表明发布最初所有流量仍然是路由到 v1 版本。

$ for((i=0;i<10;i++)) do curl http://$INGRESS_HOST/product | jq '.info[0].Service'; done
"product-v1"
"product-v1"
"product-v1"
"product-v1"
"product-v1"
"product-v1"
"product-v1"
"product-v1"
"product-v1"
"product-v1"

4. 修改 VirtualService,把 product v1 和 v2 subset 的权重值分别调整为:50,50。模拟发起 10 次请求调用 product 服务,结果如预设,v2 和 v1 版本调用次数的比例接近 1:1。

$ for((i=0;i<10;i++)) do curl http://$INGRESS_HOST/product | jq '.info[0].Service'; done
"product-v1"
"product-v2"
"product-v1"
"product-v2"
"product-v1"
"product-v2"
"product-v1"
"product-v2"
"product-v1"
"product-v1"

5. 灰度验证完成后,修改灰度发布路由规则,修改 VirtualService 调整 v1 v2 subset 权重分别为:0,100,将 100% 请求 /product 的流量路由至 product v2 版本。再次模拟发起 10 次请求验证,所有请求已被路由至 product v2,灰度发布完成。

$ for((i=0;i<10;i++)) do curl http://$INGRESS_HOST/product | jq '.info[0].Service'; done
"product-v2"
"product-v2"
"product-v2"
"product-v2"
"product-v2"
"product-v2"
"product-v2"
"product-v2"
"product-v2"
"product-v2"

会话保持

Istio Ingress Gateway 可设置基于 IP、cookie、HTTP header 的会话保持,但该策略只对 HTTP 流量生效。下面将配置 cart 服务的 IP、cookie 两种会话保持负载均衡策略。

如使用开源 Istio Ingress Gateway + CLB,配置边缘网关流量管理规则存在如下问题:

  • 手动修改关联 Service 规则:使用 Gateway 配置了 Ingress Gateway 的端口后,需要手动配置 Ingress Gateway 关联的 LoadBalancer Service 的端口规则。
  • 无法获取真实源 IP:使用默认容器网络模式时,istio-ingressgateway 通过 loadbalancer service 的方式暴露给公网访问,流量经由 CLB 到节点的 NodePort 后,kube-proxy 会将原始请求做 SNAT 和 DNAT,因此请求到达 istio-ingressgateway 时,源 IP 已不是真实 client IP。

使用 TCM Ingress Gateway + TKE 集群则可避免上述问题:

  • TCM 已实现 Gateway 端口配置自动化同步至 Ingress Gateway 相关 Kubernetes Service 及关联 CLB,我们使用 Gateway CR 即可一致化管理 Ingress Gateway 实例的端口。
  • 可以通过指定 istio-ingressgateway service 的 externalTrafficPolicy: Local 来避免流量通过 NAT 在节点之间转发,保留了真实 client IP。同时我们可以通过添加注解 service.kubernetes.io/local-svc-only-bind-node-with-pod: "true" 来指定 CLB 后端只绑定有 istio-ingressgateway Pod 的节点,避免因后端绑定了不存在 Pod 实例的节点导致健康检查失败的问题。也可以增加注解 service.cloud.tencent.com/local-svc-weighted-balance: "true" 让 CLB 根据后端节点上的 Pod 数量做加权负载均衡,避免因不同节点 Pod 实例数量不一导致的负载不均问题 6

基于 IP 的会话保持

1. Ingress Gateway 可获取真实 client IP 后,我们通过 DestinationRule 配置 cart 服务的基于 IP 的简单负载均衡:

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: cart
  namespace: base
spec:
  host: cart
  trafficPolicy:
    loadBalancer:
      consistentHash:
        useSourceIp: true
  exportTo:
    - '*'

2. 获取 cart 服务当前的 pods,一共部署了 3 个 pods。

$ kubectl get deployment cart -n base
NAME   READY   UP-TO-DATE   AVAILABLE   AGE
cart   3/3     3            3           4d23h

3. 模拟发起 10 次请求 /cart 验证,获取提供购物车服务的 pod 信息,所有请求均被路由到了同一个 pod,基于 IP 负载均衡配置成功。

$ for((i=0;i<10;i++)) do curl http://$INGRESS_HOST/cart | jq '.Info[1].Pod'; done
"cart-855f9d75ff-x47bq"
"cart-855f9d75ff-x47bq"
"cart-855f9d75ff-x47bq"
"cart-855f9d75ff-x47bq"
"cart-855f9d75ff-x47bq"
"cart-855f9d75ff-x47bq"
"cart-855f9d75ff-x47bq"
"cart-855f9d75ff-x47bq"
"cart-855f9d75ff-x47bq"
"cart-855f9d75ff-x47bq"

基于 cookie 的会话保持

1. 修改 cart 服务的 DestinationRule,配置基于 cookie 的负载均衡。cookie name 为 cookie,cookie 过期时间为 900000 ms(900 s)。

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: cart
  namespace: base
spec:
  host: cart
  trafficPolicy:
    loadBalancer:
      consistentHash:
        httpCookie:
          name: cookie
          ttl: 900000ms
  exportTo:
    - '*'

2. 发起第一次 /cart 请求,获取 Ingress Gateway 返回的 cookie ID 及 pod 信息,使用该 cookie ID 模拟发起 10 次 /cart 请求,第一次请求和后续 10 次请求,流量均被路由到了同一个 pod(在本例中是 cart-855f9d75ff-dqg6b),基于 cookie 的负载均衡配置生效。

$ curl http://$INGRESS_HOST/cart -i
...
set-cookie: cookie="bc0e96c66ff8994b"; Max-Age=900; HttpOnly
...
Pod":"cart-855f9d75ff-dqg6b"
...
$ for((i=0;i<10;i++)) do curl http://$INGRESS_HOST/cart -b 'cookie=bc0e96c66ff8994b' | jq '.Info[1].Pod'; done
"cart-855f9d75ff-dqg6b"
"cart-855f9d75ff-dqg6b"
"cart-855f9d75ff-dqg6b"
"cart-855f9d75ff-dqg6b"
"cart-855f9d75ff-dqg6b"
"cart-855f9d75ff-dqg6b"
"cart-855f9d75ff-dqg6b"
"cart-855f9d75ff-dqg6b"
"cart-855f9d75ff-dqg6b"
"cart-855f9d75ff-dqg6b"

容错

连接池管理

下面我们配置 user 服务的连接池:

1. 首先我们部署一组 curl pods (30 个)模拟向 user 服务发起并发请求,受各 pod 运行环境影响,实际并发量应该 < 30。

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    k8s-app: curl
    qcloud-app: curl
  name: curl
  namespace: default
spec:
  replicas: 30
  selector:
    matchLabels:
      k8s-app: curl
      qcloud-app: curl
  template:
    metadata:
      labels:
        k8s-app: curl
        qcloud-app: curl
    spec:
      containers:
      - args:
        - -c
        - while true; do curl http://$INGRESS_HOST/user; done
        command:
        - /bin/sh
        image: docker.io/byrnedo/alpine-curl
        imagePullPolicy: IfNotPresent
        name: curl

2. 使用 DestinationRule 配置 user 服务的连接池,为方便观察效果,我们配置 http1 的最大请求等待数为 1,http2 的最大请求数为 1。

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: user
  namespace: base
spec:
  host: user
  trafficPolicy:
    connectionPool:
      http:
        http1MaxPendingRequests: 1
        http2MaxRequests: 1
  exportTo:
    - '*'

3. 在 Grafana Dashboard 查看 user 服务的监控,可以看到配置了连接池后,因大部分请求被返回 503 Service Unavailable 状态码,因此客户端请求成功率骤降,user 服务超载配置成功,DestinationRule 连接池配置起到了很好的保护服务端稳定性的作用。
云原生应用负载均衡系列 (2): 入口流量分发、容错与高可用调度-LMLPHP

健康检查

Ingress Gateway(envoy)的 Outlier Detection 是一种被动健康检查,当流量出现了类似连续 5xx 错误(HTTP)、连接超时/失败(TCP)等行为时,将其识别为离群值从负载均衡池中剔除一段时间 8。下面我们配置 user 服务的健康检查策略。

1. 首先我们部署一组会为请求 /user 返回 503 错误的 pods 作为 user 服务的不健康 endpoints,部署完成后查看 user 服务的 endpoint 情况,有 1 个健康 user pod,1 个不健康 user pod。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-unhealthy
  namespace: base
spec:
  replicas: 1
  selector:
    matchLabels:
      app: user
  template:
    metadata:
      labels:
        app: user
    spec:
      containers:
      - command:
        - sleep
        - "9000"
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name
        - name: REGION
          value: shanghai-zone1
        image: docker.io/busybox
        imagePullPolicy: IfNotPresent
        name: user
        ports:
        - containerPort: 7000
$ kubectl get deployment -n base user user-unhealthy
NAME             READY   UP-TO-DATE   AVAILABLE   AGE
user             1/1     1            1           6d19h
user-unhealthy   1/1     1            1           3d20h

2. 当前还未配置 user 服务的 Outlier Detection(被动健康检查),不健康的 endpoint 不会被从负载均衡池中剔除,因此发起的请求部分成功(200 OK),部分失败(503 Service Unavailable)。

$ for((i=0;i<10;i++)) do curl -I http://$INGRESS_HOST/user | grep HTTP/1.1; done
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 503 Service Unavailable
HTTP/1.1 200 OK
HTTP/1.1 503 Service Unavailable
HTTP/1.1 200 OK
HTTP/1.1 503 Service Unavailable
HTTP/1.1 200 OK
HTTP/1.1 503 Service Unavailable
HTTP/1.1 503 Service Unavailable

3. 我们配置 user 服务的 DestinationRule,设置 Outlier Detection。间隔 10 秒做一次统计,从负载均衡池中剔除连续错误数为 3 以上的 endpoint 30 秒,允许最大剔除比例为 100%,最小健康比例为 0%。完成配置后我们模拟请求 user 服务,所有请求均返回 200 OK。(被动健康检查,需请求返回连续错误后才会剔除不健康 endpoint)

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: user
  namespace: base
spec:
  host: user
  trafficPolicy:
    outlierDetection:
      consecutiveErrors: 3
      interval: 10000ms
      baseEjectionTime: 30000ms
      maxEjectionPercent: 100
      minHealthPercent: 0
  exportTo:
    - '*'
$ for((i=0;i<10;i++)) do curl -I http://$INGRESS_HOST/user | grep HTTP/1.1; doneHTTP/1.1 200 OKHTTP/1.1 200 OKHTTP/1.1 200 OKHTTP/1.1 200 OKHTTP/1.1 200 OKHTTP/1.1 200 OKHTTP/1.1 200 OKHTTP/1.1 200 OKHTTP/1.1 200 OKHTTP/1.1 200 OK

重定向

下面我们配置强制使用 HTTPS 协议的重定向。

1. 用 Gateway 配置 HTTP 重定向,强制使用 HTTPS 协议。

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: apis-gw
spec:
  servers:
    - port:
        number: 80
        name: HTTP-80-h7pv
        protocol: HTTP
      hosts:
        - '*'
      tls:
        httpsRedirect: true
    - port:
        number: 443
        name: HTTPS-443-p1ph
        protocol: HTTPS
      hosts:
        - '*'
      tls:
        mode: SIMPLE
        credentialName: qcloud-$CERTIFICATE_ID
  selector:
    app: istio-ingressgateway
    istio: ingressgateway

2. 使用 HTTP 请求 /user,返回 301 重定向状态码。如果是在浏览器访问,收到重定向返回时,会重新发起新请求到新的 URI。

$ curl http://$INGRESS_HOST/user -I | grep HTTP/1.1
HTTP/1.1 301 Moved Permanently

重写

下面我们配置 /cart 的请求重写为 /clear。

1. 使用 VirtualService 配置 /cart 请求在进行实际调用前,将 URI 重写为 /clear。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: apis-vs
  namespace: default
spec:
  hosts:
    - 121.4.8.11
  gateways:
    - default/apis-gw
  http:
    - match:
        - uri:
            exact: /cart
      route:
        - destination:
            host: cart.base.svc.cluster.local
      rewrite:
        uri: /clear

2. 发起 /cart 请求,客户端不感知重写操作,由服务端执行,实际调用的是 /clear API,cart 服务的 /clear API 成功返回调用清空购物车成功的信息。

$ curl http://$INGRESS_HOST/cart -H 'UserID: 1'
{"Success":"true"}

高可用调度

随着业务规模的增加,或对跨可用区/地域容灾、数据合规性、业务之间隔离要求的提升,业务会考虑与实施部署多个 Kubernetes 集群,把同一个服务部署在跨可用区/地域的多个集群,做高可用调度。主要有两种诉求:

  1. 地域&错误感知自动 failover:根据服务的地域信息与 endpoint 健康信息确定流量的可用区/地域分发策略,当 endpoint 健康度高于阈值时,流量 100% 在本地路由,低于阈值时,视 endpoint 健康度自动 failover 一定比例流量至其他可用区/地域,直至 endpoint 全部不健康时 100% 流量自动 failover 至其他可用区/地域。endpoint 健康信息感知依赖于健康检查的能力。
  2. 地域感知流量分发 distribute :不按照地域与错误信息自动 failover 流量,管理员自定义配置跨可用区/地域多集群流量分发策略,例如配置来自上海一区的流量在上海一区和上海二区按照 80% 和 20% 的比例分发。无须感知 endpoint 健康,不依赖健康检查的能力。

地域&错误感知自动 failover

云原生应用负载均衡系列 (2): 入口流量分发、容错与高可用调度-LMLPHP

环境准备:

1. 添加一个上海二区的 TKE 集群(如使用 TCM 一键体验功能搭建环境,可跳过,TCM 一键体验已经准备了第二个可用区的服务发现集群),并在此集群部署 user 服务(replicas: 14)作为上海一区 user 服务的容灾备份服务。

apiVersion: v1
kind: Namespace
metadata:
  name: base
spec:
  finalizers:
    - kubernetes
---
apiVersion: v1
kind: Service
metadata:
  name: user
  namespace: base
  labels:
    app: user
spec:
  ports:
    - port: 7000
      name: http
  selector:
    app: user
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: user
  namespace: base
  labels:
    app: user
spec:
  replicas: 14
  selector:
    matchLabels:
      app: user
  template:
    metadata:
      labels:
        app: user
    spec:
      containers:
        - name: user
          image: ccr.ccs.tencentyun.com/zhulei/testuser:v1
          imagePullPolicy: Always
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: REGION
              value: "shanghai-zone2"
          ports:
            - containerPort: 7000

2. 调整原有上海一区集群 user 服务的 healthy 和 unhealthy pods 数量分别为 10 和 4。调整完成后一区和二区的 user 服务 endpoints 数量情况如下:

$ kubectl get deployment user user-unhealthy -n base
NAME             READY   UP-TO-DATE   AVAILABLE   AGE
user             10/10   10           10          8d
user-unhealthy   4/4     4            4           5d2h
$ kubectl get deployment user -n base
NAME   READY   UP-TO-DATE   AVAILABLE   AGE
user   14/14   14           14          5d2h

下面我们准备配置开启与测试 Istio Ingress Gateway 的地域感知负载均衡功能。

1. TCM 的地域感知功能默认开启,我们只需要配置 user 服务的 Outlier Detection,地域感知负载均衡即可生效。且默认当上海一区 user endpoints 健康比例下降至 10/14 (71.4%)后,会开始按比例转移一区的流量到二区。

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: user
  namespace: base
spec:
  host: user
  trafficPolicy:
    outlierDetection:
      consecutiveErrors: 3
      interval: 10000ms
      baseEjectionTime: 30000ms
      maxEjectionPercent: 100
      minHealthPercent: 0
  exportTo:
    - '*'

2. 当前从 Ingress Gateway(上海一区)访问 user 服务的流量,当前上海一区 user 服务健康比例还未小于 10/14 的临界值,因此访问 user 服务的流量全部由上海一区的 user endpoints 提供。发起一组请求验证,所有流量均被路由到了上海一区的 endpoints。

$ for((i=0;i<10;i++)) do curl http://$INGRESS_HOST/user | jq '.Info[0].Region'; done
"shanghai-zone1"
"shanghai-zone1"
"shanghai-zone1"
"shanghai-zone1"
"shanghai-zone1"
"shanghai-zone1"
"shanghai-zone1"
"shanghai-zone1"
"shanghai-zone1"
"shanghai-zone1"

3. 调整上海一区集群内 user 服务健康与非健康 endpoints 的比例,调整健康 endpoints 为 5,不健康endpoints 为 9。此时健康上海一区 user 服务的健康比例 5/14 已经小于 10/14,应当有部分流量被切至上海二区,发起一组请求验证,/user 流量部分被路由至上海二区,路由至上海一区/二区的比例大致均衡为 1:1。

$ for((i=0;i<10;i++)) do curl http://$INGRESS_HOST/user | jq '.Info[0].Region'; done
"shanghai-zone2"
"shanghai-zone1"
"shanghai-zone1"
"shanghai-zone1"
"shanghai-zone1"
"shanghai-zone2"
"shanghai-zone2"
"shanghai-zone2"
"shanghai-zone1"
"shanghai-zone2"

4. 继续调整上海一区集群内 user 服务健康与非健康 endpoints 的比例,健康 endpoints 为 0,不健康 endpoints 为 14,此时上海一区 user 服务健康比例为 0%,不具备提供后端服务的能力,应当将所有 /user 请求路由到上海二区。发起一组请求验证,所有流量均被路由至上海二区。

$ for((i=0;i<10;i++)) do curl http://$INGRESS_HOST/user | jq '.Info[0].Region'; done
"shanghai-zone2"
"shanghai-zone2"
"shanghai-zone2"
"shanghai-zone2"
"shanghai-zone2"
"shanghai-zone2"
"shanghai-zone2"
"shanghai-zone2"
"shanghai-zone2"

Istio 默认策略为当流量需要 failover 时,会在下一同等地域优先级的所有地域内全局负载均衡,同一个服务若有超过 2 个地域的部署,则需考虑配置 failover 优先级,例如 user 服务在广州、上海、北京均有部署时,需要配置当广州的 endpoints 健康比例下降到阈值之下时,流量 failover 的地域是最近的上海而不是更远的北京或在上海和北京全局负载均衡,使用 localityLbSetting 的 failover 参数即可配置。当 user 服务在广州、上海、北京均有部署时我们可以如下配置地域间 failover 策略,配置方式与 distribute 策略相同。

failover:
   - from: gz
     to: sh
   - from: sh
     to: gz
   - from: bj
     to: sh

地域感知流量分发 distribute

云原生应用负载均衡系列 (2): 入口流量分发、容错与高可用调度-LMLPHP

可通过 meshconfig(配置网格全局)或 DestinationRule(配置单个服务)的 distribute 参数来配置自定义地域感知流量分发策略。

1. 恢复上海一区和二区 user 服务 健康/不健康 endpoints 数量至最初状态,一区一共 14 个endpoints 全部健康,二区一共 14 个endpoints 全部健康。按照 Istio Ingress Gateway 默认的地域感知策略,从 Ingress Gateway(上海一区)访问 /user 的流量会全部路由至上海一区的 endpoints。

2. 配置 user 服务的 DestinationRule,自定义流量调度规则,来自上海一区的流量,均匀路由至上海一区和二区的 endpoints。

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: user
  namespace: base
spec:
  host: user
  trafficPolicy:
    loadBalancer:
      localityLbSetting:
        distribute:
          - from: sh/2000400001/*
            to:
              sh/2000400001/*: 50
              sh/200002/*: 50
        enabled: true
    outlierDetection:
      consecutiveErrors: 3
      interval: 10000ms
      baseEjectionTime: 30000ms
      maxEjectionPercent: 100
      minHealthPercent: 0
  exportTo:
    - '*'

3. 发起一组 /user 请求验证,流量被比较均衡的路由到了一区和二区的 endpoints,而不是 Istio Ingress Gateway 默认地域/错误感知自动 failover(100% 流量路由到上海一区)。

$ for((i=0;i<10;i++)) do curl http://$INGRESS_HOST/user | jq '.Info[0].Region'; done
"shanghai-zone2"
"shanghai-zone2"
"shanghai-zone2"
"shanghai-zone2"
"shanghai-zone1"
"shanghai-zone2"
"shanghai-zone1"
"shanghai-zone1"
"shanghai-zone2"
"shanghai-zone2"

结语

本文介绍了 Istio Ingress Gateway 流量管理的技术原理及流量管理模型。并从入口流量分发、容错与高可用调度三个方面实操演示了内容路由、权重路由、负载均衡、断路器、地域&错误感知自动 failover、地域感知流量分发等功能。

除基本的入口流量管理外,南北流量管理还有安全、可观测性、异构服务支持等场景,将在后续系列文章中继续讨论。

07-02 23:06