在本文中,我们将提出一种我们认为满足这些约束的解决方案,称为服务镜像。为了符合Linkerd的“保持简单”的设计原则,我们已竭尽全力以纯Kubernetes原语构建此解决方案,并消除了对Linkerd本身的任何依赖。这使我们能够使任何解决方案尽可能接近Kubernetes本身。换句话说,网格应该做的更少,而不是更多。
说明
构建有效的多集群Kubernetes架构面临许多挑战,包括配置,监视,部署和流量管理。在这些挑战中,我们认为服务网格能够直接解决三个特定领域:
- 可观察性:服务网格可以提供跨集群的应用程序行为的统一视图。
- 安全性:服务网格可以为跨集群流量的身份验证,授权和机密性提供保证。
- 路由:服务网格可以使一个群集中的应用程序与另一群集中的应用程序进行通信变得“容易”。
如今,通过跨多个群集独立运行Linkerd,将指标聚合到外部Prometheus或Thanos,在DNS中共享服务信息以及使用证书管理器在集群Ingress上轮换证书,可以构建实现多个目标的多集群设置控制器。 (有关如何完成此操作的更多信息,请参见我们的多集群Kubernetes Way with Linkerd文章。)
虽然可以使用,但是这种方法在跨集群调用方面有一些缺点。与集群内调用相比,此方法中的跨集群调用没有完整的指标,没有在集群边界上保留源身份,并且不能成为流量拆分的目标。最关键的是,即使要做出这些部分保证,应用程序本身也必须区分集群间调用和跨集群调用。这与Linkerd不需要更改应用程序的目标背道而驰。
Service Mirroring(服务镜像)
服务镜像的目标是允许像Linkerd这样的服务网格为集群内调用(身份,流量转移等)提供同样的保证,也可以将其应用于跨集群调用。顾名思义,服务镜像的工作原理是在集群之间“镜像”服务信息。有了服务镜像,Linkerd的完整可观察性,安全性和路由功能统一适用于集群内调用和集群调用,以及应用程序无需区分这些情况。
服务镜像是Kubernetes operator。安装完成后,它将在本地镜像远程集群的服务,以提供服务发现并允许Pod引用远程服务。它还管理配置端点,以使流量流向正确的IP地址。 (下面有更多详细信息。)
聪明的读者可能会注意到,服务镜像中的任何内容实际上都不需要服务网格。相反,服务镜像是一个独立的组件,可以与其他项目很好地组合在一起。这不仅适用于服务镜像的潜在用户,而且适用于服务镜像本身。例如,服务镜像的网关组件可以通过设计插入,并且可以由诸如Ambassador,Glo和Traefik的项目实现。
要了解服务镜像的工作原理,让我们快速浏览一下法国大革命中心的Kubernetes的起源。
两个集群的故事
那是最美好的时光,那是最糟糕的时光。我们有两个Kubernetes集群,它们之间需要发送流量。让我们通过描述这些集群并选择特定任务来设置场景。
在左侧,伦敦有一个名为foo-client的Pod。在右侧,巴黎有一个名为bar的服务。我们如何使foo-client pod可以向bar服务发出请求?
Step 1: Service 发现
伦敦群集中的Pod需要向巴黎的bar服务发送请求。通常,这将使用服务名称来完成。但是,bar在另一个群集上!让我们将服务定义从巴黎复制到伦敦。
至此,bar服务已复制到伦敦。附加了远程集群名称,既避免了本地冲突,又允许Pod显式选择加入集群发送请求。立即,伦敦的Pod可以开始解析完整的服务名称-bar-paris.default.svc.cluster.local
。伦敦的bar服务的类型为ClusterIP。伦敦将创建一个虚拟IP地址,并将其用作对解析bar-paris服务名称的pod的响应。
通过将服务从巴黎复制到伦敦,我们保持了独立的状态-我们的要求之一。巴黎有自己的国家,伦敦有自己的国家,他们彼此不依赖。如果伦敦和巴黎之间的连接断开,则服务更新将停止。没关系!由于连接失败,无论如何,流量都无法从伦敦流向巴黎。
不幸的是,我们还无法将交通发送到目的地Pod。巴黎bar的Pod选择器与伦敦的Pod选择器不匹配。实际上,我们可能应该删除选择器,以防万一。我们打算到达巴黎的交通意外地意外地到达伦敦,这是无意的。删除选择器使我们可以将服务抽象到Pod之外,并使用其他后端。
没有选择器,将无法自动创建端点对象。 Kubernetes不知道看什么,因为我们没有提供足够的信息。拥有没有选择器的服务是Kubernetes旨在与之合作的东西!像Kubernetes中的所有资源一样,您可以自己创建和管理它们。因此,如果我们可以自己创建一个端点并将其指向某个地方,那么应该将流量转发到哪里?
Step 2: Endpoint
可以将所有端点从巴黎复制到伦敦,以及服务定义。根据服务中有多少个Pod,可能会有很多数据!查看EndpointSlices,以了解跨集群复制端点可能消耗多少带宽。即使可以解决此问题,复制单个Pod IP地址也不支持分层网络-这是我们的另一项要求。我们不介绍在群集之间移动所有这些状态,而是介绍一个端点,该端点可以负责将流量路由到正确的目的地。
我们在巴黎推出了一项新服务。对于LoadBalancer类型,网关服务将分配一个负载均衡器。该负载平衡器将具有一个公共IP地址,该地址可以在巴黎内部转发流量。现在,我们可以回答伦敦的请求应发送到哪里的问题!
为了使一切正常运行,我们现在可以创建一个包含此公共IP地址的Endpoints资源,并在其中发送流量。此时,在伦敦发出的请求将解析为bar-paris的ClusterIP,并将其重写为巴黎网关服务的公共IP地址。如果网关服务的选择器与bar定位到相同的广告连播,那么此时一切将正常进行。
到目前为止,我们的计划存在两个重要问题。云负载平衡器价格昂贵,公共IP地址很少。为群集中的每个服务创建一个外部负载平衡器将很快用完IP地址,并将成本提高到无法接受的水平。
也许更为关键的是,如果网关服务直接指向bar,则现在可以在更大的Internet上使用潜在敏感的内部服务。可以配置防火墙规则以限制访问,但是感觉很脆弱。可以在单个负载均衡器上多路复用服务并限制与授权客户端的连接性是什么?
Step 3: Gateway
如果您猜想该问题的解决方案听起来很像一个入口控制器,那将是对的!入口资源允许针对一般情况进行配置。由于Ingress规范不支持通配符,因此无法直接使用Ingress资源来执行此操作。幸运的是,大多数入口控制器都支持该用例!实际上,您所选择的入口控制器很可能已经可以在Kubernetes中通过少量配置就可以处理通配符。
现在可以跟踪请求的生命周期,因为它起源于伦敦并最终到达巴黎的最终目的地。从伦敦开始,Pod将向bar-paris.default.svc.cluster.local
发送请求。当Pod查询DNS时,它将收到位于伦敦的服务的群集IP。连接后,群集IP将被重写为巴黎网关服务的公共IP地址。然后,伦敦的pod会连接到该IP地址,并将其请求转发给居住在巴黎的入口控制器。入口控制器可以查看传入请求的主机标头,并将其重写为本地bar服务。重写后,该请求最终可以到达目的地Pod,即巴黎的bar服务器。
通过在巴黎运行的负载平衡器传递请求的另一个好处是,可以在集群上本地而不是远程进行决策。由于本地负载均衡器始终可以更好地了解本地发生的情况,因此,与在群集外部发起的决策相比,该决策可能更为理想。肯定有一个额外的跃点,引入了一个额外的组件,最终将在本地群集中进行路由,但确实增加了一些延迟。折衷方案是,多集群通信不是特殊情况,服务就像第三方服务一样公开,内部和外部服务之间的工具相同。
由于在此示例中没有专用网络,因此数据将遍历公共互联网。因此,对所有跨集群流量进行加密就变得至关重要。 Linkerd会自动执行开箱即用的mTLS,并透明地处理加密问题。在群集之间共享根证书可以使Linkerd验证连接的两端并加密它们之间的所有流量。共享的根证书允许Linkerd在两个群集中的控制平面完全独立,从而满足最终要求。当Linkerd自动化mTLS时,可以配置网关以呈现通配符证书,例如* .default.svc.cluster.local,客户端随后可以对其进行验证。流量将由客户端加密和验证。
请注意,该网关适用于TCP和HTTP,但有警告。基于任意TCP的协议将不包含网关将请求转发到正确目的地所需的信息。网关负载平衡器可以映射TCP端口,为每个内部服务保留一个端口。由于服务和端点都受到管理,因此可以在不对客户端或服务有任何要求的情况下进行端口重写。通过在TLS层使用ALPN或SNI之类的东西,实际上可以大大改善这种幼稚的解决方案。不幸的是,这些解决方案通常不受支持或不可配置。
PS:本文属于翻译,原文
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。