Skip to content

网络模型与服务暴露

Kubernetes 网络模型基本原则

Kubernetes 对集群网络有着明确且严格的要求,这些要求构成了其网络模型的基础。Kubernetes 假设 Pod 之间可以直接通信,无论它们运行在集群中的哪个节点上,这种扁平化的网络结构大大简化了分布式系统的构建。具体来说,每个 Pod 都拥有一个独立的 IP 地址,而且所有 Pod 都位于一个可以直接连通的扁平网络空间中,这意味着不需要为了网络可达性而创建 NAT 映射或进行端口映射。

在 Kubernetes 的网络模型中,网络通信主要分为四种场景。

  • 同一个 Pod 内的容器之间通信,它们通过 localhost 直接通信,因为它们共享同一个网络命名空间。
  • 同一个 Node 上的不同 Pod 之间通信,它们通过 veth pair 设备和网桥进行通信。
  • 第三种是不同 Node 上的 Pod 之间通信,这需要网络插件支持跨节点的路由。
  • 第四种是 Pod 与 Service 之间的通信,这通过 Service 的虚拟 IP 和 kube-proxy 实现的负载均衡规则来完成。

这种网络模型的实现依赖于各种网络插件,最常见的是使用 CNI(Container Network Interface)规范的网络插件,如 Flannel、Calico、Weave Net 等。Flannel 使用 Overlay 网络,通过 VXLAN 或 UDP 封装来实现跨节点通信,简单易用但性能有一定损耗。Calico 则使用路由方案,通过 BGP 协议在节点间传播路由信息,性能更好但配置相对复杂。

Service 的服务暴露机制

虽然 Pod 拥有独立的 IP 地址,但 Pod 是易变的,它们的 IP 地址会随着重建而改变。Service 提供了一个稳定的访问端点,通过标签选择器来匹配后端的 Pod 集合,并为这些 Pod 提供负载均衡和服务发现能力。Service 在创建时会被分配一个虚拟 IP,称为 ClusterIP,这个 IP 仅在集群内部可达。

NodePort

NodePort 是最基础的服务暴露方式,它在每个 Node 上开放一个相同的端口,外部客户端可以通过访问任何 Node 的 IP 加上这个特定端口来访问服务。NodePort 的端口范围默认是 30000-32767,但也可以通过配置修改。当外部请求到达 Node 的 NodePort 端口时,kube-proxy 会将请求转发到对应的 Service,然后再由 Service 负载均衡到后端的 Pod。

在实际工程实践中,NodePort 虽然简单直接,但存在一些局限性。首先是不够直观,用户需要记住端口号,而且不同服务的端口不同,增加了记忆负担。其次是端口管理问题,大量服务时端口容易冲突。此外,NodePort 无法提供七层负载均衡能力,只能进行四层转发。

LoadBalancer

LoadBalancer 类型需要底层基础设施支持,通常是云厂商提供的负载均衡器服务。当创建一个 LoadBalancer 类型的 Service 时,Kubernetes 会向云厂商 API 申请创建一个外部的负载均衡器,并将负载均衡器的外部 IP 回填到 Service 的 status 中。

在云环境下使用 LoadBalancer 是非常方便的选择,云厂商会自动处理负载均衡器的创建、配置和健康检查。但在自建机房或私有云环境中,需要自己实现 LoadBalancer 的功能,常用的方案包括 MetalLB 这样的开源项目。MetalLB 使用标准路由协议(如 BGP)或 ARP/NDP 协议来宣告 IP 地址,从而在裸机环境中提供类似云厂商的负载均衡器功能。

从工程角度来看,LoadBalancer 提供了更好的用户体验,用户只需要通过一个固定的外部 IP 就能访问服务。但它也有成本考虑,云厂商的负载均衡器通常是按使用时长或流量计费的,在服务数量较多时成本会显著增加。

ExternalIP

// TODO:

Ingress

Ingress 是对 HTTP 和 HTTPS 协议的七层路由支持,本质上是一个封装好的高级 API。Ingress 建立在 LoadBalancer 类型的 Service 之上,进一步封装了一个 Nginx 反向代理服务。

nginx 集成

Ingress Controller 的核心价值在于将复杂的手工配置自动化。传统的做法需要手动编写和维护 nginx.conf 文件,每次新增或修改服务都要重启 Nginx。Ingress 通过监听集群中的 Ingress 资源变化,自动生成和更新 Nginx 配置文件,实现了配置的动态刷新,无需人工干预。以最常用的 Nginx Ingress Controller 为例,它会根据 Ingress 资源中的 host、path 等字段,自动生成对应的 nginx.conf 配置,包括 location 块、upstream 配置、rewrite 规则等。用户只需要定义想要的路由效果,具体的实现细节由 Controller 处理。

多级反向代理

在典型的生产环境中,流量路径是这样的:外部流量首先到达云厂商提供的 SLB(四层负载均衡),SLB 将流量转发到 Ingress Controller(七层负载均衡),Ingress Controller 再根据域名和路径将流量分发到不同的 Service,最后由 Service 负载均衡到后端的 Pod。这种多级架构实现了职责分离,每一层都专注于自己的任务。

第一级是集群外的 SLB,负责将外部流量引入集群。它工作在四层,性能极高,能够处理海量并发连接。SLB 通常由云厂商提供,具有高可用、自动扩缩容的能力,能够将流量均匀分发到集群的多个节点。第二级是 Ingress Controller,它工作在七层,能够理解 HTTP 协议的细节。它根据请求的 host、path、header 等信息进行智能路由,支持 TLS 卸载、灰度发布等高级功能。第三级是 Service,在集群内部进行四层负载均衡,将流量分发到健康的 Pod 实例。

这种多级负载均衡设计的优势在于每一层都可以独立扩缩容。当流量增长时,可以增加 SLB 的带宽、部署多个 Ingress Controller 实例、或者增加 Pod 副本数,每一层都可以根据自己的瓶颈进行扩展。而且这种分层提高了系统的容错能力,任何一层的故障都可以通过其他层的冗余来弥补。

从成本角度来看,使用 Ingress 加上一个外部 SLB,远比为每个服务创建独立 LoadBalancer 的成本低。大量服务可以共享同一个 Ingress Controller 和同一个外部 SLB IP,只需要为七层路由的智能调度付出一份资源,就能支持成百上千的服务暴露。

网络策略与安全

Kubernetes 默认的网络模型是"任何 Pod 可以与任何其他 Pod 通信",这在多租户环境中可能会带来安全隐患。NetworkPolicy 资源提供了控制 Pod 之间流量的能力,类似于云环境中的安全组规则。

NetworkPolicy 通过标签选择器来指定策略的适用对象,可以配置入站和出站流量的白名单规则。需要注意的是,NetworkPolicy 只是控制器,实际的网络流量控制由网络插件实现,并非所有 CNI 插件都支持 NetworkPolicy。Calico 对 NetworkPolicy 的支持比较完善,而简单的 Flannel 配置则不支持网络策略。

在生产环境中配置网络策略时,建议采用"默认拒绝,显式允许"的策略,首先创建一个拒绝所有流量的默认策略,然后根据实际需求逐步开放必要的通信路径。这种方式虽然配置工作量较大,但能够最大程度地保证集群的安全性。

网络性能优化

网络性能在容器化环境中尤为重要,因为网络 I/O 往往是分布式系统的主要瓶颈之一。传统的 Overlay 网络方案如 VXLAN 会带来额外的封装开销,在高吞吐或低延迟要求的场景下可能成为瓶颈。

针对性能敏感的场景,可以考虑使用 HostNetwork 模式,让 Pod 直接使用宿主机的网络命名空间,从而避免网络封装的开销。但这种方式会牺牲网络的隔离性,需要注意端口冲突和安全问题。另一种优化方向是使用 SR-IOV(Single Root I/O Virtualization)或 PCI passthrough 技术,让 Pod 直接访问物理网卡,实现接近裸机的网络性能。

在服务网格场景下,sidecar 模式的注入会增加网络调用的延迟,每次请求都需要经过本地 sidecar 代理的转发。对于延迟要求极高的服务,可以考虑使用 per-host 模式的代理或者使用 eBPF 技术实现的透明代理,如 Cilium 的方案,通过在内核层面处理网络策略和路由,显著降低了用户空间的处理开销。