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


引言

 

在云原生应用负载均衡系列第一篇文章《云原生应用负载均衡选型指南》介绍了云原生容器环境的入口流量管理使用场景与解决方案,用 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 访问,边缘网关

 

粤ICP备2021087867号-1