本文发布已超过一年。较旧的文章可能包含过时内容。请检查页面中的信息自发布以来是否仍然准确。
使用 Kubernetes 集群联邦构建全球分布式服务
在 Kubernetes 1.3 中,我们宣布了 Kubernetes 集群联邦,并引入了跨集群服务发现的概念,使开发者能够跨越不同可用区、区域或云提供商的联邦集群部署分片服务。正如我们在上一篇博客文章中详细介绍的那样,这使开发者能够为他们的应用实现更高的可用性,而不会牺牲服务质量。
在最新版本 Kubernetes 1.4 中,我们扩展了集群联邦以支持 Replica Sets、Secrets、Namespaces 和 Ingress 对象。这意味着您无需再单独在每个联邦集群中部署和管理这些对象。只需在联邦中创建一次,其内置控制器就会自动为您处理。
联邦 Replica Sets 利用与非联邦 Kubernetes Replica Sets 相同的配置,并自动将 Pods 分布到多个联邦集群中。默认情况下,副本均匀分布在所有集群中,但对于不希望如此的情况,我们引入了 Replica Set 首选项,允许副本仅分布在部分集群中,或以非等比例分布(定义注解)。
从 Google Cloud Platform (GCP) 开始,我们引入了 联邦 Ingress 作为 Kubernetes 1.4 的 Alpha 特性,它使外部客户端能够指向单个 IP 地址,并将请求发送到联邦中任意区域、可用区内具有可用容量的最近集群。
联邦 Secrets 在联邦中的所有集群中自动创建和管理 Secrets,确保这些 Secrets 保持全局一致和最新,即使在应用原始更新时某些集群处于离线状态。
联邦 Namespaces 类似于传统的 Kubernetes Namespaces,提供相同的功能。在联邦控制平面中创建它们可以确保它们在联邦中的所有集群之间同步。
联邦 Events 类似于传统的 Kubernetes Events,提供相同的功能。联邦 Events 只存储在联邦控制平面中,不会传递到底层 Kubernetes 集群。
让我们来看看这一切是如何工作的。我们将按区域配置 3 个集群,跨越三大洲(欧洲、北美和亚洲)。
下一步是将这些集群联邦化。Kelsey Hightower 开发了一个关于设置 Kubernetes 集群联邦的教程。请按照教程配置一个集群联邦,该联邦包含 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 后端作为外部服务暴露 |
在我们的例子中,我们将使用联邦控制平面部署 Service 和 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 Ingress 非常相似:通过进行 API 调用来指定逻辑 ingress 点所需的属性。对于联邦 Ingress,此 API 调用是定向到联邦 API 端点,而不是 Kubernetes 集群 API 端点。联邦 Ingress 的 API 与传统 Kubernetes Service 的 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 后每个集群中服务“分片”(即您的 Pods)的健康状况和容量。
- 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 集群内的客户端(即 Pods)将自动被路由到其集群中支持 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 个集群中。还可以使用 Annotation 来控制 Pods 调度到哪些集群。这可以通过向 Replica Set Spec 添加 Annotation 来实现,如下所示:
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
}
}
}
为了演示目的,我们将保持简单,将我们的 Pods 均匀分布在集群联邦中。
让我们创建联邦 Replica Set。
$ kubectl --context=federation-cluster create -f replicasets/nginx-rs.yaml
验证 Replica Sets 和 Pods 是否已在每个集群中创建。
$ 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 实例还是互联网上的任何其他地方。我们在演示中仅为了简单起见使用 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 中,联邦 Ingress 仅支持 Google Cloud Platform 集群。在未来的版本中,我们打算支持基于 Ingress 的混合云部署。
总之,我们演示了如何利用 Kubernetes 1.4 的联邦 Ingress Alpha 特性,在全球负载均衡器后部署一个多归属服务。外部客户端指向一个单一 IP 地址,并被发送到联邦中任意区域、可用区内具有可用容量的最近集群,从而在不牺牲延迟或操作简便性的情况下提供更高水平的可用性。
我们非常乐意听取关于 Kubernetes 跨集群服务的反馈意见。如需加入社区:
- 在 GitHub 上提交问题或功能请求
- 在 Slack 的 #federation 频道加入我们
- 参与 集群联邦 SIG
- 下载 Kubernetes
- 在 Twitter @Kubernetesio 上关注 Kubernetes 获取最新动态