查看原文
其他

Getting Started and Beyond|云原生应用负载均衡选型指南

冉昕 刘旭 腾讯云原生 2021-07-14

冉昕,腾讯云服务网格TCM产品经理,现负责云原生流量接入网关与应用通信可观测性等产品特性策划与设计工作。

刘旭,腾讯云高级工程师,专注容器云原生领域,有多年大规模 Kubernetes 集群管理及微服务治理经验,现负责腾讯云服务网格 TCM 数据面产品架构设计和研发工作。

引言

应用的入口流量管理一直是开发运维关注的焦点之一,随业务部署的计算资源、网络环境、应用架构的发展变更,接入层流量管理方案的发展可大致分为传统架构、云原生容器化两个阶段。为满足应用交付的效率和诉求,各阶段都涌现了不同的接入层解决方案,从最初的简单负载均衡,到后来的 HAProxy、Nginx 等反向代理,再到如今的容器化环境下的各类 Kubernetes Ingress Controller。每个发展阶段有哪些特点?面临什么挑战?都有什么解决方案?

阶段应用部署资源粒度应用架构应用访问寻址
传统架构物理/虚拟机(资源利用率低)单体或简单拆分模块基于较固定的 IP 地址管理
云原生容器化容器(资源利用率高)服务化容器 IP 动态变化,通过动态服务注册更新

传统架构阶段,业务为单体应用,三层架构;部署于物理机/虚拟机;网络环境基于 IP 地址管理,相对固定,基本不会变化;业务更新迭代的速度较慢,接入层的主要需求是具备 4 层和 7 层的负载均衡能力,用传统负载均衡器支持即可。随着应用架构演进(应用做了一定模块拆分)和迭代效率的提升,出现了一些更复杂的接入层诉求:按流量内容特征路由、灰度发布、限流、鉴权等,一般通过在负载均衡器后增加一层网络代理(e.g. Nginx)支持,网络代理 Nginx 具备更多的 7 层流量处理的能力,可通过 OpenResty 社区的 Lua 扩展上述内容路由、灰度发布、鉴权限流等高级功能。

云原生容器化阶段的理想状态是业务开发者只需专注实现业务逻辑,无需关心资源调度和运维管理,可真正做到按需使用,按量计费。虚拟机/物理机资源粒度粗糙,利用效率较低,需提前规划计算、存储、网络资源,与理想状态有较大差距。

云原生阶段,容器资源的粒度更细,利用率高,启动/销毁速度达到秒级,可灵活弹性伸缩(Kubernetes 已成为容器编排调度的业界标准,以下容器环境均代指 Kubernetes 集群);网络管理环境也发生了变更,出现 Service 的概念,一个微服务往往是由一组弹性伸缩、动态调度的容器(Pod)承载,Pod 的 IP 地址动态变化,这一组 Pod 一般以 Service 对外提供访问,流量管理是以 Service 为单位。服务化拆分业务模块构建应用更容易,加上容器环境良好的弹性伸缩能力,DevOps 理念得以很好的实施,微服务的迭代步伐加快,经常需要滚动更新。此时的入口流量管理面临如下新挑战:

  1. 需要与 Kubernetes 集成,支持转发流量到指定 Pod。
  2. 更新迭代速度加快,对服务新版本灰度发布的诉求更加强烈。
  3. 出现集群概念,集群之间的服务发现是隔离的,接入层需支持跨集群的服务发现(即接入层可选择 backend 为多个集群的 Pod );这区别于传统物理机/虚拟机阶段,没有集群隔离,只需保证网络联通性,即可配置接入层后端为任意对应服务的 IP 地址。
  4. 传统阶段到云原生阶段的迁移过程中,出现 VM、容器环境混布的情况。
基于上述挑战,出现了以下容器环境的接入层流量管理解决方案:
  1. Kubernetes 官方定义的 Ingress API:老牌网络代理(e.g. Nginx,HAProxy)或云厂商的负载均衡产品(e.g. AWS Elastic Load Balancer,腾讯云 CLB)都实现了各自的 Ingress Controller,作为单个集群的入口流量管理解决方案。灰度发布、鉴权限流等能力,视 Ingress Controller 的能力,可通过 Annotation 扩展,部分 Ingress Controller 还设计了自己的流量管理模型和语法。
  2. Service Mesh Ingress:服务网格的服务发现和管理界限大于集群纬度,以 Istio Ingress Gateway 为例,基于 Istio 跨集群的服务发现能力,backend 可以来自不同集群的服务,同时还支持注册在网格内运行在虚拟机上的服务。Istio 也设计了自己的管理模型和语法,声明式支持配置一致的南北 + 东西向流量管理。
  3. 沿用原有 VM 上部署的网络代理,转发流量至 VM 服务或 Kubernetes 集群的服务。

下面本文将从云原生容器化环境入口流量管理使用场景切入,带您了解云原生接入层流量管理的各类解决方案及优劣对比。

云原生接入层流量管理场景与解决方案

场景一:基础流量管理

入口流量管理的首个使用场景是需要将服务暴露给外部,供客户端调用。常见的方式是将服务按 URL 暴露,例如一个电商网站,需要将 /login 的请求路由到登陆服务,将 /product 的请求路由到商品服务等,该场景要求接入层具备基于流量内容路由的能力。

方案:Load Balancer + NodePort

在容器化的早期阶段,应用同时部署在虚拟机和 Kubernetes 集群上,很多用户会使用原有负载均衡(e.g. Nginx, 腾讯云 CLB)将请求分别转发到虚拟机和容器,同时受限于容器网络方案,原有负载均衡不能直接访问 Pod IP,因此需要通过 NodePort 暴露集群内的服务。

但是该方案存在以下问题:

  1. NodePort  端口数量有限(默认 30000-32767)
  2. 随着集群规模的扩大,Nginx 配置文件越来越复杂,不易管理
  3. 用户将应用发布到 Kubernetes 集群后,需要再单独修改 Nginx 配置,体验不够云原生

方案:Kubernetes Ingress

Kubernetes 提供了 Ingress API [1] 用于暴露集群内的 HTTP 服务,Ingress 支持基于 Host 和 Path 将请求路由到不同 Service。为了让 Ingress 工作,集群必须有一个正在运行的 Ingress 控制器(e.g. Nginx Ingress Controller)。原生 Ingress 语法提供简单的基于 Host,Path 路由,以及配置 TLS 的能力。

1. 基于 Host 路由

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: public-services
  namespace: default
spec:
  rules:
  - host: service1.tencent.com
    http:
      paths:
      - backend:
          serviceName: service1
          servicePort: 80
        path: /
  - host: service2.tencent.com
    http:
      paths:
      - backend:
          serviceName: service2
          servicePort: 80
        path: /

2. 基于 Path 路由

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: public-services
  namespace: default
spec:
  rules:
  - host: services.tencent.com
    http:
      paths:
      - backend:
          serviceName: service1
          servicePort: 80
        path: /service1
      - backend:
          serviceName: service2
          servicePort: 80
        path: /service2
3. TLS 配置

Ingress 也提供了 TLS 支持,可以将集群内的 HTTP 服务对外暴露为 HTTPS,我们需要先将 SSL 证书以 Secret 的形式保存在集群中,再使用 Ingress 引用刚刚创建的 Secret。

apiVersion: v1
kind: Secret
metadata:
  name: public-services-tls
  namespace: default
data:
  tls.crt: base64 encoded cert
  tls.key: base64 encoded key
type: kubernetes.io/tls
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: services-with-tls
  namespace: default
spec:
  tls:
  - hosts:
      - services.tencent.com
    secretName: public-services-tls
  rules:
    http:
      paths:
      - backend:
          serviceName: service1
          servicePort: 80
        path: /service1
      - backend:
          serviceName: service2
          servicePort: 80
        path: /service2
Kubernetes Ingress 小结:对于简单的 HTTP 流量的路由,使用 Ingress 配置非常容易,这也是当前 Ingress 受欢迎的原因(据 CNCF 2020 云原生调查报告 [2],50% 的用户正在或即将使用第三方代理做应用流量转发,其中 Nginx 和 Envoy [3] 是最受欢迎的 Ingress provider)。
但是另一方面原生 Ingress 的功能十分有限,不能满足很多复杂场景的需求。许多第三方的 Ingress Controller [4] 通过 annotation 或新的配置模型和语法扩展了原生 Ingress 的功能,但仍然受限于集群间服务发现隔离的问题,只能作为单集群入口流量管理方案。

场景二:灰度发布

服务可暴露给外部访问后,还需要考虑如何做版本发布,做平滑、无风险地迭代。常见的两种做法是按权重或流量内容切部分流量至新版本验证稳定性,无问题后逐渐过渡至新版本,即我们熟知的灰度发布、AB test。

Kubernetes Ingress API 原生并没有灰度发布的功能,Nginx ingress controller 通过 annotation 的方式扩展了原生 Ingress API 的功能,实现了灰度发布,但这种方式并不能很好地支撑控制应用流量的发布策略,相比之下,Istio CRD 配置更灵活易用,下面介绍如何使用 Istio VirtualService 配置灰度发布路由规则。

1. 基于权重

Istio 可通过 Virtual Service 配置基于权重的灰度发布,以下是配置来自 {namespace}/{gateway} 的入口流量 95% 路由到 {service} 的 current 版本,5% 路由到 canary 版本的示例:

apiVersion: ...
kind: VirtualService
metadata:
  name: canary-weight
spec:
  hosts:
    - '*'
  gateways:
    - {namespace}/{gateway}
  http:
    - route:
        - destination:
            host: {service}
            subset: current
          weight: 95
        - destination:
            host: {service}
            subset: canary
          weight: 5
2. 基于请求内容

VirtualService 也支持配置基于内容的灰度发布路由规则,以下是配置来自 {namespace}/{gateway} 的入口流量 header cookie "version=stable" 时路由到 {service} 的 current 版本,"version=canary" 时路由到 {service} 的 canary 版本的示例:

apiVersion: ...
kind: VirtualService
metadata:
  name: canary-content
spec:
  hosts:
    - '*'
  gateways:
    - {namespace}/{gateway}
  http:
    - match:
        - headers:
            cookie:
              exact: version=stable
      route:
        - destination:
            host: {service}
            subset: current
    - match:
        - headers:
            cookie:
              exact: version=canary
      route:
        - destination:
            host: {service}
            subset: canary

场景三:应用流量鉴权与限流

鉴权与限流,是保证南北流量的安全性与健壮性的两个重要能力。

接入层是访问后端服务的统一入口,保证接入层的安全是接入层流量管理的一个重要场景,一般在入口处需要配置认证与授权规则,传统架构下认证授权功能一般通过代码逻辑实现,Istio 自 1.5 之后提供了 AuthorizationPolicy 和 RequestAuthentication CRD [5],可灵活配置入口层的认证和授权规则。

1. 请求身份认证(JWT)

入口处认证请求携带的 Json Web Token,放通携带合法令牌的请求,拒绝携带非法令牌的请求。

以下是使用 Istio RequestAuthentication 配置 Ingress Gateway 放通携带合法 JWT 请求的配置示例:

apiVersion: ..
kind: RequestAuthentication
metadata:
  name: jwt-example
  namespace: istio-system
spec:
  selector:
    matchLabels:
      istio: ingressgateway
  jwtRules:
  - issuer: {issuer that issued the JWT}
    jwksUri: {URL of the provider’s public key set to validate signature of the JWT}
2. 授权

在入口处配置授权策略,根据流量内容特征,允许/拒绝流量访问,例如在入口处配置 IP 黑/白名单;或有外部鉴权服务,希望入口组件可对接外部鉴权服务,按照其返回的鉴权结果放通/拒绝流量。

以下是使用 Istio AuthorizationPolicy 为 Ingress Gateway 配置 IP block 白名单的示例:

apiVersion: ...
kind: AuthorizationPolicy
metadata:
  name: white-list
  namespace: istio-system
spec:
  selector:
    matchLabels:
      app: istio-ingressgateway
  action: ALLOW
  rules:
  - from:
    - source:
        ipBlocks: {single IP or CIDR}

Istio 1.9 增强了对 AuthorizationPolicy 对于对接外部鉴权系统的支持,可配置 Ingress Gateway 按照外部鉴权系统返回的结果放通或拒绝流量。

apiVersion: ...
kind: AuthorizationPolicy
metadata:
  name: ext-authz
  namespace: istio-system
spec:
  selector:
    matchLabels:
      app: istio-ingressgateway
  action: CUSTOM
  provider:
    name: "my-ext-authz-service"
  rules: ...
3. 限流

业务规模较大,后端服务提供给众多租户使用时,需要在入口处控制请求的速率,例如限制每个 User ID 每分钟只能请求 “/product” 接口 100 次。为了使用 Istio Ingress Gateway 的限流功能,首先需要安装 Ratelimit service,可以自行实现或直接使用社区的 ratelimit [6],然后使用 Envoyfilter 配置限流规则,具体配置方法可参考官方文档[7]。

场景四:多集群异构场景入口流量管理

随着业务规模的增加,或对容灾、数据合规性、业务之间隔离要求的提升,业务会考虑与实施部署多个 Kubernetes 集群,甚至会出现容器化环境与非容器化环境异构混布的情况,给入口流量管理又带来了一系列新的挑战。

多 Kubernetes 集群一般是基于容灾和业务隔离两方面的考虑:

(1)容灾。Kubernetes 集群有地域属性,根据应用交付提供服务的访问时效和容灾诉求,同一应用可能分布在多个不同的地理区域。多(公有)云、混合云(IDC + 公有云)架构的容灾,也需部署多个集群。跨地域多集群容灾与就近接入可通过 DNS 解析提供,但 DNS 有缓存,故障转移实际生效时间可能较长,并且无法视服务健康程度切部分流量到备份地域,只能全部切换。

Istio 基于以下能力:1. 多集群服务发现能力;2. 地域感知、故障感知、容灾流量容量规划,可实现:① 当所有集群的服务都健康时,按照请求来源地就近路由至对应服务;② 某个集群的服务出现部分故障时,视服务的健康程度转移一定比例的流量到其他集群的备份服务。

(2)业务隔离。据 CNCF 2020 云原生调查报告显示 [2],用多个集群做应用隔离是仅次于用 namespace 隔离的使用方式,使用率从 2019 年的 47% 上升到了2020年的 50%。多个业务仍共用一个流量入口时,接入层需具备多集群服务发现的能力,将流量按指定策略路由至指定集群的服务。

方案:Service Mesh Ingress

Kubernetes Ingress Controller 遇到的一个挑战是,Kubernetes 集群隔离了集群间的服务发现,Ingress Controller 只能作为集群级别的流量入口。而 Service Mesh 技术借助于控制面服务发现的能力,可发现或注册多个集群的服务甚至异构服务,打通集群间的服务发现壁垒,不受应用部署平台限制,天然提供一致的接入流量转发管理能力。

Istio 作为最受欢迎的 Service Mesh 开源项目,它的接入层 Istio Ingress Gateway 同样提供了对 Ingress API 的支持,但是不建议使用 Ingress 去配置 Ingress Gateway,这大大削弱了 Istio 的能力。Istio 对流量管理模型提供了更高程度的抽象,可以直接使用 Istio API 实现更灵活的流量管理能力,实现灰度发布,跨集群路由,地域感知等高级特性。

Istio Ingress Gateway 基于 Envoy [3] 实现,Envoy 最初由 Lyft 创建,是一款为云原生场景设计的高性能服务代理软件,后由 Lyft 捐献到了 CNCF 社区,并已从 CNCF 毕业。

1. 多 Kubernetes 集群服务管理

Istiod 可以通过网格内所有集群的 API Server 来获取 endpoints 信息,聚合多个集群的信息后,将最终生成的配置推送到 Ingress Gateway,Ingress Gateway 可以将请求按需转发至网格内所有 Pod。

2. 地域感知负载均衡

在服务网格中,一个 Pod 的地理信息包括以下 3 个部分 [8]:

  • Region(地域):通常代表一个较大的地理区域(e.g 北京 上海),在 Kubernetes 中,节点的地域由标签 topology.kubernetes.io/region 决定
  • Zone(可用区):一个地域通常包含多个可用区(e.g. 北京一区 北京二区),在 Kubernetes 中,节点的可用区由标签 topology.kubernetes.io/zone 决定
  • Sub-zone :允许对可用区做进一步划分实现更细粒度的控制,例如可以按照 rack(机架)划分,在 Kubernetes 中不存在 sub-zone 的概念,Istio 使用节点的 topology.istio.io/subzone 标签来定义 sub-zone

如果使用云厂商托管的 Kubernetes 服务,节点的 Region 和 Zone 标签已由云厂商配置,例如在 TKE 集群中,上海二区的节点会有以下标签:

  • topology.kubernetes.io/region: sh
  • topology.kubernetes.io/zone: "200002"
网格内的集群可能分布在不同地域不同可用区,大多数情况下,我们希望尽量减少跨地域/跨可用区的请求调用,因为这会增加请求时延。因此接入层需具备感知 endpoints 地理信息的能力,并支持根据地理信息配置负载均衡及故障转移策略。
(1)地域故障转移

在开启地域负载均衡的情况下,Istio 会告知 Ingress Gateway 将请求就近转发。当所有实例都正常时,请求将保持在同一地点,当实例异常时,流量会分发到下一优先地域的实例。

例如,位于 bj.bj-01 的 Ingress Gateway 转发请求的优先级如下:

优先级地理位置
1bj.bj-01Region Zone 完全匹配
2bj.bj-02Region 匹配 Zone 不匹配
3sh.sh-01/sh-02Region Zone 都不匹配
(2)地域加权负载均衡

地域加权负载均衡可以将用户定义的一定百分比的流量分发到某些地域,例如我们可以使用如下配置分发流量:

global:
  localityLbSetting:
    enabled: true
    distribute:
    - from: bj/bj-01/*
      to:
        "bj/bj-01/*": 70
        "bj/bj-02/*": 20
        "sh/sh-01/*": 10
3. 异构服务入口流量管理

除了多集群,用户在云原生改造的过程中,常常会面临部分服务已经做了容器化改造,运行在 Kubernetes 集群,部分不便改造的服务仍在虚拟机的情况,甚至会有部分使用的是云厂商 serverless 云函数服务(e.g. AWS lambda)。接入层需具备异构服务注册/发现的能力,以管理异构部署服务的南北向流量。

可以通过 Istio 提供的 WorkloadGroup 和 WorkloadEntry 将虚拟机上的服务注册到网格内,同一个服务可以同时运行在 Kubernetes 集群和虚拟机上。

Istio Ingress Gateway 小结:Istio Ingress Gateway 在入口灰度发布、安全、多集群异构流量管理等场景提供了多集群服务发现、地域感知、流量容量规划,以及更强大灵活的流量管理 API 的支持。

但与此同时,用户也不得不面对 Istio 的复杂性。需要投入资源和人力成本运维 Istiod 和 Istio Ingress Gateway,集成 metric,trace,log 等可观测性及证书管理周边系统成本较高,还需要正确配置各种 CRD(Gateway VirtualService DestinationRule 等)。

接入层解决方案功能对比

以下是容器环境下接入层解决方案:CLB+ Kubernetes Ingress /Nginx、CLB+Istio Ingress Gateway 与 NodePort 的功能对比。

‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

多集群灰度发布/跨集群容灾 Demo

下面将使用腾讯云服务网格 TCM 控制台演示 Service Mesh Ingress 做多 Kubernetes 集群环境下的灰度发布和容灾。

  1. 创建服务网格,添加两个部署服务的服务发现集群(基础监控指标自动对接到云监控,可在控制台查看,可视情况开启云原生监控,满足自定义监控诉求),勾选启用 Ingress Gateway
  2. 使用 Destination Rule 定义 frontend 服务的版本(frontend 服务在两个集群均有同样的部署)
  3. 使用 Gateway 配置 Ingress gateway 监听规则,开启 443 端口 https 访问,使用腾讯云 SSL 平台服务器证书
  4. 使用 VirtualService 配置路由规则,50% 流量路由至 v1 版本,50% 路由至 v2 版本
  5. 有访问请求后,查看工作负载(frontend,frontend-canary)监控,两个版本均有流量,比例大致 1:1
  6. 灰度结束,更改权重,100% 的流量均路由至 v2 版本,再次查看工作负载的监控数据,发现所有流量都已请求至 frontend-canary
  7. 下面我们通过调整其中一个集群的 frontend 服务工作负载 Pod 数量为 0 来模拟其中一个集群 frontend 服务故障情况,发现其中一个集群 frontend 服务故障后,仍可以正常访问该服务,查看另一集群的 frontend 服务的工作负载监控,会发现入带宽增加了一倍,表明其中一个集群的服务故障后,流量容灾切到了另一集群。
  8. 如有扩展东西向流量管理的需要,可以给业务注入 envoy sidecar,即可使用同一套 Istio API 实现南北东西向流量一致性管理,开箱即用网络拓扑、调用追踪等可观测性功能。

腾讯云服务网格 TCM,是腾讯云完全兼容 Istio 的 Service Mesh 产品,目前已实现了控制面组件托管,使用 TCM Ingress Gateway 只需要部署一组数据面 envoy pod 在业务集群,即可开箱即用上述 Istio Ingress Gateway 的所有入口流量管理能力。同时,TCM 集成了腾讯云监控、证书周边产品,提供开箱即用的可观测能力和证书配置功能。

结语

本文由业务部署发展的两个阶段引入,介绍了:

  1. 云原生容器化环境下接入层流量管理的典型场景。
  2. 入口流量管理的解决方案及优劣对比。
  3. 以腾讯云服务网格 TCM 为例,演示 Service Mesh Ingress 多集群环境下灰度发布及服务跨集群容灾的能力。
主要结论有
  1. 对于简单的 HTTP 流量的路由,使用 Kubernetes 原生 Ingress 配置非常容易,一些 Ingress Controller (e.g. Nginx, Traefik)也通过 annotation 或 CRD 扩展了原生 Ingress 的功能,但仍是集群级别的流量入口
  2. Service Mesh 级别的接入层,借助控制面服务发现的能力,可作为多集群/异构环境下的统一流量入口,可具备跨集群路由,地域感知等高级特性;后续也可平滑扩展一致语法管理东西向流量。

本文是云原生接入层流量管理系列文章的第一篇,后续我们将会推出一系列文章详细介绍入口流量管理、安全、可观测性、多集群异构入口流量管理等场景下的最佳实践。

Reference

[1] https://kubernetes.io/docs/concepts/services-networking/ingress/

[2] https://www.cncf.io/wp-content/uploads/2020/12/CNCF_Survey_Report_2020.pdf

[3] https://www.envoyproxy.io/docs/envoy/latest/intro/what_is_envoy

[4] https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/

[5] https://istio.io/latest/docs/reference/config/security/

[6] https://github.com/envoyproxy/ratelimit

[7] https://istio.io/latest/docs/tasks/policy-enforcement/rate-limit/

[8] https://istio.io/latest/docs/tasks/traffic-management/locality-load-balancing/




















  往期精选推荐  

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存