背景
karmada中多个集群要想实现多集群服务发现,成员集群之间的容器网络必须可以相互连接。这就要求集群在初始化时需要使用不同的pod和service CIDR。
如果集群已经部署好了,并且CIDR也都相同,那么就可以使用Submariner中的globalnet实现多集群网络互通。
介绍
架构
Submariner 以安全和高性能的方式连接多个 Kubernetes 集群。 Submariner 使连接的集群之间的网络扁平化,并在 Pod 和服务之间实现 IP 可达性。 Submariner 还通过 Lighthouse 提供服务发现功能。服务发现模型是使用建议的 Kubernetes 多集群服务构建的。
Submariner 由几个主要组件组成,这些组件协同工作以安全地连接多个Kubernetes集群的工作负载。
- Gateway Engine:网关引擎,管理到其他集群的安全隧道。
- Route Agent:路由代理,将跨集群流量从节点路由到活动网关引擎。
- Broker:路由代理:将跨集群流量从节点路由到活动网关引擎。
- Service Discovery:提供跨集群服务的 DNS 发现。
Submariner 具有提供附加功能的可选组件:
- Globalnet Controller:Globalnet 控制器,处理具有重叠 CIDR 的集群的互连。
基本架构图如下:
概念
- ClusterSet:一组具有高度相互信任的两个或多个集群,它们在它们之间共享服务。在集群集中,所有具有给定名称的命名空间都被认为是相同的命名空间。
- ServiceExport (CRD) :用于指定哪些服务应该在集群集中的所有集群中公开。如果多个集群从同一个命名空间导出同名服务,它们将被识别为单个逻辑服务。
- ServiceExports 必须由用户在每个集群中以及底层服务所在的命名空间内显式创建,以表示该服务应该对集群集中的其他集群可见和可发现。 ServiceExport 对象可以手动创建,也可以通过 subctl export 命令创建。
- 导出服务后,它就可以作为
<service>.<ns>.svc.clusterset.local
访问。 - 对于 Headless Services,单个 Pod 可以作为
<pod-name>.<cluster-id>.<svc-name>.<ns>.svc.clusterset.local
访问。必须是有效的 DNS-1123 标签(仅包含小写字母数字字符,以字母数字开头以字母数字结尾,最多63个字符)
- ServiceImport (CRD) :每个集群中多集群服务的表示。由 Lighthouse 在内部创建和使用,不需要任何用户操作。
Broker
-
Submariner 使用中央 Broker 组件来促进部署在参与集群中的网关引擎之间的元数据信息交换。 Broker 基本上是一组由 Kubernetes 数据存储支持的自定义资源定义 (CRD)。 Broker 还定义了一个 ServiceAccount 和 RBAC 组件,以使其他 Submariner 组件能够安全地访问 Broker 的 API。 Broker 没有部署任何 Pod 或服务。
-
Submariner 定义了两个通过 Broker 交换的 CRD: Endpoint 和 Cluster 。 Endpoint CRD 包含有关集群中活动网关引擎的信息,例如集群相互连接所需的 IP。 Cluster CRD 包含有关原始集群的静态信息,例如其服务和 Pod CIDR。
-
Broker 是部署在集群上的单例组件,其 Kubernetes API 必须可供所有参与的集群访问。如果混合使用本地和公共集群,则可以将 Broker 部署在公共集群上。 Broker 集群可以是参与集群之一,也可以是没有部署其他 Submariner 组件的独立集群。部署在每个参与集群中的网关引擎组件都配置了信息,以安全地连接到 Broker 集群的 API。
-
Broker 集群的可用性不会影响数据平面在参与集群上的运行,即当 Broker 不可用时,数据平面将继续使用最后已知的信息路由流量。但是,在此期间,控制平面组件将无法向其他集群发布新的或更新的信息,也无法从其他集群了解新的或更新的信息。当重新建立与 Broker 的连接时,每个组件将自动将其本地信息与 Broker 重新同步,并在必要时更新数据平面。
-
一旦 Submariner 被部署在一个集群上,并向 Broker 提供适当的凭据,它将与其他集群交换集群和端点对象(通过推/拉/观察),并开始形成到其他集群的连接和路由。
Gateway Engine
网关引擎组件部署在每个参与的集群中,负责建立到其他集群的安全隧道。网关引擎可以使用以下实现:
- 使用 Libreswan 的 IPsec 实现。这是当前的默认设置。
- WireGuard 的实现(通过 wgctrl 库)。
- 使用 VXLAN 的未加密隧道实现。通常适用于所有参与的集群都在本地运行、底层网络受到控制并且已经通过其他方式加密的环境。也适用于公有云上的VPC对等连接,这样无需NAT即可相互访问。
在使用 subctl 加入集群时,可以通过 –cable-driver 参数指定。
网关引擎部署为 DaemonSet,仅在标有 submariner.io/gateway=true
的节点上运行,可能有多个节点用于容错。集群中只有一个活动的 Gateway Engine 实例。他们执行领导者选举过程以确定活动实例,其他人在备用模式下等待,准备在活动实例发生故障时接管。
活动网关引擎与中央 Broker 通信,将其 Endpoint 和 Cluster 资源通告给连接到 Broker 的其他集群,同时确保它是其集群的唯一 Endpoint 。在集群中运行的 Route Agent Pod 了解本地 Endpoint 并设置必要的基础设施以将跨集群流量从所有节点路由到活动网关引擎节点。活动网关引擎还在 Broker 上建立监视,以了解其他集群公布的活动 Endpoint 和 Cluster 资源。一旦两个集群知道彼此的 Endpoints ,它们就可以建立一个安全隧道,通过该隧道可以路由流量。示例图如下:
Libreswan
make deploy using=lighthouse
Libreswan 会创建 4 个 IPsec 隧道,允许以下连接:
- Pod 子网到 Pod 子网的连接
- Pod 子网到 Service 子网的连接
- Service 子网到 Pod 子网的连接
- Service 子网到 Service 子网的连接
下面是个示例网络拓扑图:
VXLAN
make deploy using=lighthouse, vxlan
下图显示了使用 Submariner 时的简单交互(从一个集群中的一个 pod 到第二个集群中的另一个 pod 的 ping)。路由会从eth0到vxlan-tunnel网卡再到CNI。
Gateway Failover
如果活动网关引擎发生故障,其他指定节点之一上的另一个网关引擎将获得领导地位并执行协调以通告其 Endpoint 并确保它是唯一的 Endpoint 。远程集群将通过 Broker 获悉新的 Endpoint 并建立新的隧道。同样,在本地集群中运行的 Route Agent Pod 会自动更新每个节点上的路由表,以指向集群中新的活动网关节点。
这里记录了各种场景下,对数据路径的影响。
Gateway Health Check
网关引擎持续监控连接的集群的健康状况。它定期 ping 每个集群并收集统计数据,包括基本连接、往返时间 (RTT) 和平均延迟。此信息在 Gateway 资源中更新。每当网关引擎检测到对特定集群的 ping 失败时,其连接状态就会标记为错误状态。服务发现使用此信息来避免在服务发现期间出现不健康的集群。可以通过 subctl join 命令上的选项启用/禁用健康检查功能。
Load Balancer mode
负载均衡器模式仍处于实验阶段,尚未在所有云厂商或不同的故障转移场景中进行测试。负载均衡器模式旨在简化 Submariner 在工作节点没有可用专用公共 IP 的云环境中的部署。
在 subctl join 期间为集群启用时,将创建一个 LoadBalancer 类型的服务,同时公开封装数据平面端口和 NAT-T 发现端口。此负载均衡器针对标有 gateway.submariner.io/status=active
和 app=submariner-gateway
的 Pod。
当启用 LoadBalancer 模式时, preferred-server 模式会自动为集群启用,因为 IPsec 与双向连接模式不兼容,并且需要负载平衡器和客户端/服务器连接。如果发生故障转移,负载均衡器将更新到新的可用和活动网关端点。
Preferred-server mode
首选服务器模式。此模式特定于基于 IPsec 的 libreswan实现。当在 subctl join 期间为集群启用时,网关将尝试通过在服务器模式下配置 IPsec 连接来尝试与其他集群建立连接,并等待远程连接。远程集群将识别该集群的 preferred-server 模式,并尝试连接。这在本地集群无法访问端口映射的环境中很有用。
当连接的双方都处于 preferred-server 模式时,它们将比较端点名称来决定哪一个是服务器,哪个是客户端。当名称按字母顺序排列时,第一个是客户端,第二个是服务器。
SERVICE DISCOVERY
Lighthouse 项目为多集群环境中由 Submariner 连接的 Kubernetes 集群提供 DNS 发现。 Lighthouse 实现了 Kubernetes 多集群服务 API。
基本的 Lighthouse 架构
Lighthouse Agent
Lighthouse Agent 运行在各个集群中,并访问运行在 Broker 集群中的 Kubernetes API 服务器,与其他集群交换服务元数据信息。将本地Service信息导出到Broker,导入其他集群的Service信息。
Agent Workflow
工作流程如下:
- Lighthouse Agent 连接到 Broker 的 Kubernetes API 服务器。
- 对于本地集群中已创建 ServiceExport 的每个 Service,Agent 创建相应的 ServiceImport 资源并将其导出到 Broker 以供其他集群使用。
- 对于从另一个集群导出的 Broker 中的每个 ServiceImport 资源,它会在本地集群中创建它的副本。
Lighthouse DNS Server
Lighthouse DNS 服务器作为拥有域 clusterset.local 的外部 DNS 服务器运行。 CoreDNS 配置为将发送到 clusterset.local 的任何请求转发到 Lighthouse DNS 服务器,该服务器使用控制器分发的 ServiceImport 资源进行 DNS 解析。 Lighthouse DNS 服务器支持使用 A 记录和 SRV 记录进行查询。
当单个服务部署到多个集群时,Lighthouse DNS 服务器优先选择本地集群,然后再以循环方式将流量路由到其他远程集群。
Server Workflow
工作流程如下:
- Pod 尝试使用域名 clusterset.local 解析服务名称。
- CoreDNS 将请求转发到 Lighthouse DNS 服务器。
- Lighthouse DNS 服务器将使用其 ServiceImport 缓存来尝试解析请求。
- 如果一条记录存在,它将被返回,否则将返回一个 NXDomain 错误。
GLOBALNET CONTROLLER
Submariner 的一个重要用例是将不同的独立集群连接到一个 ClusterSet 中。然而,默认情况下,Submariner 的一个限制是它不处理跨集群的重叠 CIDR(ServiceCIDR 和 ClusterCIDR)。每个集群必须使用不同的 CIDR,这些 CIDR 不会与将成为 ClusterSet 一部分的任何其他集群发生冲突或重叠。如下图所示就是两个相同CIDR的集群。
这在很大程度上是有问题的,因为大多数实际部署都使用集群的默认 CIDR,因此每个集群最终都使用相同的 CIDR。更改现有集群上的 CIDR 是一个非常具有破坏性的过程,需要重启集群。因此 Submariner 需要一种方法来允许具有重叠 CIDR 的集群连接在一起,就是 Globalnet 控制器。
架构
为了支持连接集群中的重叠 CIDR,Submariner 有一个名为 Global Private Network,Globalnet ( globalnet ) 的组件。这个 Globalnet 是一个虚拟网络,专门用于支持具有全球 CIDR 的 Submariner 的多集群解决方案。每个集群都从这个虚拟全球专用网络中获得一个子网,配置为新的集群参数 GlobalCIDR (例如 242.0.0.0/8),可在部署时配置。用户还可以使用传递给 subctl join 命令的标志 globalnet-cidr 为加入代理的每个集群手动指定 GlobalCIDR。如果 Broker 中未启用 Globalnet,或者如果集群中预配置了 GlobalCIDR,则将忽略提供的 Globalnet CIDR。
Cluster-scope global egress IPs
集群范围的全局出口 IP。默认情况下,每个集群都分配有可配置数量的全局 IP,由 ClusterGlobalEgressIP 资源表示,用作跨集群通信的出口 IP。支持多个 IP 以避免临时端口耗尽问题。默认值为 8。IP 从可配置的全局 CIDR 分配。在访问远程集群的主机网络上运行的应用程序也使用集群级全局出口 IP。
Namespace-scope global egress IPs
命名空间范围的全局出口 IP。用户可以通过创建 GlobalEgressIP 资源为每个命名空间分配可配置数量的全局 IP。这些 IP 也是从全局 CIDR 分配的,用作命名空间中所有或选定 pod 的出口 IP,并优先于集群级别的全局 IP。此外,为以命名空间中特定 pod 为目标的 GlobalEgressIP 分配的全局 IP 优先于为仅以命名空间为目标的 GlobalEgressIP 分配的全局 IP。
Service global ingress IPs
服务全局入口 IP。Exported ClusterIP 类型服务自动从全局 CIDR 分配一个全局 IP 用于入口。对于 headless 服务,每个支持 pod 都分配了一个全局 IP,用于入口和出口。但是,如果后端 pod 与 GlobalEgressIP 匹配,则其分配的 IP 将用于出口。
路由和 iptables 规则配置为使用相应的全局 IP 进行入口和出口。所有地址转换都发生在集群的活动网关节点上。
submariner-globalnet
Submariner Globalnet 是一个组件,它使用其全球 IP 提供从 pod 到远程服务的跨集群连接。编译为二进制 submariner-globalnet ,它负责维护全局 IP 池,将全局 IP 池中的 IP 分配给 Pod 和服务,并在网关节点上配置所需的规则以使用全局 IP 提供跨集群连接。 Globalnet 还支持从节点(包括使用主机网络的 pod)到远程服务的全局 IP 的连接。它主要由两个关键组件组成:IP 地址管理器和 Globalnet。
IP Address Manager (IPAM)
IP 地址管理器 (IPAM) 组件执行以下操作:
- 根据集群上配置的 GlobalCIDR 创建 IP 地址池。
- 从全局池中为所有入口和出口分配 IP,并在不再需要时释放它们。
Globalnet
该组件负责对路由条目、iptables 规则进行编程,并执行以下操作:
- 为 Globalnet 规则创建初始 iptables 链。
- 对于每个 GlobalEgressIP ,创建相应的 SNAT 规则以将所有匹配 pod 的源 IP 转换为分配给 GlobalEgressIP 对象的相应全局 IP。
- 对于每个 exported 导出的服务,它在内部创建一个带有 externalIPs 的额外服务,在与导出服务相同的命名空间中,并将 externalIPs 设置为分配给相应服务的 globalIP。
- 在删除 Pod 、 Service 或 ServiceExport 时清除网关节点的规则。
连接性只是解决方案的一部分,因为 pod 仍然需要知道远程集群上服务的 IP。这是通过 Lighthouse 来实现的。 Lighthouse 控制器在为 ClusterIP 类型的服务创建 ServiceImport 时使用服务的全局 IP。对于无头服务,在创建要分发到其他集群的 EndpointSlice 资源时使用 backing pod 的全局 IP。然后,Lighthouse 插件在回复 DNS 查询时使用全球 IP。
依赖
允许 Globalnet 控制器通过以下步骤创建/更新/删除 Service 和 externalIPs :
- 禁用 DenyServiceExternalIPs(如果启用,默认是禁用的)。
- 限制使用 Service 和 externalIPs :
- OpenShift:不需要额外的配置。默认的
network.openshift.io/ExternalIPRanger
验证准入插件仅允许具有处理 network.openshift.io 组中的 service/externalips 资源权限的用户使用 Service 和 externalIPs 。默认情况下, submariner-globalnet 的 ServiceAccount 有这样一条 RBAC 规则。 - 其他 Kubernetes 发行版:启用 externalip-webhook,同时指定 allowed-external-ip-cidrs 以包含分配给集群的 GlobalCIDR 和 allowed-usernames 以包含
system:serviceaccount:submariner-operator:submariner-globalnet
。
- OpenShift:不需要额外的配置。默认的
上述步骤是必要的,因为对于每个导出的 Service ,Submariner Globalnet 在内部创建一个带有 externalIPs 的 Service ,并将 externalIPs 设置为分配给相应 Service 的globalIP。出于安全原因,某些 Kubernetes 部署不允许创建带有 externalIPs 的 Service 。(漏洞CVE-2020-8554)
ROUTE AGENT
Route Agent 组件在每个参与集群的每个节点上运行。它负责在现有的 Kubernetes CNI 插件之上设置必要的主机网络配置。路由代理接收检测到的 CNI 插件作为其配置的一部分。
kube-proxy iptables
-
对于在 iptables 模式下使用 kube-proxy 的 CNI 插件,Route Agent 负责设置 VXLAN 隧道并将跨集群流量从节点路由到集群的活动网关引擎,后者随后将流量发送到目标集群。
-
当在与活动网关引擎相同的节点上运行时,路由代理创建一个 VXLAN VTEP 接口,在本地集群中的其他工作节点上运行的路由代理实例通过与活动网关引擎节点的 VTEP 建立 VXLAN 隧道连接到该接口。 VXLAN隧道的MTU是根据主机默认接口的MTU减去VXLAN开销配置的。
-
路由代理使用从其他集群同步的 Endpoint 资源来配置路由和编写必要的 iptables 规则以启用完整的跨集群连接。
-
当活动网关引擎发生故障并且新的网关引擎接管时,路由代理将自动更新每个节点上的路由表以指向新的活动网关引擎节点。