本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

使用 Kubernetes 新的多區域叢集(又名 'Ubernetes Lite')構建高可用性應用程式

編者按:這是關於 Kubernetes 1.2 新功能的系列深度文章中的第三篇

簡介 

Kubernetes 最常請求的功能之一是能夠在多個區域執行應用程式。這有充分的理由——開發人員需要在多個域部署應用程式,以提高在單個區域中斷情況下的可用性。

Kubernetes 1.2 已於兩週前釋出,增加了對跨多個故障區域執行單個叢集的支援(GCP 稱之為“區域”,Amazon 稱之為“可用區域”,在此我們將其統稱為“區域”)。這是允許將多個 Kubernetes 叢集聯合起來(有時被親切地稱為“Ubernetes”)這一更廣泛努力的第一步。這個初始版本(稱為“Ubernetes Lite”)透過在單個雲提供商的多個區域中分散應用程式,提高了應用程式的可用性。

多區域叢集的設計刻意簡單,並且非常易於使用——無需對 Kubernetes API 或應用程式進行任何更改。您只需將現有的 Kubernetes 應用程式部署到新型多區域叢集中,您的應用程式即可自動實現區域故障彈性。

現在進入一些細節...  

Ubernetes Lite 透過利用 Kubernetes 平臺透過標籤的可擴充套件性來工作。如今,當節點啟動時,標籤會新增到系統中的每個節點。透過 Ubernetes Lite,系統已擴充套件為也新增有關其執行區域的資訊。透過這些資訊,排程程式可以對放置應用程式例項做出智慧決策。

具體來說,排程程式已經會分散 Pod 以最大程度地減少任何單個節點故障的影響。透過 Ubernetes Lite,透過 `SelectorSpreadPriority`,排程程式還將盡力將 Pod 分散到各個區域。我們應該注意,如果叢集中的區域是異構的(例如,不同數量的節點或不同型別的節點),您可能無法在區域之間實現均勻的 Pod 分散。如果需要,您可以使用同構區域(相同數量和型別的節點)來減少不均勻分散的可能性。

這種改進的標籤也適用於儲存。當建立持久卷時,`PersistentVolumeLabel` 准入控制器會自動為它們新增區域標籤。排程程式(透過 `VolumeZonePredicate` 謂詞)將確保宣告給定卷的 Pod 僅放置在與該卷相同的區域中,因為卷不能跨區域連線。

演練 

我們現在將演練如何在 Google Compute Engine (GCE) 和 Amazon EC2 上使用 Kubernetes 附帶的預設 kube-up 指令碼設定和使用多區域叢集。儘管我們重點介紹 GCE 和 EC2,但此功能可在您可以在叢集設定期間進行更改的任何 Kubernetes 1.2 部署中使用。此功能也將在 Google Container Engine (GKE) 中很快提供。

啟動您的叢集 

為 Kubernetes 建立多區域部署與單區域叢集相同,但您需要傳遞一個環境變數(`“MULTIZONE”`)以告知叢集管理多個區域。我們將首先在 GCE 和/或 EC2 上建立一個多區域感知叢集。

GCE

curl -sS https://get.k8s.io | MULTIZONE=true KUBERNETES_PROVIDER=gce
KUBE_GCE_ZONE=us-central1-a NUM_NODES=3 bash

EC2

curl -sS https://get.k8s.io | MULTIZONE=true KUBERNETES_PROVIDER=aws
KUBE_AWS_ZONE=us-west-2a NUM_NODES=3 bash

執行此命令後,您將啟動一個已準備好管理在多個區域中執行的節點的叢集。您還將啟動 `NUM_NODES` 個節點和叢集的控制平面(即 Kubernetes master),所有這些都在 `KUBE_{GCE,AWS}_ZONE` 指定的區域中。在 Ubernetes Lite 的未來迭代中,我們將支援 HA 控制平面,其中 master 元件在區域之間進行復制。在此之前,如果 master 執行的區域發生故障,master 將變得不可用。但是,在所有區域中執行的容器將繼續執行,如果它們發生故障,Kubelet 將重新啟動它們,因此應用程式本身將容忍此類區域故障。

節點已標記 

要檢視新增到節點的附加元資料,只需檢視叢集的所有標籤(此處示例在 GCE 上)

$ kubectl get nodes --show-labels

NAME STATUS AGE LABELS
kubernetes-master Ready,SchedulingDisabled 6m        
beta.kubernetes.io/instance-type=n1-standard-1,failure-domain.beta.kubernetes.
io/region=us-central1,failure-domain.beta.kubernetes.io/zone=us-central1-a,kub
ernetes.io/hostname=kubernetes-master
kubernetes-minion-87j9 Ready 6m        
beta.kubernetes.io/instance-type=n1-standard-2,failure-domain.beta.kubernetes.
io/region=us-central1,failure-domain.beta.kubernetes.io/zone=us-central1-a,kub
ernetes.io/hostname=kubernetes-minion-87j9
kubernetes-minion-9vlv Ready 6m        
beta.kubernetes.io/instance-type=n1-standard-2,failure-domain.beta.kubernetes.
io/region=us-central1,failure-domain.beta.kubernetes.io/zone=us-central1-a,kub
ernetes.io/hostname=kubernetes-minion-9vlv
kubernetes-minion-a12q Ready 6m        
beta.kubernetes.io/instance-type=n1-standard-2,failure-domain.beta.kubernetes.
io/region=us-central1,failure-domain.beta.kubernetes.io/zone=us-central1-a,kub
ernetes.io/hostname=kubernetes-minion-a12q

排程程式將在其排程決策中使用附加到每個節點的標籤(區域的 failure-domain.beta.kubernetes.io/region 和區域的 failure-domain.beta.kubernetes.io/zone)。

在第二個區域中新增更多節點 

讓我們向現有叢集新增另一組節點,但在不同的區域中執行(GCE 為 us-central1-b,EC2 為 us-west-2b)。我們再次執行 kube-up,但透過指定 `KUBE_USE_EXISTING_MASTER=1`,kube-up 將不會建立新的 master,而是重用先前建立的 master。

GCE

KUBE_USE_EXISTING_MASTER=true MULTIZONE=true KUBERNETES_PROVIDER=gce
KUBE_GCE_ZONE=us-central1-b NUM_NODES=3 kubernetes/cluster/kube-up.sh

在 EC2 上,我們還需要指定附加子網的網路 CIDR,以及 master 內部 IP 地址

KUBE_USE_EXISTING_MASTER=true MULTIZONE=true KUBERNETES_PROVIDER=aws
KUBE_AWS_ZONE=us-west-2b NUM_NODES=3 KUBE_SUBNET_CIDR=172.20.1.0/24
MASTER_INTERNAL_IP=172.20.0.9 kubernetes/cluster/kube-up.sh

再次檢視節點;將啟動並標記另外 3 個節點(此處示例在 GCE 上)

$ kubectl get nodes --show-labels

NAME STATUS AGE LABELS
kubernetes-master Ready,SchedulingDisabled 16m       
beta.kubernetes.io/instance-type=n1-standard-1,failure-domain.beta.kubernetes.
io/region=us-central1,failure-domain.beta.kubernetes.io/zone=us-central1-a,kub
ernetes.io/hostname=kubernetes-master
kubernetes-minion-281d Ready 2m        
beta.kubernetes.io/instance-type=n1-standard-2,failure-domain.beta.kubernetes.
io/region=us-central1,failure-domain.beta.kubernetes.io/zone=us-central1-b,kub
ernetes.io/hostname=kubernetes-minion-281d
kubernetes-minion-87j9 Ready 16m       
beta.kubernetes.io/instance-type=n1-standard-2,failure-domain.beta.kubernetes.
io/region=us-central1,failure-domain.beta.kubernetes.io/zone=us-central1-a,kub
ernetes.io/hostname=kubernetes-minion-87j9
kubernetes-minion-9vlv Ready 16m       
beta.kubernetes.io/instance-type=n1-standard-2,failure-domain.beta.kubernetes.
io/region=us-central1,failure-domain.beta.kubernetes.io/zone=us-central1-a,kub
ernetes.io/hostname=kubernetes-minion-9vlv
kubernetes-minion-a12q Ready 17m       
beta.kubernetes.io/instance-type=n1-standard-2,failure-domain.beta.kubernetes.
io/region=us-central1,failure-domain.beta.kubernetes.io/zone=us-central1-a,kub
ernetes.io/hostname=kubernetes-minion-a12q
kubernetes-minion-pp2f Ready 2m        
beta.kubernetes.io/instance-type=n1-standard-2,failure-domain.beta.kubernetes.
io/region=us-central1,failure-domain.beta.kubernetes.io/zone=us-central1-b,kub
ernetes.io/hostname=kubernetes-minion-pp2f
kubernetes-minion-wf8i Ready 2m        
beta.kubernetes.io/instance-type=n1-standard-2,failure-domain.beta.kubernetes.
io/region=us-central1,failure-domain.beta.kubernetes.io/zone=us-central1-b,kub
ernetes.io/hostname=kubernetes-minion-wf8i

讓我們再新增一個區域

GCE

KUBE_USE_EXISTING_MASTER=true MULTIZONE=true KUBERNETES_PROVIDER=gce
KUBE_GCE_ZONE=us-central1-f NUM_NODES=3 kubernetes/cluster/kube-up.sh

EC2

KUBE_USE_EXISTING_MASTER=true MULTIZONE=true KUBERNETES_PROVIDER=aws
KUBE_AWS_ZONE=us-west-2c NUM_NODES=3 KUBE_SUBNET_CIDR=172.20.2.0/24
MASTER_INTERNAL_IP=172.20.0.9 kubernetes/cluster/kube-up.sh

驗證您現在有 3 個區域中的節點

kubectl get nodes --show-labels

高可用應用程式,我們來了。

部署多區域應用程式 

建立 guestbook-go 示例,其中包含大小為 3 的 ReplicationController,執行一個簡單的 Web 應用程式。從此處下載所有檔案,並執行以下命令(該命令假設您已將它們下載到一個名為“guestbook-go”的目錄中)

kubectl create -f guestbook-go/

您完成了!您的應用程式現在已分散到所有 3 個區域。使用以下命令自行驗證

$ kubectl describe pod -l app=guestbook | grep Node
Node: kubernetes-minion-9vlv/10.240.0.5
Node: kubernetes-minion-281d/10.240.0.8
Node: kubernetes-minion-olsh/10.240.0.11

$ kubectl get node kubernetes-minion-9vlv kubernetes-minion-281d 
kubernetes-minion-olsh --show-labels
NAME STATUS AGE LABELS
kubernetes-minion-9vlv Ready 34m       
beta.kubernetes.io/instance-type=n1-standard-2,failure-domain.beta.kubernetes.
io/region=us-central1,failure-domain.beta.kubernetes.io/zone=us-central1-a,kub
ernetes.io/hostname=kubernetes-minion-9vlv
kubernetes-minion-281d Ready 20m       
beta.kubernetes.io/instance-type=n1-standard-2,failure-domain.beta.kubernetes.
io/region=us-central1,failure-domain.beta.kubernetes.io/zone=us-central1-b,kub
ernetes.io/hostname=kubernetes-minion-281d
kubernetes-minion-olsh Ready 3m        
beta.kubernetes.io/instance-type=n1-standard-2,failure-domain.beta.kubernetes.
io/region=us-central1,failure-domain.beta.kubernetes.io/zone=us-central1-f,kub
ernetes.io/hostname=kubernetes-minion-olsh

此外,負載均衡器會自動跨叢集中的所有區域;guestbook-go 示例包含一個負載均衡服務的示例

$ kubectl describe service guestbook | grep LoadBalancer.Ingress
LoadBalancer Ingress: 130.211.126.21

ip=130.211.126.21

$ curl -s http://${ip}:3000/env | grep HOSTNAME
  "HOSTNAME": "guestbook-44sep",

$ (for i in `seq 20`; do curl -s http://${ip}:3000/env | grep HOSTNAME; done)  
| sort | uniq
  "HOSTNAME": "guestbook-44sep",
  "HOSTNAME": "guestbook-hum5n",
  "HOSTNAME": "guestbook-ppm40",

負載均衡器正確地定位所有 Pod,即使它們位於多個區域中。

關閉叢集 

完成後,清理

GCE

KUBERNETES_PROVIDER=gce KUBE_USE_EXISTING_MASTER=true 
KUBE_GCE_ZONE=us-central1-f kubernetes/cluster/kube-down.sh
KUBERNETES_PROVIDER=gce KUBE_USE_EXISTING_MASTER=true 
KUBE_GCE_ZONE=us-central1-b kubernetes/cluster/kube-down.sh
KUBERNETES_PROVIDER=gce KUBE_GCE_ZONE=us-central1-a 
kubernetes/cluster/kube-down.sh

EC2

KUBERNETES_PROVIDER=aws KUBE_USE_EXISTING_MASTER=true KUBE_AWS_ZONE=us-west-2c 
kubernetes/cluster/kube-down.sh
KUBERNETES_PROVIDER=aws KUBE_USE_EXISTING_MASTER=true KUBE_AWS_ZONE=us-west-2b 
kubernetes/cluster/kube-down.sh
KUBERNETES_PROVIDER=aws KUBE_AWS_ZONE=us-west-2a 
kubernetes/cluster/kube-down.sh

結論 

Kubernetes 的核心理念是抽象化執行高可用、分散式應用程式的複雜性。如您所見,除了在叢集啟動時進行少量工作之外,在多個故障域中啟動應用程式例項的所有複雜性都不需要應用程式開發人員額外的工作,這正是它應該有的樣子。而我們才剛剛開始!

請加入我們的社群,幫助我們構建 Kubernetes 的未來!有很多參與方式。如果您特別對可伸縮性感興趣,您將對以下內容感興趣

當然,有關該專案的更多資訊,請訪問 www.kubernetes.io