作者
冉昕,騰訊雲服務網格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 理念得以很好的實施,微服務的迭代步伐加快,經常需要滾動更新。此時的入口流量管理面臨如下新挑戰:
- 需要與 Kubernetes 集成,支持轉發流量到指定 Pod。
- 更新迭代速度加快,對服務新版本灰度發布的訴求更加強烈。
- 出現集群概念,集群之間的服務發現是隔離的,接入層需支持跨集群的服務發現(即接入層可選擇 backend 為多個集群的 Pod );這區別於傳統物理機/虛擬機階段,沒有集群隔離,只需保證網絡聯通性,即可配置接入層后端為任意對應服務的 IP 地址。
- 傳統階段到雲原生階段的遷移過程中,出現 VM、容器環境混布的情況。
基於上述挑戰,出現了以下容器環境的接入層流量管理解決方案:
- Kubernetes 官方定義的 Ingress API:老牌網絡代理(e.g. Nginx,HAProxy)或雲廠商的負載均衡產品(e.g. AWS Elastic Load Balancer,騰訊雲 CLB)都實現了各自的 Ingress Controller,作為單個集群的入口流量管理解決方案。灰度發布、鑒權限流等能力,視 Ingress Controller 的能力,可通過 Annotation 擴展,部分 Ingress Controller 還設計了自己的流量管理模型和語法。
- Service Mesh Ingress:服務網格的服務發現和管理界限大於集群緯度,以 Istio Ingress Gateway 為例,基於 Istio 跨集群的服務發現能力,backend 可以來自不同集群的服務,同時還支持注冊在網格內運行在虛擬機上的服務。Istio 也設計了自己的管理模型和語法,聲明式支持配置一致的南北 + 東西向流量管理。
- 沿用原有 VM 上部署的網絡代理,轉發流量至 VM 服務或 Kubernetes 集群的服務。
下面本文將從雲原生容器化環境入口流量管理使用場景切入,帶您了解雲原生接入層流量管理的各類解決方案及優劣對比。
雲原生接入層流量管理場景與解決方案
場景一:基礎流量管理
入口流量管理的首個使用場景是需要將服務暴露給外部,供客戶端調用。常見的方式是將服務按 URL 暴露,例如一個電商網站,需要將 /login 的請求路由到登陸服務,將 /product 的請求路由到商品服務等,該場景要求接入層具備基於流量內容路由的能力。
方案:Load Balancer + NodePort
在容器化的早期階段,應用同時部署在虛擬機和 Kubernetes 集群上,很多用戶會使用原有負載均衡(e.g. Nginx, 騰訊雲 CLB)將請求分別轉發到虛擬機和容器,同時受限於容器網絡方案,原有負載均衡不能直接訪問 Pod IP,因此需要通過 NodePort 暴露集群內的服務。
但是該方案存在以下問題:
- NodePort 端口數量有限(默認 30000-32767)
- 隨着集群規模的擴大,Nginx 配置文件越來越復雜,不易管理
- 用戶將應用發布到 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 集群環境下的灰度發布和容災。
- 創建服務網格,添加兩個部署服務的服務發現集群(基礎監控指標自動對接到雲監控,可在控制台查看,可視情況開啟雲原生監控,滿足自定義監控訴求),勾選啟用 Ingress Gateway
- 使用 Destination Rule 定義 frontend 服務的版本(frontend 服務在兩個集群均有同樣的部署)
- 使用 Gateway 配置 ingress gateway 監聽規則,開啟 443 端口 https 訪問,使用騰訊雲 SSL 平台服務器證書
- 使用 VirtualService 配置路由規則,50% 流量路由至 v1 版本,50% 路由至 v2 版本
- 有訪問請求后,查看工作負載(frontend,frontend-canary)監控,兩個版本均有流量,比例大致 1:1
- 灰度結束,更改權重,100% 的流量均路由至 v2 版本,再次查看工作負載的監控數據,發現所有流量都已請求至 frontend-canary
- 下面我們通過調整其中一個集群的 frontend 服務工作負載 Pod 數量為 0 來模擬其中一個集群 frontend 服務故障情況,發現其中一個集群 frontend 服務故障后,仍可以正常訪問該服務,查看另一集群的 frontend 服務的工作負載監控,會發現入帶寬增加了一倍,表明其中一個集群的服務故障后,流量容災切到了另一集群。
- 如有擴展東西向流量管理的需要,可以給業務注入 envoy sidecar,即可使用同一套 Istio API 實現南北東西向流量一致性管理,開箱即用網絡拓撲、調用追蹤等可觀測性功能。
騰訊雲服務網格 TCM,是騰訊雲完全兼容 Istio 的 Service Mesh 產品,目前已實現了控制面組件托管,使用 TCM Ingress Gateway 只需要部署一組數據面 envoy pod 在業務集群,即可開箱即用上述 Istio Ingress Gateway 的所有入口流量管理能力。同時,TCM 集成了騰訊雲監控、證書周邊產品,提供開箱即用的可觀測能力和證書配置功能。
結語
本文由業務部署發展的兩個階段引入,介紹了:
- 雲原生容器化環境下接入層流量管理的典型場景。
- 入口流量管理的解決方案及優劣對比。
- 以騰訊雲服務網格 TCM 為例,演示 Service Mesh Ingress 多集群環境下灰度發布及服務跨集群容災的能力。
主要結論有:
- 對於簡單的 HTTP 流量的路由,使用 Kubernetes 原生 Ingress 配置非常容易,一些 Ingress Controller (e.g. Nginx, Traefik)也通過 annotation 或 CRD 擴展了原生 Ingress 的功能,但仍是集群級別的流量入口。
- 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/