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

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