使用Kubernetes Cluster Federation构建全球分布式服务

编者注:今天的帖子是产品经理Allan Naim和Google的工程师工程师Quinton Hoole展示的,该文章展示了如何在全局负载均衡器后面部署多宿主服务,以及如何将请求发送到最近的集群。

在Kubernetes 1.3中,我们宣布了Kubernetes集群联盟并引入了跨集群服务发现的概念,使开发人员可以部署跨不同区域,区域或云提供商的集群联合中分片的服务。如我们所详述,这使开发人员能够在不牺牲服务质量的情况下实现其应用程序的更高可用性。 以前 博客文章。

在最新版本中, Kubernetes 1.4,我们扩展了集群联合以支持副本集,机密,命名空间和Ingress对象。这意味着您不再需要在每个联合群集中分别部署和管理这些对象。只需在联盟中创建一次,然后让其内置控制器自动为您处理。

联合副本集 利用与非联合Kubernetes副本集相同的配置,并在一个或多个联合集群中自动分配Pod。默认情况下,副本会均匀地分布在所有群集中,但是对于不希望出现这种情况的情况,我们引入了“副本集”首选项,该首选项允许副本仅分布在某些群集中,或者以不相等的比例分布(定义注释)。

从Google Cloud Platform(GCP)开始,我们引入了 联合入口 作为Kubernetes 1.4 alpha功能,它使外部客户端可以指向单个IP地址,并将请求发送到在联盟的任何区域(区域)中具有可用容量的最近群集。

联合的秘密 自动在联盟中的所有群集之间创建和管理机密信息,即使这些群集在应用原始更新后处于离线状态,也可以自动确保这些秘密在全球范围内保持一致和最新。

联合命名空间 与传统的相似 Kubernetes命名空间 提供相同的功能。在联合控制平面中创建它们可以确保它们在联合中的所有集群之间同步。

联合活动 与提供相同功能的传统Kubernetes事件类似。联合身份验证事件仅存储在联合身份验证控制平面中,并且不会传递到基础kubernetes集群。

让我们逐步了解所有这些东西的工作原理。我们将为每个地区提供3个群集,覆盖3大洲(欧洲,北美和亚洲)。

下一步是联合这些集群。 Kelsey Hightower开发了 教程 用于设置Kubernetes集群联盟。按照教程配置集群联合,在3个GCP区域中的每个区域的3个区域中分别配置集群,分别是us-central1,euro-west1和asia-east1。就本篇博文而言,我们将在us-central1-b区域中配置联合身份验证控制平面。请注意,也可以使用更多高度可用的多群集部署,但是为了简单起见,此处不再使用。

博客文章的其余部分假定您已配置了运行的Kubernetes Cluster Federation。

让我们确认我们在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从实例元数据服务器检索区域,并连接到卷安装路径
复制集/nginx-rs.yaml部署由nginx和busybox容器组成的Pod
ingress / ingress.yaml创建一个具有全局VIP的负载均衡器,该VIP将请求分发到最近的nginx后端
服务/nginx.yaml将Nginx后端公开为外部服务

在我们的示例中,我们将使用联合控制平面部署服务和入口对象。的 ConfigMap 联盟目前不支持该对象,因此我们将在每个基础联盟中手动部署该对象。我们的集群部署将如下所示:

我们将部署在9个群集中分片的服务。后端部署将由一个Pod和2个容器组成:

  • busybox容器,用于获取区域并输出嵌入区域的HTML到Pod卷安装路径中
  • nginx容器从该Pod卷的安装路径读取并提供包含其运行区域的HTML

首先,在federation-cluster上下文中创建一个联邦服务对象。

$ kubectl --context=federation-cluster create -f 服务/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

现在创建一个联合入口。联合入口的创建方式与传统方式几乎相同 Kubernetes入口:通过进行API调用来指定逻辑入口点的所需属性。对于联合入口,此API调用定向到联合身份验证API端点,而不是Kubernetes集群API端点。联邦入口的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

创建后,联合入口控制器将自动:

  1. 1.在您的集群联合基础上的每个集群中创建匹配的Kubernetes Ingress对象
  2. 2.确保所有这些集群内入口对象共享相同的逻辑全局L7(即HTTP(S))负载均衡器和IP地址
  3. 3.监控每个群集中此入口后的服务“碎片”(即您的Pod)的运行状况和容量
  4. 4.确保即使在Pod,群集,可用性区域或区域中断的情况下,所有客户端连接始终都路由到适当的正常后端服务端点 我们可以验证入口对象在基础群集中是否匹配。请注意,所有9个群集的入口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

让我们快速浏览一下副本集:

$ cat 复制集/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: ""

副本集由9个副本组成,它们均匀分布在集群联合中的9个集群中。注释也可以用于控制Pod计划调度到哪些群集。这可以通过向“副本集”规范中添加注释来实现,如下所示:

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均匀地分布在Cluster Federation中。

让我们创建联合副本集:

$ kubectl --context=federation-cluster create -f 复制集/nginx-rs.yaml

验证副本集和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服务和相关的入口。总而言之,我们使用全局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集群支持Federated Ingress。在将来的版本中,我们打算支持基于混合云Ingress的部署。

总而言之,我们逐步利用了Kubernetes 1.4 联合入口 alpha功能在全局负载均衡器后面部署了多宿主服务。外部客户端指向单个IP地址,并被发送到联盟中任何区域,区域中具有可用容量的最近的群集,从而在不牺牲等待时间或简化操作的情况下提供了更高的可用性。

我们希望听到有关Kubernetes跨集群服务的反馈。要加入社区: