本文发布已超过一年。较旧的文章可能包含过时内容。请检查页面中的信息自发布以来是否仍然准确。

使用 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. 1. 在集群联邦下的每个集群中创建匹配的 Kubernetes Ingress 对象。
  2. 2. 确保所有这些集群内 Ingress 对象共享同一个逻辑全局 L7(即 HTTP(S))负载均衡器和 IP 地址。
  3. 3. 监控此 Ingress 后每个集群中服务“分片”(即您的 Pods)的健康状况和容量。
  4. 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 跨集群服务的反馈意见。如需加入社区: