Kubernetes 多集群方案 Cilium Cluster Mesh 使用
Cluster Mesh 是 Cilium 的多集群實現(xiàn),可以幫助 Cilium 實現(xiàn)跨數(shù)據(jù)中心、跨 VPC 的多 Kubernetes 集群管理,Cluster Mesh 主要有以下功能:
1.通過隧道或直接路由的方式,在多個 Kubernetes 集群間進行 Pod IP 路由,而無需任何網(wǎng)關(guān)或代理。 2.使用標準 Kubernetes 服務(wù)發(fā)現(xiàn)機制。 3.跨多個集群的網(wǎng)絡(luò)策略。策略可以使用 Kubernetes 原生的 NetworkPolicy 資源或者擴展的 CiliumNetworkPolicy CRD。 4.透明加密本集群以及跨集群節(jié)點間所有通信的流量。

接下來讓我們一起看看 Cilium Cluster Mesh 有哪些具體的使用場景。
1 使用場景
1.1 高可用
對大多數(shù)人來說,高可用是最普遍的使用場景。可以在多個區(qū)域(regions)或可用區(qū)(availability zones)中運行多個 Kubernetes 集群,并在每個集群中運行相同服務(wù)的副本。一旦發(fā)生異常,請求可以故障轉(zhuǎn)移到其他集群。

1.2 共享服務(wù)
某些公共基礎(chǔ)服務(wù)可以在集群間進行共享(如密鑰管理,日志記錄,監(jiān)控或 DNS 服務(wù)等),以避免額外的資源開銷。

1.3 拆分有狀態(tài)和無狀態(tài)服務(wù)
運行有狀態(tài)或無狀態(tài)服務(wù)的操作復(fù)雜性是非常不同的。無狀態(tài)服務(wù)易于擴展,遷移和升級。完全使用無狀態(tài)服務(wù)運行集群可使集群保持靈活和敏捷。有狀態(tài)服務(wù)(例如 MySQL,Elasticsearch, Etcd 等)可能會引入潛在的復(fù)雜依賴,遷移有狀態(tài)服務(wù)通常涉及存儲的遷移。為無狀態(tài)和有狀態(tài)服務(wù)分別運行獨立的集群可以將依賴復(fù)雜性隔離到較少數(shù)量的集群中。

2 架構(gòu)
Cilium Cluster Mesh 的架構(gòu)如下:
每個 Kubernetes 集群都維護自己的 etcd 集群,保存自身集群的狀態(tài)。來自多個集群的狀態(tài)永遠不會在本集群的 etcd 中混淆。 每個集群通過一組?etcd 代理暴露自己的 etcd,在其他集群中運行的 Cilium agent 連接到 etcd 代理以監(jiān)視更改。 Cilium 使用?clustermesh-apiserver?Pod 來建立多集群的互聯(lián),在?clustermesh-apiserver?Pod 中有兩個容器:其中 apiserver 容器負責(zé)將多集群的相關(guān)信息寫入 etcd 容器;etcd 容器(etcd 代理)用來存儲 Cluster Mesh 相關(guān)的配置信息。 從一個集群到另一個集群的訪問始終是只讀的。這確保了故障域保持不變,即一個集群中的故障永遠不會傳播到其他集群。

3 前提條件
3.1 安裝相關(guān)軟件
安裝 Kubectl:https://kubernetes.io/zh/docs/tasks/tools/ 安裝 Kind:https://kind.sigs.k8s.io/docs/user/quick-start/#installation 安裝 Helm:https://helm.sh/docs/intro/install/ 安裝 Cilium Cli:https://github.com/cilium/cilium-cli
Kind [1](Kubernetes in Docker) 是一個使用 Docker 容器運行本地 Kubernetes 集群的工具。為了方便實驗,本文使用 Kind 來搭建 Kubernetes 多集群環(huán)境。
3.2 環(huán)境要求
1.必須為所有 Kubernetes 的工作節(jié)點分配唯一的 IP 地址,并且節(jié)點之間 IP 路由可達。 2.每個集群都要分配唯一的 Pod CIDR。 3.Cilium 必須使用 etcd 作為 kv 存儲。 4.集群之間的網(wǎng)絡(luò)必須互通,具體的通信的端口號參見防火墻規(guī)則 [2]。
本實驗相關(guān)的配置文件可以在:?cluster_mesh [3]?獲取。
4 準備 Kubernetes 環(huán)境
準備兩個 Kind 配置文件用于搭建 Kubernetes 集群。
c1 集群配置文件。
#?kind-config1.yaml
kind:?Cluster
apiVersion:?kind.x-k8s.io/v1alpha4
nodes:
-?role:?control-plane
-?role:?worker
networking:
??disableDefaultCNI:?true?#?禁用默認的?CNI
??podSubnet:?"10.10.0.0/16"
??serviceSubnet:?"10.11.0.0/16"
c2 集群 配置文件。
#?kind-config2.yaml
kind:?Cluster
apiVersion:?kind.x-k8s.io/v1alpha4
nodes:
-?role:?control-plane
-?role:?worker
networking:
??disableDefaultCNI:?true?#?禁用默認的?CNI
??podSubnet:?"10.20.0.0/16"
??serviceSubnet:?"10.21.0.0/16"
使用?kind create cluster?命令創(chuàng)建兩個 Kubernetes 集群。
kind?create?cluster?--config?kind-config1.yaml?--name?c1
kind?create?cluster?--config?kind-config2.yaml?--name?c2

查看兩個 Kubernetes 集群。
kubectl?get?node?--context?kind-c1?-o?wide
kubectl?get?node?--context?kind-c2?-o?wide

5 安裝 Cilium
添加 Helm Repo。
helm?repo?add?cilium?https://helm.cilium.io/
在c1 集群上安裝 Cilium,使用?--kube-context?參數(shù)指定不同的集群上下文。必須為每個集群分配一個唯一的名稱和集群 id,cluster.id?參數(shù)指定集群 id,范圍 1-255,cluster.name?參數(shù)指定集群名稱。
helm?install?--kube-context?kind-c1?cilium?cilium/cilium?--version?1.11.4?\
??--namespace?kube-system?\
??--set?ipam.mode=kubernetes?\
??--set?cluster.id=1?\
??--set?cluster.name=cluster1
在 c2 集群上安裝 Cilium。
helm?install?--kube-context?kind-c2?cilium?cilium/cilium?--version?1.11.4?\
??--namespace?kube-system?\
??--set?ipam.mode=kubernetes?\
??--set?cluster.id=2?\
??--set?cluster.name=cluster2
查看 Cilium Pod 狀態(tài)。
kubectl?--context?kind-c1?get?pod?-A
kubectl?--context?kind-c2?get?pod?-A

查看 Cilium 狀態(tài)。
cilium?status?--context?kind-c1
cilium?status?--context?kind-c2

6 安裝 Metallb(可選)
在?7 啟用 Cluster Mesh?章節(jié)中會介紹發(fā)布?clustermesh-apiserver?服務(wù)使用的 Service 類型,建議使用 LoadBalancer 類型的 Service,這樣可以保證提供一個穩(wěn)定且唯一的 LoadBalancer IP。在公有云提供的 Kubernetes 集群中,LoadBalancer 類型的 Service 通常會通過公有云的負載均衡設(shè)備(例如 AWS 的 ELB,阿里云的 SLB 等)來發(fā)布。在私有環(huán)境中可以使用?MetalLB [4]?實現(xiàn)。
準備兩個集群的 Metallb 配置文件。c1 集群配置文件。注意分配的網(wǎng)絡(luò)要和節(jié)點 IP 在同一網(wǎng)段。
#?metallb-config1.yaml
configInline:
??peers:
??address-pools:
??-?name:?default
????protocol:?layer2
????addresses:
????-?172.22.0.50-172.22.0.100
c2 集群配置文件。
configInline:
??peers:
??address-pools:
??-?name:?default
????protocol:?layer2
????addresses:
????-?172.22.0.101-172.22.0.150
使用以下命令在 c1 和 c2 集群中部署 Metallb。
helm?repo?add?bitnami?https://charts.bitnami.com/bitnami
helm?install?--kube-context?kind-c1?metallb?bitnami/metallb?\
??--namespace?kube-system?\
??-f?metallb-config1.yaml
??
helm?install?--kube-context?kind-c2?metallb?bitnami/metallb?\
??--namespace?kube-system?\
??-f?metallb-config2.yaml
查看 Metallb Pod 狀態(tài)。

7 啟用 Cluster Mesh
使用?cilium clustermesh enable?命令在 c1 集群上啟用 Cluster Mesh:
--create-ca?參數(shù)表示自動創(chuàng)建 CA 證書,該證書需要在集群之間共享,以確保跨集群的 mTLS 能夠正常工作。--service-type?參數(shù)指定發(fā)布?clustermesh-apiserver?服務(wù)的方式,有以下 3 種方式:LoadBalancer(推薦): 使用 LoadBalancer 類型的 Service 發(fā)布服務(wù),這樣可以使用穩(wěn)定的 LoadBalancer IP,通常是最佳選擇。 NodePort: 使用 NodePort 類型的 Service 發(fā)布服務(wù),如果一個節(jié)點消失,Cluster Mesh 將不得不重新連接到另一個節(jié)點,可能會造成網(wǎng)絡(luò)的中斷。 ClusterIP: 使用 ClusterIP 類型的 Service 發(fā)布服務(wù),這要求 ClusterIP 在集群間是可路由的。
cilium?clustermesh?enable?--create-ca?--context?kind-c1?--service-type?LoadBalancer
執(zhí)行命令后會在集群中部署 clustermesh-apiserver 服務(wù),并生成相關(guān)必要的證書。

創(chuàng)建的 CA 保存在?kube-system?Namespace 下的 cilium-ca Secret 中。
$?kubectl?--context?kind-c1?get?secret?-n?kube-system?cilium-ca?-o?yaml
apiVersion:?v1
data:
??ca.crt:?LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNFekNDQWJxZ0F3SUJBZ0lVZmVPNHlYbVZSSU1ZZVppSjZyODJ6L05FejBVd0NnWUlLb1pJemowRUF3SXcKYURFTE1Ba0dBMVVFQmhNQ1ZWTXhGakFVQmdOVkJBZ1REVk5oYmlCR2NtRnVZMmx6WTI4eEN6QUpCZ05WQkFjVApBa05CTVE4d0RRWURWUVFLRXdaRGFXeHBkVzB4RHpBTkJnTlZCQXNUQmtOcGJHbDFiVEVTTUJBR0ExVUVBeE1KClEybHNhWFZ0SUVOQk1CNFhEVEl5TURVd09UQXpNemt3TUZvWERUSTNNRFV3T0RBek16a3dNRm93YURFTE1Ba0cKQTFVRUJoTUNWVk14RmpBVUJnTlZCQWdURFZOaGJpQkdjbUZ1WTJselkyOHhDekFKQmdOVkJBY1RBa05CTVE4dwpEUVlEVlFRS0V3WkRhV3hwZFcweER6QU5CZ05WQkFzVEJrTnBiR2wxYlRFU01CQUdBMVVFQXhNSlEybHNhWFZ0CklFTkJNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVTQVNHRERDdnhsUmpYNTEwMEpCQnoxdXIKb29sMktUNVh6MUNYS1paVk5Pc1M5ZmVrOEJUOTRqTXpZcHpsZW5hZXdwczVDZGhWckkvSU9mK2RtaTR3UjZOQwpNRUF3RGdZRFZSMFBBUUgvQkFRREFnRUdNQThHQTFVZEV3RUIvd1FGTUFNQkFmOHdIUVlEVlIwT0JCWUVGTlVwCjBBRVROZ0JHd2ZEK0paRDFWV2w2elNvVk1Bb0dDQ3FHU000OUJBTUNBMGNBTUVRQ0lHZUszUklreUJzQnFxL0MKdzRFTU9nMjk1T244WDFyYVM5QVZMZmlzS2JJVEFpQW5Da3NQTm9BYmZVZ1lyMkVGaFZZaDU0bjlZMVlyU0NlZAprOEZ3Nnl2MWNBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
??ca.key:?LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU9uWG9WTmhIdEJ0TTFaMFFlTWE5UWlLV1QvdXVNMk9jUXNmU252bXEwL2RvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFU0FTR0REQ3Z4bFJqWDUxMDBKQkJ6MXVyb29sMktUNVh6MUNYS1paVk5Pc1M5ZmVrOEJUOQo0ak16WXB6bGVuYWV3cHM1Q2RoVnJJL0lPZitkbWk0d1J3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
kind:?Secret
metadata:
??creationTimestamp:?"2022-05-09T03:44:03Z"
??name:?cilium-ca
??namespace:?kube-system
??resourceVersion:?"20625"
??uid:?7e4b2f21-815d-4191-974b-316c629e325c
type:?Opaque
將 c1 集群的 Cilium CA 證書導(dǎo)入集群 c2。
#?將?c1?集群的?Cilium?CA?證書導(dǎo)出
kubectl?get?secret?--context?kind-c1?-n?kube-system?cilium-ca?-o?yaml?>?cilium-ca.yaml
#?將?CA?證書導(dǎo)入?c2?集群
kubectl?apply?-f?cilium-ca.yaml?--context?kind-c2
在 c2 集群上啟用 Cluster Mesh。
cilium?clustermesh?enable?--context?kind-c2?--service-type?LoadBalancer

查看 c1 和 c2 集群的 clustermesh-apiserver Pod 狀態(tài)。

查看 c1 和 c2 集群的 clustermesh-apiserver Service,可以看到 Servie 的類型是 LoadBalancer,這是 Metallb 分配的 IP 地址。
kubectl?--context?kind-c1?get?svc?-A?
kubectl?--context?kind-c2?get?svc?-A?

查看 Cilium 狀態(tài)。
cilium?status?--context?kind-c1
cilium?status?--context?kind-c2

查看 c1 和 c2 集群的 Cluster Mesh 狀態(tài),當(dāng)前兩個集群都已經(jīng)成功啟用 Cluster Mesh,但是還未互相連接。
cilium?clustermesh?status?--context?kind-c1
cilium?clustermesh?status?--context?kind-c2

8 連接集群
在 c1 集群上執(zhí)行?cilium clustermesh connect?命令連接 c2 集群。只需要在一個集群上執(zhí)行即可。
cilium?clustermesh?connect?--context?kind-c1?--destination-context?kind-c2

查看 Cilium Cluster Mesh 狀態(tài),此時 c1 和 c1 集群已經(jīng)建立了 Cluster Mesh 連接。
cilium?clustermesh?status?--context?kind-c1
cilium?clustermesh?status?--context?kind-c2

現(xiàn)在我們已經(jīng)成功建立了集群間的互聯(lián),接下來驗證一下 Cluster Mesh 模式下的負載均衡和網(wǎng)絡(luò)策略。
9 負載均衡
9.1 全局負載均衡
在集群中部署兩個應(yīng)用,其中 x-wing 是客戶端,rebel-base 是服務(wù)端,要求對 rebel-base 服務(wù)實現(xiàn)全局負載均衡。需要保證每個集群中的 rebel-base 服務(wù)名稱相同并且在相同的命名空間中,然后添加?io.cilium/global-service: "true"?聲明為全局服務(wù),這樣 Cilium 便會自動對兩個集群中的 Pod 執(zhí)行負載均衡。
apiVersion:?v1
kind:?Service
metadata:
??name:?rebel-base
??annotations:
????io.cilium/global-service:?"true"?#?啟用全局負載均衡
spec:
??type:?ClusterIP
??ports:
??-?port:?80
??selector:
????name:?rebel-bas
在 c1 和 c2 集群創(chuàng)建應(yīng)用服務(wù)。
kubectl?apply?-f?cluster1.yaml?--context?kind-c1
kubectl?apply?-f?cluster2.yaml?--context?kind-c2
查看服務(wù)。
kubectl?--context?kind-c1?get?pod
kubectl?--context?kind-c1?get?svc
kubectl?--context?kind-c2?get?pod
kubectl?--context?kind-c2?get?svc
在任意一個集群訪問 rebel-base 服務(wù),可以看到流量被分發(fā)到了兩個集群。
for?i?in?{1..10};?do?kubectl?exec?--context?kind-c1?-ti?deployment/x-wing?--?curl?rebel-base;?done

9.2 禁用全局服務(wù)共享
默認情況下,全局服務(wù)將在多個集群中的后端進行負載均衡。如果想要禁止本集群的服務(wù)被共享給其他集群,可以設(shè)置?io.cilium/shared-service: "false"?注解來實現(xiàn)。
kubectl?annotate?service?rebel-base?\
io.cilium/shared-service="false"?--overwrite?--context?kind-c1
在 c1 集群可以訪問到兩個集群的 rebel-base 服務(wù)。
for?i?in?{1..10};?do?kubectl?exec?--context?kind-c1?-ti?deployment/x-wing?--?curl?rebel-base;?done

但是此時 c2 集群就只能訪問到本集群的 rebel-base 服務(wù)了。
for?i?in?{1..10};?do?kubectl?exec?--context?kind-c2?-ti?deployment/x-wing?--?curl?rebel-base;?done

將 c1 集群 rebel-base 服務(wù)的注解?io.cilium/shared-service?去掉。
kubectl?annotate?service?rebel-base?io.cilium/shared-service-?--context?kind-c1
現(xiàn)在 c2 集群可以重新訪問兩個集群的 rebel-base 服務(wù)了。
for?i?in?{1..10};?do?kubectl?exec?--context?kind-c2?-ti?deployment/x-wing?--?curl?rebel-base;?done

10 網(wǎng)絡(luò)策略
創(chuàng)建 CiliumNetworkPolicy 策略只允許 c1 集群中帶有 x-wing 標簽的 Pod 訪問 c2 集群中帶有 rebel-base 標簽的 Pod。集群名字是在?5 安裝 Cilium章節(jié)中通過?--cluster-name?參數(shù)指定的,也可以在?cilium-config?Configmap 中找到。除了應(yīng)用服務(wù)之間的流量,還需注意放行 DNS 的流量,否則無法直接通過 Service 名字進行訪問。
#?networkpolicy.yaml
apiVersion:?cilium.io/v2
kind:?CiliumNetworkPolicy
metadata:
??name:?"allow-dns"
spec:
??endpointSelector:?{}
??egress:
????-?toEndpoints:
????????-?matchLabels:
????????????io.kubernetes.pod.namespace:?kube-system
????????????k8s-app:?kube-dns
??????toPorts:
????????-?ports:
????????????-?port:?"53"
??????????????protocol:?UDP
??????????rules:
????????????dns:
??????????????-?matchPattern:?"*"
---
apiVersion:?"cilium.io/v2"
kind:?CiliumNetworkPolicy
metadata:
??name:?"allow-cross-cluster"
spec:
??description:?"Allow?x-wing?in?cluster1?to?contact?rebel-base?in?cluster2"
??endpointSelector:
????matchLabels:
??????name:?x-wing
??????io.cilium.k8s.policy.cluster:?cluster1
??egress:
??-?toEndpoints:
????-?matchLabels:
????????name:?rebel-base
????????io.cilium.k8s.policy.cluster:?cluster2
Kubernetes 的網(wǎng)絡(luò)策略不會自動發(fā)布到所有集群,你需要在每個集群上下發(fā)?NetworkPolicy?或?CiliumNetworkPolicy。
kubectl?--context?kind-c1?apply?-f?networkpolicy.yaml
kubectl?--context?kind-c2?apply?-f?networkpolicy.yaml
在 c1 集群上訪問 rebel-base 服務(wù),可以看到只有分發(fā)到 c2 集群上的請求才可以成功得到響應(yīng)。
kubectl?exec?--context?kind-c1?-ti?deployment/x-wing?--?curl?rebel-base

11 Troubleshooting
在啟用 Cluster Mesh 的時候遇到以下報錯。
cilium?clustermesh?status?--context?kind-c1

查看 Pod 信息發(fā)現(xiàn)拉取的鏡像不存在。
kubectl?--context?kind-c1?describe?pod?-n?kube-system??clustermesh-apiserver-754c5479dd-zsg8t

到 Cilium 鏡像倉庫上看了下發(fā)現(xiàn)鏡像后面的 sha256 值對不上。

編輯 clustermesh-apiserver Deployment 的鏡像,將鏡像版本后面的 shasum 值去掉即可。
kubectl?edit?--context?kind-c1?deployment?-n?kube-system?clustermesh-apiserver
kubectl?edit?--context?kind-c2?deployment?-n?kube-system?clustermesh-apiserver

12 參考資料
[1] Kind: https://kind.sigs.k8s.io/ [2] 防火墻規(guī)則: https://docs.cilium.io/en/stable/operations/system_requirements/#firewall-requirements [3] cluster_mesh: https://github.com/cr7258/kubernetes-guide/tree/master/cilium/cluster_mesh [4] MetalLB: https://metallb.universe.tf/ [5] 深入了解Cilium多集群: https://cloudnative.to/blog/deep-dive-into-cilium-multi-cluster/ [6] API server for Cilium ClusterMesh: https://github.com/cilium/cilium/tree/master/clustermesh-apiserver [7] Setting up Cluster Mesh: https://docs.cilium.io/en/stable/gettingstarted/clustermesh/clustermesh/#gs-clustermesh [8] BurlyLuo/clustermesh: https://github.com/BurlyLuo/clustermesh [9] eCHO Episode 41: Cilium Clustermesh: https://www.youtube.com/watch?v=VBOONHW65NU&t=2653s [10] Deep Dive into Cilium Multi-cluster: https://cilium.io/blog/2019/03/12/clustermesh [11] Multi Cluster Networking with Cilium and Friends: https://cilium.io/blog/2022/04/12/cilium-multi-cluster-networking [12] Kubernetes Multi-Cluster Networking -Cilium Cluster Mesh: https://itnext.io/kubernetes-multi-cluster-networking-cilium-cluster-mesh-bca0f5367d84 [13] A multi-cluster shared services architecture with Amazon EKS using Cilium ClusterMesh: https://aws.amazon.com/cn/blogs/containers/a-multi-cluster-shared-services-architecture-with-amazon-eks-using-cilium-clustermesh/
