本文已超過一年。較舊的文章可能包含過時的內容。請確認頁面中的資訊自發布以來是否已變得不正確。

使用 Kubernetes 叢集聯邦建構全球分散式服務

在 Kubernetes 1.3 中,我們宣布了 Kubernetes 叢集聯邦,並介紹了跨叢集服務探索的概念,使開發人員能夠部署跨越不同區域、地區或雲端供應商的叢集聯邦分片的服務。正如我們先前的博客文章中所詳述,這使開發人員能夠在不犧牲服務品質的情況下,實現應用程式的更高可用性。

在最新版本 Kubernetes 1.4 中,我們擴展了叢集聯邦以支援副本集、密鑰、命名空間和 Ingress 物件。這表示您不再需要在每個聯邦叢集中個別部署和管理這些物件。只需在聯邦中建立一次,並讓其內建控制器自動為您處理即可。

聯邦副本集 利用與非聯邦 Kubernetes 副本集相同的配置,並自動在一個或多個聯邦叢集之間分配 Pod。預設情況下,副本平均分配在所有叢集之間,但對於不希望如此行為的情況,我們推出了副本集偏好設定,允許將副本僅分配在某些叢集之間,或以不相等的比例分配 ( 定義註釋 )。

從 Google Cloud Platform (GCP) 開始,我們推出了 聯邦 Ingress 作為 Kubernetes 1.4 Alpha 功能,使外部用戶端能夠指向單一 IP 位址,並將請求發送到聯邦中任何區域、區域內最近的可用容量叢集。

聯邦密鑰 自動在聯邦中的所有叢集之間建立和管理密鑰,即使在應用原始更新時某些叢集處於離線狀態,也能自動確保這些密鑰在全球範圍內保持一致且最新。

聯邦命名空間 類似於傳統的 Kubernetes 命名空間,提供相同的功能。在聯邦控制平面中建立它們可確保它們在聯邦中的所有叢集之間同步。

聯邦事件 類似於傳統的 Kubernetes 事件,提供相同的功能。聯邦事件僅儲存在聯邦控制平面中,不會傳遞到底層的 Kubernetes 叢集。

讓我們逐步了解所有這些東西是如何運作的。我們將在每個區域佈建 3 個叢集,跨越 3 大洲(歐洲、北美洲和亞洲)。

下一步是聯邦這些叢集。Kelsey Hightower 開發了一個 教學課程,用於設定 Kubernetes 叢集聯邦。按照本教學課程配置叢集聯邦,其中包含 us-central1、europe-west1 和 asia-east1 這 3 個 GCP 區域中每個區域的 3 個區域中的叢集。就本博客文章而言,我們將在 us-central1-b 區域中佈建聯邦控制平面。請注意,也提供更高可用性的多叢集部署,但為了簡潔起見,此處未使用。

本博客文章的其餘部分假設您已佈建正在運行的 Kubernetes 叢集聯邦。

讓我們驗證我們在 3 個區域中運行 9 個叢集。

$ kubectl --context=federation-cluster get clusters


NAME              STATUS    AGE  
gce-asia-east1-a     Ready     17m  
gce-asia-east1-b     Ready     15m  
gce-asia-east1-c     Ready     10m  
gce-europe-west1-b   Ready     7m  
gce-europe-west1-c   Ready     7m  
gce-europe-west1-d   Ready     4m  
gce-us-central1-a    Ready     1m  
gce-us-central1-b    Ready     53s  
gce-us-central1-c    Ready     39s
您可以在 此處 下載本博客文章中使用的原始碼。原始碼包含以下檔案
configmaps/zonefetch.yaml從實例元數據伺服器檢索區域,並串連到卷掛載路徑中
replicasets/nginx-rs.yaml部署一個由 nginx 和 busybox 容器組成的 Pod
ingress/ingress.yaml建立一個具有全域 VIP 的負載平衡器,將請求分配到最近的 nginx 後端
services/nginx.yaml將 nginx 後端公開為外部服務

在我們的範例中,我們將使用聯邦控制平面部署服務和 Ingress 物件。ConfigMap 物件目前不受聯邦支援,因此我們將在每個底層的聯邦叢集中手動部署它。我們的叢集部署將如下所示

我們將部署一個分散在我們 9 個叢集中的服務。後端部署將包含一個具有 2 個容器的 Pod

  • busybox 容器,用於獲取區域並將嵌入區域的 HTML 輸出到 Pod 卷掛載路徑中
  • nginx 容器,用於從該 Pod 卷掛載路徑讀取並提供包含其運行區域的 HTML

讓我們從在聯邦叢集上下文中建立聯邦服務物件開始。

$ kubectl --context=federation-cluster create -f services/nginx.yaml

服務需要幾分鐘才能在 9 個叢集中傳播。

$ kubectl --context=federation-cluster describe services nginx


Name:                   nginx  
Namespace:              default  
Labels:                 app=nginx  
Selector:               app=nginx  
Type:                   LoadBalancer  
IP:  
LoadBalancer Ingress:   108.59.xx.xxx, 104.199.xxx.xxx, ...  
Port:                   http    80/TCP

NodePort:               http    30061/TCP  
Endpoints:              <none>  
Session Affinity:       None

現在讓我們建立聯邦 Ingress。聯邦 Ingress 的建立方式與傳統 Kubernetes Ingress 非常相似:發出一個 API 呼叫,指定您邏輯 Ingress 點的期望屬性。對於聯邦 Ingress,此 API 呼叫會定向到聯邦 API 端點,而不是 Kubernetes 叢集 API 端點。聯邦 Ingress 的 API 與傳統 Kubernetes 服務的 API 100% 相容。

$ cat ingress/ingress.yaml   

apiVersion: extensions/v1beta1  
kind: Ingress  
metadata:  
  name: nginx  
spec:  
  backend:  
    serviceName: nginx  
    servicePort: 80
$ kubectl --context=federation-cluster create -f ingress/ingress.yaml   
ingress "nginx" created

建立後,聯邦 Ingress 控制器會自動執行以下操作

  1. 1. 在叢集聯邦的每個底層叢集中建立相符的 Kubernetes Ingress 物件
  2. 2. 確保所有這些叢集內 Ingress 物件共享相同的邏輯全域 L7(即 HTTP(S))負載平衡器和 IP 位址
  3. 3. 監控每個叢集中此 Ingress 後面的服務「分片」(即您的 Pod)的健康狀況和容量
  4. 4. 即使在 Pod、叢集、可用性區域或區域中斷的情況下,也始終確保所有用戶端連線都路由到適當的健康後端服務端點。我們可以驗證底層叢集中的 Ingress 物件是否相符。請注意,所有 9 個叢集的 Ingress IP 位址都相同。
$ for c in $(kubectl config view -o jsonpath='{.contexts[*].name}'); do kubectl --context=$c get ingress; done  

NAME      HOSTS     ADDRESS   PORTS     AGE  
nginx     \*                   80        1h  
NAME      HOSTS     ADDRESS          PORTS     AGE  
nginx     \*         130.211.40.xxx   80        40m  
NAME      HOSTS     ADDRESS          PORTS     AGE  
nginx     \*         130.211.40.xxx   80        1h  
NAME      HOSTS     ADDRESS          PORTS     AGE  
nginx     \*         130.211.40.xxx   80        26m  
NAME      HOSTS     ADDRESS          PORTS     AGE  
nginx     \*         130.211.40.xxx   80        1h  
NAME      HOSTS     ADDRESS          PORTS     AGE  
nginx     \*         130.211.40.xxx   80        25m  
NAME      HOSTS     ADDRESS          PORTS     AGE  
nginx     \*         130.211.40.xxx   80        38m  
NAME      HOSTS     ADDRESS          PORTS     AGE  
nginx     \*         130.211.40.xxx   80        3m  
NAME      HOSTS     ADDRESS          PORTS     AGE  
nginx     \*         130.211.40.xxx   80        57m  
NAME      HOSTS     ADDRESS          PORTS     AGE  
nginx     \*         130.211.40.xxx   80        56m

請注意,在 Google Cloud Platform 的情況下,邏輯 L7 負載平衡器不是單一物理裝置(這將會造成單點故障和單一全域網路路由瓶頸),而是一個 真正的全域、高可用性負載平衡託管服務,可透過單一靜態 IP 位址在全球範圍內存取。

如果您聯邦 Kubernetes 叢集(即 Pod)內的用戶端存在且健康,則會自動路由到支援其叢集中 Ingress 的聯邦服務的叢集本地分片;否則,會路由到不同叢集中最近的健康分片。請注意,這涉及與 HTTP(S) 負載平衡器的網路行程,該負載平衡器位於您的本機 Kubernetes 叢集之外,但在同一個 GCP 區域內。

下一步是排程服務後端。讓我們首先在聯邦中的每個叢集中建立 ConfigMap。

我們透過將 ConfigMap 提交到聯邦中的每個叢集來完成此操作。

$ for c in $(kubectl config view -o jsonpath='{.contexts[\*].name}'); do kubectl --context=$c create -f configmaps/zonefetch.yaml; done

讓我們快速查看一下我們的副本集

$ cat replicasets/nginx-rs.yaml


apiVersion: extensions/v1beta1  
kind: ReplicaSet  
metadata:  
  name: nginx  
  labels:  
    app: nginx  
    type: demo  
spec:  
  replicas: 9  
  template:  
    metadata:  
      labels:  
        app: nginx  
    spec:  
      containers:  
      - image: nginx  
        name: frontend  
        ports:  
          - containerPort: 80  
        volumeMounts:  
        - name: html-dir  
          mountPath: /usr/share/nginx/html  
      - image: busybox  
        name: zone-fetcher  
        command:  
          - "/bin/sh"  
          - "-c"  
          - "/zonefetch/zonefetch.sh"  
        volumeMounts:  
        - name: zone-fetch  
          mountPath: /zonefetch  
        - name: html-dir  
          mountPath: /usr/share/nginx/html  
      volumes:  
        - name: zone-fetch  
          configMap:  
            defaultMode: 0777  
            name: zone-fetch  
        - name: html-dir  
          emptyDir:  
            medium: ""

副本集由 9 個副本組成,平均分佈在叢集聯邦內的 9 個叢集中。註釋也可用於控制將 Pod 排程到哪些叢集。這是透過將註釋新增至副本集規格來完成的,如下所示

apiVersion: extensions/v1beta1  
kind: ReplicaSet  
metadata:  
  name: nginx-us  
  annotations:  
    federation.kubernetes.io/replica-set-preferences: ```  
        {  
            "rebalance": true,  
            "clusters": {  
                "gce-us-central1-a": {  
                    "minReplicas": 2,  
                    "maxReplicas": 4,  
                    "weight": 1  
                },  
                "gce-us-central10b": {  
                    "minReplicas": 2,  
                    "maxReplicas": 4,  
                    "weight": 1  
                }  
            }  
        }

為了我們的演示目的,我們將保持簡單,並將我們的 Pod 平均分佈在叢集聯邦中。

讓我們建立聯邦副本集

$ kubectl --context=federation-cluster create -f replicasets/nginx-rs.yaml

驗證副本集和 Pod 是否已在每個叢集中建立

$ for c in $(kubectl config view -o jsonpath='{.contexts[\*].name}'); do kubectl --context=$c get rs; done  

NAME      DESIRED   CURRENT   READY     AGE  
nginx     1         1         1         42s  
NAME      DESIRED   CURRENT   READY     AGE  
nginx     1         1         1         14m  
NAME      DESIRED   CURRENT   READY     AGE  
nginx     1         1         1         45s  
NAME      DESIRED   CURRENT   READY     AGE  
nginx     1         1         1         46s  
NAME      DESIRED   CURRENT   READY     AGE  
nginx     1         1         1         47s  
NAME      DESIRED   CURRENT   READY     AGE  
nginx     1         1         1         48s  
NAME      DESIRED   CURRENT   READY     AGE  
nginx     1         1         1         49s  
NAME      DESIRED   CURRENT   READY     AGE  
nginx     1         1         1         49s  
NAME      DESIRED   CURRENT   READY     AGE  
nginx     1         1         1         49s


$ for c in $(kubectl config view -o jsonpath='{.contexts[\*].name}'); do kubectl --context=$c get po; done  

NAME          READY     STATUS    RESTARTS   AGE  
nginx-ph8zx   2/2       Running   0          25s  
NAME          READY     STATUS    RESTARTS   AGE  
nginx-sbi5b   2/2       Running   0          27s  
NAME          READY     STATUS    RESTARTS   AGE  
nginx-pf2dr   2/2       Running   0          28s  
NAME          READY     STATUS    RESTARTS   AGE  
nginx-imymt   2/2       Running   0          30s  
NAME          READY     STATUS    RESTARTS   AGE  
nginx-9cd5m   2/2       Running   0          31s  
NAME          READY     STATUS    RESTARTS   AGE  
nginx-vxlx4   2/2       Running   0          33s  
NAME          READY     STATUS    RESTARTS   AGE  
nginx-itagl   2/2       Running   0          33s  
NAME          READY     STATUS    RESTARTS   AGE  
nginx-u7uyn   2/2       Running   0          33s  
NAME          READY     STATUS    RESTARTS   AGE  
nginx-i0jh6   2/2       Running   0          34s

以下說明了 nginx 服務和相關 Ingress 的部署方式。總而言之,我們有一個全域 VIP (130.211.23.176),使用全域 L7 負載平衡器公開,該負載平衡器將請求轉發到最近的可用容量叢集。

為了測試這一點,我們將啟動 2 個 Google Cloud Engine (GCE) 實例,一個在 us-west1-b 中,另一個在 asia-east1-a 中。所有用戶端請求都會透過最短的網路路徑自動路由到最接近請求來源的叢集中健康的 Pod。例如,來自亞洲的 HTTP(S) 請求將直接路由到亞洲最近的可用容量叢集。如果亞洲沒有此類叢集,則請求將路由到下一個最近的叢集(在本例中為美國)。無論請求是來自 GCE 實例還是網路上任何其他位置,這都有效。我們僅使用 GCE 實例來簡化演示。

我們可以透過 Cloud Console 或發出 gcloud SSH 命令直接 SSH 連線到 VM。

$ gcloud compute ssh test-instance-asia --zone asia-east1-a

-----

user@test-instance-asia:~$ curl 130.211.40.186  
<!DOCTYPE html>  
<html>  
<head>  
<title>Welcome to the global site!</title>  
</head>  
<body>  
<h1>Welcome to the global site! You are being served from asia-east1-b</h1>  
<p>Congratulations!</p>


user@test-instance-asia:~$ exit

----


$ gcloud compute ssh test-instance-us --zone us-west1-b

----

user@test-instance-us:~$ curl 130.211.40.186  
<!DOCTYPE html>  
<html>  
<head>  
<title>Welcome to the global site!</title>  
</head>  
<body>  
<h1>Welcome to the global site! You are being served from us-central1-b</h1>  
<p>Congratulations!</p>


----

Kubernetes 叢集聯邦可以包含在不同雲端供應商(例如 GCP、AWS)和內部部署(例如 OpenStack)中運行的叢集。但是,在 Kubernetes 1.4 中,聯邦 Ingress 僅在 Google Cloud Platform 叢集之間受支援。在未來版本中,我們計劃支援混合雲基於 Ingress 的部署。

總而言之,我們逐步了解了如何利用 Kubernetes 1.4 聯邦 Ingress Alpha 功能,在全域負載平衡器後面部署多宿主服務。外部用戶端指向單一 IP 位址,並被發送到聯邦中任何區域、區域內最近的可用容量叢集,從而在不犧牲延遲或操作便利性的情況下提供更高的可用性。

我們很樂意聽取關於 Kubernetes 跨叢集服務的回饋。若要加入社群