# 使用联邦服务进行跨集群服务发现

本文介绍了如何使用 k8s 联邦服务跨多个 k8s 集群部署通用服务。 这使您可以轻松地为 k8s 应用程序实现跨集群服务发现和可用区容错。

# 前提要求

本指南假定您有一个已安装运行的 k8s 集群联邦,如果没有,请转到集群联邦管理指南 以了解如何启动一个集群联邦(或让您的集群管理员安装一个)。其他教程,例如 Kelsey Hightower 的这个教程也可以帮助您。

一般来说,你应对 k8s 的工作原理有一个基本了解,特别是服务。

# 概览

联邦服务的创建方式和传统 k8s Services 非常相似,只需进行一次 API 调用即可指定所需的服务属性。在联邦服务环境下,此 API 调用将定向到联邦 API 端点,而不是 k8s 集群 API 端点,联邦服务的 API 与传统 k8s Services 的 API 100%兼容

一旦创建,联邦服务自动:

  1. 在集群联邦底层的每个集群中创建对应的 k8s 服务,
  2. 监视那些服务 “shards” (以及它们所在的集群)的健康状况,
  3. 在公共 DNS 提供商(例如 Google Cloud DNS 或 AWS Route 53 )中管理一组 DNS 记录,从而确保您的联邦服务的客户端可以随时无缝地找到合适的健康服务端点,即使在集群可用区或区域中断的情况下也是如此。

在集群联邦内部的客户端(即 Pods ),如果集群联邦存在且健康,会自动在所在集群中找到集群联邦的本地分片,如果不存在,将会寻找最接近健康的分片。

# 混合云功能

k8s 集群联邦可以包含运行在不同云提供商(例如 Google Cloud,AWS )中的集群,以及本地私有云(例如在 OpenStack 上)。 只需在适当的云提供商 和/或 地区创建您需要的所有集群,并且向联邦 API Server 注册每个集群的 API 端点和凭证(参考集群联邦管理指南了解详细信息)。

之后,您的应用程序和服务可以跨越不同的集群和云提供商,详情如下所述。

# 创建联邦服务

通常这样完成,例如:

kubectl --context=federation-cluster create -f services/nginx.yaml

’–context=federation-cluster’参数告诉 kubectl 使用合适的凭证将请求提交给联邦 API 端点。 如果你还没有配置这样的上下文,访问集群联邦管理指南或管理教程 其中之一了解如何做到这一点。

如上所述,联邦服务将自动在联邦底层的所有集群中创建和维护对应的 k8s 服务。

您可以通过检查每个基础集群来验证这一点,例如:

kubectl --context=gce-asia-east1a get services nginx
NAME      CLUSTER-IP     EXTERNAL-IP      PORT(S)   AGE
nginx     10.63.250.98   104.199.136.89   80/TCP    9m

上面假定您的客户端中为您的集群在该区域中配置了一个名为’gce-asia-east1a’的上下文, 底层服务的名称和命名空间将自动匹配上面创建的联邦服务的名称和命名空间( 如果碰巧在这些集群中有已经存在的相同名称和命名空间的服务,它们将被联邦自动采用并更新以符合您联邦服务的规范 - 无论哪种方式,最终结果都将一样)。

联邦服务的状态将自动反映底层 k8s 服务的实时状态,例如:

$kubectl --context=federation-cluster describe services nginx

Name:                   nginx
Namespace:              default
Labels:                 run=nginx
Selector:               run=nginx
Type:                   LoadBalancer
IP:                     10.63.250.98
LoadBalancer Ingress:   104.197.246.190, 130.211.57.243, 104.196.14.231, 104.199.136.89, ...
Port:                   http    80/TCP
Endpoints:              <none>
Session Affinity:       None
No events.

请注意,联邦服务的’LoadBalancer Ingress’地址与所有基础 k8s 服务的’LoadBalancer Ingress’地址相对应( 一旦这些被分配 - 这可能需要几秒钟)。为了集群间和提供商间服务分片的网络正常工作, 您的服务需要有一个外部可见的 IP 地址。Service Type:Loadbalancer 通常用于此, 尽管也有其他选项(例如 External IP’s)存在。

还要注意,我们还没有配置任何后端 Pod 来接收指向这些地址的网络流量(即’Service Endpoints’),所以联邦服务还没有认为这些服务是健康的服务分片,因此还没有将这些服务添加到这个联邦服务的 DNS 记录中(后面更多关于这方面的内容)。

# 添加后端 pods

为了使底层服务分片健康,我们需要在服务后面添加后端 Pod。 这目前是直接针对底层集群的 API 端点完成的(尽管将来联邦服务器将能够使用单个命令为您完成所有这些工作,以节省您的麻烦)。 例如,要在 13 个基础集群中创建后端 Pod:

for CLUSTER in asia-east1-c asia-east1-a asia-east1-b \
                        europe-west1-d europe-west1-c europe-west1-b \
                        us-central1-f us-central1-a us-central1-b us-central1-c \
                        us-east1-d us-east1-c us-east1-b
do
  kubectl --context=$CLUSTER run nginx --image=nginx:1.11.1-alpine --port=80
done

请注意,kubectl run 会自动添加 run = nginx 标签,从而将后端 Pod 与对应的 service 相关联。

# 验证公共 DNS 记录

一旦上面的 Pods 成功启动,并开始监听连接,k8s 将报告 Pods 作为在该集群服务的健康端点(通过自动健康检查)。 集群联邦将依次考虑这些服务“分片”中的每一个都是健康的,并通过自动配置相应的公共 DNS 记录以将其放置在服务中。 您可以使用您的首选接口到您配置的 DNS 提供商来验证这一点。例如,如果您的联邦配置为使用 Google Cloud DNS,并且托管 DNS 域名为’example.com’:

$ gcloud dns managed-zones describe example-dot-com
creationTime: '2016-06-26T18:18:39.229Z'
description: Example domain for k8s Cluster Federation
dnsName: example.com.
id: '3229332181334243121'
kind: dns#managedZone
name: example-dot-com
nameServers:
- ns-cloud-a1.googledomains.com.
- ns-cloud-a2.googledomains.com.
- ns-cloud-a3.googledomains.com.
- ns-cloud-a4.googledomains.com.
$ gcloud dns record-sets list --zone example-dot-com
NAME                                                            TYPE      TTL     DATA
example.com.                                                    NS        21600   ns-cloud-e1.googledomains.com., ns-cloud-e2.googledomains.com.
example.com.                                                    OA        21600   ns-cloud-e1.googledomains.com. cloud-dns-hostmaster.google.com. 1 21600 3600 1209600 300
nginx.mynamespace.myfederation.svc.example.com.                 A         180     104.197.246.190, 130.211.57.243, 104.196.14.231, 104.199.136.89,...
nginx.mynamespace.myfederation.svc.us-central1-a.example.com.   A         180     104.197.247.191
nginx.mynamespace.myfederation.svc.us-central1-b.example.com.   A         180     104.197.244.180
nginx.mynamespace.myfederation.svc.us-central1-c.example.com.   A         180     104.197.245.170
nginx.mynamespace.myfederation.svc.us-central1-f.example.com.   CNAME     180     nginx.mynamespace.myfederation.svc.us-central1.example.com.
nginx.mynamespace.myfederation.svc.us-central1.example.com.     A         180     104.197.247.191, 104.197.244.180, 104.197.245.170
nginx.mynamespace.myfederation.svc.asia-east1-a.example.com.    A         180     130.211.57.243
nginx.mynamespace.myfederation.svc.asia-east1-b.example.com.    CNAME     180     nginx.mynamespace.myfederation.svc.asia-east1.example.com.
nginx.mynamespace.myfederation.svc.asia-east1-c.example.com.    A         180     130.211.56.221
nginx.mynamespace.myfederation.svc.asia-east1.example.com.      A         180     130.211.57.243, 130.211.56.221
nginx.mynamespace.myfederation.svc.europe-west1.example.com.    CNAME     180     nginx.mynamespace.myfederation.svc.example.com.
nginx.mynamespace.myfederation.svc.europe-west1-d.example.com.  CNAME     180     nginx.mynamespace.myfederation.svc.europe-west1.example.com.
... etc.

注意:如果您的联邦配置为使用 AWS Route53,则可以使用其中一个等效的 AWS 工具,例如:

$ aws route53 list-hosted-zones

$ aws route53 list-resource-record-sets --hosted-zone-id Z3ECL0L9QLOVBX

无论您使用哪种 DNS 服务程序,任何 DNS 查询工具(例如’dig’或’nslookup’)允许您查看联邦为您创建的记录。 请注意,您应该直接在您的 DNS 提供商处指出这些工具(例如 dig @ns-cloud-e1.googledomains.com ...), 或者根据您配置的 TTL 预计延迟(默认为 180 秒)看到更新,由于中间 DNS 服务器的缓存.

# 关于上面的例子的一些说明

  1. 请注意,每个至少有一个健康后端端点的服务分片有一个正常(‘A’)记录。 例如,在 us-central1-a 中,104.197.247.191 是该区域中服务分片的外部 IP 地址,而在 asia-east1-a 中,地址是 130.211.56.221。
  2. 同样,还有区域’A’记录,包括该地区所有健康的分片。 例如,’us-central1’。这些区域记录对于不具有特定区域首选项的客户端以及下述局部自动化和故障转移机制的构建块非常有用。
  3. 对于当前没有健康后端端点的区域,使用 CNAME(‘Canonical Name’)记录将这些查询别名(自动重定向)到下一个最接近的健康区域。 在本例中,us-central1-f 中的服务分片当前没有健康的后端端点(即 Pods ),因此已创建 CNAME 记录自动将查询重定向到该区域中的其他分片(本例中为 us-central1 )。
  4. 同样,如果封闭区域内没有健康的碎片,搜索就会进一步扩展。在 europe-west1-d 可用性区域,没有健康的后端,所以查询被重定向到更广泛的 europe-west1 (其也没有健康的后端 Pods),并且继续到全局健康的地址(‘nginx.mynamespace .myfederation.svc.example.com。’)。

上述 DNS 记录集自动保持与联邦服务系统全局所有服务分片的当前健康状态同步。 DNS 解析器库(由所有客户端调用)自动遍历 ‘CNAME’ 和 ‘A’ 记录的层次结构,以返回正确的健康 IP 地址集。然后,客户端可以选择任何一个返回的地址来启动网络连接(如果需要,可以自动切换到其他等效地址之一)。

# 发现联邦服务

# 从联邦集群内部的 pods

默认情况下,k8s 集群预先配置了集群本地 DNS 服务器(‘KubeDNS’),以及智能构建的 DNS 搜索路径,这些路径一起确保您在 Pod 内运行的软件发出的“myservice”,“myservice.mynamespace”,“bobsservice.othernamespace”等 DNS 查询会自动扩展并正确解析到在本地集群中运行的服务相应的服务 IP。

随着联邦服务和跨集群服务发现的引入,此概念将扩展到涵盖在全局范围内跨集群联邦的任何其他集群中运行的 k8s 服务。 要充分利用此扩展范围,请使用稍微不同的 DNS 名称(形式” <服务名称>.<命名空间>.<联邦名称>",例如 myservice.mynamespace.myfederation )来解析联邦服务。 你没有明确地选择这种行为时使用不同的 DNS 名称还可避免现有应用程序意外地穿越跨区域或跨区域网络,并且您因此可能会收到不必要的网络费用或延迟。

因此,使用我们的 NGINX 演示上面的服务,以及刚刚描述的联邦服务 DNS 名称表单,我们来看一个例子: us-central1-f 可用区中的集群中的 Pod 需要连接我们的 NGINX 服务。现在可以使用服务的联邦 DNS 名称

而不是使用服务在传统集群本地的 DNS (```"nginx.mynamespace"```, 将自动扩展到
```"nginx.mynamespace.svc.cluster.local"```)名称,
这将自动扩展,并解析到我的 NGINX 服务的最接近健康的分片,无论在世界任何地方。
如果在本地集群中存在健康的分片,该服务的集群本地(通常 10.x.y.z)IP 地址将被返回(由集群本地 KubeDNS ),
这几乎完全等同于非联邦服务解决方案(接近因为 KubeDNS 实际上为本地联邦服务返回一个 CNAME 和一个 A 记录,
但是应用程序会忽略这个小的技术差异)。


但是如果这个服务在本地集群中不存在的话(或者服务存在,但是没有健康的后端 pods ),那么 DNS 查询会自动扩展为
```"nginx.mynamespace.myfederation.svc.us-central1-f.example.com”```
(即逻辑上"找到离我的可用区域最近的其中一个分片的外部 IP "),这个扩展是由 KubeDNS 自动执行的,它返回相关的 CNAME 记录。
在上面的例子中,这导致了自动遍历 DNS 记录的层次结构,并且在本地 us-central1 区域的联邦服务的外部 IP 之一处结束
(即 104.197.247.191,104.197.244.180 or 104.197.245.170)。 


当然,可以通过明确指定适当的 DNS 名称,而不是依靠 DNS 的自动扩展,
明确地定位可用区域和 Pod 以外的区域中的服务分片,而不是依靠自动 DNS 扩展。例如,
"nginx.mynamespace.myfederation.svc.europe-west1.example.com" 将解析欧洲所有目前健康的服务分片,
即使发布查询的 Pod 位于美国,也不管在美国是否有健康的服务碎片。这对于远程监控和其他类似的应用是有用的。


### 来自集群联邦之外的其他客户端


上述讨论大部分同样适用于外部客户端,只是所述的自动 DNS 扩展已不再可用。
因此,外部客户端需要指定联邦服务的完全限定的 DNS 名称之一,是区域名称,
地区名称或全球名称。出于方便的原因,在服务中手动配置其他静态 CNAME 记录通常是一个好主意,例如:

``` shell
eu.nginx.acme.com        CNAME nginx.mynamespace.myfederation.svc.europe-west1.example.com.
us.nginx.acme.com        CNAME nginx.mynamespace.myfederation.svc.us-central1.example.com.
nginx.acme.com           CNAME nginx.mynamespace.myfederation.svc.example.com.

这样,您的客户就可以使用左侧的简短表格,并且可以自动将其路由到本国最近的健康分片。 k8s 集群联邦自动处理所有必需的故障转移。未来的版本将进一步改善。

# 处理后端 pods 和整个集群的失败

标准 k8s 服务 cluster-IP 已经确定无响应的单独 Pod 端点自动以低延迟(几秒钟)退出服务。 此外,如上所述,k8s 集群联邦系统会自动监控集群的健康状况,以及您的联邦服务的所有分片背后的端点,根据需要采取分片进入和退出服务(例如当服务后面的所有端点,或者整个集群或可用区域都停止运行时,或者相反,从停机状态恢复)。由于 DNS 缓存固有的延迟( 默认情况下,联邦服务 DNS 记录的缓存超时或 TTL 配置为 3 分钟,但可以进行调整)。 在灾难性失败的情况下,所有客户可能需要花费很长时间才能完全故障切换到另一个集群。 但是,考虑到每个区域服务端点可以返回的离散 IP 地址的数量(例如上面 us-central1,有三个选择) 许多客户端将自动故障切换到其中一个替代 IP 的时间少于给定的适当配置。

Last Updated: 4/15/2023, 8:33:17 PM