服務網格入口網關對比
本分來自:https://www.sohu.com/a/314009713_465944
目錄:
- 内部服務間的通信
- Cluster IP
- Istio Sidecar Proxy
- 如何從外部網絡通路?
- NodePort
- LoadBalancer
- Ingress
- 如何為服務網格選擇入口網關?
- K8s Ingress
- Istio Gateway
- 應用對API Gateway的需求
- 采用API Gateway + Sidecar Proxy作為服務網格的流量入口
- 參考資料
在啟用了Istio服務網格的Kubernetes叢集中,預設情況下隻能在叢集内部通路網格中的服務,要如何才能從外部網絡通路這些服務呢? Kubernetes和Istio提供了NodePort,LoadBalancer,Kubernetes Ingress,Istio Gateway等多種外部流量入口的方式,面對這麼多種方式,我們在産品部署中應該如何選擇?
本文将對Kubernetes和Istio對外提供服務的各種方式進行詳細介紹和對比分析,并根據分析結果提出一個可用于産品部署的解決方案。
說明:閱讀本文要求讀者了解Kubernetes和Istio的基本概念,包括Pod、Service、NodePort、LoadBalancer、Ingress、Gateway、VirtualService等。如對這些概念不熟悉,可以在閱讀過程中參考文後的相關連結。
内部服務間的通信
首先,我們來回顧一下Kubernetes叢集内部各個服務之間互相通路的方法。
1
Cluster IP
Kubernetes以Pod作為應用部署的最小機關。kubernetes會根據Pod的聲明對其進行排程,包括建立、銷毀、遷移、水準伸縮等,是以Pod 的IP位址不是固定的,不友善直接采用Pod IP對服務進行通路。
為解決該問題,Kubernetes提供了Service資源,Service對提供同一個服務的多個Pod進行聚合。一個Service提供一個虛拟的Cluster IP,後端對應一個或者多個提供服務的Pod。在叢集中通路該Service時,采用Cluster IP即可,Kube-proxy負責将發送到Cluster IP的請求轉發到後端的Pod上。
Kube-proxy是一個運作在每個節點上的go應用程式,支援三種工作模式:
- userspace 該模式下kube-proxy會為每一個Service建立一個監聽端口。發向Cluster IP的請求被Iptables規則重定向到Kube-proxy監聽的端口上,Kube-proxy根據LB算法選擇一個提供服務的Pod并和其建立連結,以将請求轉發到Pod上。 該模式下,Kube-proxy充當了一個四層Load balancer的角色。由于kube-proxy運作在userspace中,在進行轉發處理時會增加兩次核心和使用者空間之間的資料拷貝,效率較另外兩種模式低一些;好處是當後端的Pod不可用時,kube-proxy可以重試其他Pod。

Kube-proxy userspace模式(來自Kubernetes官網文檔[1])
- iptables 為了避免增加核心和使用者空間的資料拷貝操作,提高轉發效率,Kube-proxy提供了iptables模式。在該模式下,Kube-proxy為service後端的每個Pod建立對應的iptables規則,直接将發向Cluster IP的請求重定向到一個Pod IP。 該模式下Kube-proxy不承擔四層代理的角色,隻負責建立iptables規則。該模式的優點是較userspace模式效率更高,但不能提供靈活的LB政策,當後端Pod不可用時也無法進行重試。
Kube-proxy iptables模式(來自Kubernetes官網文檔[1])
- ipvs 該模式和iptables類似,kube-proxy監控Pod的變化并建立相應的ipvs rules。ipvs也是在kernel模式下通過netfilter實作的,但采用了hash table來存儲規則,是以在規則較多的情況下,Ipvs相對iptables轉發效率更高。除此以外,ipvs支援更多的LB算法。如果要設定kube-proxy為ipvs模式,必須在作業系統中安裝IPVS核心子產品。
Kube-proxy ipvs模式(來自Kubernetes官網文檔[1])
2
Istio Sidecar Proxy
Cluster IP解決了服務之間互相通路的問題,但從上面Kube-proxy的三種模式可以看到,Cluster IP的方式隻提供了服務發現和基本的LB功能。如果要為服務間的通信應用靈活的路由規則以及提供Metrics collection,distributed tracing等服務管控功能,就必須得依靠Istio提供的服務網格能力了。
在Kubernetes中部署Istio後,Istio通過iptables和Sidecar Proxy接管服務之間的通信,服務間的互相通信不再通過Kube-proxy,而是通過Istio的Sidecar Proxy進行。請求流程是這樣的:Client發起的請求被iptables重定向到Sidecar Proxy,Sidecar Proxy根據從控制面擷取的服務發現資訊和路由規則,選擇一個後端的Server Pod建立連結,代理并轉發Client的請求。
Istio Sidecar Proxy和Kube-proxy的userspace模式的工作機制類似,都是通過在使用者空間的一個代理來實作用戶端請求的轉發和後端多個Pod之間的負載均衡。兩者的不同點是:Kube-Proxy工作在四層,而Sidecar Proxy則是一個七層代理,可以針對HTTP,GRPS等應用層的語義進行處理和轉發,是以功能更為強大,可以配合控制面實作更為靈活的路由規則和服務管控功能。
Istio Sidecar Proxy
如何從外部網絡通路?
Kubernetes的Pod IP和Cluster IP都隻能在叢集内部通路,而我們通常需要從外部網絡上通路叢集中的某些服務,Kubernetes提供了下述幾種方式來為叢集提供外部流量入口。
1
Nodeport
NodePort在叢集中的主機節點上為Service提供一個代理端口,以允許從主機網絡上對Service進行通路。Kubernetes官網文檔隻介紹了NodePort的功能,并未對其實作原理進行解釋。下面我們通過實驗來分析NodePort的實作機制。
www.katacoda.com 這個網站提供了一個互動式的Kubernetes playground,注冊即可免費實驗kubernetes的相關功能,下面我們就使用Katacoda來分析Nodeport的實作原理。
在浏覽器中輸入這個網址:https://www.katacoda.com/courses/kubernetes/networking-introduction, 打開後會提供了一個實驗用的Kubernetes叢集,并可以通過網元模拟Terminal連接配接到叢集的Master節點。
執行下面的指令建立一個nodeport類型的service。
kubectl apply -f nodeport.yaml
檢視建立的service,可以看到kubernetes建立了一個名為webapp-nodeport-svc的service,并為該service在主機節點上建立了30080這個Nodeport。
master $ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 36m
webapp1-nodeport-svc NodePort 10.103.188.73 <none> 80:30080/TCP 3m
webapp-nodeport-svc後端對應兩個Pod,其Pod的IP分别為10.32.0.3和10.32.0.5。
master $ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IPNODE NOMINATED NODE
webapp1-nodeport-deployment-785989576b-cjc5b 1/1 Running 0 2m 10.32.0.3
webapp1-nodeport-deployment-785989576b-tpfqr 1/1 Running 0 2m 10.32.0.5
通過netstat指令可以看到Kube-proxy在主機網絡上建立了30080監聽端口,用于接收從主機網絡進入的外部流量。
master $ netstat -lnp|grep 30080
tcp6 0 0 :::30080 :::* LISTEN 7427/kube-proxy
下面是Kube-proxy建立的相關iptables規則以及對應的說明。可以看到Kube-proxy為Nodeport建立了相應的IPtable規則,将發向30080這個主機端口上的流量重定向到了後端的兩個Pod IP上。
iptables-save > iptables-dump
# Generated by iptables-save v1.6.0 on Thu Mar 28 07:33:57 2019
*nat
# Nodeport規則鍊
:KUBE-NODEPORTS - [0:0]
# Service規則鍊
:KUBE-SERVICES - [0:0]
# Nodeport和Service共用的規則鍊
:KUBE-SVC-J2DWGRZTH4C2LPA4 - [0:0]
:KUBE-SEP-4CGFRVESQ3AECDE7 - [0:0]
:KUBE-SEP-YLXG4RMKAICGY2B3 - [0:0]
# 将host上30080端口的外部tcp流量轉到KUBE-SVC-J2DWGRZTH4C2LPA4鍊
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/webapp1-nodeport-svc:"-m tcp --dport 30080 -j KUBE-SVC-J2DWGRZTH4C2LPA4
#将發送到Cluster IP 10.103.188.73的内部流量轉到KUBE-SVC-J2DWGRZTH4C2LPA4鍊
KUBE-SERVICES -d 10.103.188.73/32 -p tcp -m comment --comment "default/webapp1-nodeport-svc: cluster IP"-m tcp --dport 80 -j KUBE-SVC-J2DWGRZTH4C2LPA4
#将發送到webapp1-nodeport-svc的流量轉交到第一個Pod(10.32.0.3)相關的規則鍊上,比例為50%
-A KUBE-SVC-J2DWGRZTH4C2LPA4 -m comment --comment "default/webapp1-nodeport-svc:"-m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-YLXG4RMKAICGY2B3
#将發送到webapp1-nodeport-svc的流量轉交到第二個Pod(10.32.0.5)相關的規則鍊上
-A KUBE-SVC-J2DWGRZTH4C2LPA4 -m comment --comment "default/webapp1-nodeport-svc:"-j KUBE-SEP-4CGFRVESQ3AECDE7
#将請求重定向到Pod 10.32.0.3
-A KUBE-SEP-YLXG4RMKAICGY2B3 -p tcp -m comment --comment "default/webapp1-nodeport-svc:"-m tcp -j DNAT --to-destination 10.32.0.3:80
#将請求重定向到Pod 10.32.0.5
-A KUBE-SEP-4CGFRVESQ3AECDE7 -p tcp -m comment --comment "default/webapp1-nodeport-svc:"-m tcp -j DNAT --to-destination 10.32.0.5:80
從上面的實驗可以看到,通過将一個Service定義為NodePort類型,Kubernetes會通過叢集中node上的Kube-proxy為該Service在主機網絡上建立一個監聽端口。Kube-proxy并不會直接接收該主機端口進入的流量,而是會建立相應的Iptables規則,并通過Iptables将從該端口收到的流量直接轉發到後端的Pod中。
NodePort的流量轉發機制和Cluster IP的iptables模式類似,唯一不同之處是在主機網絡上開了一個“NodePort”來接受外部流量。從上面的規則也可以看出,在建立Nodeport時,Kube-proxy也會同時為Service建立Cluster IP相關的iptables規則。
備注:除采用iptables進行流量轉發,NodePort應該也可以提供userspace模式以及ipvs模式,這裡未就這兩種模式進行實驗驗證。
從分析得知,在NodePort模式下,叢集内外部的通訊如下圖所示:
NodePort
2
LoadBalancer
NodePort提供了一種從外部網絡通路Kubernetes叢集内部Service的方法,但該方法存在下面一些限制,導緻這種方式主要适用于程式開發,不适合用于産品部署:
- Kubernetes cluster host的IP必須是一個well-known IP,即用戶端必須知道該IP。但Cluster中的host是被作為資源池看待的,可以增加删除,每個host的IP一般也是動态配置設定的,是以并不能認為host IP對用戶端而言是well-known IP。
- 用戶端通路某一個固定的host IP的方式存在單點故障。假如一台host當機了,kubernetes cluster會把應用 reload到另一節點上,但用戶端就無法通過該host的nodeport通路應用了。
- 通過一個主機節點作為網絡入口,在網絡流量較大時存在性能瓶頸。
為了解決這些問題,Kubernetes提供了LoadBalancer。通過将Service定義為LoadBalancer類型,Kubernetes在主機節點的NodePort前提供了一個四層的負載均衡器。該四層負載均衡器負責将外部網絡流量分發到後面的多個節點的NodePort端口上。
下圖展示了Kubernetes如何通過LoadBalancer方式對外提供流量入口,圖中LoadBalancer後面接入了兩個主機節點上的NodePort,後端部署了三個Pod提供服務。根據叢集的規模,可以在LoadBalancer後面可以接入更多的主機節點,以進行負荷分擔。
NodeBalancer
備注:LoadBalancer類型需要雲服務提供商的支援,Service中的定義隻是在Kubernetes配置檔案中提出了一個要求,即為該Service建立Load Balancer,至于如何建立則是由Google Cloud或Amazon Cloud等雲服務商提供的,建立的Load Balancer的過程不在Kubernetes Cluster的管理範圍中。
目前AWS, Azure, CloudStack, GCE 和 OpenStack 等主流的公有雲和私有雲提供商都可以為Kubernetes提供Load Balancer。一般來說,公有雲提供商還會為Load Balancer提供一個External IP,以提供Internet接入。如果你的産品沒有使用雲提供商,而是自建Kubernetes Cluster,則需要自己提供LoadBalancer。
3
Ingress
LoadBalancer類型的Service提供的是四層負載均衡器,當隻需要向外暴露一個服務的時候,采用這種方式是沒有問題的。但當一個應用需要對外提供多個服務時,采用該方式則要求為每一個四層服務(IP+Port)都建立一個外部load balancer。
一般來說,同一個應用的多個服務/資源會放在同一個域名下,在這種情況下,建立多個Load balancer是完全沒有必要的,反而帶來了額外的開銷和管理成本。另外直接将服務暴露給外部使用者也會導緻了前端和後端的耦合,影響了後端架構的靈活性,如果以後由于業務需求對服務進行調整會直接影響到用戶端。
在這種情況下,我們可以通過使用Kubernetes Ingress來統一網絡入口。Kubernetes Ingress聲明了一個應用層(OSI七層)的負載均衡器,可以根據HTTP請求的内容将來自同一個TCP端口的請求分發到不同的Kubernetes Service,其功能包括:
- 按HTTP請求的URL進行路由
同一個TCP端口進來的流量可以根據URL路由到Cluster中的不同服務,如下圖所示:
基于URL路由
- 按HTTP請求的Host進行路由
同一個IP進來的流量可以根據HTTP請求的Host路由到Cluster中的不同服務,如下圖所示:
基于Host路由
Ingress 規則定義了對七層網關的要求,包括URL分發規則,基于不同域名的虛拟主機,SSL證書等。Kubernetes使用Ingress Controller 來監控Ingress規則,并通過一個七層網關來實作這些要求,一般可以使用Nginx,HAProxy,Envoy等。
雖然Ingress Controller通過七層網關為後端的多個Service提供了統一的入口,但由于其部署在叢集中,是以并不能直接對外提供服務。實際上Ingress需要配合NodePort和LoadBalancer才能提供對外的流量入口,如下圖所示:
采用Ingress, NodePortal和LoadBalancer提供外部流量入口的拓撲結構
上圖描述了如何采用Ingress配合NodePort和Load Balancer為叢集提供外部流量入口,從該拓撲圖中可以看到該架構的伸縮性非常好,在NodePort,Ingress,Pod等不同的接入層面都可以對系統進行水準擴充,以應對不同的外部流量要求。
上圖隻展示了邏輯架構,下面的圖展示了具體的實作原理:
采用Ingress, NodePortal和LoadBalancer提供外部流量入口的實作原理
流量從外部網絡到達Pod的完整路徑如下:
- 外部請求先通過四層Load Balancer進入内部網絡
- Load Balancer将流量分發到後端多個主機節點上的NodePort(userspace轉發)
- 請求從NodePort進入到Ingress Controller(iptabes規則,Ingress Controller本身是一個NodePort類型的Service)
- Ingress Controller根據Ingress rule進行七層分發,根據HTTP的URL和Host将請求分發給不同的Service(userspace轉發)
- Service将請求最終導入到後端提供服務的Pod中(iptabes規則)
從前面的介紹可以看到,K8S Ingress提供了一個基礎的七層網關功能的抽象定義,其作用是對外提供一個七層服務的統一入口,并根據URL/HOST将請求路由到叢集内部不同的服務上。
如何為服務網格選擇入口網關?
在Istio服務網格中,通過為每個Service部署一個sidecar代理,Istio接管了Service之間的請求流量。控制面可以對網格中的所有sidecar代理進行統一配置,實作了對網格内部流量的路由控制,進而可以實作灰階釋出,流量鏡像,故障注入等服務管控功能。但是,Istio并沒有為入口網關提供一個較為完善的解決方案。
1
K8s Ingress
在0.8版本以前,Istio預設采用K8s Ingress來作為Service Mesh的流量入口。K8s Ingress統一了應用的流量入口,但存在兩個問題:
- K8s Ingress是獨立在Istio體系之外的,需要單獨采用Ingress rule進行配置,導緻系統入口和内部存在兩套互相獨立的路由規則配置,運維和管理較為複雜。
- K8s Ingress rule的功能較弱,不能在入口處實作和網格内部類似的路由規則,也不具備網格sidecar的其它能力,導緻難以從整體上為應用系統實作灰階釋出、分布式跟蹤等服務管控功能。
采用Kubernetes Ingress作為服務網格的流量入口
2
Istio Gateway
Istio社群意識到了Ingress和Mesh内部配置割裂的問題,是以從0.8版本開始,社群采用了 Gateway 資源代替K8s Ingress來表示流量入口。
Istio Gateway資源本身隻能配置L4-L6的功能,例如暴露的端口,TLS設定等;但Gateway可以和綁定一個VirtualService,在VirtualService 中可以配置七層路由規則,這些七層路由規則包括根據按照服務版本對請求進行導流,故障注入,HTTP重定向,HTTP重寫等所有Mesh内部支援的路由規則。
Gateway和VirtualService用于表示Istio Ingress的配置模型,Istio Ingress的預設實作則采用了和Sidecar相同的Envoy proxy。
通過該方式,Istio控制面用一緻的配置模型同時控制了入口網關和内部的sidecar代理。這些配置包括路由規則,政策檢查、Telementry收集以及其他服務管控功能。
采用 Istio Ingress Gateway作為服務網格的流量入口
3
應用對API Gateway的需求
采用Gateway和VirtualService實作的Istio Ingress Gateway提供了網絡入口處的基礎通信功能,包括可靠的通信和靈活的路由規則。但對于一個服務化應用來說,網絡入口除了基礎的通訊功能之外,還有一些其他的應用層功能需求,例如:
- 第三方系統對API的通路控制
- 使用者對系統的通路控制
- 修改請求/傳回資料
- 服務API的生命周期管理
- 服務通路的SLA、限流及計費
- ….
Kubernetes ingress, Istio gateway and API gateway的功能對比
API Gateway需求中很大一部分需要根據不同的應用系統進行定制,目前看來暫時不大可能被納入K8s Ingress或者Istio Gateway的規範之中。為了滿足這些需求,湧現出了各類不同的k8s Ingress Controller以及Istio Ingress Gateway實作,包括Ambassador ,Kong, Traefik, Gloo等。
這些網關産品在實作在提供基礎的K8s Ingress能力的同時,提供了強大的API Gateway功能,但由于缺少統一的标準,這些擴充實作之間互相之間并不相容。而且遺憾的是,目前這些Ingress controller都還沒有正式提供和Istio 控制面內建的能力。
備注:
Ambassador将對Istio路由規則的支援納入了Roadmap https://www.getambassador.io/user-guide/with-istio/
Istio聲稱支援Istio-Based Route Rule Discovery (尚處于實驗階段) https://gloo.solo.io/introduction/architecture/
4
采用API Gateway + Sidecar Proxy作為服務網格的流量入口
在目前難以找到一個同時具備API Gateway和Isito Ingress能力的網關的情況下,一個可行的方案是使用API Gateway和Sidecar Proxy一起為服務網格提供外部流量入口。
由于API Gateway已經具備七層網關的功能,Mesh Ingress中的Sidecar隻需要提供VirtualService資源的路由能力,并不需要提供Gateway資源的網關能力,是以采用Sidecar Proxy即可。網絡入口處的Sidecar Proxy和網格内部應用Pod中Sidecar Proxy的唯一一點差別是:該Sidecar隻接管API Gateway向Mesh内部的流量,并不接管外部流向API Gateway的流量;而應用Pod中的Sidecar需要接管進入應用的所有流量。
采用API Gateway + Sidecar Proxy為服務網格提供流量入口
備注:在實際部署時,API Gateway前端需要采用NodePort和LoadBalancer提供外部流量入口。為了突出主題,對上圖進行了簡化,沒有畫出NodePort和LoadBalancer。
采用API Gateway和Sidecar Proxy一起作為服務網格的流量入口,既能夠通過對網關進行定制開發滿足産品對API網關的各種需求,又可以在網絡入口處利用服務網格提供的靈活的路由能力和分布式跟蹤,政策等管控功能,是服務網格産品入口網關的一個理想方案。
性能方面的考慮:從上圖可以看到,采用該方案後,外部請求的處理流程在入口處增加了Sidecar Proxy這一跳,是以該方式會帶來少量的性能損失,但該損失是完全可以接受的。
對于請求時延而言,在服務網格中,一個外部請求本來就要經過較多的代理和應用程序的處理,在Ingress處增加一個代理對整體的時延影響基本忽略不計,而且對于絕大多數應用來說,網絡轉發所占的時間比例本來就很小,99%的耗時都在業務邏輯。如果系統對于增加的該時延非常敏感,則我建議重新考慮是否應該采用微服務架構和服務網格,畢竟任何架構模式都不是萬能的,不能因為有了錘子,看什麼都像釘子。
對于吞吐量而言,如果入口處的網絡吞吐量存在瓶頸,則可以通過對API Gateway + Sidecar Proxy組成的Ingress整體進行水準擴充,來對入口流量進行負荷分擔,以提高網格入口的網絡吞吐量。
posted on
2020-09-16 17:55
BetterManEddy
閱讀(131)
評論(0)
編輯
收藏
舉報