本文是 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 网桥(通常是 cbr0cni0)。

┌──────────────────────────────────────────────────────────┐
│                        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)        │
└────────────────────────┼────────────────────────────────┘
                         │
                      物理网络

数据包流向:

  1. Pod A 发出目标为 10.244.1.3 的数据包
  2. 数据包通过 veth pair 到达宿主机侧的 vethA 设备
  3. vethA 连接在 cni0 网桥上,数据包进入网桥
  4. 网桥查找 MAC 地址表,发现目标在 vethB 端口
  5. 数据包从 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 关键建议

  1. kube-proxy 模式选择:如果集群 Service 数量可能超过 1000,务必从一开始就使用 IPVS 模式。后期从 iptables 切换到 IPVS 虽然可行,但需要滚动重启 kube-proxy 并可能导致短暂的连接中断。

  2. MTU 配置:务必根据实际网络环境正确配置 MTU。VXLAN 需要减去 50 字节,IPIP 需要减去 20 字节。MTU 不一致是很多"大包就丢、小包正常"问题的根因。

  3. Pod CIDR 规划:提前规划好 Pod CIDR 范围,预留足够的地址空间。默认的 /16 可以支持约 65000 个 Pod,对于大集群可能需要更大的范围。

  4. 监控与告警:无论选择哪种方案,都应该监控以下指标:

    • conntrack 表使用率
    • CNI Pod 健康状态
    • 节点间网络延迟
    • DNS 解析延迟和失败率
    • iptables/IPVS 规则数量
  5. 升级策略:CNI 升级属于高风险操作,建议先在非生产环境充分验证,生产环境采用逐节点滚动升级,并做好回滚预案。


系列文章预告:下一篇将深入讲解 K8s Service 的完整实现——从 ClusterIP、NodePort、LoadBalancer 到 Ingress 和 Gateway API,结合源码分析数据包从进入集群到到达 Pod 的完整链路。


Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐