一、什么是 Kubernetes 网络
将 Kubernetes 的 Master 和 Worker 节点部署到一组主机节点(Node)上,称这组主机节点为 Kubernetes 集群。在 Kubernetes 集群中会运行很多由容器组成的 Pods,Pods 之间使用 CNI 提供的容器网络(Pods Network)进行通信。但容器并非永存,在节点资源不足或容器升级时都会进行 Pod 重建,此时容器网络会为 Pod 分配一个新的网络地址。为了固定容器服务提供的地址与端口,使用 Service 网络提供的 VIP 去代理真实节点的地址与端口。
这三层网络各司其职又彼此依赖:Node Network 提供物理连通性,Pods Network 解决容器间直通,Service Network 在 Pod IP 变动时提供稳定的访问入口,理解它们之间的协作关系,是排查集群网络问题的基础。
- Node Network:即宿主机网络,需要保证三层互通。
- Pods Network:同一个 Pod 中的容器之间通过本地回路(loopback)通信,不同 Pod 中的容器无需 NAT 能直接通信,主机网络与 Pod 网络也无需 NAT 能直接通信。
- Service Network:为 Pods 中运行的程序提供固定的 IP 与端口,以便于集群内外的访问。
二、Pods 网络的实现与选择
我们在上面描述过 Pods 网络的需求。目前实现 Pods 网络有 underlay 和 overlay 两种方案,以及两者共存的混合方案。这里的 underlay 和 overlay 网络并非特指某种具体网络方案,它们仅表示网络是否做了逻辑性的封装。
底层的基础架构网络被称为 underlay 网络,这层网络在数据中心通常更加注重承载能力、高可用性等特性。
上层的逻辑性网络被称为 overlay 网络,这层网络更加注重实现多租户、流量控制与安全等高级功能。
我们的需求是实现 Pods 网络中的 Pod 能不通过 NAT 进行直接通信,以及 Pod 与 Node 不通过 NAT 的直接通信,所以我们只需了解如何在这两类网络中实现即可。
2.1 基于 underlay 网络的 Pods 互通技术
在 underlay 网络中,我们一般通过路由实现网络互通。Flannel 就提供了一种最朴素的 underlay 网络互通方案,hostgw(主机网关)。
hostgw 的原理非常直接:每台宿主机作为各自 Pod 网段的网关,集群中的其他节点通过静态路由知道如何到达该网段。例如,当 Node A 上的 Pod 要访问 Node B 上的 Pod 时,流量首先发送到 Node B 作为网关的地址,再由 Node B 的内核路由转发到对应的 Pod。这种方式简单高效,不需要额外的封装开销,性能接近物理网络。
hostgw 的局限性在于要求所有节点必须在同一个二层网络(VLAN)中,否则跨子网的路由将无法生效。节点之间的路由信息需要手动维护或通过其他机制同步,在大规模集群中管理成本较高。
Calico 通过 BGP 提供了一种全三层的 underlay 网络互通方案。Calico 为每个节点分配一个子网,并通过 BGP 协议将路由信息广播到集群中的其他节点。当 Pod 需要与另一个节点上的 Pod 通信时,流量通过主机路由表直接发送到目标节点,无需任何封装。
Calico 的 BGP 模式性能接近原生网络,因为数据包无需封装解封装。BGP 本身是成熟的分布式路由协议,天然适合大规模集群的动态拓扑。Calico 还能与数据中心现有的 BGP 基础设施直接集成,不需要在集群边界做额外的路由转换。但 BGP 模式要求底层网络支持 BGP 协议,跨子网通信需要额外配置 BGP Peer,这对不具备网络设备管理权限的租户来说是个门槛。
Terway 通过交换机级联的方式实现网络互通:首先创建一个虚拟交换机用于 192.168.0.0/16 网络段,然后在该交换机下接入 Node 专用网络交换机用于 192.168.0.0/19 网段,接入 Pods 专用网络交换机用于 192.168.32.0/19 网段。这样也能实现 Pods 网络的需求。为了让 Pod 可以访问 Service 时能经过宿主机的 network namespace 中的 iptables 规则,所以另外增加了一个 veth 网卡打通 Pod 和宿主机的网络,并将集群的 Service 网段指向到这个 veth 网卡上。
2.2 基于 overlay 网络的 Pods 互通技术
在 overlay 网络中,只需 underlay 网络能三层互通,overlay 网络便可以基于这层基础网络实现任意类型的网络。
最朴素的依旧是 Flannel 提供的用户态 UDP 程序实现的 overlay 网络。
UDP overlay 的原理是将原始 IP 包封装在 UDP 包中进行传输。以 Flannel 的 UDP 后端为例,在发送端,内核将 Pod 的流量截获并转发到用户态的 flanneld 进程,该进程再将包封装成 UDP 消息发送到目标节点;在接收端,目标节点的 flanneld 接收 UDP 包,解封装后将原始 IP 包交给内核处理。
这种方式的优势在于兼容性强,只要网络能通 UDP 就能工作。但代价是性能损耗显著,每个包都需要经过用户态处理,涉及多次内存拷贝。在 Kubernetes 早期或低版本内核环境中,UDP 封装是唯一可用的选项,如今已不推荐在生产环境使用。
再者就是传统数据中心常用的 VXLAN overlay 网络(大多数网络插件都支持)。
VXLAN(Virtual Extensible LAN)是一种网络虚拟化技术,通过在三层网络上构建二层隧道来扩展 VLAN 的数量限制。相比 UDP 封装,VXLAN 由内核模块直接处理,效率高得多。VXLAN 使用 24 位的 VNI(VXLAN Network Identifier),理论上可支持 1600 万个虚拟网络,远超 VLAN 的 4094 个限制。
在 Kubernetes 中,VXLAN 通常采用「首节点封装」模式:源节点将 Pod 的流量封装成 VXLAN 包,通过三层网络发送到目标节点的目标 Pod。这一过程对 Pod 透明,Pod 以为自己直接在和同网段的其他 Pod 通信,实际上流量已经过隧道封装。
还有一种增强模式被称为 geneve,它结合了 VXLAN 的框架和灵活扩展头的能力,可以携带更多元数据,但目前实际使用较少。
2.3 CNI 的选择
Calico、Flannel、Cilium 三大主流 CNI 插件的具体实现原理(Felix/Bird 的 BGP 互连、Flannel 的 VXLAN 数据路径、Cilium 的 eBPF 数据路径)在 CNI 深度解析 中逐个拆解,这里只从选型视角给一张决策表:
| CNI | 网络模型 | 网络策略 | 适用场景 |
|---|---|---|---|
| Flannel | overlay 为主(VXLAN),支持 host-gw | 不支持 | 小规模、策略要求低、架构要简单 |
| Calico | underlay(BGP)为主,支持 IPIP/VXLAN | 支持 | 中大规模、需要 BGP 三层互连和精细策略 |
| Cilium | eBPF,可替代 kube-proxy | 支持(含 DNS/HTTP 层) | 大规模、对可观测性和性能有要求 |
一句话选型:规模小且不用策略选 Flannel,规模中等要策略选 Calico,规模大且要可观测性选 Cilium。三者的取舍边界和数据路径细节见 CNI 深度解析。
三、Service 网络的类型与作用
由于 Pod 在 Kubernetes 集群中是非永久性资源,滚动升级或节点资源不足等情况下都会发生重启,这会导致 Pod 的重新调度和 IP 分配。为了能相对固定地访问 Pod,Kubernetes 使用 Service 抽象了 Pod 的访问。
创建 Service 资源时,一般情况下会生成对应的 EndPoint 资源,EndPoint 记录了 Service 对应的所有 Pod 的访问地址。
一般情况下,由 kube-proxy 实现 Service 网络。kube-proxy 会监听 Service 和 EndPoint 资源的变化去修改 iptables/ipvs 规则,从而实现虚拟 IP 的效果。
Service 实现了 Pod 的服务发现和负载均衡。从可访问性来看,可以将 Service 网络分为集群网络和外部接入网络。
3.1 Service 集群网络
Service 集群网络通过 ClusterIP + Port 对 PodIP + Port 进行映射。
通过以下 YAML 可以创建一个 Service,它将:
- 创建一个名称为 my-service 的 Service
- 选择所有包含 MyApp 标签的 Pod
- 目标端口为 9376
- EndPoint 控制器会根据该 Service 的选择器自动生成 EndPoint 资源
- kube-proxy 会根据 EndPoint 资源从 ClusterIP 池获取一个 IP 用于映射到这些 Pod 上
apiVersion: v1kind: Servicemetadata: name: nginx-servicespec: ports: - port: 30080 targetPort: 80 selector: name: nginx-pod3.2 Service 外部接入网络
从互联网访问集群有如下类型的实现:
NodePort
在每个节点上通过设置 NAT iptables 规则,将本机地址与 Pod 地址做转换,实现 Pod 的外部访问。
apiVersion: v1kind: Servicemetadata: name: nginx-servicespec: type: NodePort ports: - port: 30080 targetPort: 80 nodePort: 30001 selector: name: nginx-podLoadBalancer(可以由 MetalLB 提供实现)
LoadBalancer 在 NodePort 的基础上,由云厂商的外部负载均衡器(如 AWS ELB、阿里云 SLB)分配一个外部可达的 IP,将流量转发到节点上的 NodePort。在自建机房没有云厂商 LB 时,MetalLB 通过 L2 ARP 或 BGP 宣告这个外部 IP,实现同样的效果。
apiVersion: v1kind: Servicemetadata: name: svc-nc-2-loadbalancerspec: type: LoadBalancer ports: - name: http port: 80 protocol: TCP targetPort: http - name: https port: 443 protocol: TCP targetPort: https selector: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/component: controllerexternalIPs
最简单的方法是可以为服务定义一个外部 IP 实现外网访问。但这个 IP 在 kube-proxy ipvs 模式中不能定义为节点 IP,否则会使节点到主节点之间的连接中断。
apiVersion: v1kind: Servicemetadata: name: svc-nc-2-externalspec: externalIPs: - 172.18.40.208 ports: - port: 8000 targetPort: 1500 protocol: TCP selector: app: pod-nc-2四、通过 Ingress 聚合 Service
通过部署 Ingress Controller 可以实现通过修改 Ingress 资源去代理多个 Service。也就是说,可以通过 Ingress 实现用一个地址暴露多个服务。
可以通过 IngressClass 资源(spec.ingressClassName 字段,1.18+)指定使用哪个 Ingress Controller,旧的 kubernetes.io/ingress.class 注解仍可用但已是历史写法。
Ingress Controller 也需要通过 Service 才能实现外网访问,所以 Ingress Controller 需要配合 LoadBalancer Service 使用。
# networking.k8s.io/v1 从 1.19 起 GA,旧的 networking.k8s.io/v1beta1 已在 1.22 移除apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: ingress-ncspec: rules: - host: nc.test.powercloud.com http: paths: - path: /nc1 pathType: Prefix backend: service: name: svc-nc-1 port: number: 81 - path: /nc2 pathType: Prefix backend: service: name: svc-nc-2 port: number: 82五、通过域名访问服务
Kubernetes 内置一个集群 DNS,该 DNS 支持正向查找(A Record)、端口查找(SRV 记录)、反向 IP 地址查找(PTR 记录)及其他功能。
创建 Service 资源时,集群 DNS 会自动生成记录:
| Service 类型 | DNS 名称 | 解析结果 |
|---|---|---|
| 普通 Service | {service}.{namespace}.svc.{domain} | Cluster IP |
| Headless Service | {service}.{namespace}.svc.{domain} | 后端 Pod IP 列表 |
可以部署自己的 DNS 实现自定义 DNS 记录,也可以利用 K8s 集群 DNS 的 rewrite 插件实现 CNAME 记录。下面是 CoreDNS ConfigMap 的一个示例,通过 rewrite name 指令把外部域名映射到集群内 Service:
kubectl edit configmap coredns -n kube-systemapiVersion: v1kind: ConfigMapmetadata: name: coredns namespace: kube-systemdata: Corefile: | .:53 { errors health { lameduck 5s } ready kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa ttl 30 } # 将 kuboard-v3.kuboard.svc.cluster.local 重写为 kuboard.core.powercloud.com # CNAME rewrite name kuboard.core.powercloud.com kuboard-v3.kuboard.svc.cluster.local # 集群 hosts hosts { {外部ip} kuboard.core.powercloud.com } prometheus :9153 forward . /etc/resolv.conf { max_concurrent 1000 } cache 30 reload loadbalance }六、网络策略
Kubernetes 网络策略可以支持任意 Pod 到 Pod 之间的入站与出站限制。
网络策略通过标签选择器定义规则,支持以下几种限制:
- Ingress 规则:控制哪些 Pod/namespace 可以访问目标 Pod
- Egress 规则:控制目标 Pod 可以访问哪些 Pod/namespace
- 命名空间隔离:基于命名空间标签的网络隔离
- IP 地址段限制:基于 CIDR 的访问控制
参考资料
- 服务、负载均衡和网络 - Kubernetes 官网 - 官方网络概念文档
- Kubernetes 网络权威指南:基础、原理与实践 - 网络原理深度解析
- nginx-ingress-controller - Ingress 配置指南
- Kubernetes 网络讲解 - 网络模型详解
- Kubernetes 网络插件基准测试 - CNI 性能对比
- Kubernetes 网络策略 - 网络隔离策略文档
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
部分信息可能已经过时






