本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。

使用 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