乐趣区

关于腾讯云:云原生应用负载均衡系列-2-入口流量分发容错与高可用调度

引言

在云原生利用负载平衡系列第一篇文章《云原生利用负载平衡选型指南》介绍了云原生容器环境的入口流量治理应用场景与解决方案,用 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)实现 的。

Istio 流量治理模型及 API 介绍

Istio 设计了本人的流量治理 API,次要通过 Gateway,VirtualService,DestinationRule 这几个 CR(Kubernetes Custom Resource 2)实现。

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

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

入口流量治理实际

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

环境筹备

环境筹备能够应用 TCM 控制台「一键体验」性能,将主动为您筹备 TCM + 2 个跨可用区 Kubernetes 集群 + TCM demo 的初始环境。

咱们筹备一个 Istio Ingress Gateway(应用腾讯云服务网格 TCM 演示)+ Kubernetes 集群(应用腾讯云容器服务 TKE 演示)环境,首先创立一个服务网格实例,并在网格实例中创立 Istio Ingress Gateway,而后增加一个 TKE 集群作为网格的服务发现集群。

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

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

入口流量散发

利用公布

业务须要将多个后端模块提供的 API 裸露供客户端调用,须要配置网关路由规定,将申请门路 /product 的流量路由至 product 服务,将 /cart 的申请路由至 cart 服务,将 /user 的申请路由至 user 服务。

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"

灰度公布

业务须要降级 product 服务,曾经实现筹备新版本镜像,能够开始部署新版本的 pods。心愿服务端在降级时需思考版本的平滑、无风险迭代,按百分比调整流量逐渐切换至新版本服务,灰度验证新版本无问题后再下线旧版本。

以下是配置 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"

会话放弃

同一后端服务个别由多个实例(Pod)承载,通常须要将入口流量在多个后端实例负载平衡(例如 product 服务)负载平衡,此时配置的是 round robin、random 或最小连接数等负载平衡算法,放弃多个后端实例平衡解决流量。
在一些特定状况下,须要将来自同一用户的申请路由到雷同后端实例,保障某些须要会话放弃的服务(例如 cart 购物车服务)可能失常工作。

会话放弃有两种:

  • 基于 IP 的简略会话放弃:来自同一 IP 地址的申请断定为同一用户,路由至雷同后端服务实例。实现简略、不便,但无奈撑持多个客户端应用代理拜访后端服务的场景,此时同一 IP 地址不代表同一用户。
  • 基于 cookie(或其余 7 层信息)的会话放弃:用户第一次申请时,边缘网关为其插入 cookie 并返回,后续客户端应用此 cookie 拜访,边缘网关依据 cookie 将流量负载至后端服务实例。

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"

容错

连接池治理

连接池是放弃分布式系统(服务化利用)稳固的重要配置。分布式系统中其中一个服务因申请数暴增而有故障危险时,疾速返回失败信息尽快将压力施加给上游服务能无效防止整个零碎产生雪崩。咱们可通过连接池为有须要的服务配置 TCP/HTTP 的连接数 / 申请数阈值,达到阈值后回绝解决新增流量返回错误信息,能无效爱护服务运行的稳定性 7。

上面咱们配置 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 连接池配置起到了很好的爱护服务端稳定性的作用。

健康检查

当后端服务实例(Pod)在解决流量过程中产生故障时(间断返回谬误,成功率升高到阈值之下等),Ingress Gateway 须要能够配置将故障的 endpoints 从衰弱负载平衡池中剔除的策略,保障客户端调用能够由状态失常的后端服务实例解决。
另外,地区感知负载平衡性能也须要开启异样检测,感知各地 endpoint 的衰弱状态能力确定流量调度策略。

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

重定向

当利用被迁徙到新的 URI,同时又须要放弃原有链接可用,此时须要配置 HTTP 重定向 9。重定向可利用于以下场景:

  1. server 端保护 / 停机期间迁徙到新的 URI
  2. 强制应用 HTTPS 协定
  3. 多域名扩充利用笼罩用户人群

为确保通过 Ingress Gateway 拜访后端 user 服务的申请强制应用更平安的 HTTPS 协定,须要配置 Ingress Gateway 的 HTTP 重定向。

上面咱们配置强制应用 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

重写

应用重定向,客户端能够感知拜访地址的变动,并且被重定向的流量实际上发动了两次申请能力失常拜访,有肯定性能损耗。而重写则向客户端屏蔽了地址的变动,齐全由服务端进行重写操作,使客户端申请地址与服务端治理解藕。

TCM demo 的 cart 服务提供的 API 资源产生了变动,实现了 /clear 清空购物车的 API,心愿在客户端无感知的状况下,/cart 申请理论调用的是 /clear API。

上面咱们配置 /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

随着 TCM demo 网站后盾规模减少,对后盾服务容灾的诉求也提上日程,心愿实现 user 服务的跨集群(本次以集群跨可用区为例)容灾,在上海二区新增一个业务集群部署 user 备份服务,流量依然是从上海一区的 ingress gateway 拜访 user。心愿在上海一区 user 服务 endpoints 都衰弱时,就近拜访本可用区的 user,当上海一区 user endpoints 衰弱比例降落到肯定水平时(例如 71.4%),开始视衰弱水平转移肯定比例的流量到上海二区的 user endpoints,直至上海一区 user endpoints 衰弱水平齐全降落为 0% 时将流量齐全切到上海二区 user 备份。

环境筹备:

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

TL;DR. 以下内容是地区感知负载平衡获取 endpoints 衰弱水平、地区信息、确定流量转移比例的背景常识补充。

实现跨可用区 / 地区多集群地区感知负载平衡,视服务 endpoints 地区信息及衰弱水平按比例 failover 流量,须要具备以下能力:

1. 联通多集群网络并发现所有集群的服务:

跨集群网络连通后,Istiod 能够从多个集群的 API Server 中获取服务与 endpoint 信息,并推送给数据面代理 Envoy Pod。

2. 获取服务的地理位置信息:

实现就近拜访与容灾,须要服务的地理位置信息,在服务网格中,地理位置由有如下三个层级的信息:region,zone,subzone。其中 region 和 zone 的信息别离来自集群节点的 topology.kubernetes.io/region 标签和 topology.kubernetes.io/region 标签。这两个标签在 TKE 集群中曾经提供 10,例如上海一区节点的标签为:topology.kubernetes.io/region: shtopology.kubernetes.io/zone: "2000400001";上海二区节点的标签为:topology.kubernetes.io/region: shtopology.kubernetes.io/zone: "200002"

Kubernetes 不存在 subzone 的概念,Istio 引入了 topology.istio.io/subzone 的标签定义 subzone,可依据须要配置。

在 Istio 默认应用的地区优先负载平衡策略中,优先级如下:

  • Priority 0 最高优先级,同地区同可用区;
  • Priority 1,同地区不同可用区;
  • Priority 2 最低优先级,不同地区。

3. 获取服务 endpoints 的衰弱信息:

endpoints 衰弱信息获取依赖开启 Istio 的健康检查:Outlier Detection。地区 & 谬误主动 failover 性能依赖健康检查,未开启时,数据面无奈得悉服务 endpoints 的健康状况,默认依照全局的形式进行流量负载平衡。地区感知流量散发 distribute 不依赖开启健康检查。

4. 断定服务衰弱状态 & 确定流量转移比例:

一个服务会部署多个正本,服务的衰弱状态不是相对的 0 和 1 的状态,流量的转移是一个逐渐转移的过程,不衰弱 endpoints 超过肯定比例时,再开始按比例逐渐进行流量转移。Istio 的地区负载平衡默认应用地区优先的策略,即管制面通知数据面在衰弱状态下,优先思考将申请发送到地理位置最近的实例,天文优先级最高可用区 / 地区 endpoints 衰弱度 100% 时,所有的申请都会路由到这个地区,不会做流量转移,endpoints 不衰弱比例超过某阈值,流量将开始按比例逐渐转移。

这个阈值由 envoy 的 overprovisioning factor 管制,默认为 1.4,依据该 factor 及服务 endpints 衰弱比例可确定不同天文 Priority Level 的流量比例。例如,假如目前某服务在广州和上海两个地区均有 endpoints,流量入口 Ingress Gateway 部署在广州。通过广州 Ingress Gateway 拜访该服务的流量,依照优先级广州的服务为 P0 优先级,上海的服务为 P1 优先级。假如上海作为容灾的地区,,endpoints 衰弱比例始终为 100%,假如广州上海两个地区的权重相等,overprovisioning factor 为默认值 1.4。两个地区流量负载比例计算过程如下:

  • 广州服务 endpoints 衰弱比例:P0_health = 广州服务衰弱 endpoints 数量 / 广州服务 endpoints 总数;
  • 广州服务流量负载比例:P0_traffic = min(1, P0_health * 1.4);
  • 上海服务流量负载比例:P1_traffic = 1 – P0_traffic = max(0, 1 – P0_health * 1.4)。

依照该计算规定,overprovisioning factor 为 1.4 时:

  • 广州服务 endpoints 衰弱比例 P0_health 低于 71.4% 时,该地区拜访该服务的流量才会开始切换至上海地区;
  • 当广州地区的 endpoints 衰弱比例为 50% 时,会有 1 – 50% * 1.4 = 30% 的流量转移到上海地区的服务;
  • 当广州地区 endpoints 齐全不可用 P0_health = 0% 时,流量才会被齐全切换到上海地区。

PX_traffic = min(100, PX_health 1.4 100) 反映某地区某服务以后的流量承载能力,Envoy 社区称为衰弱评分。当所有地区的衰弱评分总和低于 100 时,Envoy 则认为以后衰弱状态没有齐全解决申请的的能力,此时 Envoy 会依据衰弱评分的比例调配申请。例如广州和上海的衰弱评分别离为 20 和 30 时,别离会承当 40% 和 60% 的负载 11。

地区感知流量散发 distribute

业务不心愿流量依据地区和衰弱信息主动 failover,而是自定义流量散发策略,来自 Istio Ingress Gateway(上海一区)的 /user 申请在一区和二区依照 1:1 的比例平衡散发,而不是利用 Istio 默认的主动 failover 策略:100% 衰弱时,来自上海一区的申请 100% 负载平衡至上海一区 user endpoints。

可通过 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、地区感知流量散发等性能。

除根本的入口流量治理外,南北流量治理还有平安、可观测性、异构服务反对等场景,将在后续系列文章中持续探讨。

【腾讯云原生】云说新品、云研新术、云游新活、云赏资讯,扫码关注同名公众号,及时获取更多干货!!

退出移动版