Cilium 多集羣 Cluster Mesh 介紹

Cluster Mesh 是 Cilium 的多集羣實現,可以幫助 Cilium 實現跨數據中心、跨 VPC 的多 Kubernetes 集羣管理,Cluster Mesh 主要有以下功能:

接下來讓我們一起看看 Cilium Cluster Mesh 有哪些具體的使用場景。

1 使用場景

1.1 高可用

對大多數人來說,高可用是最普遍的使用場景。可以在多個區域(regions)或可用區(availability zones)中運行多個 Kubernetes 集羣,並在每個集羣中運行相同服務的副本。一旦發生異常,請求可以故障轉移到其他集羣。

1.2 共享服務

某些公共基礎服務可以在集羣間進行共享(如密鑰管理,日誌記錄,監控或 DNS 服務等),以避免額外的資源開銷。

1.3 拆分有狀態和無狀態服務

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

2 架構

Cilium Cluster Mesh 的架構如下:

3 前提條件

3.1 安裝相關軟件

Kind [1](Kubernetes in Docker) 是一個使用 Docker 容器運行本地 Kubernetes 集羣的工具。爲了方便實驗,本文使用 Kind 來搭建 Kubernetes 多集羣環境。

3.2 環境要求

本實驗相關的配置文件可以在: cluster_mesh [3] 獲取。

4 準備 Kubernetes 環境

準備兩個 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 命令創建兩個 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 參數指定不同的集羣上下文。必須爲每個集羣分配一個唯一的名稱和集羣 id,cluster.id 參數指定集羣 id,範圍 1-255,cluster.name 參數指定集羣名稱。

helm install --kube-context kind-c1 cilium cilium/cilium --version 1.11.4 \
  --namespace kube-system \
  --set ipam.mode=kubernetes \
  --set cluster.id=\
  --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=\
  --set cluster.name=cluster2

查看 Cilium Pod 狀態。

kubectl --context kind-c1 get pod -A
kubectl --context kind-c2 get pod -A

查看 Cilium 狀態。

cilium status --context kind-c1
cilium status --context kind-c2

6 安裝 Metallb(可選)

在 7 啓用 Cluster Mesh 章節中會介紹發佈 clustermesh-apiserver 服務使用的 Service 類型,建議使用 LoadBalancer 類型的 Service,這樣可以保證提供一個穩定且唯一的 LoadBalancer IP。在公有云提供的 Kubernetes 集羣中,LoadBalancer 類型的 Service 通常會通過公有云的負載均衡設備(例如 AWS 的 ELB,阿里雲的 SLB 等)來發布。在私有環境中可以使用 MetalLB [4] 實現。

準備兩個集羣的 Metallb 配置文件。c1 集羣配置文件。注意分配的網絡要和節點 IP 在同一網段。

# 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 狀態。

7 啓用 Cluster Mesh

使用 cilium clustermesh enable 命令在 c1 集羣上啓用 Cluster Mesh:

cilium clustermesh enable --create-ca --context kind-c1 --service-type LoadBalancer

執行命令後會在集羣中部署 clustermesh-apiserver 服務,並生成相關必要的證書。

創建的 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 證書導入集羣 c2。

# 將 c1 集羣的 Cilium CA 證書導出
kubectl get secret --context kind-c1 -n kube-system cilium-ca -o yaml > cilium-ca.yaml
# 將 CA 證書導入 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 狀態。

查看 c1 和 c2 集羣的 clustermesh-apiserver Service,可以看到 Servie 的類型是 LoadBalancer,這是 Metallb 分配的 IP 地址。

kubectl --context kind-c1 get svc -A 
kubectl --context kind-c2 get svc -A

查看 Cilium 狀態。

cilium status --context kind-c1
cilium status --context kind-c2

查看 c1 和 c2 集羣的 Cluster Mesh 狀態,當前兩個集羣都已經成功啓用 Cluster Mesh,但是還未互相連接。

cilium clustermesh status --context kind-c1
cilium clustermesh status --context kind-c2

8 連接集羣

在 c1 集羣上執行 cilium clustermesh connect 命令連接 c2 集羣。只需要在一個集羣上執行即可。

cilium clustermesh connect --context kind-c1 --destination-context kind-c2

查看 Cilium Cluster Mesh 狀態,此時 c1 和 c1 集羣已經建立了 Cluster Mesh 連接。

cilium clustermesh status --context kind-c1
cilium clustermesh status --context kind-c2

現在我們已經成功建立了集羣間的互聯,接下來驗證一下 Cluster Mesh 模式下的負載均衡和網絡策略。

9 負載均衡

9.1 全局負載均衡

在集羣中部署兩個應用,其中 x-wing 是客戶端,rebel-base 是服務端,要求對 rebel-base 服務實現全局負載均衡。需要保證每個集羣中的 rebel-base 服務名稱相同並且在相同的命名空間中,然後添加 io.cilium/global-service: "true" 聲明爲全局服務,這樣 Cilium 便會自動對兩個集羣中的 Pod 執行負載均衡。

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 集羣創建應用服務。

kubectl apply -f cluster1.yaml --context kind-c1
kubectl apply -f cluster2.yaml --context kind-c2

查看服務。

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 服務,可以看到流量被分發到了兩個集羣。

for i in {1..10}; do kubectl exec --context kind-c1 -ti deployment/x-wing -- curl rebel-base; done

9.2 禁用全局服務共享

默認情況下,全局服務將在多個集羣中的後端進行負載均衡。如果想要禁止本集羣的服務被共享給其他集羣,可以設置 io.cilium/shared-service: "false" 註解來實現。

kubectl annotate service rebel-base \
io.cilium/shared-service="false" --overwrite --context kind-c1

在 c1 集羣可以訪問到兩個集羣的 rebel-base 服務。

for i in {1..10}; do kubectl exec --context kind-c1 -ti deployment/x-wing -- curl rebel-base; done

但是此時 c2 集羣就只能訪問到本集羣的 rebel-base 服務了。

for i in {1..10}; do kubectl exec --context kind-c2 -ti deployment/x-wing -- curl rebel-base; done

將 c1 集羣 rebel-base 服務的註解 io.cilium/shared-service 去掉。

kubectl annotate service rebel-base io.cilium/shared-service- --context kind-c1

現在 c2 集羣可以重新訪問兩個集羣的 rebel-base 服務了。

for i in {1..10}; do kubectl exec --context kind-c2 -ti deployment/x-wing -- curl rebel-base; done

10 網絡策略

創建 CiliumNetworkPolicy 策略只允許 c1 集羣中帶有 x-wing 標籤的 Pod 訪問 c2 集羣中帶有 rebel-base 標籤的 Pod。集羣名字是在 5 安裝 Cilium 章節中通過 --cluster-name 參數指定的,也可以在 cilium-config Configmap 中找到。除了應用服務之間的流量,還需注意放行 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 的網絡策略不會自動發佈到所有集羣,你需要在每個集羣上下發 NetworkPolicy 或 CiliumNetworkPolicy

kubectl --context kind-c1 apply -f networkpolicy.yaml
kubectl --context kind-c2 apply -f networkpolicy.yaml

在 c1 集羣上訪問 rebel-base 服務,可以看到只有分發到 c2 集羣上的請求才可以成功得到響應。

kubectl exec --context kind-c1 -ti deployment/x-wing -- curl rebel-base

11 Troubleshooting

在啓用 Cluster Mesh 的時候遇到以下報錯。

cilium clustermesh status --context kind-c1

查看 Pod 信息發現拉取的鏡像不存在。

kubectl --context kind-c1 describe pod -n kube-system  clustermesh-apiserver-754c5479dd-zsg8t

到 Cilium 鏡像倉庫上看了下發現鏡像後面的 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 參考資料

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/szlBkBjCIVE4mmtP4EiEzA