服務網格(Service mesh)是當前新興的架構模式,越來越受到人們的青睞。與Kubernetes一起,服務網格可以形成一個強大的平臺,它可以解決在微服務叢集或服務基礎設施上發現的高度分散式環境中出現的技術需求。服務網格是一個專門的基礎設施層,用於促進微服務之間的服務到服務通訊。
服務網格解決了基於微服務的應用中典型的通訊需求,包括加密隧道、健康檢查、斷路器、負載均衡以及流量許可。如果離開微服務來解決這些需求,會導致開發過程中產生高昂的費用和耗時。
在本文中,我們將對服務網格架構模式解決的最常見的微服務通訊需求進行概述。
微服務動態和內在挑戰
當你意識到微服務實現了相當多的與最初分配給它們的業務邏輯無關的程式碼時,問題就出現了。此外,有可能你有多個微服務在非標準化的流程中實現了類似的功能。換句話說,微服務開發團隊應該專注於業務邏輯,並將低階通訊能力留給特定的層。
繼續推進我們的方案,需要考慮微服務的內在動態。在給定的時間內,你可能由於以下幾個原因而擁有一個微服務的多個例項:
- 吞吐量(Throughput):根據傳入的請求,你可能擁有更多或更少的微服務例項
- 金絲雀釋出
- 藍綠部署
- A/B測試
簡而言之,微服務到微服務的通訊有特定的需求和問題需要解決。以下圖片展示了這一方案:
該圖示描述了幾個技術挑戰。顯然,Microservice 1的主要職責是均衡所有Microservice 2例項之間的負載。因此,Microservice 1必須弄清楚我們在請求時刻有多少個Microservice 2例項。換句話說,Microservice 1必須實現服務發現和負載均衡。
另一方面,Microservice 2必須實現一些服務註冊功能以告知Microservice 1何時有全新的例項。
想要擁有一個完全動態的環境,以下這些功能應該是微服務開發的一部分:
- 流量控制:負載均衡的自然演變。我們想指定應該傳送到每個Microservice 2例項的請求數量。 在Microservice
1和2之間加密通訊 - 藉助斷路器和健康檢查以解決和克服網路問題
總而言之,主要問題是開發團隊花費了大量資源編寫十分複雜的程式碼,而這些程式碼與微服務預期交付的業務邏輯不直接相關。
有潛力的解決方案
如何將所有微服務都可以呼叫的外部標準化元件中的所有非功能和操作功能外部化?例如,下圖編譯了所有功能,這些功能不屬於給定的微服務。因此,在確定所有功能之後,我們需要決定在哪裡實現它們。
Solution #1 :將所有功能封裝在一個library中
開發者將負責呼叫library提供的函式來解決微服務通訊需求。
這個解決方案有幾個缺點:
這是一個緊密耦合的解決方案,意味著微服務高度依賴於library
這個模式對於分佈和升級新版本的library來說並不容易
這不符合微服務多語言的原則,因為這會將不同的程式語言應用於不同的上下文。
Solution #2:透明代理(Transparent Proxy)
這個解決方案實現了同樣的功能集合。但是,採用了一種非常不同的方法:每個微服務都有一個特定的元件,扮演代理的角色,負責處理它的傳入和傳出流量。代理解決了我們之前描述的庫的缺點,具體如下:
- 代理是透明的,這意味著微服務不會意識到它正在附近執行並實現了與其他微服務進行通訊所需的所有功能。
- 由於它是一個透明的代理,開發者不需要改變程式碼來引用代理。因此,從微服務開發的角度來看,升級代理將是一個並不會對開發流程造成太大影響。
- 代理可以使用微服務使用的不同技術和程式語言進行開發。
服務網格架構模式
雖然透明代理的方法給微服務開發團隊和微服務通訊需求帶來了一些好處,但仍有一些缺失的部分:
- 代理只是執行策略來實現通訊需求,例如負載均衡、金絲雀釋出等。
- 由什麼來負責定義這樣的策略,並在所有執行的代理上釋出呢?
解決方案架構需要另一個元件,這些元件將被管理員用來定義策略,它將負責向代理傳播策略。
以下圖片展示了最終架構,也就是服務網格模式:
如你所見,該模式包含了我們所描述的兩個主要元件。
- 資料平面:也被稱為sidecar,它扮演著透明代理的角色。同樣,每個微服務都會有自己的資料平面,攔截所有的入站和出站流量,並應用之前描述的策略。
- 控制平面:由管理員用來定義策略併發布到資料平面。
一些重要的事情需要注意:
- 這是一個 "push-based "的架構。資料平面不做 "呼叫 "來獲取策略——那將會消耗網路。
- 資料平面通常向控制平面或特定的基礎設施報告使用指標。
手把手教你使用Rancher、Kong和Kong Mesh
Kong提供了一個企業級的綜合服務連線平臺,其包括了API gateway、Kubernetes ingress controller以及服務網格實現。該平臺允許使用者部署多個環境,如本地、混合雲、多區域以及多雲環境。
讓我們藉助執行在獨立於雲架構(cloud-agnostic)的Kubernetes叢集上的金絲雀釋出來實現服務網格,該叢集可能包括GKE叢集或任何其他的Kubernetes發行版。服務網格將由Kong Mesh實現,並由Kong for Kubernetes作為Kubernetes Ingress Controller。一般而言,ingress controller負責定義進入你的Kubernetes叢集的入口點,暴露部署在其內部的微服務,並對其實行消費策略。
首先,確保你已經安裝Rancher以及正在執行一個由Rancher管理的Kubernetes叢集。在登入到Rancher之後,選在我們將要使用的Kubernetes叢集,在本例中為“kong-rancher”。點選Cluster Explorer。你將會重定向到以下頁面:
現在,讓我們從服務網格開始:
1、 Kong Mesh Helm Chart
回到Rancher Cluster Manger主頁並再次選擇你的叢集。點選選單欄的“Tools”選項然後點選Catalogs,以建立一個新的catalog。點選Add Catalog按鈕,將Kong Mesh的Helm chart收錄其中(https://kong.github.io/kong-mesh-charts/)。
選擇Global作為範圍,Helm v3作為Helm版本。
現在點選Apps和Launch來檢視在Catalog中可用的Kong Mesh。請注意,Kong作為Rancher的合作伙伴預設提供了Kong for Kubernetes的Helm chart:
2、 安裝Kong Mesh
點選頂部選單欄Namespaces選項並建立一個“kong-mesh-system”名稱空間。
將滑鼠移到kong-rancher頂部選單選項上,點選kong-rancher活動叢集。
點選Launch kubetcl
建立一個名為“license.json”的檔案,用於存放你從Kong Mesh收到的license。格式如下:
{“license”:
{“version”:1,“signature”:“6a7c81af4b0a42b380be25c2816a2bb1d761c0f906ae884f93eeca1fd16c8b5107cb6997c958f45d247078ca50a25399a5f87d546e59ea3be28284c3075a9769”,“payload”:
{“customer”:“Kong_SE_Demo_H1FY22”,“license_creation_date”:“2020-11-30”,“product_subscription”:“Kong Enterprise Edition”,“support_plan”:“None”,“admin_seats”:“5”,“dataplanes”:“5”,“license_expiration_date”:“2021-06-30”,“license_key”:“XXXXXXXXXXXXX”}}}
現在使用以下命令建立一個Kubernetes通用金鑰:
kubectl create secret generic kong-mesh-license -n kong-mesh-system --from-file=./license.json
關閉kubectl會話,點選Default專案以及頂部選單欄的Apps。點選Launch按鈕並選擇kong-mesh Helm chart。
點選Use an existing namespace並選擇我們剛剛建立的那個。這有幾個引數(https://artifacthub.io/packages/helm/kong-mesh/kong-mesh)來配置Kong Mesh,但我們將保留所有預設值。點選Launch之後,你應該看到Kong Mesh應用程式部署完成。
你可以再次使用Rancher Cluster Explorer來檢查安裝。點選左側選單的Pods並選擇kong-mesh-system的名稱空間。
你也可以像這樣使用kubectl:
NAMESPACE NAME READY STATUS RESTARTS AGE
cattle-system cattle-cluster-agent-785fd5f54d-r7x8r 1/1 Running 0 75m
fleet-system fleet-agent-77c78f9c74-f97tv 1/1 Running 0 75m
kong-mesh-system kuma-control-plane-5b9c6f4598-nvq8q 1/1 Running 0 16m
kube-system event-exporter-gke-666b7ffbf7-n9lfl 2/2 Running 0 76m
kube-system fluentbit-gke-xqsdv 2/2 Running 0 76m
kube-system gke-metrics-agent-gjrqr 1/1 Running 0 76m
kube-system konnectivity-agent-4c4hf 1/1 Running 0 76m
kube-system kube-dns-66d6b7c877-tq877 4/4 Running 0 76m
kube-system kube-dns-autoscaler-5c78d65cd9-5hcxs 1/1 Running 0 76m
kube-system kube-proxy-gke-c-kpwnf-default-0-be059c1c-49qp 1/1 Running 0 76m
kube-system l7-default-backend-5b76b455d-v6dvg 1/1 Running 0 76m
kube-system metrics-server-v0.3.6-547dc87f5f-qntjf 2/2 Running 0 75m
kube-system prometheus-to-sd-fdf9j 1/1 Running 0 76m
kube-system stackdriver-metadata-agent-cluster-level-68d94db6-64n4r 2/2 Running 1 75m
3、 微服務部署
我們的Service Mesh部署是基於一個簡單的微服務到微服務的通訊場景。由於我們執行的是金絲雀釋出,被呼叫的微服務有兩個版本:
“magnanimo”:通過Kong暴露Kubernetes ingress controller。
“benigno”:提供了一個 “hello” endpoint,在這個端點中,它呼應了當前的datetime。它有一個金絲雀釋出,會傳送一個稍微不同的響應。
下圖展示了這一架構:
建立一個帶有sidecar注入註釋的名稱空間。你可以再次使用Rancher Cluster Manager:選擇你的叢集,然後單擊Projects/Namespaces。點選Add Namespace。輸入 “kong-mesh-app” 作為名稱,幷包含一個帶有 “kuma.io/sidecar-injection” 鍵和 “enabled” 作為其值的註釋。
當然,你也可以選擇使用kubectl
kubectl create namespace kong-mesh-app
kubectl annotate namespace kong-mesh-app kuma.io/sidecar-injection=enabled
Submit the following declaration to deploy Magnanimo injecting the Kong Mesh data plane
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: magnanimo
namespace: kong-mesh-app
spec:
replicas: 1
selector:
matchLabels:
app: magnanimo
template:
metadata:
labels:
app: magnanimo
spec:
containers:
- name: magnanimo
image: claudioacquaviva/magnanimo
ports:
- containerPort: 4000
---
apiVersion: v1
kind: Service
metadata:
name: magnanimo
namespace: kong-mesh-app
labels:
app: magnanimo
spec:
type: ClusterIP
ports:
- port: 4000
name: http
selector:
app: magnanimo
EOF
使用Rancher Cluster Manager檢查你的部署。將滑鼠移動至kong-rancher選單上,點選Default專案,可以看到當前的部署情況:
點選magnanimo檢查部署的細節,包括其pods:
點選magnanimo pod,檢查其內部執行的容器。
我們可以看到,pod有兩個執行的容器:
- magnanimo:微服務實際執行的地方。
- kuma-sidecar:在部署的時候注入,作為Kong Mesh的資料平面。
同理,部署Benigno的時候,也有自己的sidecar:
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: benigno-v1
namespace: kong-mesh-app
spec:
replicas: 1
selector:
matchLabels:
app: benigno
template:
metadata:
labels:
app: benigno
version: v1
spec:
containers:
- name: benigno
image: claudioacquaviva/benigno
ports:
- containerPort: 5000
---
apiVersion: v1
kind: Service
metadata:
name: benigno
namespace: kong-mesh-app
labels:
app: benigno
spec:
type: ClusterIP
ports:
- port: 5000
name: http
selector:
app: benigno
EOF
And finally, deploy Benigno canary release. Notice that the canary release will be abstracted by the same Benigno Kubernetes Service created before:
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: benigno-v2
namespace: kong-mesh-app
spec:
replicas: 1
selector:
matchLabels:
app: benigno
template:
metadata:
labels:
app: benigno
version: v2
spec:
containers:
- name: benigno
image: claudioacquaviva/benigno\_rc
ports:
- containerPort: 5000
EOF
使用以下命令檢查部署和Pods:
$ kubectl get pod --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
cattle-system cattle-cluster-agent-785fd5f54d-r7x8r 1/1 Running 0 75m
fleet-system fleet-agent-77c78f9c74-f97tv 1/1 Running 0 75m
kong-mesh-app benigno-v1-fd4567d95-drnxq 2/2 Running 0 110s
kong-mesh-app benigno-v2-b977c867b-lpjpw 2/2 Running 0 30s
kong-mesh-app magnanimo-658b67fb9b-tzsjp 2/2 Running 0 5m3s
kong-mesh-system kuma-control-plane-5b9c6f4598-nvq8q 1/1 Running 0 16m
kube-system event-exporter-gke-666b7ffbf7-n9lfl 2/2 Running 0 76m
kube-system fluentbit-gke-xqsdv 2/2 Running 0 76m
kube-system gke-metrics-agent-gjrqr 1/1 Running 0 76m
kube-system konnectivity-agent-4c4hf 1/1 Running 0 76m
kube-system kube-dns-66d6b7c877-tq877 4/4 Running 0 76m
kube-system kube-dns-autoscaler-5c78d65cd9-5hcxs 1/1 Running 0 76m
kube-system kube-proxy-gke-c-kpwnf-default-0-be059c1c-49qp 1/1 Running 0 76m
kube-system l7-default-backend-5b76b455d-v6dvg 1/1 Running 0 76m
kube-system metrics-server-v0.3.6-547dc87f5f-qntjf 2/2 Running 0 75m
kube-system prometheus-to-sd-fdf9j 1/1 Running 0 76m
kube-system stackdriver-metadata-agent-cluster-level-68d94db6-64n4r 2/2 Running 1 75m
$ kubectl get service --all-namespaces
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default kubernetes ClusterIP 10.0.16.1 <none> 443/TCP 79m
kong-mesh-app benigno ClusterIP 10.0.20.52 <none> 5000/TCP 4m6s
kong-mesh-app magnanimo ClusterIP 10.0.30.251 <none> 4000/TCP 7m18s
kong-mesh-system kuma-control-plane ClusterIP 10.0.21.228 <none> 5681/TCP,5682/TCP,443/TCP,5676/TCP,5678/TCP,5653/UDP 18m
kube-system default-http-backend NodePort 10.0.19.10 <none> 80:32296/TCP 79m
kube-system kube-dns ClusterIP 10.0.16.10 <none> 53/UDP,53/TCP 79m
kube-system metrics-server ClusterIP 10.0.20.174 <none> 443/TCP 79m
你也可以使用Kong Mesh控制檯來檢查微服務和資料平面。在Terminal上執行以下命令:
kubectl port-forward service/kuma-control-plane -n kong-mesh-system 5681
重定向你的瀏覽器到http://localhost:5681/gui。點選Skip to Dashboard和All Data Plane Proxies:
啟動一個迴圈,看看金絲雀釋出的執行情況。注意服務已經被部署為ClusterIP型別,所以你需要用 “port-forward”直接暴露它們。下一步將展示如何用Ingress Controller暴露服務。
在本地terminal上執行:
kubectl port-forward service/magnanimo -n kong-mesh-app 4000
開啟另一個Terminal,開始迴圈。請求要到Magnanimo提供的4000埠。路徑“/hw2 ”將請求路由到Benigno服務,它後面有兩個endpoint,與Benigno兩個版本有關:
while [1]; do curl http://localhost:4000/hw2; echo; done
你應該看到類似下方的結果:
Hello World, Benigno: 2020-11-20 12:57:05.811667
Hello World, Benigno: 2020-11-20 12:57:06.304731
Hello World, Benigno, Canary Release: 2020-11-20 12:57:06.789208
Hello World, Benigno: 2020-11-20 12:57:07.269674
Hello World, Benigno, Canary Release: 2020-11-20 12:57:07.755884
Hello World, Benigno, Canary Release: 2020-11-20 12:57:08.240453
Hello World, Benigno: 2020-11-20 12:57:08.728465
Hello World, Benigno: 2020-11-20 12:57:09.208588
Hello World, Benigno, Canary Release: 2020-11-20 12:57:09.689478
Hello World, Benigno, Canary Release: 2020-11-20 12:57:10.179551
Hello World, Benigno: 2020-11-20 12:57:10.662465
Hello World, Benigno: 2020-11-20 12:57:11.145237
Hello World, Benigno, Canary Release: 2020-11-20 12:57:11.618557
Hello World, Benigno: 2020-11-20 12:57:12.108586
Hello World, Benigno, Canary Release: 2020-11-20 12:57:12.596296
Hello World, Benigno, Canary Release: 2020-11-20 12:57:13.093329
Hello World, Benigno: 2020-11-20 12:57:13.593487
Hello World, Benigno, Canary Release: 2020-11-20 12:57:14.068870
4、 控制金絲雀釋出的成本
正如我們所見,兩個Benigno微服務釋出的請求使用了迴圈策略。也就是說,我們無法控制金絲雀釋出的花銷。Service Mesh允許我們定義何時以及如何將金絲雀釋出暴露給我們的consumer(在本例中指Magnanimo微服務)。
要定義一個策略來控制流向兩個版本的流量,需要使用下面這個宣告。它說90%的流量應該流向當前版本,而只有10%的流量應該重定向到金絲雀釋出。
cat <<EOF | kubectl apply -f -
apiVersion: kuma.io/v1alpha1
kind: TrafficRoute
mesh: default
metadata:
namespace: default
name: route-1
spec:
sources:
- match:
kuma.io/service: magnanimo\_kong-mesh-app\_svc\_4000
destinations:
- match:
kuma.io/service: benigno\_kong-mesh-app\_svc\_5000
conf:
split:
- weight: 90
destination:
kuma.io/service: benigno\_kong-mesh-app\_svc\_5000
version: v1
- weight: 10
destination:
kuma.io/service: benigno\_kong-mesh-app\_svc\_5000
version: v2
EOF
應用宣告之後,你應該看到如下結果:
Hello World, Benigno: 2020-11-20 13:05:02.553389
Hello World, Benigno: 2020-11-20 13:05:03.041120
Hello World, Benigno: 2020-11-20 13:05:03.532701
Hello World, Benigno: 2020-11-20 13:05:04.021804
Hello World, Benigno: 2020-11-20 13:05:04.515245
Hello World, Benigno, Canary Release: 2020-11-20 13:05:05.000644
Hello World, Benigno: 2020-11-20 13:05:05.482606
Hello World, Benigno: 2020-11-20 13:05:05.963663
Hello World, Benigno, Canary Release: 2020-11-20 13:05:06.446599
Hello World, Benigno: 2020-11-20 13:05:06.926737
Hello World, Benigno: 2020-11-20 13:05:07.410605
Hello World, Benigno: 2020-11-20 13:05:07.890827
Hello World, Benigno: 2020-11-20 13:05:08.374686
Hello World, Benigno: 2020-11-20 13:05:08.857266
Hello World, Benigno: 2020-11-20 13:05:09.337360
Hello World, Benigno: 2020-11-20 13:05:09.816912
Hello World, Benigno: 2020-11-20 13:05:10.301863
Hello World, Benigno: 2020-11-20 13:05:10.782395
Hello World, Benigno: 2020-11-20 13:05:11.262624
Hello World, Benigno: 2020-11-20 13:05:11.743427
Hello World, Benigno: 2020-11-20 13:05:12.221174
Hello World, Benigno: 2020-11-20 13:05:12.705731
Hello World, Benigno: 2020-11-20 13:05:13.196664
Hello World, Benigno: 2020-11-20 13:05:13.680319
5、 安裝Kong for Kubernetes
讓我們回到Rancher中安裝我們的Kong for Kubernetes Ingress Controller,並控制服務網格的暴露。在Rancher Catalog頁面中,點選Kong圖示。接受預設值,然後點選Launch:
你應該看到Kong和Kong Mesh這兩個應用程式都已經部署完成:
再次使用kubectl檢查安裝:
$ kubectl get pod --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
cattle-system cattle-cluster-agent-785fd5f54d-r7x8r 1/1 Running 0 84m
fleet-system fleet-agent-77c78f9c74-f97tv 1/1 Running 0 83m
kong-mesh-app benigno-v1-fd4567d95-drnxq 2/2 Running 0 10m
kong-mesh-app benigno-v2-b977c867b-lpjpw 2/2 Running 0 8m47s
kong-mesh-app magnanimo-658b67fb9b-tzsjp 2/2 Running 0 13m
kong-mesh-system kuma-control-plane-5b9c6f4598-nvq8q 1/1 Running 0 24m
kong kong-kong-754cd6947-db2j9 2/2 Running 1 72s
kube-system event-exporter-gke-666b7ffbf7-n9lfl 2/2 Running 0 85m
kube-system fluentbit-gke-xqsdv 2/2 Running 0 84m
kube-system gke-metrics-agent-gjrqr 1/1 Running 0 84m
kube-system konnectivity-agent-4c4hf 1/1 Running 0 84m
kube-system kube-dns-66d6b7c877-tq877 4/4 Running 0 84m
kube-system kube-dns-autoscaler-5c78d65cd9-5hcxs 1/1 Running 0 84m
kube-system kube-proxy-gke-c-kpwnf-default-0-be059c1c-49qp 1/1 Running 0 84m
kube-system l7-default-backend-5b76b455d-v6dvg 1/1 Running 0 85m
kube-system metrics-server-v0.3.6-547dc87f5f-qntjf 2/2 Running 0 84m
kube-system prometheus-to-sd-fdf9j 1/1 Running 0 84m
kube-system stackdriver-metadata-agent-cluster-level-68d94db6-64n4r 2/2 Running 1 84m
$ kubectl get service --all-namespaces
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default kubernetes ClusterIP 10.0.16.1 <none> 443/TCP 85m
kong-mesh-app benigno ClusterIP 10.0.20.52 <none> 5000/TCP 10m
kong-mesh-app magnanimo ClusterIP 10.0.30.251 <none> 4000/TCP 13m
kong-mesh-system kuma-control-plane ClusterIP 10.0.21.228 <none> 5681/TCP,5682/TCP,443/TCP,5676/TCP,5678/TCP,5653/UDP 24m
kong kong-kong-proxy LoadBalancer 10.0.26.38 35.222.91.194 80:31867/TCP,443:31039/TCP 78s
kube-system default-http-backend NodePort 10.0.19.10 <none> 80:32296/TCP 85m
kube-system kube-dns ClusterIP 10.0.16.10 <none> 53/UDP,53/TCP 85m
kube-system metrics-server ClusterIP 10.0.20.174 <none> 443/TCP 85m
6、 建立Ingress
通過下面的宣告,我們將通過一個Ingress和它的路由 “/route1” 來暴露Magnanimo微服務。
cat <<EOF | kubectl apply -f -
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: route1
namespace: kong-mesh-app
annotations:
konghq.com/strip-path: "true"
spec:
rules:
- http:
paths:
- path: /route1
backend:
serviceName: magnanimo
servicePort: 4000
EOF
現在,臨時的 “port-forward” 暴露機制可以被正式的Ingress所取代。而我們的迴圈也可以開始消耗Ingress,結果如下:
while [1]; do curl http://35.222.91.194/route1/hw2; echo; done