本文已发布超过一年。较旧的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已变得不正确。
使用Kubernetes集群联邦构建全球分布式服务
在 Kubernetes 1.3 中,我们宣布了 Kubernetes 集群联邦,并引入了跨集群服务发现的概念,使开发人员能够部署一个跨越不同区域、地区或云提供商的集群联邦进行分片的服务。这使开发人员能够为其应用程序实现更高的可用性,而不会牺牲服务质量,正如我们在之前的博客文章中所详述的那样。
在最新版本Kubernetes 1.4中,我们扩展了集群联邦以支持 Replica Sets、Secrets、Namespaces 和 Ingress 对象。这意味着您不再需要在每个联邦集群中单独部署和管理这些对象。只需在联邦中创建它们一次,并让其内置控制器自动为您处理即可。
联邦 Replica Sets利用与非联邦 Kubernetes Replica Sets 相同的配置,并自动将 Pod 分布到一个或多个联邦集群中。默认情况下,副本均匀分布在所有集群中,但对于不希望这样做的的情况,我们引入了 Replica Set 偏好设置,允许副本仅分布在某些集群中,或以不等的比例分布(定义注解)。
从 Google Cloud Platform (GCP) 开始,我们引入了联邦 Ingress作为 Kubernetes 1.4 alpha 功能,该功能使外部客户端指向单个 IP 地址,并将请求发送到联邦中任何区域、可用区中具有可用容量的最近的集群。
联邦 Secrets在联邦中的所有集群中自动创建和管理 Secrets,自动确保这些 Secrets 保持全局一致和最新,即使在应用原始更新时某些集群处于脱机状态。
联邦 Namespaces类似于传统的Kubernetes Namespaces,提供相同的功能。在联邦控制平面中创建它们可确保它们在联邦中的所有集群之间同步。
联邦 Events类似于传统的 Kubernetes Events,提供相同的功能。联邦 Events 仅存储在联邦控制平面中,不会传递到底层的 Kubernetes 集群。
让我们逐步了解所有这些是如何工作的。我们将为每个区域配置 3 个集群,跨越 3 个大洲(欧洲、北美和亚洲)。
下一步是联邦这些集群。Kelsey Hightower 开发了一个用于设置 Kubernetes 集群联邦的教程。按照本教程配置一个集群联邦,其中在 3 个 GCP 区域(us-central1、europe-west1 和 asia-east1)的每个区域中有 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,其中包含它正在运行的可用区
让我们首先在 federation-cluster 上下文中创建一个联邦服务对象。
$ 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 Ingresses 非常相似:通过发出一个指定逻辑 Ingress 点所需属性的 API 调用。对于联邦 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. 在集群联邦下的每个集群中创建匹配的 Kubernetes Ingress 对象
- 2. 确保所有这些集群内的 Ingress 对象共享相同的逻辑全局 L7(即 HTTP(S))负载均衡器和 IP 地址
- 3. 监视每个集群中此 Ingress 后面的服务“分片”(即您的 Pod)的运行状况和容量
- 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
让我们快速查看一下我们的 Replica Set
$ 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: ""
Replica Set 由 9 个副本组成,均匀分布在集群联邦内的 9 个集群中。也可以使用注解来控制将 Pod 调度到哪个集群。这是通过将注解添加到 Replica Set 规范中来实现的,如下所示
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 均匀分布在集群联邦中。
让我们创建联邦 Replica Set
$ kubectl --context=federation-cluster create -f replicasets/nginx-rs.yaml
验证是否在每个集群中创建了 Replica Set 和 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 的部署方式。总而言之,我们有一个使用全局 L7 负载均衡器公开的全局 VIP (130.211.23.176),该负载均衡器将请求转发到具有可用容量的最近的集群。
为了进行测试,我们将启动 2 个 Google Cloud Engine (GCE) 实例,一个在 us-west1-b 中,另一个在 asia-east1-a 中。所有客户端请求都会通过最短的网络路径自动路由到最靠近请求来源的集群中的运行状况良好的 Pod。例如,来自亚洲的 HTTP(S) 请求将直接路由到亚洲中具有可用容量的最近的集群。如果亚洲中没有此类集群,则请求将路由到下一个最近的集群(在本例中为美国)。无论请求是来自 GCE 实例还是来自 Internet 上的任何其他位置,此方法都适用。我们在演示中仅使用 GCE 实例是为了简单起见。
我们可以使用 Cloud Console 或发出 gcloud SSH 命令直接 SSH 进入虚拟机。
$ 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 中,仅支持在 Google Cloud Platform 集群上进行联邦 Ingress。在未来的版本中,我们计划支持基于混合云 Ingress 的部署。
总而言之,我们演练了利用 Kubernetes 1.4 联邦 Ingress alpha 功能来部署全局负载均衡器后面的多宿主服务。外部客户端指向单个 IP 地址,并被发送到联邦中任何区域、可用区中具有可用容量的最近的集群,从而在不牺牲延迟或易于操作的情况下提供更高水平的可用性。
我们很乐意听到关于 Kubernetes 跨集群服务的反馈。要加入社区:
- 在 GitHub 上发布问题或功能请求
- 在 Slack 上的 #federation 频道加入我们
- 参与 集群联邦 SIG
- 下载 Kubernetes
- 在 Twitter @Kubernetesio 上关注 Kubernetes,获取最新更新