一般而言,Kubernetes 中的集群网络主要涉及以下几点核心要求:
1、服务的安全和隔离
2、Pod 的连接、网络和 IP 分配
3、设置网络以从多个物理节点构建集群抽象
4、跨服务的多个实例的流量负载平衡
5、控制对服务的外部访问
6、在公共和私有云环境中使用 Kubernetes 网络。
为了了解 Kubernetes 网络的不同方面,我们首先描述在 Pod 中创建服务一直到在公共云和私有云中访问该服务时会发生什么。同时,我们强调了对 Ingress 的需求以及它如何适应整个 Kubernetes 集群网络模型。
Pod 内访问
首先,我们先预设这样一种场景:针对一个有两个节点的简单 Kubernetes 集群,当 Kubernetes 创建及运行一个 Pod 时,它会在自己的隔离网络中运行(使用网络命名空间)。参考示意图如下所示:
这对服务意味着什么?服务在 Pod 网络中的 Pod 内运行。在该 Pod 网络上分配的 IP 地址(用于服务)在 Pod 外部则不可访问。那么如何访问该服务呢?
Pod 访问主机网络
Kubernetes 在多个物理节点或机器之上构建了一个集群的抽象。物理节点有自己的网络堆栈。由 Kubernetes 创建的 Pod 为在 Pod 内运行的服务创建了一个隔离的网络堆栈。
要访问此服务(或 Pod 内的 IP 地址),需要路由/桥接在 Pod 网络和主机网络之间创建路径。容器网络接口或 CNI 设置与在节点和 Pod 之间创建流量路径相关的网络。 常见的 CNI 插件主要有以下几种:Calico、Cilium、Flannel 以及其他生产环境落地较少的等。
当 Kubernetes 创建一个 Pod 时,它会调用 CNI 回调。这些回调导致调用 CNI 提供程序服务来为 Pod 设置 IP 地址并将 Pod 网络与主机网络连接。
跨节点访问
在实际的业务场景中,一个服务往往驻留在一个或多个 Pod 中,同时,这些 Pod 中的每一个都可可能驻留在一个物理节点或多个物理节点上。例如,假设一项服务分布在两个物理节点上的两个 Pod 中。
当流量发往该服务时(分布在两个节点上的两个 Pod 上),Kubernetes 如何在它们之间负载均衡流量?
Kubernetes 使用集群 IP 的抽象。任何发往集群 IP 的流量都会在 Pod(服务运行所在的 Pod )之间进行负载平衡。
为了对 Pod 中的服务实例进行负载平衡,需要设置网络以访问这些 Pod 中的服务。这些 Pod 可能在集群的不同物理节点上运行。为服务连接集群 IP 可确保发送到集群 IP 的流量可以发送到运行该服务的所有 Pod;无论 Pod 在哪个物理节点上运行。
Cluster IP 的实现和实现是通过 kube-proxy 组件和 iptables、ipvs 或用户空间流量引导等机制来实现。
集群外部访问
流经 ClusterIP 的流量在可能需要跨越多个物理节点的 Pod 之间进行负载平衡,然而,通常 ClusterIP 只能从集群中的节点访问。或者,换句话说,Kubernetes 中的网络确保对 ClusterIP 的外部访问受到限制。
访问集群外的 ClusterIP 需要显式声明,以使其在 Kubernetes 集群中的节点之外可访问。比如,节点端口等。
Kubernetes 中的 NodePort 将 Node IP(和端口)与 ClusterIP 连接起来。所定义的 NodePort 可提供本地网络上的 IP 地址,然后,发送到此 NodePort IP(和端口)的流量被路由至 ClusterIP 并最终负载平衡到 Pod(和服务)。具体可参考如下示意图所示:
公有云访问
NodePort 使服务可以在集群外部访问,但 IP 地址往往仅在本地集群环境可用,比如,开发、测试环境等。然而,基于生产环境所需,我们往往需要公有云上所提供的基础设施作为流量入口接入及转发。LoadBalancer 服务是一种将公共 IP(或 DNS)与 NodePort 服务相关联的方法。
当在 Kubernetes 集群中创建 LoadBalancer 类型的服务时,默认会分配一个公共 IP 并在云提供商(如 AWS、GCP、OCI、Azure 等)上设置负载均衡器。云负载均衡器配置为将流经至外部 IP 的流量通过管道传输到 NodePort 服务。具体可参考如下示意图所示:
私有云访问
在私有云中运行时,创建 LoadBalancer 类型的服务需要一个可以配置负载均衡器的 Kubernetes 控制器。在目前的解决方案中,一种这样的实现便是 MetalLb 。MetalLB 是裸机 Kubernetes 集群的负载均衡器实现,使用标准路由协议。其基于分配的 IP 地址来路由集群内的外部流量。从本质上来讲,MetalLB 旨在通过提供与标准网络设备集成的网络 LoadBalancer 实现来纠正这种不平衡,以便使得裸机集群上的外部服务也尽可能地正常发挥作用。
MetalLB 实现了一个实验性的 FRR 模式,它使用 FRR 容器作为处理 BGP 会话的后端。它提供了原生 BGP 实现所不具备的功能,例如将 BGP 会话与 BFD 会话配对,以及发布 IPV6 地址。
MetalLB 是一种可用于裸机环境的 Kubernetes 外部负载均衡器实现。它是谷歌开发的一个简单的负载均衡器,具有为负载均衡器类型的 Service 分配公共 IP 地址(External IP)和向 External IP 公开路由信息等两个功能。基于 MetalLb 设计特性,其主要涉及以下 2 种核心功能:
1、地址分配:当创建 LoadBalancer Service 时,MetalLB 会为其分配 IP 地址。这个 IP 地址是从预先配置的 IP 地址库获取的。同样,当 Service 删除后,已分配的 IP 地址会重新回到地址库。
2、对外广播:分配了 IP 地址之后,需要让集群外的网络知道这个地址的存在。MetalLB 使用了标准路由协议实现:ARP、NDP 或者 BGP。
广播的方式有两种,第一种是 Layer 2 模式,使用 ARP(ipv4)/NDP(ipv6) 协议;第二种是 BPG。
我们来看一下 MetalLB 网络参考示意图,如下所示:
基于上述参考拓扑结构图,我们可以看到:当有外部流量请求访问时,路由器和 ipvs 会根据设置的路由信息调整连接目的地。因此,从某种角度而言,MetalLB 本身并非是一种负载均衡组件设施,而是基于负载均衡场景而设计。
无入口的公有云访问
有几种方法可以访问在公共云上的 Kubernetes 集群中运行的服务。在公共云上,当服务类型为 LoadBalancer 时,会分配一个外部 IP 用于外部访问。
1、服务直接声明为 LoadBalancer 类型。
针对当服务被声明为 LoadBalancer 类型时,它将直接从外部负载均衡器接收流量。在如下图中,服务 Route 服务声明为 LoadBalancer 类型,它直接从外部负载均衡器接收流量。
2、控制和配置代理的 Ingress 服务可以声明为 LoadBalancer 类型。然后可以在此 Ingress 服务上创建路由和策略,以将外部流量路由到目标服务。
像 Envoy/Nginx/HAProxy/Traefik 这样的代理可以通过将其作为服务运行并定义此 LoadBalancer 类型的服务来接收进入集群的所有外部流量。这些代理可以使用 L7 路由和安全规则进行配置。这些规则的集合形成了 Ingress 规则。基于 Ingress - 将服务置于可通过负载均衡器从外部访问的代理后面。可以在服务之前放置一层 L7 代理,以应用 L7 路由和策略。为此,需要一个入口控制器。Ingress Controller 是 Kubernetes 集群内的服务,配置为 LoadBalancer 类型以接收外部流量。
Ingress Controller 使用定义的 L7 路由规则和 L7 策略将流量路由到服务。具体可参考如下示意图所示:
其实,从本质上来讲,在 Ingress 运行 Ingress Controller 和执行策略有几个明显的优势,具体:
1、Ingress 提供了一种可移植的机制来在 Kubernetes 集群内执行策略。在集群内实施的策略更容易跨云移植。可以使用 Kubernetes 服务扩展来水平扩展。
2、多个代理可以使用 Kubernetes 服务进行水平扩展,L7 结构的弹性使其更易于操作和扩展。
3、L7 策略可以与集群内的服务一起托管,具有集群原生状态存储。
4、让 L7 策略更接近服务可以简化服务和 API 的策略执行和故障排除。
在用于细粒度流量控制的插件,例如,EnRoute 云原生边缘路由网关,其使用 Envoy 作为底层代理来提供 L7 入口功能。从设计角度而言,EnRoute 具有模块化架构,紧密反映了 Envoy 的可扩展模型,其能够可以在路由级别或服务级别定义插件/过滤器,以在 Ingress 层执行 L7 策略。
除此之外,EnRoute 在社区版中完全免费提供高级限速插件,没有任何限制。使用深度 L7 状态为我们的 API 和微服务计时提供了一项关键需求,基于 EnRoutes 灵活的速率限制功能以及极大的灵活性以使得各种速率限制用例匹配具有重要意义。