【K8S】Kubernetes 网络模型深度解析:Pod通信原理、CNI插件对比与kube-proxy模式详解
本文是 Kubernetes 系列的网络专题篇,将从底层原理出发,深入剖析 K8s 网络模型的设计哲学、Pod 间通信的数据包流转路径、主流 CNI 插件的架构差异,以及 kube-proxy 的三种工作模式。内容较长,建议收藏后阅读。
一、K8s 网络模型基础
1.1 三大基本原则
Kubernetes 网络模型的设计基于一个核心目标:让每个 Pod 都拥有一个集群内唯一的 IP 地址,并且 Pod 之间可以直接通信。具体来说,K8s 对集群网络提出了三条不可违背的基本原则:
| 原则 | 说明 |
|---|---|
| Pod 间通信无需 NAT | 任意两个 Pod 之间可以直接用对方的 Pod IP 通信,不经过地址转换 |
| Node 与 Pod 通信无需 NAT | 节点上的进程可以直接与该节点或其他节点上的 Pod 通信 |
| Pod 看到的自身 IP 与外部看到的一致 | Pod 内部看到的 IP 地址就是其他 Pod 访问它时使用的地址 |
这三条原则看似简单,实际上对网络实现提出了很高的要求。传统 Docker 网络使用 bridge + NAT 方案,每个容器的 IP 只在宿主机内部可见,跨主机通信必须依赖端口映射。而 K8s 要求的是一个扁平的网络拓扑——所有 Pod 仿佛在同一个大的二层网络中。
1.2 网络命名空间与 veth pair
理解 K8s 网络的前提是掌握两个 Linux 网络基础概念:
Network Namespace(网络命名空间)
Linux 内核通过网络命名空间实现网络资源的隔离。每个命名空间拥有独立的网络设备、IP 地址、路由表、iptables 规则和套接字。Pod 中的容器就运行在独立的网络命名空间中。
# 查看宿主机上所有网络命名空间
ip netns list
# 在指定命名空间中执行命令
ip netns exec <ns-name> ip addr show
# 查看某个容器的网络命名空间(先获取容器PID)
PID=$(docker inspect -f '{{.State.Pid}}' <container-id>)
nsenter -t $PID -n ip addr
veth pair(虚拟以太网对)
veth pair 是成对出现的虚拟网络设备,类似于一根网线的两端。数据从一端进入,必然从另一端出来。K8s 利用 veth pair 将 Pod 的网络命名空间与宿主机的网络命名空间连接起来。
# 创建一对veth设备
ip link add veth0 type veth peer name veth1
# 将veth1移入指定命名空间
ip link set veth1 netns <ns-name>
# 查看veth pair对应关系
ip link show type veth
ethtool -S veth0 # 查看peer_ifindex
二、Pod 通信原理详解
2.1 同 Pod 内容器通信
同一个 Pod 内的所有容器共享同一个 Network Namespace,这是通过 pause 容器(也称 infra 容器)实现的。Pod 中第一个启动的就是 pause 容器,它负责创建并持有网络命名空间,其他业务容器加入该命名空间。
┌─────────────────────────────────────────┐
│ Pod (共享 Network NS) │
│ │
│ ┌───────────┐ ┌───────────┐ │
│ │ Container │ │ Container │ │
│ │ A │ │ B │ │
│ │ :8080 │ │ :3306 │ │
│ └───────────┘ └───────────┘ │
│ │ │ │
│ └──── lo ──────┘ │
│ (localhost) │
│ │
│ eth0: 10.244.1.5 │
└─────────────────────────────────────────┘
因此,同 Pod 内容器之间的通信直接通过 localhost 即可:
apiVersion: v1
kind: Pod
metadata:
name: multi-container-pod
spec:
containers:
- name: web
image: nginx:1.24
ports:
- containerPort: 80
- name: sidecar
image: busybox
command: ['sh', '-c', 'while true; do wget -qO- http://localhost:80; sleep 5; done']
注意:同 Pod 内容器共享网络栈,因此不能绑定相同的端口号,否则会发生端口冲突。
2.2 同 Node Pod 间通信
同一个节点上不同 Pod 之间的通信,依赖 veth pair 和 Linux 网桥(通常是 cbr0 或 cni0)。
┌──────────────────────────────────────────────────────────┐
│ Node │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Pod A │ │ Pod B │ │
│ │ 10.244.1.2 │ │ 10.244.1.3 │ │
│ │ eth0 │ │ eth0 │ │
│ └──────┬──────┘ └──────┬──────┘ │
│ │ veth pair │ veth pair │
│ vethA│ vethB│ │
│ │ │ │
│ ┌────┴────────────────────────────┴────┐ │
│ │ Linux Bridge (cni0) │ │
│ │ 10.244.1.1/24 │ │
│ └──────────────────────────────────────┘ │
│ │ │
│ eth0 │ (Node IP: 192.168.1.10) │
└────────────────────────┼────────────────────────────────┘
│
物理网络
数据包流向:
- Pod A 发出目标为
10.244.1.3的数据包 - 数据包通过 veth pair 到达宿主机侧的
vethA设备 vethA连接在cni0网桥上,数据包进入网桥- 网桥查找 MAC 地址表,发现目标在
vethB端口 - 数据包从
vethB通过 veth pair 进入 Pod B 的网络命名空间
# 查看网桥设备
brctl show cni0
# 或使用 ip 命令
bridge link show
# 查看网桥上的MAC地址表
bridge fdb show br cni0
# 验证veth pair对端
ip link show vethXXXXXX
cat /sys/class/net/vethXXXXXX/ifindex
2.3 跨 Node Pod 间通信
跨节点通信是 K8s 网络的核心难题。由于 Pod IP 是集群内部分配的私有地址,物理网络并不认识这些地址的路由。业界主要有两种方案:
Overlay 网络方案(封装方案)
将原始数据包封装在宿主机网络可以路由的外层报文中,典型代表是 VXLAN:
原始数据包: [Pod A IP -> Pod B IP | payload]
↓ 封装
VXLAN报文: [Node A IP -> Node B IP | VXLAN Header | Pod A IP -> Pod B IP | payload]
┌─── Node A ───┐ 物理网络 ┌─── Node B ───┐
│ Pod A │ │ Pod B │
│ 10.244.1.2 │ │ 10.244.2.3 │
│ │ │ │ │ │
│ cni0 │ │ cni0 │
│ │ │ │ │ │
│ flannel.1 │ ── VXLAN Tunnel ──────> │ flannel.1 │
│ (VTEP) │ │ (VTEP) │
│ │ │ │ │ │
│ eth0 │═══════════════════════════>│ eth0 │
│192.168.1.10 │ 封装后的UDP包 │192.168.1.20 │
└──────────────┘ └──────────────┘
优点:对底层网络无要求,只要节点间三层可达即可
缺点:封装/解封装带来额外的 CPU 开销和 MTU 损耗(VXLAN 头部占50字节)
路由方案(非封装方案)
通过配置路由规则,让物理网络直接路由 Pod IP 段的流量:
# Node A 的路由表
10.244.1.0/24 dev cni0 # 本地Pod网段
10.244.2.0/24 via 192.168.1.20 # Node B 的Pod网段走Node B
# Node B 的路由表
10.244.2.0/24 dev cni0 # 本地Pod网段
10.244.1.0/24 via 192.168.1.10 # Node A 的Pod网段走Node A
优点:无封装开销,性能接近原生网络
缺点:要求节点在同一个二层网络,或者需要 BGP 等动态路由协议支持
2.4 Pod 与外部通信(SNAT)
Pod 访问集群外部时,由于 Pod IP 是私有地址(外部网络不可达),必须经过 SNAT(源地址转换)将源 IP 替换为节点 IP:
Pod (10.244.1.2) → 外部服务 (8.8.8.8)
数据包出节点时:
源IP: 10.244.1.2 → SNAT → 192.168.1.10 (Node IP)
目的IP: 8.8.8.8
响应数据包回来时:
目的IP: 192.168.1.10 → DNAT → 10.244.1.2 (conntrack记录)
# 查看NAT规则
iptables -t nat -L POSTROUTING -n -v | grep MASQUERADE
# 典型的SNAT规则(由kube-proxy或CNI创建)
-A POSTROUTING -s 10.244.0.0/16 ! -d 10.244.0.0/16 -j MASQUERADE
三、CNI 插件架构
3.1 CNI 规范与工作流程
CNI(Container Network Interface)是 CNCF 制定的容器网络标准接口规范。它定义了容器运行时如何调用网络插件来配置容器的网络。
CNI 插件需要实现三个核心操作:
| 操作 | 触发时机 | 功能 |
|---|---|---|
| ADD | Pod 创建时 | 为容器配置网络(分配IP、设置路由、连接网桥等) |
| DEL | Pod 销毁时 | 清理网络资源(释放IP、删除veth、清理规则等) |
| CHECK | 定期检查 | 验证容器网络配置是否正常 |
工作流程:
kubelet
│
├── CRI (Container Runtime Interface)
│ └── 创建 Pod sandbox (pause容器, 含 Network Namespace)
│
└── 调用 CNI 插件
│
├── 读取 /etc/cni/net.d/ 下的配置文件
├── 执行 /opt/cni/bin/ 下的二进制文件
│ ├── ADD: 配置网络
│ │ ├── 创建 veth pair
│ │ ├── 分配 IP 地址 (IPAM)
│ │ ├── 设置路由
│ │ └── 返回结果给 kubelet
│ ├── DEL: 清理网络
│ └── CHECK: 健康检查
└── 返回 Pod IP 给 kubelet
3.2 CNI 配置文件
# CNI配置文件位置
ls /etc/cni/net.d/
# 输出示例: 10-flannel.conflist 或 10-calico.conflist
# 查看配置内容
cat /etc/cni/net.d/10-flannel.conflist
典型的 CNI 配置文件(conflist 格式):
{
"name": "cbr0",
"cniVersion": "1.0.0",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
},
{
"type": "bandwidth",
"capabilities": {
"bandwidth": true
}
}
]
}
3.3 CNI 二进制文件
# CNI二进制文件位置
ls /opt/cni/bin/
# 常见输出:
# bandwidth bridge dhcp firewall flannel host-device
# host-local ipvlan loopback macvlan portmap ptp
# sbr static tuning vlan vrf
# 手动测试CNI插件(调试用)
cat <<EOF | CNI_COMMAND=ADD CNI_CONTAINERID=test CNI_NETNS=/var/run/netns/test \
CNI_IFNAME=eth0 CNI_PATH=/opt/cni/bin /opt/cni/bin/bridge
{
"cniVersion": "1.0.0",
"name": "mynet",
"type": "bridge",
"bridge": "cni0",
"ipam": {
"type": "host-local",
"subnet": "10.244.1.0/24"
}
}
EOF
四、三大主流 CNI 深度对比
4.1 Flannel
Flannel 是 CoreOS 开发的早期 CNI 方案,设计理念是简单、够用。它为每个节点分配一个子网,通过 etcd 存储子网分配信息。
VXLAN 封装模式
这是 Flannel 的默认模式,通过 VXLAN 隧道连接各节点的 Pod 网络:
# Flannel DaemonSet 配置 (kube-flannel-cfg ConfigMap)
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-flannel-cfg
namespace: kube-flannel
data:
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan",
"VNI": 1,
"Port": 4789,
"DirectRouting": true
}
}
DirectRouting: true 表示同子网内使用直接路由,跨子网才使用 VXLAN 封装,可以减少不必要的封装开销。
host-gw 直接路由模式
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "host-gw"
}
}
host-gw 模式直接在节点路由表中添加到其他节点 Pod 子网的路由,以对端节点 IP 作为网关(下一跳)。要求所有节点在同一个二层网络中。
# host-gw模式下的路由表
ip route show | grep 10.244
# 10.244.0.0/24 via 192.168.1.11 dev eth0
# 10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1
# 10.244.2.0/24 via 192.168.1.12 dev eth0
安装 Flannel
# 安装Flannel(官方推荐方式)
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
# 验证安装
kubectl get pods -n kube-flannel
kubectl get nodes -o wide # 确认节点状态为Ready
Flannel 优缺点
- 优点:配置简单、社区成熟、资源消耗低、上手门槛低
- 缺点:不支持 NetworkPolicy、功能单一、VXLAN模式性能一般、排障手段有限
4.2 Calico
Calico 是目前生产环境使用最广泛的 CNI 方案,基于 BGP 路由协议实现纯三层网络,同时支持强大的 NetworkPolicy。
核心组件
| 组件 | 功能 |
|---|---|
| Felix | 运行在每个节点上的 agent,负责编程路由和 ACL 规则 |
| BIRD | BGP 客户端,负责向集群中其他节点广播路由信息 |
| confd | 监听 etcd/K8s API 中的配置变化,动态生成 BIRD 配置 |
| calicoctl | 管理工具,用于配置和查看 Calico 资源 |
| Typha | 可选的中间层代理,减轻大规模集群下 API Server 的压力 |
BGP 路由方案
Calico 默认使用 BGP(Border Gateway Protocol)方案,每个节点作为一个虚拟路由器,通过 BGP 协议与其他节点交换 Pod 子网路由信息:
┌─── Node A ───┐ ┌─── Node B ───┐
│ Pod A │ │ Pod B │
│ 10.244.1.2 │ │ 10.244.2.3 │
│ │ │ │ │ │
│ cali* │ │ cali* │
│ (无网桥) │ │ (无网桥) │
│ │ │ │ │ │
│ 路由表 │ │ 路由表 │
│ │ │ │ │ │
│ BIRD ──────│───── BGP Peering ─────────│────── BIRD │
│ eth0 │ │ eth0 │
│192.168.1.10 │ │192.168.1.20 │
└──────────────┘ └──────────────┘
Calico 不使用 Linux 网桥,而是为每个 Pod 的 veth pair 在宿主机侧设置一条 /32 的路由规则:
# Calico节点上的路由表
ip route show | grep cali
# 10.244.1.2 dev cali1234abcd scope link
# 10.244.1.3 dev cali5678efgh scope link
# blackhole 10.244.1.0/26 proto bird
# 跨节点路由
ip route show | grep bird
# 10.244.2.0/26 via 192.168.1.20 dev eth0 proto bird
IPIP 模式
当节点不在同一二层网络时,Calico 使用 IP-in-IP 封装模式:
# Calico IPPool 配置
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
name: default-ipv4-ippool
spec:
cidr: 10.244.0.0/16
ipipMode: Always # Always/CrossSubnet/Never
vxlanMode: Never
natOutgoing: true
nodeSelector: all()
CrossSubnet 是推荐配置:同子网内使用 BGP 直接路由(高性能),跨子网时使用 IPIP 封装(兼容性好)。
安装 Calico
# 使用 Tigera Operator 安装(推荐)
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/tigera-operator.yaml
# 创建自定义资源配置
cat <<EOF | kubectl apply -f -
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
name: default
spec:
calicoNetwork:
bgp: Enabled
ipPools:
- blockSize: 26
cidr: 10.244.0.0/16
encapsulation: VXLANCrossSubnet
natOutgoing: Enabled
nodeSelector: all()
linuxDataplane: Iptables
nodeAddressAutodetectionV4:
firstFound: true
EOF
# 验证安装
kubectl get tigerastatus
watch kubectl get pods -n calico-system
# 使用calicoctl查看节点状态
calicoctl node status
calicoctl get ippool -o wide
calicoctl get workloadendpoint
Calico 优缺点
- 优点:性能优秀(纯路由无封装开销)、完整的 NetworkPolicy 支持、成熟稳定、社区活跃、支持大规模集群
- 缺点:配置相对复杂、BGP 在某些云环境受限、IPIP 模式仍有封装开销、排障需要 BGP 知识
4.3 Cilium
Cilium 是新一代 CNI 方案,基于 eBPF 技术实现数据平面,彻底改变了传统的 iptables 网络架构。
eBPF 方案原理
Cilium 将网络逻辑编译为 eBPF 程序并加载到内核中,直接在数据包处理路径上执行,避免了 iptables 规则匹配的线性开销:
传统方案:
数据包 → netfilter → iptables规则链(O(n)) → 转发
Cilium eBPF方案:
数据包 → eBPF程序(O(1) hash查找) → 转发
无 kube-proxy 模式
Cilium 可以完全替代 kube-proxy,使用 eBPF 实现 Service 负载均衡:
# Cilium Helm values.yaml - 替代 kube-proxy
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: cilium
namespace: kube-system
spec:
values:
kubeProxyReplacement: true
k8sServiceHost: "10.0.0.1" # API Server IP
k8sServicePort: "6443" # API Server Port
# 启用eBPF主机路由(绕过iptables)
bpf:
masquerade: true
hostLegacyRouting: false
# DSR模式(直接服务器返回,减少一跳)
loadBalancer:
mode: dsr
algorithm: maglev # 一致性哈希
# 启用Hubble可观测性
hubble:
enabled: true
relay:
enabled: true
ui:
enabled: true
# 启用带宽管理
bandwidthManager:
enabled: true
bbr: true
L7 可观测性与 Hubble
Cilium 内置的 Hubble 组件提供了深度的网络可观测性:
# 安装 Cilium CLI
curl -L --remote-name-all https://github.com/cilium/cilium-cli/releases/latest/download/cilium-linux-amd64.tar.gz
tar xzvf cilium-linux-amd64.tar.gz && mv cilium /usr/local/bin/
# 安装Cilium(使用Helm)
helm repo add cilium https://helm.cilium.io/
helm install cilium cilium/cilium --version 1.15.0 \
--namespace kube-system \
--set kubeProxyReplacement=true \
--set k8sServiceHost=${API_SERVER_IP} \
--set k8sServicePort=6443 \
--set hubble.enabled=true \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true
# 验证Cilium状态
cilium status
cilium connectivity test
# 使用Hubble观察网络流量
hubble observe --namespace default
hubble observe --protocol http --to-label app=nginx
hubble observe --verdict DROPPED # 观察被丢弃的包
# 端口转发访问Hubble UI
kubectl port-forward -n kube-system svc/hubble-ui 12000:80
Cilium 优缺点
- 优点:极致性能(eBPF原生)、可替代 kube-proxy、L3-L7 全栈可观测、强大的 NetworkPolicy(含L7)、Service Mesh 集成
- 缺点:内核版本要求高(>=5.4,推荐>=5.10)、学习曲线陡峭、eBPF 调试困难、相对年轻
4.4 CNI 综合对比
| 维度 | Flannel | Calico | Cilium |
|---|---|---|---|
| 数据平面 | VXLAN/host-gw | BGP路由/IPIP/VXLAN | eBPF |
| 性能 | 中等 | 优秀 | 极致 |
| NetworkPolicy | 不支持 | 完整支持(L3/L4) | 完整支持(L3/L4/L7) |
| 加密 | 不支持 | WireGuard | WireGuard/IPsec |
| 可观测性 | 基础 | 中等 | 优秀(Hubble) |
| 内核要求 | 3.10+ | 3.10+ | 5.4+(推荐5.10+) |
| 配置复杂度 | 低 | 中等 | 较高 |
| Service LB | 依赖kube-proxy | 依赖kube-proxy | 可替代kube-proxy |
| 多集群 | 不支持 | 支持 | 原生支持(ClusterMesh) |
| Service Mesh | 不支持 | 不支持 | 原生支持(sidecar-free) |
| 社区活跃度 | 一般(维护模式) | 活跃 | 非常活跃 |
| 适用场景 | 开发测试/小集群 | 生产环境通用 | 高性能/云原生深度场景 |
| 代表用户 | 小团队/学习环境 | 大多数企业 | 字节跳动/Google/AWS |
五、kube-proxy 工作模式
kube-proxy 是 K8s 中负责 Service 实现的组件,运行在每个节点上,监听 API Server 中 Service 和 Endpoints 的变化,维护网络规则以实现 Service 的虚拟 IP(ClusterIP)到后端 Pod 的负载均衡。
5.1 userspace 模式(已废弃)
这是最早期的实现,kube-proxy 进程自身作为代理监听端口,所有到 Service 的流量都要经过用户态程序转发:
Client → ClusterIP:Port → iptables → kube-proxy(用户态) → Pod
由于每个数据包都需要在内核态和用户态之间切换,性能极差,已在新版本中废弃。
5.2 iptables 模式
iptables 模式是目前的默认模式。kube-proxy 为每个 Service 生成一组 iptables 规则,利用内核 netfilter 框架直接在内核态完成数据包的 DNAT 和负载均衡。
工作原理
Client Pod → ClusterIP:Port
│
▼
PREROUTING链 → KUBE-SERVICES链
│
├── 匹配 ClusterIP + Port → KUBE-SVC-XXXX 链
│ │
│ ├── 50% probability → KUBE-SEP-AAAA (DNAT to Pod1 IP:Port)
│ └── 50% probability → KUBE-SEP-BBBB (DNAT to Pod2 IP:Port)
│
▼
Pod1 或 Pod2
规则链详解
# 查看Service相关的iptables规则
iptables -t nat -L KUBE-SERVICES -n | head -30
# 查看特定Service的规则链
# 假设有一个名为nginx-svc的ClusterIP Service
iptables -t nat -L KUBE-SVC-XXXXXXXXXXXXXXXX -n
# 输出示例:
# Chain KUBE-SVC-XXXXXXXXXXXXXXXX (1 references)
# target prot opt source destination
# KUBE-SEP-AAAAAAAAAAAAAAAA all -- 0.0.0.0/0 0.0.0.0/0 statistic mode random probability 0.33333
# KUBE-SEP-BBBBBBBBBBBBBBBB all -- 0.0.0.0/0 0.0.0.0/0 statistic mode random probability 0.50000
# KUBE-SEP-CCCCCCCCCCCCCCCC all -- 0.0.0.0/0 0.0.0.0/0
# 查看具体Endpoint的DNAT规则
iptables -t nat -L KUBE-SEP-AAAAAAAAAAAAAAAA -n
# 输出示例:
# Chain KUBE-SEP-AAAAAAAAAAAAAAAA (1 references)
# target prot opt source destination
# KUBE-MARK-MASQ all -- 10.244.1.5 0.0.0.0/0
# DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp to:10.244.1.5:80
# 统计iptables规则数量
iptables -t nat -L -n | wc -l
iptables-save | wc -l
iptables 模式的问题
- 规则数量线性增长:每个 Service 的每个端口每个 Endpoint 都需要若干条规则,1000个 Service × 10个 Pod = 数万条规则
- 规则更新非增量:任何一个 Endpoint 变化都需要刷新全部规则
- 随机负载均衡:只支持等权随机,不支持加权、最少连接等高级算法
- 无法优雅终止长连接:规则删除后已建立的连接可能中断
5.3 IPVS 模式
IPVS(IP Virtual Server)是 Linux 内核中专门用于四层负载均衡的模块,kube-proxy 的 IPVS 模式利用它来实现 Service 负载均衡。
工作原理
# IPVS模式下,kube-proxy会:
# 1. 为每个Service创建一个虚拟服务器(绑定ClusterIP到dummy接口kube-ipvs0)
# 2. 将后端Pod注册为Real Server
# 查看IPVS虚拟服务器列表
ipvsadm -Ln
# 输出示例:
# IP Virtual Server version 1.2.1 (size=4096)
# Prot LocalAddress:Port Scheduler Flags
# -> RemoteAddress:Port Forward Weight ActiveConn InActConn
# TCP 10.96.0.1:443 rr
# -> 192.168.1.10:6443 Masq 1 3 0
# TCP 10.96.0.10:53 rr
# -> 10.244.0.2:53 Masq 1 0 0
# -> 10.244.0.3:53 Masq 1 0 0
# TCP 10.96.128.5:80 rr
# -> 10.244.1.5:80 Masq 1 2 5
# -> 10.244.2.3:80 Masq 1 3 4
# -> 10.244.1.8:80 Masq 1 1 6
# 查看kube-ipvs0虚拟接口
ip addr show kube-ipvs0
# 2: kube-ipvs0: <BROADCAST,NOARP> mtu 1500
# inet 10.96.0.1/32 scope global kube-ipvs0
# inet 10.96.0.10/32 scope global kube-ipvs0
# inet 10.96.128.5/32 scope global kube-ipvs0
调度算法
IPVS 支持多种调度算法,通过 kube-proxy 参数配置:
| 算法 | 标识 | 说明 | 适用场景 |
|---|---|---|---|
| 轮询 | rr | 依次轮流分发(默认) | 后端性能均匀 |
| 最少连接 | lc | 优先分配给连接数最少的后端 | 长连接场景 |
| 源地址哈希 | sh | 相同源IP固定发往同一后端 | 会话保持需求 |
| 目标地址哈希 | dh | 相同目标IP固定发往同一后端 | 缓存场景 |
| 最短期望延迟 | sed | 考虑权重的最少连接 | 后端性能差异大 |
| 永不排队 | nq | 优先发给空闲服务器 | 快速响应场景 |
# kube-proxy ConfigMap - 启用IPVS模式
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-proxy
namespace: kube-system
data:
config.conf: |
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: "ipvs"
ipvs:
scheduler: "rr" # 调度算法
syncPeriod: "30s"
minSyncPeriod: "2s"
tcpTimeout: "0s"
tcpFinTimeout: "0s"
udpTimeout: "0s"
# 启用IPVS模式需要加载内核模块
modprobe ip_vs
modprobe ip_vs_rr
modprobe ip_vs_wrr
modprobe ip_vs_sh
modprobe nf_conntrack
# 验证模块加载
lsmod | grep ip_vs
cut -f1 -d " " /proc/modules | grep ip_vs
# 修改kube-proxy配置后重启
kubectl edit configmap kube-proxy -n kube-system
# 修改 mode: "ipvs"
kubectl rollout restart daemonset kube-proxy -n kube-system
连接追踪
IPVS 模式仍然依赖 conntrack(连接追踪)来处理 SNAT/DNAT:
# 查看连接追踪表
conntrack -L
conntrack -L -d 10.96.128.5 # 查看特定Service的连接追踪
# 查看conntrack表大小和使用情况
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max
# 常见问题:conntrack表满导致丢包
# 调优建议
sysctl -w net.netfilter.nf_conntrack_max=1048576
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600
5.4 iptables vs IPVS 对比与选型
| 维度 | iptables | IPVS |
|---|---|---|
| 规则复杂度 | O(n) 线性匹配 | O(1) 哈希查找 |
| 大规模性能 | 1000+ Service 时明显下降 | 万级 Service 仍稳定 |
| 负载均衡算法 | 仅随机(概率) | rr/lc/sh/dh/sed/nq等 |
| 规则更新 | 全量刷新 | 增量更新 |
| 会话保持 | 不支持 | 支持 |
| 连接优雅终止 | 差 | 支持 weight=0 graceful |
| 内核依赖 | 无额外要求 | 需要ipvs内核模块 |
| 调试便捷性 | iptables命令即可 | 需要ipvsadm |
| 稳定性 | 久经考验 | 同样稳定 |
选型建议:
- 集群 Service 数量 < 1000:两者皆可,iptables 更简单
- 集群 Service 数量 > 1000:强烈建议 IPVS
- 需要高级负载均衡算法:选 IPVS
- 需要会话保持:选 IPVS
- 追求极致性能:考虑 Cilium 替代 kube-proxy
六、NetworkPolicy 网络策略
6.1 默认行为
Kubernetes 的默认网络行为是全通的——任何 Pod 可以与任何 Pod 通信,不受任何限制。NetworkPolicy 是用来收紧这个策略的机制。
重要:NetworkPolicy 需要 CNI 插件支持才能生效。Flannel 不支持,Calico 和 Cilium 支持。
一旦对某个 Pod 应用了任何一条 NetworkPolicy:
- 该 Pod 的未被显式允许的流量将被默认拒绝
- 只有匹配规则中允许的流量才能通过
6.2 Ingress 规则示例
限制只有特定 namespace 和 label 的 Pod 才能访问目标 Pod:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: production
spec:
# 策略应用的目标Pod
podSelector:
matchLabels:
app: backend
tier: api
# 策略类型
policyTypes:
- Ingress
# 入站规则
ingress:
- from:
# 条件1:来自同namespace、标签为app=frontend的Pod
- podSelector:
matchLabels:
app: frontend
# 条件2:来自标签为env=production的namespace
- namespaceSelector:
matchLabels:
env: production
ports:
- protocol: TCP
port: 8080
- protocol: TCP
port: 8443
# 默认拒绝所有入站流量(零信任起点)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {} # 空选择器 = 选中namespace下所有Pod
policyTypes:
- Ingress
# 没有ingress规则 = 拒绝所有入站
6.3 Egress 规则示例
限制 Pod 的出站流量,防止敏感数据外泄:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: restrict-egress
namespace: production
spec:
podSelector:
matchLabels:
app: payment-service
policyTypes:
- Egress
egress:
# 允许访问内部数据库
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432
# 允许DNS解析(必须!否则域名解析失败)
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
# 允许访问外部HTTPS API(限定CIDR)
- to:
- ipBlock:
cidr: 203.0.113.0/24
ports:
- protocol: TCP
port: 443
6.4 微服务间访问控制实战
一个典型的三层微服务架构(frontend → backend → database)的网络策略:
---
# 1. 默认拒绝所有流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: microservice
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# 2. 允许所有Pod访问DNS
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: microservice
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
---
# 3. Frontend: 允许接收外部流量(Ingress Controller),允许访问Backend
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-policy
namespace: microservice
spec:
podSelector:
matchLabels:
tier: frontend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
ports:
- protocol: TCP
port: 80
egress:
- to:
- podSelector:
matchLabels:
tier: backend
ports:
- protocol: TCP
port: 8080
---
# 4. Backend: 只允许Frontend访问,只允许出站到Database
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-policy
namespace: microservice
spec:
podSelector:
matchLabels:
tier: backend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
tier: frontend
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
tier: database
ports:
- protocol: TCP
port: 5432
- to:
- podSelector:
matchLabels:
tier: cache
ports:
- protocol: TCP
port: 6379
---
# 5. Database: 只允许Backend访问
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: database-policy
namespace: microservice
spec:
podSelector:
matchLabels:
tier: database
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
tier: backend
ports:
- protocol: TCP
port: 5432
验证 NetworkPolicy 是否生效:
# 测试连通性
kubectl run test-pod --image=busybox --rm -it --restart=Never -- \
wget --timeout=3 -qO- http://backend-svc.microservice:8080/health
# 查看Calico中实际生效的策略
calicoctl get networkpolicy -n microservice -o wide
calicoctl get globalnetworkpolicy -o wide
# 查看Cilium中的策略执行情况
cilium policy get -n microservice
kubectl exec -n kube-system cilium-xxxxx -- cilium policy get
七、常见网络问题排查
7.1 DNS 解析失败
DNS 问题是 K8s 集群中最常见的网络故障之一,排查步骤如下:
# Step 1: 确认CoreDNS Pod状态
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50
# Step 2: 在问题Pod内测试DNS
kubectl exec -it <pod-name> -- nslookup kubernetes.default
kubectl exec -it <pod-name> -- cat /etc/resolv.conf
# 确认nameserver指向CoreDNS Service IP(通常是10.96.0.10)
# Step 3: 直接测试CoreDNS Pod
kubectl run dns-test --image=busybox:1.36 --rm -it --restart=Never -- \
nslookup kubernetes.default.svc.cluster.local 10.96.0.10
# Step 4: 检查CoreDNS Service和Endpoints
kubectl get svc -n kube-system kube-dns
kubectl get endpoints -n kube-system kube-dns
# 确认Endpoints不为空
# Step 5: 查看CoreDNS ConfigMap
kubectl get configmap coredns -n kube-system -o yaml
# Step 6: 检查是否有NetworkPolicy阻止DNS流量
kubectl get networkpolicy -A | grep -i dns
# Step 7: 抓包分析
# 在CoreDNS Pod所在节点抓DNS包
tcpdump -i any port 53 -nn -vv
常见 DNS 问题及解决方案:
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 解析超时 | CoreDNS Pod 不健康 | 重启 CoreDNS,检查资源限制 |
| NXDOMAIN | search域配置错误 | 检查 /etc/resolv.conf |
| 间歇性失败 | conntrack 表满 | 增大 conntrack_max |
| 5秒延迟 | DNS 并发查询竞争 | Pod DNS Config 设置 single-request-reopen |
# 解决5秒DNS延迟问题
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
dnsConfig:
options:
- name: single-request-reopen
- name: ndots
value: "2" # 减少无效DNS查询
containers:
- name: app
image: myapp:latest
7.2 Pod 无法跨节点通信
# Step 1: 确认CNI Pod状态
kubectl get pods -n kube-system | grep -E 'flannel|calico|cilium'
kubectl logs -n kube-system <cni-pod-name> --tail=100
# Step 2: 检查节点路由表
ip route show | grep -E '10.244|cali|flannel'
# 确认有到其他节点Pod子网的路由
# Step 3: 检查CNI网络设备
ip link show type vxlan # VXLAN设备
ip link show type ipip # IPIP隧道
ip -d link show flannel.1 # Flannel VTEP
ip -d link show tunl0 # Calico IPIP
# Step 4: 检查节点间连通性
# 在Node A上ping Node B的Pod网段网关
ping 10.244.2.1
# Step 5: 检查防火墙规则
iptables -L -n | grep -i drop
iptables -L FORWARD -n -v # 确认FORWARD链默认策略
# 常见问题:云平台安全组未放行VXLAN/IPIP协议
# VXLAN: UDP 4789
# IPIP: 协议号4 (Protocol 4)
# BGP: TCP 179
# Step 6: 使用tcpdump在两端抓包
# Node A:
tcpdump -i eth0 host <Node-B-IP> and udp port 4789 -nn
# Node B:
tcpdump -i flannel.1 -nn
# Step 7: 检查MTU设置
ip link show eth0 # 物理网卡MTU(通常1500)
ip link show flannel.1 # VXLAN设备MTU(应为1450,减去50字节头部)
ip link show cni0 # 网桥MTU
# MTU不一致会导致大包丢失
kubectl exec <pod> -- ping -s 1400 -M do <target-pod-ip>
7.3 Service 不通
# Step 1: 确认Service和Endpoints
kubectl get svc <service-name> -o wide
kubectl get endpoints <service-name>
# 如果Endpoints为空,说明没有匹配的健康Pod
# Step 2: 确认Pod标签与Service selector匹配
kubectl get pods --show-labels | grep <app-label>
kubectl describe svc <service-name> | grep Selector
# Step 3: 确认Pod的readinessProbe通过
kubectl describe pod <pod-name> | grep -A5 "Readiness"
kubectl get pods | grep "0/1" # 未就绪的Pod不会出现在Endpoints中
# Step 4: 检查kube-proxy状态
kubectl get pods -n kube-system | grep kube-proxy
kubectl logs -n kube-system <kube-proxy-pod> --tail=50
# Step 5: 检查iptables/ipvs规则
# iptables模式
iptables -t nat -L KUBE-SERVICES -n | grep <ClusterIP>
# IPVS模式
ipvsadm -Ln | grep -A5 <ClusterIP>
# Step 6: 直接访问Pod IP确认后端正常
kubectl exec <test-pod> -- curl <pod-ip>:<port>
# Step 7: 检查SessionAffinity配置
kubectl get svc <service-name> -o jsonpath='{.spec.sessionAffinity}'
7.4 常用排查命令汇总
# ==================== 网络命名空间相关 ====================
# 进入Pod的网络命名空间执行命令(无需Pod内安装工具)
# 获取Pod所在节点和容器ID
kubectl get pod <pod-name> -o jsonpath='{.status.hostIP} {.status.containerStatuses[0].containerID}'
# 在节点上使用nsenter进入容器网络命名空间
PID=$(crictl inspect <container-id> | jq .info.pid)
nsenter -t $PID -n ip addr
nsenter -t $PID -n ss -tlnp
nsenter -t $PID -n iptables -t nat -L -n
nsenter -t $PID -n ip route
# ==================== 抓包分析 ====================
# Pod内抓包(需要安装tcpdump或使用nsenter)
nsenter -t $PID -n tcpdump -i eth0 -nn -w /tmp/pod-traffic.pcap
# 抓取特定Pod的流量(在节点上对veth设备抓包)
# 先找到Pod对应的veth设备
ip link show | grep -A1 "$(nsenter -t $PID -n cat /sys/class/net/eth0/iflink)"
tcpdump -i veth12345 -nn
# 抓取Service相关流量
tcpdump -i any host <ClusterIP> -nn
# ==================== 连接追踪 ====================
# 查看conntrack记录
conntrack -L -d <service-ip>
conntrack -L -s <pod-ip>
conntrack -S # 查看conntrack统计信息(丢包等)
# 清除特定连接追踪记录(解决残留连接问题)
conntrack -D -d <old-pod-ip>
# ==================== iptables 深度排查 ====================
# 查看规则命中计数
iptables -t nat -L KUBE-SERVICES -n -v --line-numbers
iptables -t nat -Z # 重置计数器
# 追踪数据包经过的iptables规则(调试用,会产生大量日志)
iptables -t raw -A PREROUTING -d <ClusterIP> -j TRACE
# 查看追踪日志
dmesg | grep TRACE
# ==================== IPVS 排查 ====================
ipvsadm -Ln --stats # 查看统计信息
ipvsadm -Ln --rate # 查看速率
ipvsadm -Ln --connection # 查看当前连接
ipvsadm --zero # 清零统计
# ==================== 综合诊断工具 ====================
# 检查节点网络组件状态
kubectl get nodes -o wide
kubectl describe node <node-name> | grep -A10 "Conditions"
# 批量测试Pod连通性
kubectl get pods -o wide | awk 'NR>1{print $6}' | while read ip; do
kubectl exec test-pod -- timeout 2 curl -s $ip:80 > /dev/null && echo "$ip OK" || echo "$ip FAIL"
done
八、总结与选型建议
8.1 CNI 选型决策树
需要网络策略(NetworkPolicy)?
├── 否 → 集群规模?
│ ├── < 50节点 → Flannel (host-gw模式优先)
│ └── > 50节点 → Flannel (VXLAN + DirectRouting)
│
└── 是 → 内核版本 >= 5.4?
├── 否 → Calico (BGP + CrossSubnet IPIP)
└── 是 → 需要L7可观测/Service Mesh?
├── 否 → Calico (依然是最稳妥选择)
└── 是 → Cilium (eBPF全栈方案)
8.2 各场景推荐方案
| 场景 | CNI 推荐 | kube-proxy 模式 | 理由 |
|---|---|---|---|
| 开发/测试集群 | Flannel | iptables | 配置最简单,问题最少 |
| 中小生产集群(<500 Node) | Calico | IPVS | 功能完整,性能优秀 |
| 大规模集群(>1000 Node) | Calico + Typha | IPVS | Typha 减轻 API Server 压力 |
| 高性能计算场景 | Cilium | 无(Cilium替代) | eBPF 零拷贝,性能极致 |
| 多租户强隔离 | Calico/Cilium | IPVS | 需要完善的 NetworkPolicy |
| 边缘计算/IoT | Flannel | iptables | 资源占用最低 |
| Service Mesh 场景 | Cilium | 无(Cilium替代) | sidecar-free mesh 降低资源开销 |
8.3 关键建议
-
kube-proxy 模式选择:如果集群 Service 数量可能超过 1000,务必从一开始就使用 IPVS 模式。后期从 iptables 切换到 IPVS 虽然可行,但需要滚动重启 kube-proxy 并可能导致短暂的连接中断。
-
MTU 配置:务必根据实际网络环境正确配置 MTU。VXLAN 需要减去 50 字节,IPIP 需要减去 20 字节。MTU 不一致是很多"大包就丢、小包正常"问题的根因。
-
Pod CIDR 规划:提前规划好 Pod CIDR 范围,预留足够的地址空间。默认的 /16 可以支持约 65000 个 Pod,对于大集群可能需要更大的范围。
-
监控与告警:无论选择哪种方案,都应该监控以下指标:
- conntrack 表使用率
- CNI Pod 健康状态
- 节点间网络延迟
- DNS 解析延迟和失败率
- iptables/IPVS 规则数量
-
升级策略:CNI 升级属于高风险操作,建议先在非生产环境充分验证,生产环境采用逐节点滚动升级,并做好回滚预案。
系列文章预告:下一篇将深入讲解 K8s Service 的完整实现——从 ClusterIP、NodePort、LoadBalancer 到 Ingress 和 Gateway API,结合源码分析数据包从进入集群到到达 Pod 的完整链路。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)