Getting Started and Beyond|雲原生應用負載均衡選型指南

騰訊雲原生發表於2021-05-12

作者

冉昕,騰訊雲服務網格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. 地域感知、故障感知、容災流量容量規劃,可實現: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 轉發請求的優先順序如下:

優先順序 地理位置
1 bj.bj-01 Region Zone 完全匹配
2 bj.bj-02 Region 匹配 Zone 不匹配
3 sh.sh-01/sh-02 Region 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 等)。

接入層解決方案功能對比

以下是騰訊雲容器環境下常見的接入層解決方案功能對比。

多叢集灰度釋出/跨叢集容災 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/

相關文章