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

使用 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 在区域之间的均匀分散。如果需要,你可以使用同构区域(相同数量和类型的节点)来降低不均匀分散的可能性。

这种改进的标签也适用于存储。创建持久卷时,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 将变得不可用。但是,在所有区域中运行的容器将继续运行,如果它们失败,则由 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