Kubernetes 通信链路详细分析 目录
1. Pod 与 Pod 通信 1.1 同节点 Pod 间通信 当两个 Pod 在同一个节点 上运行时,它们的通信路径如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ┌─────────────────────────────────────────────────────────────────────┐ │ Node (Linux Host) │ │ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ Pod A │ │ Pod B │ │ │ │ netns A │ │ netns B │ │ │ │ 10.1.1.10 │ │ 10.1.1.11 │ │ │ │ eth0 │ │ eth0 │ │ │ │ │ │ │ │ │ │ │ └───┼────────┘ └────┼────────┘ │ │ │ veth pair A │ │ │ │◀─────────────────────────▶│ │ │ │ │ │ │ ┌───┴───────────────────────────┴───────────────────────────┐ │ │ │ CNI Bridge (cni0) │ │ │ │ │ │ │ │ MAC 地址表: │ │ │ │ ┌─────────────┬─────────────┬─────────────┐ │ │ │ │ │ Pod IP │ MAC │ veth │ │ │ │ │ ├─────────────┼─────────────┼─────────────┤ │ │ │ │ │ 10.1.1.10 │ aa:aa:aa:aa│ vethA │ │ │ │ │ │ 10.1.1.11 │ bb:bb:bb:bb│ vethB │ │ │ │ │ └─────────────┴─────────────┴─────────────┘ │ │ │ │ │ │ │ │ ARP 缓存: │ │ │ │ 10.1.1.11 → bb:bb:bb:bb (vethB) │ │ │ │ │ │ │ └─────────────────────────┬───────────────────────────────────┘ │ │ │ │ │ ┌────────┴────────┐ │ │ │ Root Netns │ │ │ │ eth0 (物理网卡)│ │ │ └────────────────┘ │ └─────────────────────────────────────────────────────────────────────┘
详细通信流程(10步) 步骤 1: Pod A 准备发送数据
1 2 3 4 5 6 7 Pod A 应用调用 socket API 发送数据到 10.1.1.11:80 数据包内容: 源 IP: 10.1.1.10 (Pod A eth0) 目标 IP: 10.1.1.11 (Pod B eth0) 源 MAC: aa:aa:aa:aa:aa:aa (Pod A eth0) 目标 MAC: ? (未知,需要 ARP 查询)
步骤 2: Pod A 发送 ARP 请求
1 2 3 4 5 6 7 Pod A 检查本地路由表: 10.1.1.0/24 via eth0 (同网段,直接发送) Pod A 发送 ARP Request: Who has 10.1.1.11? Tell 10.1.1.10 广播到: ff:ff:ff:ff:ff:ff
步骤 3: veth pair 传输 ARP
1 2 3 4 5 ARP 请求通过 veth pair A 传输到 Node 根网络命名空间 veth pair 是成对创建的: - 一端在 Pod A 的 netns (名称通常是 eth0) -另一端在 Node 根 netns (如 vethA)
步骤 4: CNI Bridge 接收并处理 ARP
1 2 3 4 5 6 7 8 9 cni0 网桥收到 ARP Request: 1. 学习源 MAC: - 记录 10.1.1.10 -> aa:aa:aa:aa -> vethA 2. 广播 ARP 到所有端口 (除了收到数据的端口 vethA): - vethB (Pod B 的 veth pair) - 其他 veth 端口 - 可能有 tunnel 端口
步骤 5: Pod B 收到 ARP 请求并响应
1 2 3 4 5 6 Pod B 收到 ARP Request (因为 ARP 广播到 vethB): Pod B 发送 ARP Reply: 10.1.1.11 is at bb:bb:bb:bb:bb:bb 这个 Reply 会被 CNI Bridge 收到
步骤 6: CNI Bridge 学习并转发 ARP Reply
1 2 3 4 5 6 7 cni0 处理 ARP Reply: 1. 学习 MAC 地址: - 记录 10.1.1.11 -> bb:bb:bb:bb -> vethB 2. 查 MAC 表,知道 10.1.1.10 在 vethA: - 直接转发到 vethA (不再广播)
步骤 7: Pod A 收到 ARP Reply 并缓存
1 2 3 4 5 6 7 Pod A 收到 ARP Reply: 10.1.1.11 is at bb:bb:bb:bb:bb:bb Pod A 缓存 ARP: 10.1.1.11 -> bb:bb:bb:bb:bb:bb (缓存 20 分钟) 现在可以发送实际数据包了
步骤 8: Pod A 发送实际数据包
1 2 3 4 5 6 7 数据包: 源 IP: 10.1.1.10 目标 IP: 10.1.1.11 源 MAC: aa:aa:aa:aa:aa:aa 目标 MAC: bb:bb:bb:bb:bb:bb 通过 veth pair A 发送到 cni0
步骤 9: CNI Bridge 转发数据包
1 2 3 4 5 6 7 8 cni0 收到数据包: 1. 查 MAC 表: - 目标 MAC bb:bb:bb:bb -> vethB 2. 从 vethB 端口转发 (直接发送,不再广播) 3. 通过 veth pair B 到达 Pod B 的 netns
步骤 10: Pod B 接收数据包
1 2 3 4 Pod B eth0 收到数据包: 目标 IP: 10.1.1.11 (匹配本机) 内核将数据交给 Pod B 内的应用进程
关键组件详解
组件
作用
关键操作
veth pair
虚拟网线,连接 Pod netns 和 Node netns
成对创建,一端在 Pod,一端在 Node
CNI Bridge
二层交换机,连接同节点所有 Pod
MAC 地址学习,ARP 广播/转发
ARP 缓存
记录 IP-MAC 映射
动态学习,超时删除
路由表
判断目标是否同网段
同一网段走 bridge,不同网段走网关
源码位置 1 2 3 4 5 6 7 8 CNI 实现: pkg/kubelet/network/cni/cni.go - CNI 插件主逻辑 pkg/kubelet/network/cni/cni_sysfs.go - 网络配置 pkg/kubelet/network/plugins.go - 网络插件接口 Kubelet 网络初始化: pkg/kubelet/network/network.go - 网络初始化流程 pkg/kubelet/network_host.go - Host 网络接口
1.2 跨节点 Pod 间通信 当两个 Pod 在不同节点 上运行时,通信需要经过物理网络。
1.2.1 整体架构图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 ┌─────────────────────────────────────────────────────────────────────────────┐ │ 跨节点 Pod 通信架构 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ Node A (192.168.1.10/24) Node B (192.168.1.11/24) │ │ ┌─────────────────────────┐ ┌─────────────────────────┐ │ │ │ Pod A (10.1.1.10/24) │ │ Pod B (10.2.1.10/24) │ │ │ │ │ │ │ │ │ │ │ │ ▼ │ │ ▼ │ │ │ │ ┌──────┐ │ │ ┌──────┐ │ │ │ │ │ eth0 │ │ │ │ eth0 │ │ │ │ │ └──┬───┘ │ │ └──┬───┘ │ │ │ │ │ vethA │ │ │ vethB │ │ │ │ ▼ │ │ ▼ │ │ │ │ ┌────────┐ │ │ ┌────────┐ │ │ │ │ │ cni0 │ │ │ │ cni0 │ │ │ │ │ └──┬─────┘ │ │ └──┬─────┘ │ │ │ │ │ │ │ │ │ │ │ │ │ flannel.1 │ │ │ flannel.1 │ │ │ │ │ (VTEP) │ │ │ (VTEP) │ │ │ │ │ 10.1.1.0/24 │ │ │ 10.2.1.0/24 │ │ │ │ │ VNI: 1 │ │ │ VNI: 1 │ │ │ │ └────────┬─────────┘ │ └────────┬─────────┘ │ │ │ │ UDP:8472 │ │ UDP:8472 │ │ └───────────────┼─────────────────┘ ├───────────────┘ │ │ │ │ ┌─────┴─────┐ ┌─────┴─────┐ │ │ eth0 │ │ eth0 │ │ │ 192.168.1.10│ │ 192.168.1.11│ │ └─────┬─────┘ └─────┬─────┘ │ │ │ │ ┌────────────────┼───────────────────────────────────┘ │ │ │ 物理网络 (交换机 L2) │ ▼ ▼ │ ┌─────────────────────────────────────────────┐ │ │ 物理网络 (Layer 2 Switch) │ │ └─────────────────────────────────────────────┘ │ │ └─────────────────────────────────────────────────────────────────────────────┘
1.2.2 Flannel VXLAN 详细通信流程(15步) 步骤 1: Pod A 发送数据包(应用层)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Pod A 应用发送请求到 10.2.1.10:80 Socket 创建: connect(fd, "10.2.1.10", 80) 数据包生成: TCP Segment: 源端口: 40000 目标端口: 80 IP Header: 源 IP: 10.1.1.10 目标 IP: 10.2.1.10 Ethernet Header: 源 MAC: aa:aa:aa:aa:aa:aa 目标 MAC: ? (需要 ARP)
步骤 2: Pod A 本地路由决策
1 2 3 4 5 6 7 8 9 Pod A 路由表: 10.0.0.0/8 via eth0 (匹配 10.2.1.10) 目标 10.2.1.10 不在同网段 10.1.1.0/24 需要发送到网关 (但 Kubernetes 没有网关 IP) 实际行为: 在 CNI 层面,路由会指向 cni0 bridge cni0 需要知道如何到达 10.2.1.0/24 网络
步骤 3: CNI Bridge 查询路由表
1 2 3 4 5 6 7 8 9 10 Node A 路由表: 10.1.1.0/24 dev cni0 (同网段,直接送达) 10.2.1.0/24 via 192.168.1.11 dev eth0 (跨节点,走物理网卡) 注意: 这是 Flannel 分配的路由! flanneld 进程会配置这个路由 当查询 10.2.1.10 时: 匹配路由: 10.2.1.0/24 via 192.168.1.11 dev eth0 需要发送到 Node B (192.168.1.11)
步骤 4: ARP 查询 VTEP MAC
1 2 3 4 5 6 7 发送到 192.168.1.11 需要知道 Next Hop MAC Node A 发送 ARP: Who has 192.168.1.11? Tell 192.168.1.10 物理交换机广播,Node B 响应: 192.168.1.11 is at cc:cc:cc:cc:cc:cc
步骤 5: 创建 VXLAN 隧道端点 (VTEP)
1 2 3 4 5 6 7 8 9 10 flannel.1 是 VTEP (VXLAN Tunnel Endpoint): 属性: 本地 IP: 192.168.1.10 VNI: 1 (VXLAN Network Identifier) 端口: 8472 (Linux 内核 VXLAN 端口) Node B 的 flannel.1: 本地 IP: 192.168.1.11 VNI: 1
步骤 6: 封装原始数据包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 flannel.1 将 Pod A 的数据包封装为 VXLAN: ┌────────────────────────────────────────────────────────────┐ │ 外层 Ethernet Header │ │ 源 MAC: aa:bb:cc:dd:ee:ff (Node A 物理网卡 MAC) │ │ 目标 MAC: 11:22:33:44:55:66 (物理网关/下一跳 MAC) │ ├────────────────────────────────────────────────────────────┤ │ 外层 IP Header │ │ 源 IP: 192.168.1.10 (Node A 物理 IP) │ │ 目标 IP: 192.168.1.11 (Node B 物理 IP) │ ├────────────────────────────────────────────────────────────┤ │ 外层 UDP Header │ │ 源端口: 随机 │ │ 目标端口: 8472 (VXLAN 端口) │ ├────────────────────────────────────────────────────────────┤ │ VXLAN Header │ │ Flags: 0100 (I flag = 1, 表示有 VNI) │ │ VNI: 1 │ │ Reserved: 24 bits │ ├────────────────────────────────────────────────────────────┤ │ 原始 Ethernet Header (被封装) │ │ 源 MAC: aa:aa:aa:aa:aa:aa │ │ 目标 MAC: bb:bb:bb:bb:bb:bb │ ├────────────────────────────────────────────────────────────┤ │ 原始 IP Header │ │ 源 IP: 10.1.1.10 │ │ 目标 IP: 10.2.1.10 │ ├────────────────────────────────────────────────────────────┤ │ 原始 TCP/应用数据 │ │ (Pod A 原本要发送的数据) │ └────────────────────────────────────────────────────────────┘
步骤 7: 通过物理网络发送
1 2 3 4 5 6 7 8 9 10 11 封装后的数据包: 外层: 源 MAC: Node A 物理 MAC 目标 MAC: Node B 物理 MAC 源 IP: 192.168.1.10 目标 IP: 192.168.1.11 通过物理网卡 eth0 发送 物理交换机根据目标 MAC 转发到 Node B
步骤 8: Node B 接收封装数据包
1 2 3 4 5 6 7 Node B eth0 接收数据包: 目标 IP: 192.168.1.11 (本机 IP) 目标 UDP 端口: 8472 (VXLAN) Linux 内核协议栈处理: - 识别 UDP 端口 8472 - 交给 VXLAN 模块处理
步骤 9: VXLAN 解封装
1 2 3 4 5 6 7 8 9 10 11 Node B 的 VXLAN 模块 (flannel.1): 1. 验证 VNI: - 收到的 VNI: 1 - 本地 VNI: 1 - 匹配成功 2. 提取内部数据包: - 原始 Ethernet Header - 原始 IP Header (10.1.1.10 -> 10.2.1.10) - 原始 TCP/应用数据
步骤 10: 发送到 Pod B
1 2 3 4 5 6 7 8 9 10 11 解封装后,数据包相当于在 Node B 的 cni0 网桥上: 原始数据包: 源 MAC: aa:aa:aa:aa:aa:aa 目标 MAC: bb:bb:bb:bb:bb:bb 源 IP: 10.1.1.10 目标 IP: 10.2.1.10 cni0 网桥: - 查 MAC 表: 目标 MAC bb:bb:bb:bb:bb:bb -> vethB - 通过 veth pair B 发送到 Pod B
步骤 11-15: Pod B 接收响应并返回 (反向流程)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Pod B 处理请求后,准备响应: 响应数据包: 源 IP: 10.2.1.10 (Pod B) 目标 IP: 10.1.1.10 (Pod A) 通过 veth pair B -> cni0 cni0 查路由: 10.1.1.0/24 via 192.168.1.10 dev eth0 封装过程同上,只是源和目标反转: Node B flannel.1 封装: 外层源 IP: 192.168.1.11 外层目标 IP: 192.168.1.10 内层: 10.2.1.10 -> 10.1.1.10 Node A 接收并解封装
1.2.3 VXLAN 原理详解 VXLAN (Virtual Extensible LAN) 是一种网络虚拟化技术,通过 UDP 封装在三层网络上创建二层虚拟网络。
VXLAN 核心概念 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ┌─────────────────────────────────────────────────────────────────────────────┐ │ VXLAN 核心概念 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ VNI (VXLAN Network Identifier): │ │ ──────────────────────────────── │ │ - 24 位标识符,支持 16M+ 虚拟网络 │ │ - 类似于 VLAN ID,但范围更大 │ │ - 每个 VNI 对应一个虚拟二层网络 │ │ │ │ VTEP (VXLAN Tunnel Endpoint): │ │ ───────────────────────────── │ │ - VXLAN 隧道端点,负责封装/解封装 │ │ - 通常是节点上的一个虚拟接口 (如 flannel.1) │ │ - 拥有独立的 MAC 和 IP 地址 │ │ │ │ UDP 端口 4789 (Linux 默认 8472): │ │ ────────────────────────────────────── │ │ - IANA 标准端口: 4789 │ │ - Linux 内核: 8472 (旧版本兼容) │ │ - 承载 VXLAN 数据包 │ │ │ │ 组播模式 vs Leader 模式: │ │ ────────────────────────── │ │ - 组播模式: 使用组播发现其他 VTEP,适合小规模 │ │ - Leader 模式: flanneld 充当 leader,分发ARP缓存,更适合大规模 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
VXLAN 数据包封装原理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 ┌─────────────────────────────────────────────────────────────────────────────┐ │ VXLAN 封装与解封装流程 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ 原始数据包 (Pod 间通信): │ │ ───────────────────────── │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Ethernet: SRC_MAC(PodA) -> DST_MAC(PodB) │ │ │ │ IP: SRC_IP(10.1.1.10) -> DST_IP(10.2.1.10) │ │ │ │ TCP: SRC_PORT(40000) -> DST_PORT(80) │ │ │ │ Payload: Application Data │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ 封装 │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ VXLAN Header: VNI=1, Flags=0100 │ │ │ │ UDP Header: SRC_PORT=随机, DST_PORT=8472 │ │ │ │ IP Header: SRC_IP=192.168.1.10 -> DST_IP=192.168.1.11 │ │ │ │ Ethernet: SRC_MAC=NodeA -> DST_MAC=下一跳MAC │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ 解封装 │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Ethernet: SRC_MAC(PodA) -> DST_MAC(PodB) │ │ │ │ IP: SRC_IP(10.1.1.10) -> DST_IP(10.2.1.10) │ │ │ │ TCP: SRC_PORT(40000) -> DST_PORT(80) │ │ │ │ Payload: Application Data │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
VXLAN 封装/解封装设备 (VTEP) 工作机制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 ┌─────────────────────────────────────────────────────────────────────────────┐ │ VTEP 工作机制 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ 1. 本地 MAC 学习 (MAC Learning): │ │ ───────────────────────────────── │ │ 当 VTEP 收到发往本地 Pod 的数据包时: │ │ - 学习 "远程 Pod MAC -> 远程 VTEP IP" 映射 │ │ - 记录到 FDB (Forwarding Database) │ │ │ │ FDB 表示例: │ │ ┌─────────────────────────────────────────────────┐ │ │ │ MAC Address │ VTEP IP │ Interface │ │ │ ├─────────────────────┼───────────────┼────────────│ │ │ │ aa:aa:aa:aa:aa:aa │ - │ vethA │ (本地) │ │ │ bb:bb:bb:bb:bb:bb │ 192.168.1.11│ tunnel0 │ (远程) │ │ └─────────────────────────────────────────────────┘ │ │ │ │ 2. ARP 缓存 (ARP Proxy): │ │ ────────────────────── │ │ flanneld 维护 ARP 缓存,避免广播: │ │ - 本地 ARP: 正常广播学习 │ │ - 远程 ARP: 通过 flanneld 分发的缓存 │ │ │ │ 3. 封装过程: │ │ ──────────── │ │ VTEP 收到去往远程 Pod 的数据包: │ │ 1. 查找 FDB,确定目标 VTEP IP │ │ 2. 添加 VXLAN Header (VNI) │ │ 3. 添加 UDP/IP/Ethernet 外层头 │ │ 4. 发送到物理网络 │ │ │ │ 4. 解封装过程: │ │ ───────────── │ │ VTEP 收到 VXLAN 数据包: │ │ 1. 验证 VNI 匹配 │ │ 2. 移除外层头 │ │ 3. 根据内层 MAC 转发到本地 Pod │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
VXLAN 与 VLAN 对比
特性
VXLAN
VLAN
网络标识
24 位 VNI (16M+ 网络)
12 位 VLAN ID (4K 网络)
二层范围
可跨三层网络
局限于单个交换机
封装方式
UDP 封装
无封装 (纯二层)
隧道
需要 VTEP
不需要
规模
支持 16M+ 虚拟网络
4K 虚拟网络
典型应用
云环境、多租户
数据中心网络隔离
1.2.4 Calico BGP 路由模式 (无封装) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 ┌─────────────────────────────────────────────────────────────────────────────┐ │ Calico BGP 路由模式 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ Node A 路由表 (由 Bird BGP 进程配置): │ │ ───────────────────────────────────── │ │ 10.1.1.0/24 dev cni0 (本地 Pod 网段) │ │ 10.2.1.0/24 via 192.168.1.11 dev eth0 (通过 BGP 学习到的) │ │ │ │ Node B 路由表 (由 Bird BGP 进程配置): │ │ ───────────────────────────────────── │ │ 10.2.1.0/24 dev cni0 (本地 Pod 网段) │ │ 10.1.1.0/24 via 192.168.1.10 dev eth0 (通过 BGP 学习到的) │ │ │ │ 数据包转发 (无封装,纯三层): │ │ ──────────────────────────────── │ │ Pod A -> 10.1.1.10 │ │ │ │ │ │ eth0 -> cni0 (因为目标 10.2.1.10 匹配 10.2.1.0/24) │ │ │ │ │ ▼ │ │ cni0 -> eth0 (查路由表 10.2.1.0/24 via 192.168.1.11) │ │ │ │ │ │ 直接从物理网卡发送,无任何封装 │ │ │ (目标 MAC 是 Node B 的 MAC, 目标 IP 是 Pod B 的 IP) │ │ ▼ │ │ 物理网络 (普通路由转发) │ │ │ │ │ ▼ │ │ Node B eth0 │ │ │ │ │ │ 目标 IP 是 10.2.1.10,匹配本地路由 10.2.1.0/24 dev cni0 │ │ ▼ │ │ cni0 -> veth pair -> Pod B │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
1.2.4 Calico 跨节点通信详细流程 Calico 采用纯三层路由方案,通过 BGP 协议在节点间分发路由信息。
Calico 架构组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 ┌─────────────────────────────────────────────────────────────────────────────┐ │ Calico 架构 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ Node A Node B │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ Felix │◄──── BGP 协议 ────────▶│ Felix │ │ │ │ (路由/ACL) │ │ (路由/ACL) │ │ │ └──────┬──────┘ └──────┬──────┘ │ │ │ │ │ │ ┌──────┴──────┐ ┌──────┴──────┐ │ │ │ Bird │◄──── BGP 会话 ─────────▶│ Bird │ │ │ │ (BGP 客户端)│ │ (BGP 客户端)│ │ │ └──────┬──────┘ └──────┬──────┘ │ │ │ │ │ │ ┌──────┴──────┐ ┌──────┴──────┐ │ │ │ calico-node │ │ calico-node │ │ │ │ DaemonSet │ │ DaemonSet │ │ │ └─────────────┘ └─────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ Route Reflector (可选,大规模集群) │ │ │ │ │ │ │ │ Node A ◄─────── BGP RR ─────────▶ Node B │ │ │ │ Node C ◄─────── BGP RR ─────────▶ Node D │ │ │ │ │ │ │ │ 作用:替代全互联 BGP,减少 BGP 连接数 (N*(N-1)/2 → N) │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
Calico 详细通信流程(12步) 步骤 1: Pod A 发送数据包
1 2 3 4 5 6 7 Pod A (10.1.1.10) 发送请求到 Pod B (10.2.1.10): 数据包: 源 IP: 10.1.1.10 目标 IP: 10.2.1.10 源 MAC: aa:aa:aa:aa:aa:aa 目标 MAC: ? (需要 ARP)
步骤 2: Pod A 本地路由决策
1 2 3 4 5 Pod A 路由表: 10.0.0.0/8 via eth0 (K8s 默认) 目标 10.2.1.10 匹配 10.0.0.0/8 发送到 cni0 网桥
步骤 3: cni0 网桥转发
1 2 3 4 5 6 cni0 网桥: - 查 MAC 表找目标 MAC - 转发到 eth0 注意: cni0 在 Calico 中主要用于本地 Pod 通信 跨节点流量直接通过 eth0 发出
步骤 4: Node A 路由查找
1 2 3 4 5 Node A 路由表 (由 Felix/Bird 配置): 10.1.1.0/24 dev cni0 (本地 Pod 网段) 10.2.1.0/24 via 192.168.1.11 dev eth0 (BGP 学习到的) 目标 10.2.1.10 匹配 10.2.1.0/24 via 192.168.1.11
步骤 5: ARP 解析下一跳 MAC
1 2 3 4 5 6 7 需要发送到 192.168.1.11 (Node B) Node A 发送 ARP: Who has 192.168.1.11? Tell 192.168.1.10 物理交换机转发 ARP,Node B 响应: 192.168.1.11 is at bb:bb:bb:bb:bb:bb
步骤 6: 数据包发送
1 2 3 4 5 6 7 8 9 10 数据包: 源 MAC: Node A MAC 目标 MAC: Node B MAC (bb:bb:bb:bb:bb:bb) 源 IP: 10.1.1.10 目标 IP: 10.2.1.10 注意: 这里是关键区别! - 目标 IP 保持为 Pod IP (10.2.1.10) - 目标 MAC 是 Node B 的 MAC - 没有额外的封装头
步骤 7: 物理网络转发
1 2 3 4 5 交换机根据目标 MAC (bb:bb:bb:bb:bb:bb) 转发到 Node B 整个转发过程: 交换机只看到 Node A MAC -> Node B MAC 完全基于 L2 转发
步骤 8: Node B 接收数据包
1 2 3 4 5 6 7 Node B eth0 接收: 目标 MAC: bb:bb:bb:bb:bb:bb (Node B MAC) 目标 IP: 10.2.1.10 内核协议栈处理: - 识别目标 IP 是本机网段 - 发送到 cni0 网桥
步骤 9: cni0 网桥转发
1 2 3 4 5 6 Node B cni0 收到数据包: 源 MAC: aa:aa:aa:aa:aa:aa 目标 IP: 10.2.1.10 cni0 查 MAC 表: 10.2.1.10 -> veth pair B
步骤 10: 发送到 Pod B
1 2 3 4 5 通过 veth pair B 发送到 Pod B 的 netns Pod B eth0 收到: 目标 IP: 10.2.1.10 (匹配本机) 交给应用进程
步骤 11: Pod B 响应
1 2 3 4 5 Pod B 发送响应到 10.1.1.10: 源 IP: 10.2.1.10 目标 IP: 10.1.1.10 流程同上,方向反转
步骤 12: Node B 返回路由
1 2 3 4 Node B 路由表: 10.1.1.0/24 via 192.168.1.10 dev eth0 (BGP 学习) 数据包返回 Node A,整个通信完成
IP-in-IP 模式 (可选封装) Calico 支持两种模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ┌─────────────────────────────────────────────────────────────────────────────┐ │ Calico 模式对比 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ 1. Route (纯三层,默认) │ │ ────────────────────────── │ │ 数据包直接发送,无封装: │ │ 源 IP: Pod A IP (10.1.1.10) │ │ 目标 IP: Pod B IP (10.2.1.10) │ │ │ │ 要求: 集群网络可路由 (节点间三层可达) │ │ │ │ 2. IP-in-IP (封装模式) │ │ ─────────────────────── │ │ 外层再封装一层 IP: │ │ 外层: 192.168.1.10 -> 192.168.1.11 │ │ 内层: 10.1.1.10 -> 10.2.1.10 │ │ │ │ 适用场景: 网络不可路由 (如公有云 VPC) │ │ │ │ 配置: │ │ calicoctl get bgpconfiguration -o yaml │ │ # 设置 ipipMode: Always │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
Calico 路由分发机制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 BGP 路由分发流程: 1. Felix 在每个节点上配置路由: - 本地 Pod 网段路由 - 指向本地 Pod 的路径 2. Bird BGP 进程: - 读取 Felix 配置的路由 - 与集群内其他 Bird 建立 BGP 会话 - 通告本地路由给对方 - 接收对方通告的路由并写入内核 3. 全互联 vs Route Reflector: ──────────────────────────── 全互联 (小规模集群): - 每 Node 与所有其他 Node 建立 BGP 会话 - N 个节点 = N*(N-1)/2 个 BGP 会话 - 适合 10-50 个节点的集群 Route Reflector (大规模集群): - 部署 Route Reflector 节点 - 所有 Node 只与 RR 建立 BGP 会话 - N 个节点 = N 个 BGP 会话 - 适合 50+ 节点的集群
Calico 不同模式原理详解 Calico 支持三种数据平面模式,适用于不同的网络环境:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 ┌─────────────────────────────────────────────────────────────────────────────┐ │ Calico 数据平面模式 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ 1. IPIP Mode (封装模式) │ │ ────────────────────────── │ │ │ │ 原理: │ │ - 在原始 IP 包外再封装一层 IP 头 │ │ - 外层源/目标 IP 是节点物理 IP │ │ - 内层源/目标 IP 是 Pod IP │ │ │ │ 数据包格式: │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 外层 IP: SRC=192.168.1.10 DST=192.168.1.11 │ │ │ │ 内层 IP: SRC=10.1.1.10 DST=10.2.1.10 │ │ │ │ TCP/应用数据 │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ 适用场景: │ │ - 节点间网络不可路由 (如跨 VPC、公有云) │ │ - 网络 ACL 限制严格的環境 │ │ │ │ 配置: │ │ ipipMode: Always | CrossSubnet | Never │ │ │ │ 2. VXLAN Mode (封装模式) │ │ ────────────────────────── │ │ │ │ 原理: │ │ - 使用 UDP 封装,与 Flannel 类似 │ │ - 支持多播 ARP 发现 │ │ - 更好的扩展性 │ │ │ │ 数据包格式: │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ UDP: SRC=随机 DST=4789 │ │ │ │ VXLAN: VNI=1 │ │ │ │ 内层 IP: SRC=10.1.1.10 DST=10.2.1.10 │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ 适用场景: │ │ - 需要 VXLAN 特性的大规模部署 │ │ - 多租户环境需要 VNI 隔离 │ │ │ │ 3. Direct (纯三层,默认) │ │ ────────────────────────── │ │ │ │ 原理: │ │ - 纯三层路由,无任何封装 │ │ - Pod IP 直接暴露在物理网络 │ │ - 需要节点间三层可达 │ │ │ │ 数据包格式: │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ IP: SRC=10.1.1.10 DST=10.2.1.10 │ │ │ │ TCP/应用数据 │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ 适用场景: │ │ - 节点间三层可路由 (如本地数据中心) │ │ - 对性能要求高 │ │ - 网络基础设施支持 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
Calico Felix 组件工作原理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ┌─────────────────────────────────────────────────────────────────────────────┐ │ Felix 工作流程 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ Felix 是运行在每个节点上的 agent,负责: │ │ │ │ 1. 路由编程 (Route Programming): │ │ ───────────────────────────────── │ │ - 在内核路由表中安装 Pod 网段路由 │ │ - 指向本地 Pod 的本地路由 │ │ - 指向远程 Pod 的下一跳路由 │ │ │ │ 2. ACL 编程 (ACL Programming): │ │ ──────────────────────────── │ │ - 在内核中安装 iptables 规则 │ │ - 实现 NetworkPolicy │ │ - 过滤入口/出口流量 │ │ │ │ 3. 状态汇报 (State Reporting): │ │ ──────────────────────────── │ │ - 监控接口状态 │ │ - 汇报给 Typha (大规模时) │ │ │ │ 配置文件示例 (/etc/calico/felix.cfg): │ │ ─────────────────────────────────── │ │ [Global] │ │ BGPASN=64512 │ │ IPinIPEnabled=true │ │ IPinIPMode=CrossSubnet │ │ │ │ [Node] │ │ Hostname=node-a │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
Calico Typha 组件 (大规模集群) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 ┌─────────────────────────────────────────────────────────────────────────────┐ │ Typha 扩展机制 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ 问题: │ │ - Felix 需要从 API Server 同步数据 │ │ - 大规模集群时,Felix 数量多,API Server 压力大 │ │ │ │ 解决方案: Typha (专用数据分发服务) │ │ ──────────────────────────────── │ │ │ │ ┌──────────────┐ │ │ │ Typha DaemonSet │ │ │ │ (2-3 个副本) │ │ │ └───────┬───────┘ │ │ │ │ │ ┌───────────────┼───────────────┐ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ Felix │ │ Felix │ │ Felix │ │ │ │ Node A │ │ Node B │ │ Node C │ │ │ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ 优势: │ │ - Typha 缓存数据,减少 API Server 负载 │ │ - Felix 从 Typha 订阅增量更新 │ │ - 支持 1000+ 节点集群 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
1.2.5 CNI 插件对比
特性
Flannel (VXLAN)
Calico (BGP)
Cilium (eBPF)
Weave
跨节点封装
VXLAN (UDP)
无封装
无封装/eBPF
VXLAN/sleeve
封装位置
内核 VTEP
物理网络
eBPF
内核
性能
中等 (额外封装)
高 (无封装)
最高 (零拷贝)
中等
路由学习
flanneld 分发
BGP 协议
eBPF 探针
八卦协议
网络策略
支持
支持
支持
支持
IPAM
flannel 分配
Calico IPAM
Cilium IPAM
Weave IPAM
源码位置 1 2 3 4 5 6 7 8 9 10 11 Flannel: pkg/kubelet/network/cni/plugins/flake/flannel/ - flannel CNI 插件 vendor/github.com/flannel-io/flannel/ - flannel 主代码 pkg/util/encap/ - VXLAN 封装工具 Calico: pkg/kubelet/network/plugins/plugins.go - CNI 插件接口 vendor/github.com/projectcalico/ - Calico 库 Cilium: vendor/github.com/cilium/cilium/ - Cilium eBPF
2. Pod 到 Service 通信 2.1 ClusterIP 类型 ClusterIP 是 Kubernetes Service 的默认类型,通过 kube-proxy 实现负载均衡。
2.1.1 架构图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 ┌─────────────────────────────────────────────────────────────────────────────┐ │ ClusterIP Service 通信 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ │ │ │ Pod A │ 连接 ClusterIP: 10.96.0.100 │ │ │ (client) │ │ │ │ │ 1. DNS 解析: my-svc -> 10.96.0.100 │ │ │ │ 2. 发送 SYN 到 10.96.0.100:80 │ │ │ ┌───────┐ │ │ │ │ │ app │ │ │ │ │ └───┬───┘ │ │ │ │ │ │ │ │ └──────┼──────┘ │ │ │ eth0 │ │ │ │ │ ┌──────┴─────────────────────────────────────────────────────────────┐ │ │ │ Node A 网络命名空间 │ │ │ │ │ │ │ │ ┌─────────┐ ┌──────────────────────────────────────────┐ │ │ │ │ │ eth0 │─────▶│ iptables/ipvs │ │ │ │ │ └─────────┘ │ │ │ │ │ │ │ PREROUTING │ │ │ │ │ │ └─ KUBE-SERVICES │ │ │ │ │ │ └─ KUBE-SVC-XXXXXXXX:80 │ │ │ │ │ │ ├─ KUBE-SEP-XXXXXXXX:80 │ │ │ │ │ │ ├─ KUBE-SEP-XXXXXXXX:80 │ │ │ │ │ │ └─ KUBE-SEP-XXXXXXXX:80 │ │ │ │ │ │ │ │ │ │ │ │ 每个 KUBE-SEP 进行 DNAT: │ │ │ │ │ │ 10.96.0.100:80 -> PodIP:PodPort │ │ │ │ │ │ │ │ │ │ │ └────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ DNAT 后转发 │ │ │ └──────────────────────────────┼──────────────────────────────────────────┘ │ │ │ │ │ ┌───────┴───────┐ │ │ │ 路由表 │ │ │ │ │ │ │ │ Pod 网段路由 │ │ │ └───────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
2.1.2 iptables 模式详细流程(12步) 步骤 1: Pod A 发起请求
1 2 3 4 5 6 7 8 9 10 11 12 Pod A 应用代码: import requests resp = requests.get("http://my-svc:80/api") DNS 解析 (my-svc): my-svc.default.svc.cluster.local -> 10.96.0.100 Pod A 发送 TCP SYN: 源 IP: 10.1.1.10 目标 IP: 10.96.0.100 源端口: 40000 目标端口: 80
步骤 2: Pod A 本地路由
1 2 3 4 5 Pod A 路由表: 10.96.0.0/12 via eth0 (Service 网段) 目标 10.96.0.100 匹配 10.96.0.0/12 发送到默认网关 (cni0)
步骤 3: Node A 接收数据包
1 2 3 4 5 6 7 8 cni0 网桥收到数据包: 源 MAC: Pod A veth MAC 目标 MAC: cni0 MAC (因为目标是 Service IP) 查路由: 10.96.0.100 匹配 KUBE-SERVICES 链 数据包进入 iptables nat 表 PREROUTING
步骤 4: iptables PREROUTING 处理
1 2 3 4 iptables 规则: -A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES PREROUTING 链跳转到 KUBE-SERVICES 链
步骤 5: KUBE-SERVICES 链匹配
1 2 3 4 5 6 7 8 9 KUBE-SERVICES 规则: -A KUBE-SERVICES -d 10.96.0.100/32 -p tcp --dport 80 -m comment --comment \ "default/my-svc:80 cluster IP" -j KUBE-SVC-XXXXXXXX 匹配条件: -d 10.96.0.100/32 ✓ (目标 ClusterIP) -p tcp --dport 80 ✓ 跳转到 KUBE-SVC-XXXXXXXX 链
步骤 6: KUBE-SVC-XXXXXXXX 负载均衡分发
1 2 3 4 5 6 7 8 9 10 KUBE-SVC-XXXXXXXX 链 (假设 3 个 Endpoints): -A KUBE-SVC-XXXXXXXX -m statistic --mode random --probability 0.33333 \ -j KUBE-SEP-AAAAAAAA -A KUBE-SVC-XXXXXXXX -m statistic --mode random --probability 0.50000 \ -j KUBE-SEP-BBBBBBBB -A KUBE-SVC-XXXXXXXX -j KUBE-SEP-CCCCCCCC 假设随机选中 KUBE-SEP-AAAAAAAA 跳转到 KUBE-SEP-AAAAAAAA 链
步骤 7: KUBE-SEP-AAAAAAAA DNAT
1 2 3 4 5 6 7 8 9 10 11 12 KUBE-SEP-AAAAAAAA 规则: -A KUBE-SEP-AAAAAAAA -m comment --comment "default/my-svc:80" \ -j DNAT --to-destination 10.1.1.20:80 # 注意: 实际 KUBE-SEP 规则通常不带 -s 源地址匹配 # DNAT 操作: 目标 IP: 10.96.0.100:80 → 10.1.1.20:80 (ClusterIP + Port 转换为 PodIP + Port) iptables 记录 ConnTrack: 原始: 10.1.1.10:40000 -> 10.96.0.100:80 转换: 10.1.1.10:40000 -> 10.1.1.20:80
步骤 8: DNAT 后路由决策
1 2 3 4 5 6 iptables DNAT 后,数据包目标变为 10.1.1.20:80 查 Node A 路由表: 10.1.1.0/24 dev cni0 (本地网络) 数据包仍然是发往 10.1.1.20 (同节点)
步骤 9: cni0 网桥转发
1 2 3 4 cni0 查 MAC 表: 10.1.1.20 -> veth pair 到 Pod X 数据包通过 veth pair 发送到 Pod X
步骤 10: Pod X 接收请求
1 2 3 4 5 6 7 8 9 Pod X eth0 收到数据包: 目标 IP: 10.1.1.20 (Pod X 的 IP) 目标端口: 80 内核 TCP/IP 栈处理: - 匹配本地端口 80 - 交给监听 80 端口的进程 Pod X 应用收到请求
步骤 11: Pod X 发送响应
1 2 3 4 5 6 7 8 9 Pod X 处理后发送响应: TCP SYN-ACK: 源 IP: 10.1.1.20 (Pod X) 目标 IP: 10.1.1.10 (Pod A) 源端口: 80 目标端口: 40000 注意: 源 IP 是 Pod X,目标 IP 是 Pod A
步骤 12: 响应通过 ConnTrack 返回
1 2 3 4 5 6 7 8 9 10 11 Node A iptables 的 ConnTrack 处理: 1. 响应数据包进入 PREROUTING 2. ConnTrack 查表: 原始: 10.1.1.10:40000 -> 10.96.0.100:80 状态: RELATED,ESTABLISHED 3. 自动反向 DNAT: 源 IP: 10.1.1.20:80 -> 10.96.0.100:80 4. Pod A 收到响应: 源 IP: 10.96.0.100 (ClusterIP!) 目标 IP: 10.1.1.10
2.1.3 ipvs 模式详细流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 ┌─────────────────────────────────────────────────────────────────────────────┐ │ ipvs 负载均衡模式 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ipvs 工作在 Linux 内核层 (netfilter之上),性能更高 │ │ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ ipvsadm 规则 │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ │ │ │ │ IP Virtual Server version 1.2.1 (size=4096) │ │ │ │ │ │ │ │ TCP 10.96.0.100:80 rr <- Service │ │ │ │ -> 10.1.1.20:80 Route (Endpoint 1) │ │ │ │ -> 10.1.1.21:80 Route (Endpoint 2) │ │ │ │ -> 10.1.1.22:80 Route (Endpoint 3) │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ │ ipvs 负载均衡算法: │ │ ─────────────────── │ │ rr (轮询) - 依次分发到每个 endpoint │ │ wrr (加权轮询) - 根据权重分发 │ │ lc (最小连接) - 选择连接数最少的 endpoint │ │ wlc (加权最小连接) - 结合权重和连接数 │ │ sh (源哈希) - 相同源 IP 总是分发到同一 endpoint │ │ dh (目标哈希) - 相同目标 IP 总是分发到同一 endpoint │ │ │ │ 通信流程: │ │ ──────── │ │ 1. Pod A 发送请求到 10.96.0.100:80 │ │ 2. 内核 ipvs 模块接收 (在 PREROUTING 之前) │ │ 3. ipvs 根据算法选择 endpoint (如 10.1.1.20:80) │ │ 4. ipvs 转发模式: │ │ - DR (Direct Routing): 直接修改 MAC,绕过 iptables │ │ - NAT (NAT 模式): 类似 iptables DNAT │ │ - tunnel (IPIP): 隧道模式 │ │ 5. 响应直接返回,不经过 ipvs (ConnTrack 处理) │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
2.1.4 iptables vs ipvs 对比
特性
iptables
ipvs
工作层级
netfilter (内核)
IPVS (内核)
匹配方式
遍历规则链
哈希查找 O(1)
扩展性
规则多时性能下降
恒定高性能
算法支持
随机/概率
8种算法
健康检查
无
支持
连接复用
无
支持
典型规则数
1000+
几十
2.1.5 kube-proxy iptables 规则详解 kube-proxy 在所有运行节点上都会创建 iptables 规则,规则结构相同。
kube-proxy 部署机制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ┌─────────────────────────────────────────────────────────────────────────────┐ │ kube-proxy 部署架构 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ kube-proxy 以 DaemonSet 形式部署: │ │ ───────────────────────────────────── │ │ │ │ kubectl get daemonset -n kube-system kube-proxy │ │ NAMESPACE NAME DESIRED CURRENT READY │ │ kube-system kube-proxy 5 5 5 │ │ │ │ 每个节点运行一个 kube-proxy Pod: │ │ ────────────────────────────────────────── │ │ │ │ ┌─────────────────┐ │ │ │ kube-proxy Pod │ │ │ │ (每个节点一个) │ │ │ └────────┬────────┘ │ │ │ │ │ ┌───────────────────┼───────────────────┐ │ │ ▼ ▼ ▼ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ Node A │ │ Node B │ │ Node C │ │ │ │ iptables│ │ iptables│ │ iptables│ │ │ │ rules │ │ rules │ │ rules │ │ │ └─────────┘ └─────────┘ └─────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
所有节点规则结构相同 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ┌─────────────────────────────────────────────────────────────────────────────┐ │ 所有节点的 iptables 结构相同 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ Node A Node B Node C │ │ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │ │ │ PREROUTING │ │ PREROUTING │ │ PREROUTING │ │ │ │ └─ KUBE-SVC │ │ └─ KUBE-SVC │ │ └─ KUBE-SVC │ │ │ └────────────────┘ └────────────────┘ └────────────────┘ │ │ │ │ 规则内容: │ │ ───────── │ │ KUBE-SERVICES: ALL Services (集群中所有 Service) │ │ KUBE-SVC-XXXX: ALL Endpoints (该 Service 的所有 Pod) │ │ KUBE-SEP-YYYY: 指向具体 Pod IP:Port │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
iptables 规则链路详解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 ┌─────────────────────────────────────────────────────────────────────────────┐ │ iptables 规则链路 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ PREROUTING 链 (外部流量进入): │ │ ───────────────────────────────── │ │ -A PREROUTING -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS │ │ ↑ 进入本地 IP 的流量 (NodePort) │ │ │ │ -A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES │ ↑ 其他服务流量 │ │ │ │ OUTPUT 链 (本地进程访问 Service): │ │ ───────────────────────────────── │ │ -A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES │ ↑ Pod 内进程访问 Service IP │ │ │ │ KUBE-SERVICES 链 (匹配目标 Service): │ │ ───────────────────────────────────── │ │ -A KUBE-SERVICES -d 10.96.0.100/32 -p tcp --dport 80 \ │ │ -m comment --comment "default/my-svc:80 cluster IP" -j KUBE-SVC-XXXXX │ ↑ 匹配 ClusterIP │ │ │ │ KUBE-SVC-XXXXX 链 (负载均衡到 Endpoints): │ │ ───────────────────────────────────────── │ │ -A KUBE-SVC-XXXXX -m statistic --mode random --probability 0.33333 \ │ │ -j KUBE-SEP-AAAA │ │ -A KUBE-SVC-XXXXX -m statistic --mode random --probability 0.50000 \ │ │ -j KUBE-SEP-BBBBBBBB │ │ -A KUBE-SVC-XXXXX -j KUBE-SEP-CCCCCCCC │ │ ↑ 概率负载均衡到各个 Endpoint │ │ │ │ KUBE-SEP-XXXX 链 (DNAT 到具体 Pod): │ │ ───────────────────────────────── │ │ -A KUBE-SEP-AAAA -m comment --comment "default/my-svc:80" \ │ │ -j DNAT --to-destination 10.1.1.20:80 │ │ ↑ DNAT 到具体 Pod IP:Port │ │ │ │ POSTROUTING 链 (Egress SNAT): │ │ ───────────────────────── │ │ -A POSTROUTING -m comment --comment "kubernetes service connections" \ │ │ -j KUBE-MARK-MASQ │ │ ↑ 标记需要 SNAT 的流量 │ │ │ │ -A POSTROUTING -s 10.1.1.0/24 ! -d 10.0.0.0/8 -j MASQUERADE │ │ ↑ Pod 访问外部 IP 时 SNAT │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
Endpoints 跨节点规则示例 1 2 3 4 5 假设: Service: my-svc (10.96.0.100) -> 3 个 Endpoints - Pod X: 10.1.1.20 (在 Node A) - Pod Y: 10.1.1.21 (在 Node B) - Pod Z: 10.1.1.22 (在 Node B)
所有节点 (Node A, B, C) 的 KUBE-SVC-XXXX 规则都一样:
1 2 3 4 KUBE-SVC-XXXX: -m statistic --mode random --probability 0.33333 -j KUBE-SEP-AAAA (10.1.1.20) -m statistic --mode random --probability 0.50000 -j KUBE-SEP-BBBBBBBB (10.1.1.21) -j KUBE-SEP-CCCCCCCC (10.1.1.22)
每个 KUBE-SEP 都指向实际的 Pod IP:
1 2 3 4 5 6 7 8 KUBE-SEP-AAAA: -j DNAT --to-destination 10.1.1.20:80 <- Pod X 的实际 IP KUBE-SEP-BBBBBBBB: -j DNAT --to-destination 10.1.1.21:80 <- Pod Y 的实际 IP KUBE-SEP-CCCCCCCC: -j DNAT --to-destination 10.1.1.22:80 <- Pod Z 的实际 IP
跨节点流量转发示例 1 2 3 4 5 6 7 8 9 Client 在 Node C 访问 my-svc:80 ───────────────────────────────────── 1. Node C 收到请求,进入 iptables PREROUTING 2. 匹配 KUBE-SERVICES -> KUBE-SVC-XXXX 3. KUBE-SVC-XXXX 随机选中 KUBE-SEP-BBBBBBBB 4. DNAT 到 10.1.1.21:80 (Pod Y,在 Node B) 5. 查路由: 10.1.1.21 不在本地,通过物理网络转发到 Node B 6. Node B 的 cni0 收到,转发给 Pod Y
ConnTrack 作用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ┌─────────────────────────────────────────────────────────────────────────────┐ │ ConnTrack 连接跟踪 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ConnTrack 记录活跃连接,实现反向转换: │ │ ───────────────────────────────────────── │ │ │ │ 请求流程: │ │ 原始: 10.1.1.10:40000 -> 10.96.0.100:80 │ │ DNAT后: 10.1.1.10:40000 -> 10.1.1.20:80 │ │ ConnTrack 记录这条转换关系 │ │ │ │ 响应流程 (ConnTrack 自动反向): │ │ 收到: 10.1.1.20:80 -> 10.1.1.10:40000 │ │ 查表: 原始连接 10.1.1.10:40000 -> 10.96.0.100:80 │ │ 反向: 自动将源 IP 从 10.1.1.20 转换回 10.96.0.100 │ │ 结果: 10.96.0.100:80 -> 10.1.1.10:40000 │ │ Client 看到的是 Service IP │ │ │ │ 查看 ConnTrack 表: │ │ conntrack -L | grep 10.96.0.100 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
iptables 规则查看命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 iptables -t nat -L -n -v iptables -t nat -L KUBE-SERVICES -n -v iptables -t nat -L KUBE-SVC-XXXXXXXX -n -v iptables -t nat -L KUBE-SEP-XXXXXXXX -n -v iptables -t nat -L KUBE-SERVICES -n | wc -l iptables -t nat -L POSTROUTING -n -v ipvsadm -L -n ipvsadm -L -n --stats
iptables vs ipvs 规则对比 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ┌─────────────────────────────────────────────────────────────────────────────┐ │ iptables vs ipvs 规则对比 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ iptables 模式: │ │ ───────────────── │ │ - 规则数量: 1000+ (每个 Service + Endpoint) │ │ - 匹配方式: 遍历规则链 O(n) │ │ - 更新方式: 全量刷新 │ │ - 适用场景: 小规模集群 (< 100 Services) │ │ │ │ ipvs 模式: │ │ ──────────── │ │ - 规则数量: 几十 (每个 Service) │ │ - 匹配方式: 哈希查找 O(1) │ │ - 更新方式:增量更新 │ │ - 适用场景: 大规模集群 │ │ │ │ 切换模式: │ │ ───────── │ │ kube-proxy --proxy-mode=ipvs │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
2.1.6 iptables 五链四表完整流程图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 ┌─────────────────────────────────────────────────────────────────────────────┐ │ iptables 数据包处理流程 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ Linux 内核协议栈 │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ PREROUTING 链 (路由决策前) │ │ │ │ │ │ │ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ │ │ │ raw │ │ mangle │ │ nat (DNAT)│ │ filter │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ - NOTRACK │ │ - MARK │ │ - KUBE- │ │ (不常用) │ │ │ │ │ │ │ │ - TOS │ │ SERVICES│ │ │ │ │ │ │ │ │ │ - TTL │ │ - KUBE- │ │ │ │ │ │ │ │ │ │ - WL │ │ NODEPORTS│ │ │ │ │ │ │ └───────────┘ └───────────┘ └───────────┘ └───────────┘ │ │ │ │ │ │ │ │ ┌───────────────────────────────────────────────────────────────┐ │ │ │ │ │ 路由决策 (Routing Decision) │ │ │ │ │ └───────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ ┌───────────────┴───────────────┐ │ │ │ │ ▼ ▼ │ │ │ │ ┌────────────────┐ ┌────────────────┐ │ │ │ │ │ 本地进程 │ │ 转发 │ │ │ │ │ │ (Local) │ │ (Forward) │ │ │ │ │ └───────┬────────┘ └───────┬────────┘ │ │ │ │ │ │ │ │ │ │ ▼ ▼ │ │ │ │ ┌────────────────────┐ ┌────────────────────┐ │ │ │ │ │ INPUT 链 │ │ FORWARD 链 │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┌─────┐ ┌─────┐ │ │ ┌─────┐ ┌─────┐ │ │ │ │ │ │ │mangle│ │filter│ │ │ │mangle│ │filter│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │-MARK │ │ACCEPT│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │DROP │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │REJECT│ │ │ │ │ │ │ │ │ │ │ │ │ └─────┘ └─────┘ │ │ └─────┘ └─────┘ │ │ │ │ │ └────────────────────┘ └────────────────────┘ │ │ │ │ │ │ │ │ │ │ ▼ ▼ │ │ │ │ ┌─────────────────────────────────────────────┐ │ │ │ │ │ 本地进程 (Application) │ │ │ │ │ └─────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ 响应数据包 │ │ │ │ │ 生成 │ │ │ │ ▼ │ │ │ └──────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ OUTPUT 链 (本地生成) │ │ │ │ │ │ │ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ │ │ │ raw │ │ mangle │ │ nat (SNAT)│ │ filter │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ - MARK │ │ - KUBE- │ │ - OUTPUT │ │ │ │ │ │ │ │ │ │ SERVICES│ │ policy │ │ │ │ │ │ │ │ │ │ - MASQUERADE│ │ │ │ │ │ │ └───────────┘ └───────────┘ └───────────┘ └───────────┘ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ POSTROUTING 链 (出站前) │ │ │ │ │ │ │ │ ┌───────────┐ ┌───────────┐ │ │ │ │ │ mangle │ │ nat (SNAT)│ │ │ │ │ │ │ │ │ │ │ │ │ │ - CLASSIFY│ │ - KUBE- │ │ │ │ │ │ │ │ MARK-MASQ│ │ │ │ │ │ │ │ - MASQUERADE│ │ │ │ │ │ │ │ │ │ │ │ │ └───────────┘ └───────────┘ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────┐ │ │ │ 物理网卡/虚拟网卡 │ │ │ │ (eth0, cni0, veth) │ │ │ └─────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
四表详解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 ┌─────────────────────────────────────────────────────────────────────────────┐ │ iptables 四表 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ 1. raw 表 (原始表) │ │ │ │ ───────────────────── │ │ │ │ │ │ │ │ 优先级: HIGHEST (最先处理) │ │ │ │ │ │ │ │ 用途: │ │ │ │ - 决定是否对数据包进行连接跟踪 (NOTRACK) │ │ │ │ - 绕过 ConnTrack │ │ │ │ │ │ │ │ 常用规则: │ │ │ │ -A PREROUTING -j NOTRACK │ │ │ │ -A OUTPUT -j NOTRACK │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ 2. mangle 表 (标记表) │ │ │ │ ───────────────────── │ │ │ │ │ │ │ │ 优先级: HIGH │ │ │ │ │ │ │ │ 用途: │ │ │ │ - 修改数据包字段 (TOS, TTL, MARK) │ │ │ │ - 流量分类 │ │ │ │ - QoS │ │ │ │ │ │ │ │ 常用规则: │ │ │ │ -A PREROUTING -j MARK --set-mark 0x1 │ │ │ │ -A POSTROUTING -j CLASSIFY --set-class 1:0 │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ 3. nat 表 (网络地址转换表) │ │ │ │ ───────────────────── │ │ │ │ │ │ │ │ 优先级: NORMAL │ │ │ │ │ │ │ │ 用途: │ │ │ │ - DNAT (目标地址转换) - PREROUTING │ │ │ │ - SNAT (源地址转换) - POSTROUTING │ │ │ │ - MASQUERADE (动态源地址转换) │ │ │ │ │ │ │ │ 常用规则: │ │ │ │ -A PREROUTING -j DNAT --to-destination 10.1.1.20:80 │ │ │ │ -A POSTROUTING -j MASQUERADE │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ 4. filter 表 (过滤表) │ │ │ │ ───────────────────── │ │ │ │ │ │ │ │ 优先级: LOW (最后处理) │ │ │ │ │ │ │ │ 用途: │ │ │ │ - 包过滤 (ACCEPT, DROP, REJECT) │ │ │ │ - 防火墙规则 │ │ │ │ │ │ │ │ 常用规则: │ │ │ │ -A INPUT -j ACCEPT │ │ │ │ -A FORWARD -j DROP │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
五链详解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 ┌─────────────────────────────────────────────────────────────────────────────┐ │ iptables 五链 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ 五链概述 │ │ │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ │ │ │ │ ┌──────────┬────────────────────────────────────────────────┐ │ │ │ │ │ 链 │ 位置和用途 │ │ │ │ │ ├──────────┼────────────────────────────────────────────────┤ │ │ │ │ │ PREROUTING│ 路由前 - 网络接口接收数据包后 │ │ │ │ │ │ │ 用于 DNAT (外部访问本地服务) │ │ │ │ │ ├──────────┼────────────────────────────────────────────────┤ │ │ │ │ │ INPUT │ 路由后 - 发往本地进程的数据包 │ │ │ │ │ │ │ 用于允许/禁止进入本地应用 │ │ │ │ │ ├──────────┼────────────────────────────────────────────────┤ │ │ │ │ │ FORWARD │ 路由后 - 需要转发的数据包 │ │ │ │ │ │ │ 用于网络转发 (K8s Pod 通信) │ │ │ │ │ ├──────────┼────────────────────────────────────────────────┤ │ │ │ │ │ OUTPUT │ 本地生成 - 进程发出的数据包 │ │ │ │ │ │ │ 用于本地进程访问外部 (Egress SNAT) │ │ │ │ │ ├──────────┼────────────────────────────────────────────────┤ │ │ │ │ │ POSTROUTING│ 路由后 - 数据包发出前 │ │ │ │ │ │ │ 用于 SNAT, MASQUERADE │ │ │ │ │ └──────────┴────────────────────────────────────────────────┘ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ K8s 中的链应用 │ │ │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ │ │ │ │ PREROUTING: │ │ │ │ ─────────── │ │ │ │ - KUBE-NODEPORTS: NodePort 外部访问 │ │ │ │ - KUBE-SERVICES: ClusterIP 访问 │ │ │ │ │ │ │ │ OUTPUT: │ │ │ │ ─────── │ │ │ │ - KUBE-SERVICES: 本地 Pod 访问 Service │ │ │ │ │ │ │ │ FORWARD: │ │ │ │ ─────── │ │ │ │ - KUBE-FORWARD: Pod 流量转发 │ │ │ │ │ │ │ │ POSTROUTING: │ │ │ │ ─────────── │ │ │ │ - KUBE-MARK-MASQ: Egress 流量标记 │ │ │ │ - MASQUERADE: Pod 访问外部 IP │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
K8s iptables 完整流程示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 ┌─────────────────────────────────────────────────────────────────────────────┐ │ K8s 外部访问 NodePort 完整流程 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ 外部 Client 访问 http://NodeIP:30080/api │ │ │ │ 1. 数据包到达 Node eth0 │ │ └─ 目标 IP: NodeIP, 目标端口: 30080 │ │ │ │ 2. PREROUTING 链 │ │ └─ raw/PREROUTING: NOTRACK (可选) │ │ └─ mangle/PREROUTING: (可选 MARK) │ │ └─ nat/PREROUTING: │ │ └─ KUBE-NODEPORTS 链 │ │ └─ 匹配 --dport 30080 │ │ └─ -j KUBE-SVC-XXXX │ │ │ │ 3. KUBE-SVC-XXXX 链 │ │ └─ 负载均衡规则 │ │ └─ -j KUBE-SEP-AAAA (随机选中) │ │ │ │ 4. KUBE-SEP-AAAA 链 │ │ └─ DNAT --to-destination PodIP:TargetPort │ │ │ │ 5. 路由决策 │ │ └─ 目标 IP 变为 PodIP │ │ └─ 查路由表: 决定发往本地还是转发 │ │ │ │ 6. FORWARD 链 (如果需要转发) │ │ └─ KUBE-FORWARD 链 │ │ └─ -j ACCEPT │ │ │ │ 7. POSTROUTING 链 │ │ └─ 如果 Pod 在其他节点: MASQUERADE (SNAT) │ │ │ │ 8. 数据包从 eth0 发出到物理网络 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
2.2 NodePort 类型 NodePort 通过宿主机端口暴露服务,允许外部访问。
2.2.1 详细架构图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 ┌─────────────────────────────────────────────────────────────────────────────┐ │ NodePort 通信详细流程 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ 外部 Client │ │ │ │ │ │ http://192.168.1.10:30080 │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │ │ Node A │ │ │ │ │ │ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ │ │ eth0 (物理网卡) │ │ │ │ │ │ 192.168.1.10/24 │ │ │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ 进入 PREROUTING │ │ │ │ ▼ │ │ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ │ │ iptables nat 表 │ │ │ │ │ │ │ │ │ │ │ │ ┌─────────────────────────────────────────────────────────┐│ │ │ │ │ │ │ KUBE-NODEPORTS ││ │ │ │ │ │ │ -A KUBE-NODEPORTS -p tcp --dport 30080 -j KUBE-SVC-XXXX││ │ │ │ │ │ └─────────────────────────────────────────────────────────┘│ │ │ │ │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ │ │ ┌─────────────────────────────────────────────────────────┐│ │ │ │ │ │ │ KUBE-SVC-XXXX (负载均衡到 endpoints) ││ │ │ │ │ │ │ -A KUBE-SVC-XXXX -j KUBE-SEP-AAAA ││ │ │ │ │ │ │ ... ││ │ │ │ │ │ └─────────────────────────────────────────────────────────┘│ │ │ │ │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ │ │ ┌─────────────────────────────────────────────────────────┐│ │ │ │ │ │ │ KUBE-SEP-AAAA (DNAT 到 Pod X) ││ │ │ │ │ │ │ -A KUBE-SEP-AAAA -j DNAT --to 10.1.1.20:80 ││ │ │ │ │ │ └─────────────────────────────────────────────────────────┘│ │ │ │ │ │ │ │ │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ └──────────────────────────────┼─────────────────────────────────────┘ │ │ │ │ │ │ DNAT 后 │ │ ▼ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ cni0 网桥 │ │ │ │ │ │ │ │ 路由: 10.1.1.0/24 dev cni0 │ │ │ │ │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ Pod X │ │ Pod Y │ │ Pod Z │ │ │ │ 10.1.1.20 │ │ 10.1.1.21 │ │ 10.1.1.22 │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
2.2.2 NodePort 通信流程(10步) 步骤 1: 外部 Client 发起请求
1 2 3 4 5 6 7 外部 Client: curl http://192.168.1.10:30080/api TCP SYN: 源 IP: 客户端公网 IP (如 203.0.113.50) 目标 IP: Node A 公网 IP (192.168.1.10) 目标端口: 30080 (NodePort)
步骤 2: Node A 物理网卡接收
1 2 3 4 5 6 7 8 eth0 (192.168.1.10) 接收: 目标 MAC: Node A 物理 MAC 目标 IP: 192.168.1.10 目标端口: 30080 Linux 内核协议栈处理: - 识别是发往本机 IP + 端口 30080 - 交由 iptables 处理
步骤 3: iptables PREROUTING
1 2 3 4 5 6 PREROUTING 链: -A PREROUTING -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS 匹配 addrtype --dst-type LOCAL: - dst 是本机 IP (192.168.1.10) - 跳转到 KUBE-NODEPORTS 链
步骤 4: KUBE-NODEPORTS 匹配
1 2 3 4 5 6 7 8 KUBE-NODEPORTS 规则: -A KUBE-NODEPORTS -p tcp --dport 30080 -m comment --comment \ "default/my-svc:80 nodeport" -j KUBE-SVC-XXXX 匹配: -p tcp --dport 30080 ✓ 跳转到 KUBE-SVC-XXXX 链
步骤 5: KUBE-SVC-XXXX 负载均衡
1 2 3 4 5 6 7 8 KUBE-SVC-XXXX (假设 3 个 endpoints): -A KUBE-SVC-XXXX -m statistic --mode random --probability 0.33 \ -j KUBE-SEP-AAAA -A KUBE-SVC-XXXX -m statistic --mode random --probability 0.50 \ -j KUBE-SEP-BBBB -A KUBE-SVC-XXXX -j KUBE-SEP-CCCC 假设命中 KUBE-SEP-AAAA
步骤 6: DNAT 到 Pod IP
1 2 3 4 5 6 7 8 9 10 KUBE-SEP-AAAA 规则: -A KUBE-SEP-AAAA -j DNAT --to-destination 10.1.1.20:80 DNAT: 目标 IP: 192.168.1.10:30080 -> 10.1.1.20:80 (NodeIP:NodePort -> PodIP:TargetPort) ConnTrack 记录: 原始: 203.0.113.50:xxxxx -> 192.168.1.10:30080 转换: 203.0.113.50:xxxxx -> 10.1.1.20:80
步骤 7: DNAT 后路由
1 2 3 4 5 6 7 8 数据包 DNAT 后: 源 IP: 203.0.113.50 (Client) 目标 IP: 10.1.1.20 (Pod X) 查路由表: 10.1.1.0/24 dev cni0 发送到 cni0 网桥
步骤 8: cni0 转发到 Pod X
1 2 3 4 cni0 查 MAC 表: 10.1.1.20 -> veth pair X 通过 veth pair 发送到 Pod X
步骤 9: Pod X 处理请求
1 2 3 4 5 Pod X eth0 收到: 目标 IP: 10.1.1.20 目标端口: 80 应用收到 HTTP 请求,处理并响应
步骤 10: 响应返回
1 2 3 4 5 6 7 8 Pod X 发送响应: 源 IP: 10.1.1.20 目标 IP: 203.0.113.50 Node A ConnTrack 反向转换: 源 IP: 10.1.1.20:80 -> 192.168.1.10:30080 Client 收到响应,源 IP 看起来像是 NodeIP
2.3 LoadBalancer 类型 LoadBalancer 配合云服务商提供外部负载均衡。
2.3.1 云平台 LoadBalancer 通信流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 ┌─────────────────────────────────────────────────────────────────────────────┐ │ LoadBalancer 通信流程 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ 1. 创建 LoadBalancer Service │ │ ──────────────────────────── │ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ kube-apiserver │ │ │ │ │ │ │ │ 创建 Service type=LoadBalancer │ │ │ │ 调用 Cloud Controller Manager │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ Cloud Controller Manager │ │ │ │ │ │ │ │ 云平台 API 调用: │ │ │ │ - 创建负载均衡器 (ELB/ALB/NLB) │ │ │ │ - 配置监听器 (port 80 -> NodePort 30080) │ │ │ │ - 配置目标组 (NodeIP:NodePort) │ │ │ │ - 配置健康检查 │ │ │ │ │ │ │ │ 更新 Service 状态: │ │ │ │ status.loadBalancer.ingress[0].ip = 203.0.113.50 │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ 2. 外部 Client 访问 │ │ ───────────────── │ │ │ │ Client │ │ │ │ │ │ https://203.0.113.50:443 │ │ ▼ │ │ ┌───────────────────────────────────────────────────────────────────┐ │ │ │ 云平台负载均衡器 (如 AWS ELB) │ │ │ │ │ │ │ │ 监听器: 443 -> NodePort:30080 │ │ │ │ 健康检查: 30080 -> /healthz │ │ │ │ │ │ │ │ 后端: Node A:30080, Node B:30080, Node C:30080 │ │ │ └───────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ 选择健康的后端节点 (如 Node B) │ │ ▼ │ │ ┌───────────────────────────────────────────────────────────────────┐ │ │ │ Node B (192.168.1.11) │ │ │ │ │ │ │ │ eth0: 192.168.1.11:30080 ──┐ │ │ │ │ │ iptables DNAT │ │ │ │ ▼ │ │ │ │ kube-proxy -> Pod X │ │ │ └───────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
2.3.2 详细通信流程(12步) 步骤 1: 创建 LoadBalancer Service
1 2 3 4 5 6 7 8 9 10 11 12 apiVersion: v1 kind: Service metadata: name: my-app spec: type: LoadBalancer selector: app: my-app ports: - port: 443 targetPort: 8443 protocol: TCP
步骤 2: Cloud Controller Manager 创建 LB
1 2 3 4 5 6 7 8 9 10 CCM 调用云平台 API: AWS: CreateLoadBalancer GCP: forwardingRules.insert Azure: LoadBalancer.CreateOrUpdate 创建的 LB 配置: - 外部 IP: 203.0.113.50 - 前端端口: 443 - 后端端口: NodePort (随机分配,如 30080) - 健康检查: HTTP /healthz on NodePort
步骤 3: CCM 配置节点为 LB 后端
1 2 3 4 5 6 7 云平台自动将所有 Node 加入目标组: TargetGroup: - Node A:30080 (健康) - Node B:30080 (健康) - Node C:30080 (不健康,移除) 云平台定期执行健康检查
步骤 4: 外部 Client 发起请求
1 2 3 4 5 6 7 8 9 Client: curl https://203.0.113.50/api DNS: myapp.example.com -> 203.0.113.50 TCP SYN 到 LB: 目标 IP: 203.0.113.50 目标端口: 443
步骤 5: LB 接收并分配后端
1 2 3 4 5 6 7 LB 负载均衡算法选择后端: 假设选择 Node A (192.168.1.10:30080) LB 转发 TCP: 源 IP: Client IP 目标 IP: 192.168.1.10 目标端口: 30080
步骤 6: Node A 接收请求
1 2 3 4 5 Node A eth0 收到: 目标 IP: 192.168.1.10 (Node A) 目标端口: 30080 iptables PREROUTING -> KUBE-NODEPORTS
步骤 7-12: 同 NodePort 流程
1 2 3 4 5 6 步骤 7: KUBE-NODEPORTS 匹配端口 30080 步骤 8: KUBE-SVC-XXXX 负载均衡 步骤 9: DNAT 到 Pod IP:Port 步骤 10: cni0 转发到 Pod 步骤 11: Pod 处理请求 步骤 12: 响应返回 (经过 ConnTrack)
2.4 Headless Service Headless Service 不分配 ClusterIP,DNS 直接解析到 Pod IP。
2.4.1 详细架构图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 ┌─────────────────────────────────────────────────────────────────────────────┐ │ Headless Service DNS 解析 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ │ │ │ Pod A │ │ │ │ (client) │ │ │ └──────┬──────┘ │ │ │ │ │ │ 1. DNS 查询 │ │ │ my-headless.default.svc.cluster.local │ │ │ │ │ ┌──────┴──────────────────────────────────────────────────────────────┐ │ │ │ Pod A 的 /etc/resolv.conf │ │ │ │ │ │ │ │ nameserver 169.254.20.10 <- nodelocaldns │ │ │ │ search default.svc.cluster.local svc.cluster.local cluster.local │ │ │ │ options ndots:5 │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ 2. UDP DNS 查询到 10.96.0.10:53 │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ CoreDNS / kube-dns │ │ │ │ │ │ │ │ 配置文件 (/etc/coredns/Corefile): │ │ │ │ ────────────────────────────── │ │ │ │ cluster.local { │ │ │ │ kubernetes cluster.local in-addr.arpa ip6.arpa { │ │ │ │ pods insecure │ │ │ │ service kube-dns │ │ │ │ prometheus metrics │ │ │ │ } │ │ │ │ } │ │ │ │ │ │ │ │ Service 变更时,Endpoints Controller 更新 DNS 记录 │ │ │ │ │ │ │ │ Headless Service DNS 记录 (无 ClusterIP): │ │ │ │ ──────────────────────────────────── │ │ │ │ my-headless.default.svc.cluster.local. 300 IN A 10.1.1.20 │ │ │ │ my-headless.default.svc.cluster.local. 300 IN A 10.1.1.21 │ │ │ │ my-headless.default.svc.cluster.local. 300 IN A 10.1.1.22 │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ 3. DNS 响应 (多个 A 记录) │ │ ▼ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ Pod X │ │ Pod Y │ │ Pod Z │ │ │ │ 10.1.1.20 │ │ 10.1.1.21 │ │ 10.1.1.22 │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
2.4.2 DNS 记录格式对比 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ┌─────────────────────────────────────────────────────────────────────────────┐ │ 普通 Service vs Headless Service DNS │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ 普通 ClusterIP Service: │ │ ─────────────────────── │ │ my-app.default.svc.cluster.local. 300 IN A 10.96.0.100 │ │ │ │ 返回: 单个 ClusterIP │ │ │ │ Headless Service: │ │ ───────────────── │ │ my-headless.default.svc.cluster.local. 300 IN A 10.1.1.20 │ │ my-headless.default.svc.cluster.local. 300 IN A 10.1.1.21 │ │ my-headless.default.svc.cluster.local. 300 IN A 10.1.1.22 │ │ │ │ 返回: 所有 Pod IP (无 ClusterIP) │ │ │ │ Pod 级别 DNS (需启用): │ │ ────────────────── │ │ 10-1-1-20.default.pod.cluster.local. 300 IN A 10.1.1.20 │ │ (格式: pod-ip-with-dashes.namespace.pod.cluster.local) │ │ │ │ SRV 记录 (用于发现端口): │ │ ──────────────────── │ │ _http._tcp.my-app.default.svc.cluster.local. 300 IN SRV │ │ 0 100 80 my-app.default.svc.cluster.local. │ │ │ │ Headless SRV 记录: │ │ _http._tcp.my-headless.default.svc.cluster.local. 300 IN SRV │ │ 0 100 8080 10-1-1-20.default.pod.cluster.local. │ │ 0 100 8080 10-1-1-21.default.pod.cluster.local. │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
3. 外部到 Service 通信 3.1 Ingress 通信 Ingress 通过 HTTP/HTTPS 路由到后端 Service。
3.1.1 详细架构图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 ┌─────────────────────────────────────────────────────────────────────────────┐ │ Ingress 通信详细流程 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ 外部 User │ │ │ │ │ │ https://app.example.com/api/v1/users │ │ │ │ │ ▼ │ │ ┌───────────────────────────────────────────────────────────────────────┐ │ │ │ 域名 DNS 解析 │ │ │ │ │ │ │ │ app.example.com -> 203.0.113.100 (Ingress Controller External IP) │ │ │ │ │ │ │ └───────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌───────────────────────────────────────────────────────────────────────┐ │ │ │ Ingress Controller (nginx-ingress) │ │ │ │ │ │ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ │ │ Ingress 规则 │ │ │ │ │ │ │ │ │ │ │ │ apiVersion: networking.k8s.io/v1 │ │ │ │ │ │ kind: Ingress │ │ │ │ │ │ metadata: │ │ │ │ │ │ name: my-app-ingress │ │ │ │ │ │ spec: │ │ │ │ │ │ rules: │ │ │ │ │ │ - host: app.example.com │ │ │ │ │ │ http: │ │ │ │ │ │ paths: │ │ │ │ │ │ - path: /api/v1 │ │ │ │ │ │ pathType: Prefix │ │ │ │ │ │ backend: │ │ │ │ │ │ service: │ │ │ │ │ │ name: my-api-svc │ │ │ │ │ │ port: │ │ │ │ │ │ number: 8080 │ │ │ │ │ │ - path: /web │ │ │ │ │ │ backend: │ │ │ │ │ │ service: │ │ │ │ │ │ name: my-web-svc │ │ │ │ │ │ port: │ │ │ │ │ │ number: 80 │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ Nginx 配置生成: │ │ │ │ ────────────── │ │ │ │ server { │ │ │ │ server_name app.example.com; │ │ │ │ │ │ │ │ location /api/v1/ { │ │ │ │ proxy_pass http://my-api-svc:8080; │ │ │ │ } │ │ │ │ │ │ │ │ location /web { │ │ │ │ proxy_pass http://my-web-svc:80; │ │ │ │ } │ │ │ │ } │ │ │ │ │ │ │ └───────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ proxy_pass 到 Service │ │ ▼ │ │ ┌───────────────────────────────────────────────────────────────────────┐ │ │ │ kube-proxy (iptables/ipvs) │ │ │ │ │ │ │ │ Service: my-api-svc │ │ │ │ ClusterIP: 10.96.0.100 │ │ │ │ Endpoints: 10.1.1.20:8080, 10.1.1.21:8080 │ │ │ │ │ │ │ └───────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ 负载均衡到 Pod │ │ ▼ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ Pod X │ │ Pod Y │ │ │ │ 10.1.1.20 │ │ 10.1.1.21 │ │ │ │ :8080 │ │ :8080 │ │ │ └─────────────┘ └─────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
3.1.2 Ingress TLS 终止流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 ┌─────────────────────────────────────────────────────────────────────────────┐ │ Ingress TLS 终止流程 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ 1. Client 发起 HTTPS 请求 │ │ ─────────────────────── │ │ │ │ Client: │ │ TLS ClientHello │ │ SNI: app.example.com │ │ (TLS 加密的应用数据) │ │ │ │ 2. Ingress Controller 接收 (TLS Pass-through 或 终止) │ │ ────────────────────────────────────────────────────────── │ │ │ │ 模式 A: TLS 终止 (Termination) │ │ ────────────────────────────── │ │ Ingress Controller: │ │ - 使用配置的证书解密 HTTPS │ │ - 提取 HTTP 请求 (明文) │ │ - proxy_pass 到后端 Service (HTTP) │ │ │ │ 模式 B: TLS Pass-through │ │ ──────────────────────────── │ │ Ingress Controller: │ │ - 不解密,直接转发加密数据 │ │ - 代理到后端 Pod (HTTPS) │ │ - 后端 Pod 需要配置 TLS │ │ │ │ TLS 终止配置: │ │ ───────────── │ │ apiVersion: networking.k8s.io/v1 │ │ kind: Ingress │ │ metadata: │ │ name: my-tls-ingress │ │ spec: │ │ tls: │ │ - hosts: │ │ - app.example.com │ │ secretName: my-app-tls │ │ rules: │ │ - host: app.example.com │ │ ... │ │ │ │ Secret 存储: │ │ ────────── │ │ kubectl create secret tls my-app-tls \ │ │ --cert=path/to/cert.pem \ │ │ --key=path/to/key.pem │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
4. DNS 解析流程 4.1 完整 DNS 解析架构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 ┌─────────────────────────────────────────────────────────────────────────────┐ │ DNS 解析完整流程 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ Pod 内部 │ │ │ │ │ │ │ │ 应用发起 DNS 查询: │ │ │ │ ───────────────── │ │ │ │ import socket │ │ │ │ socket.getaddrinfo("my-svc.default.svc.cluster.local", "80") │ │ │ │ │ │ │ │ /etc/resolv.conf (Pod 内): │ │ │ │ ───────────────────────────── │ │ │ │ nameserver 169.254.20.10 <- nodelocaldns (推荐) │ │ │ │ nameserver 10.96.0.10 <- CoreDNS (无 nodelocaldns 时) │ │ │ │ search default.svc.cluster.local svc.cluster.local cluster.local │ │ │ │ options ndots:5 timeout:2 attempts:2 │ │ │ │ │ │ │ │ ndots:5 含义: │ │ │ │ - 查询名称少于 5 个点,先搜索 search 列表 │ │ │ │ - 如 "my-svc" 会补全为 "my-svc.default.svc.cluster.local" │ │ │ │ - 减少集群内部 DNS 查询 │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ nodelocaldns (缓存层) │ │ │ │ │ │ │ │ 架构: │ │ │ │ - DaemonSet 运行在每个节点 │ │ │ │ - 监听 169.254.20.10:53 (在节点网络命名空间) │ │ │ │ - 使用 skfilter 绑定到该 IP,不占用端口 │ │ │ │ │ │ │ │ 缓存策略: │ │ │ │ - A 记录: 缓存 TTL 或默认 30 秒 │ │ │ │ - NXDOMAIN (不存在): 缓存 5 秒 │ │ │ │ │ │ │ │ 优势: │ │ │ │ - 减少到 CoreDNS 的 DNS 查询 │ │ │ │ - 降低 DNS 查询延迟 (本机 vs 网络) │ │ │ │ │ │ │ │ Pod 端点: │ │ │ │ /etc/resolv.conf 指向 169.254.20.10:53 │ │ │ │ ( kubelet 通过 --cluster-dns 配置) │ │ │ │ │ │ │ │ ┌───────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ │ │ │ │ 查询 my-svc.default.svc.cluster.local │ │ │ │ │ │ │ │ │ │ │ │ nodelocaldns 缓存检查: │ │ │ │ │ │ - 命中: 直接返回 │ │ │ │ │ │ - 未命中: 转发到上游 CoreDNS (10.96.0.10) │ │ │ │ │ │ │ │ │ │ │ └───────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ CoreDNS (集群 DNS) │ │ │ │ │ │ │ │ Deployment: kube-dns │ │ │ │ replicas: 2+ │ │ │ │ Service: kube-dns │ │ │ │ ClusterIP: 10.96.0.10 │ │ │ │ │ │ │ │ Corefile 配置: │ │ │ │ ────────────── │ │ │ │ .:53 { │ │ │ │ kubernetes cluster.local in-addr.arpa ip6.arpa { │ │ │ │ pods insecure │ │ │ │ fallthrough in-addr.arpa ip6.arpa │ │ │ │ } │ │ │ │ │ │ │ │ cache 30 │ │ │ │ forward . 8.8.8.8 │ │ │ │ } │ │ │ │ │ │ │ │ ┌───────────────────────────────────────────────────────────────┐ │ │ │ │ │ DNS 记录类型 │ │ │ │ │ │ │ │ │ │ │ │ Service (ClusterIP): │ │ │ │ │ │ my-svc.default.svc.cluster.local. 300 A 10.96.0.100 │ │ │ │ │ │ │ │ │ │ │ │ Service (Headless): │ │ │ │ │ │ my-headless.default.svc.cluster.local. 300 A 10.1.1.20 │ │ │ │ │ │ my-headless.default.svc.cluster.local. 300 A 10.1.1.21 │ │ │ │ │ │ │ │ │ │ │ │ Service (ExternalName): │ │ │ │ │ │ my-ext.default.svc.cluster.local. 300 CNAME external.com. │ │ │ │ │ │ │ │ │ │ │ │ Pod: │ │ │ │ │ │ 10-1-1-20.default.pod.cluster.local. 300 A 10.1.1.20 │ │ │ │ │ │ (需要启用 pod.insecure 特性开关) │ │ │ │ │ │ │ │ │ │ │ │ SRV: │ │ │ │ │ │ _http._tcp.my-svc.default.svc.cluster.local. 30 SRV │ │ │ │ │ │ 0 100 80 my-svc.default.svc.cluster.local. │ │ │ │ │ │ │ │ │ │ │ └───────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ 外部域名查询: │ │ │ │ - CoreDNS 收到外部域名查询 │ │ │ │ - forward . 8.8.8.8 转发到上游 DNS │ │ │ │ - 返回结果给 Pod │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
4.2 DNS 查询详细流程(8步) 步骤 1: 应用发起 DNS 查询
1 2 3 4 5 import "net" addrs, err := net.LookupHost("my-svc.default.svc.cluster.local" )
步骤 2: 读取 /etc/resolv.conf
1 2 3 4 /etc/resolv.conf: nameserver 169.254.20.10 <- nodelocaldns search default.svc.cluster.local svc.cluster.local cluster.local options ndots:5
步骤 3: nodelocaldns 查询
1 2 3 4 5 Pod 内查询 169.254.20.10:53 nodelocaldns 检查缓存: - 缓存命中: 直接返回 - 未命中: 转发到 CoreDNS (10.96.0.10)
步骤 4: CoreDNS 处理查询
1 2 3 4 5 6 7 8 9 10 11 CoreDNS 收到查询: QNAME: my-svc.default.svc.cluster.local QTYPE: A QCLASS: IN CoreDNS 插件链处理: 1. kubernetes 插件: - 检查是否集群内部域名 (*.cluster.local) - 匹配则通过 Kubernetes API 查找 - CoreDNS 维护本地缓存 (从 API Server 同步) - 返回 Service ClusterIP
步骤 5: 查询 Kubernetes API
1 2 3 4 5 6 7 CoreDNS 通过 list/watch 从 kube-apiserver 获取: 1. Service "my-svc" 的 ClusterIP 2. 对应的 Endpoints/EndpointSlice 列表 CoreDNS 缓存这些数据,无需每次查询 API Server 结果: 10.96.0.100
步骤 6: CoreDNS 返回响应
1 2 3 4 5 6 DNS Response: Transaction ID: 0x1234 Flags: QR=1 (响应), AA=1 (权威), RD=1 Answers: my-svc.default.svc.cluster.local. 300 IN A 10.96.0.100 TTL: 300 秒
步骤 7: nodelocaldns 缓存并返回
1 2 3 nodelocaldns: - 缓存 A 记录 (TTL 300 秒) - 返回给 Pod
步骤 8: 应用收到 IP
1 2 addrs = ["10.96.0.100" ]
5. 网络流量架构图 5.1 完整流量架构图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 ┌─────────────────────────────────────────────────────────────────────────────┐ │ Kubernetes 网络架构 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │ │ 集群外部网络 │ │ │ │ │ │ │ │ Internet │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ ┌─────────────┐ │ │ │ │ │ Cloud LB / │ │ │ │ │ │ Ingress │ │ │ │ │ │ Controller │ │ │ │ │ └──────┬──────┘ │ │ │ │ │ │ │ │ └─────────┼──────────────────────────────────────────────────────────────┘ │ │ │ │ │ ┌─────────┼──────────────────────────────────────────────────────────────┐ │ │ │ │ Node A (192.168.1.10) │ │ │ │ │ │ │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ │ │ │ Pod A │ │ Pod B │ │ Ingress│ │ │ │ │ │ │ :80 │ │ :8080 │ │ Pod │ │ │ │ │ │ └───┬─────┘ └────┬────┘ └───┬─────┘ │ │ │ │ │ │ │ │ │ │ │ │ │ └────────────┬┴────────────┘ │ │ │ │ │ │ │ │ │ │ │ ┌────────────────▼────────────────┐ │ │ │ │ │ │ CNI Bridge (cni0) │ │ │ │ │ │ │ ┌────────────────────────────────┐│ │ │ │ │ │ │ │ kube-proxy ││ │ │ │ │ │ │ │ (iptables / ipvs) ││ │ │ │ │ │ │ └────────────────────────────────┘│ │ │ │ │ │ └────────────────┬────────────────┘ │ │ │ │ │ │ │ │ │ │ │ ┌────────────────▼────────────────┐ │ │ │ │ │ │ eth0 (物理网卡) │ │ │ │ │ │ └────────────────────────────────────┘ │ │ │ │ │ │ │ │ └─────────┼───────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ 物理网络 (Layer 2 / Layer 3) │ │ │ │ │ ┌─────────┼───────────────────────────────────────────────────────────────┐ │ │ │ │ Node B (192.168.1.11) │ │ │ │ │ │ │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ │ │ │ Pod C │ │ Pod D │ │ Pod E │ │ │ │ │ │ └────┬────┘ └────┬────┘ └────┬────┘ │ │ │ │ │ └───────────┼┴────────────┘ │ │ │ │ │ │ │ │ │ │ │ ┌────────────────▼────────────────┐ │ │ │ │ │ │ CNI Bridge (cni0) │ │ │ │ │ │ └────────────────┬────────────────┘ │ │ │ │ │ │ │ │ │ │ │ ┌────────────────▼────────────────┐ │ │ │ │ │ │ eth0 (物理网卡) │ │ │ │ │ │ └────────────────────────────────────┘ │ │ │ │ │ │ │ │ └─────────┼───────────────────────────────────────────────────────────────┘ │ │ │ │ │ ┌─────────┼───────────────────────────────────────────────────────────────┐ │ │ │ ▼ Kubernetes 控制平面 │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │ kube-apiserver│ │ kube-controller│ │ kube-scheduler│ │ etcd │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ │ │ │ │ │ │ │ └───────────────┴────────────────┘ │ │ │ │ │ │ │ │ │ ┌───────────────────────────▼──────────────────────────┐ │ │ │ │ │ CoreDNS / kube-dns │ │ │ │ │ │ (通常以 Pod 形式运行) │ │ │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
5.2 数据包封装对比
通信场景
封装层数
封装类型
说明
同节点 Pod 间
1 层
无封装
直接通过 veth + bridge
Flannel 跨节点 Pod
4 层
VXLAN
UDP → IP → Ethernet
Calico 跨节点 Pod
1 层
无封装
纯三层路由
Weave 跨节点 Pod
2-4 层
VXLAN/UDP
取决于模式
Cilium 跨节点 Pod
1 层
无封装
eBPF 加速
6. 关键源码位置 6.1 网络相关代码
组件
源码路径
说明
Kubelet 网络
pkg/kubelet/network/
网络插件管理
CNI 插件
pkg/kubelet/network/cni/
CNI 实现
Kube-proxy
pkg/kube-proxy/
Service 负载均衡
DNS
cluster/addons/dns/
DNS 部署配置
NetworkPolicy
pkg/kubelet/network/plugins.go
网络策略
6.2 相关接口 1 2 3 4 5 6 7 8 9 10 11 12 13 type NetworkPlugin interface { Init(host Host, hairpinMode bool , mtu int ) error SetUpPod(namespace string , name string , podInfraUID types.UID) error TearDownPod(namespace string , name string , podInfraUID types.UID) error GetPodNetworkStatus(namespace string , name string , podInfraUID types.UID) (*PodNetworkStatus, error ) } type LoadBalancer interface { NewLoadBalancer(service *v1.Service) (LoadBalancer, error ) ExecHealthCheck(node string ) error }
7. 常见通信问题排查 7.1 Pod 间通信问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 kubectl get pod -n <namespace> -o wide kubectl exec -it <pod-name> -- /bin/sh kubectl exec -it <pod-name> -- ping <target-pod-ip> kubectl exec -it <pod-name> -- curl -k https://<service-name> journalctl -u kubelet -n 100 cat /etc/cni/net.d/ip link show type bridge ip route show ip route show table all ip neigh show ip -d link show flannel.1 bridge fdb show dev flannel.1
7.2 Service 通信问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 kubectl get svc -o wide kubectl describe svc <service-name> kubectl get endpoints <service-name> kubectl get pod -n kube-system -l k8s-app=kube-proxy kubectl logs <kube-proxy-pod> -n kube-system iptables -t nat -L -n | grep <service-name> iptables -t nat -L -n -v | head -50 ipvsadm -L -n ipvsadm -L -n --stats conntrack -L | grep <pod-ip> conntrack -L | grep <service-ip> kubectl exec -it <pod-name> -- nslookup <service-name> kubectl exec -it <pod-name> -- dig <service-name>
8. 总结 通信链路对比表
源 → 目标
通信方式
负载均衡位置
DNS 解析
Pod → Pod (同节点)
直接 veth + bridge
无
无
Pod → Pod (跨节点)
路由/VXLAN
无
无
Pod → ClusterIP
kube-proxy
iptables/ipvs
是
Pod → NodePort
kube-proxy
iptables/ipvs
是
Pod → Headless
直接 Pod IP
无
是
External → NodePort
kube-proxy
iptables
否
External → LoadBalancer
云平台
云平台
否
关键网络组件职责
组件
职责
CNI
Pod 网络配置,IP 分配,跨节点网络
kube-proxy
Service 到 Pod 的负载均衡
CoreDNS
服务发现和 DNS 解析
NetworkPolicy
Pod 级别访问控制
Cloud Controller
云平台网络集成
9. 补充通信场景 9.1 Pod 内部容器间通信 同一个 Pod 内多个容器通过 localhost 直接通信,它们共享同一个网络命名空间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ┌─────────────────────────────────────────────────────────────────────┐ │ Pod 内部容器间通信 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ Pod │ │ │ │ │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │ Main │ │ Sidecar │ │ Init │ │ │ │ │ │ Container │ │ Container │ │ Container │ │ │ │ │ │ :80 │ │ :9090 │ │ :8080 │ │ │ │ │ └──────┬──────┘ └──────┬──────┘ └─────────────┘ │ │ │ │ │ │ │ │ │ │ │ ┌───────────┴───────────┐ │ │ │ │ │ │ Network Namespace │ │ │ │ │ │ │ (共享 eth0, lo) │ │ │ │ │ │ └───────────┬───────────┘ │ │ │ │ │ │ │ │ │ └─────────┼─────────────────┼──────────────────────────────────┘ │ │ │ │ │ │ │ localhost:9090│ │ │ │◀─────────────────▶│ │ │ │ │ │ └────────────┴─────────────────┴────────────────────────────────────────┘
Pod 内容器通信详细流程(5步) 步骤 1: Pod 创建时的网络命名空间设置
1 2 3 4 5 6 7 8 9 10 11 12 Kubelet 创建 Pod 流程: 1. 创建 pause 容器 (暂停进程) - 创建网络命名空间 netns - 创建 veth pair: eth0 <-> vethxxx - 配置 netns 内的 eth0 IP (从 CNI 获取) - pause 容器持有这个 netns 2. 主容器加入 pause 容器的网络命名空间 - 使用 --net=container:pause - 或 --network=container:pause (Docker 语法) 结果: 所有容器共享同一个网络命名空间
步骤 2: 容器网络命名空间视图
1 2 3 4 5 6 7 8 # 在 Pod 内查看网络 / # ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue inet 127.0.0.1/8 scope host lo 2: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> inet 10.1.1.20/24 所有容器看到相同的 eth0 和 lo
步骤 3: Main 容器连接 Sidecar
1 2 3 4 5 6 7 8 9 conn, err := net.Dial("tcp" , "localhost:9090" ) conn, err := net.Dial("tcp" , "127.0.0.1:9090" ) conn, err := net.Dial("tcp" , ":9090" )
步骤 4: localhost 连接的特殊性
1 2 3 4 5 6 7 8 9 数据包流向: 应用 -> 127.0.0.1:9090 -> 主机内核协议栈 -> 本地端口 9090 监听进程 不经过: - eth0 网卡 - veth pair - 物理网络
步骤 5: Init 容器特点
1 2 3 4 5 6 7 8 9 10 11 Init 容器特点: - 在主容器之前启动 - 完成初始化后退出 - 不属于 running 状态 - 仍然共享网络命名空间 示例: 等待数据库就绪 initContainers: - name: wait-for-db image: busybox command: ['sh', '-c', 'until nc -z db:5432; do sleep 1; done']
9.2 Pod 到 API Server 通信 Pod 通过 ServiceAccount 自动挂载的 Token 访问 API Server。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ┌─────────────────────────────────────────────────────────────────────┐ │ Pod 到 API Server 通信 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ │ │ │ Pod │ │ │ │ (client) │ │ │ └──────┬──────┘ │ │ │ │ │ │ 1. 读取 /var/run/secrets/kubernetes.io/serviceaccount/ │ │ │ - token (JWT) │ │ │ - ca.crt (API Server 证书) │ │ │ - namespace (Pod 所在命名空间) │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ API Server (kubernetes Service) │ │ │ │ │ │ │ │ Endpoint: 10.96.0.1 (ClusterIP) │ │ │ │ │ │ │ │ 认证方式: │ │ │ │ - Token (ServiceAccount JWT) │ │ │ │ - TLS 证书 (双向 TLS) │ │ │ │ │ │ │ │ 授权方式: │ │ │ │ - RBAC (Role/ClusterRole + RoleBinding/ClusterRoleBinding) │ │ │ │ - ABAC (Attribute-based) │ │ │ │ - Webhook │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘
Pod 到 API Server 详细流程(8步) 步骤 1: ServiceAccount Token 自动挂载
1 2 3 4 5 6 7 8 Kubelet 创建 Pod 时: 1. 为 Pod 分配 ServiceAccount (默认 default) 2. 在 Pod 内挂载 ServiceAccount secrets: - /var/run/secrets/kubernetes.io/serviceaccount/ ├── token (JWT) ├── ca.crt (API Server CA) └── namespace (文件内容为命名空间名)
步骤 2: Token 内容解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # token 文件是 Opaque token (不是标准 JWT) cat /var/run/secrets/kubernetes.io/serviceaccount/token # 内容是一串签名后的数据,格式如下: BearerToken= aHR0cHM6Ly9rdWJlcm5ldGVzLmNsdXN0ZXIubG9jYWw6MTIzNDU2Nzg5YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo= # Token 内部 Base64 编码的内容包含: { "iss": "kubernetes/serviceaccount", "kubernetes.io/serviceaccount/namespace": "default", "kubernetes.io/serviceaccount/secret-name": "default-token-xxxxx", "kubernetes.io/serviceaccount/service-account-name": "default", "sub": "system:serviceaccount:default:default" } # 注意: K8s ServiceAccount token 不是 RFC 7519 标准 JWT # API Server 使用 --service-account-issuer 和 --service-account-signing-key 生成的 # 才能被验证为 JWT 格式,Pod 挂载的通常是 Opaque token
步骤 3: Client 配置 InClusterConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import "k8s.io/client-go/rest" config, err := rest.InClusterConfig() if err != nil { panic (err) } clientset, err := kubernetes.NewForConfig(config)
步骤 4: TLS 握手
1 2 3 4 1. Client 使用 CA.crt 验证 API Server 证书 2. Client 发送请求,带上 JWT Token 3. API Server 验证 Token 签名 4. API Server 提取 Token 中的 ServiceAccount 信息
步骤 5: API Server 认证
1 2 3 4 5 6 7 8 9 API Server 认证插件处理: 1. Authentication Token Review: - 验证 JWT signature (使用 ServiceAccount 公钥) - 检查 Token payload 2. 提取身份信息: - User: system:serviceaccount:default:default - Groups: ["system:serviceaccounts", "system:serviceaccounts:default"]
步骤 6: API Server 授权
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Authorizer 检查 (RBAC): 1. 查看 User 的 RoleBindings: kind: RoleBinding metadata: name: default-binding subjects: - kind: ServiceAccount name: default namespace: default roleRef: kind: Role name: pod-reader 2. 检查请求权限: - 需要的 verb: get, list, watch - 需要的 resource: pods - 命名空间是否匹配
步骤 7: Admission Control
1 2 3 4 5 6 7 8 Admission Controllers: - AlwaysPullImages - DefaultStorageClass - NodeRestriction - ... Mutating 阶段: 可能修改请求对象 Validating 阶段: 决定是否允许请求
步骤 8: 请求处理
1 2 3 4 5 6 7 请求通过认证、授权、Admission 后: - 写入 etcd - 返回响应给 Client 示例: GET /api/v1/namespaces/default/pods Response: Pod list JSON
9.3 ExternalName Service 通信 ExternalName Service 返回 CNAME 记录,将服务名映射到外部域名。
详细通信流程(5步) 步骤 1: 创建 ExternalName Service
1 2 3 4 5 6 7 8 apiVersion: v1 kind: Service metadata: name: my-database namespace: default spec: type: ExternalName externalName: database.example.com
步骤 2: CoreDNS 生成 CNAME 记录
1 2 3 4 5 DNS 查询 my-database.default.svc.cluster.local CoreDNS 响应: my-database.default.svc.cluster.local. 300 IN CNAME database.example.com. database.example.com. 300 IN A 203.0.113.10
步骤 3: Pod 解析外部域名
1 2 3 4 5 6 7 conn, err := net.Dial("tcp" , "my-database:5432" )
步骤 4: 直接网络连接
1 2 3 4 5 6 Pod 直接连接到 externalName: - 不经过 kube-proxy - 不经过 Service 负载均衡 - DNS 返回什么就连接什么 连接: Pod IP:任意端口 -> database.example.com IP:5432
步骤 5: ExternalName 流量特点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ┌────────────────────────────────────────────────────────────────┐ │ ExternalName 流量特点 │ ├────────────────────────────────────────────────────────────────┤ │ │ │ 无 kube-proxy 参与: │ │ - 没有 iptables/ipvs 规则 │ │ - 没有 DNAT 转换 │ │ - 没有负载均衡 │ │ │ │ 直接 DNS 解析: │ │ - CNAME 展平 (客户端跟随 CNAME) │ │ - 直接连接外部 IP │ │ │ │ 适用场景: │ │ - 外部数据库服务 │ │ - 外部 API 端点 │ │ - 遗留系统集成 │ │ │ └────────────────────────────────────────────────────────────────┘
9.4 Pod 到外部网络 (Egress) 通信 Pod 访问集群外部网络时,需要进行 SNAT (Source Network Address Translation)。
详细通信流程(10步) 步骤 1: Pod 发送请求到外部 IP
1 2 3 4 5 6 Pod A 应用: socket.connect("8.8.8.8", 53) 数据包: 源 IP: 10.1.1.10 (Pod IP) 目标 IP: 8.8.8.8
步骤 2: Pod 本地路由
1 2 3 4 5 6 Pod A 路由表: 10.0.0.0/8 via eth0 (K8s 默认路由) # 或者精确匹配 目标 8.8.8.8 不在 10.0.0.0/8 范围 发送到网关 (cni0)
步骤 3: Node 路由决策
1 2 3 4 5 6 7 Node A 路由表: 10.1.1.0/24 dev cni0 (Pod 网段) 192.168.1.0/24 dev eth0 (Node 网段) default via 192.168.1.1 (物理网关) 目标 8.8.8.8 匹配 default 发送到物理网关 192.168.1.1
步骤 4: SNAT 转换
1 2 3 4 5 6 7 8 9 10 11 iptables POSTROUTING: -A POSTROUTING -s 10.1.1.0/24 ! -d 10.0.0.0/8 -j MASQUERADE MASQUERADE 操作: 源 IP: 10.1.1.10 -> 192.168.1.10 (Node IP) 源端口: 重新分配 ConnTrack 记录: 原始: 10.1.1.10:40000 -> 8.8.8.8:53 转换: 192.168.1.10:50000 -> 8.8.8.8:53
步骤 5: 通过物理网络发送
1 2 3 4 5 6 7 数据包: 源 IP: 192.168.1.10 (Node A IP) 源 MAC: Node A MAC 目标 IP: 8.8.8.8 目标 MAC: 192.168.1.1 (网关 MAC) 物理交换机发送到网关
步骤 6: 外部服务器接收
1 2 3 4 5 6 外部 DNS 服务器 8.8.8.8 收到: 源 IP: 192.168.1.10 (看起来是 Node A) 目标 IP: 8.8.8.8 目标端口: 53 响应发送回 192.168.1.10
步骤 7: Node 接收响应
1 2 3 4 5 6 7 Node A 收到响应: 源 IP: 8.8.8.8 目标 IP: 192.168.1.10 ConnTrack 查表: 查找 192.168.1.10:50000 -> 8.8.8.8:53 找到对应记录,状态 ESTABLISHED
步骤 8: DNAT 反向转换
1 2 3 4 5 6 7 8 ConnTrack 自动反向 SNAT: 原始响应: 8.8.8.8 -> 192.168.1.10:50000 转换后: 8.8.8.8 -> 10.1.1.10:40000 (Pod IP) Pod A 看到: 源 IP: 8.8.8.8 目标 IP: 10.1.1.10 (自己!)
步骤 9: ConnTrack 作用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ConnTrack 表的作用: 1. 记录活跃连接: conntrack -L | grep 10.1.1.10 2. 自动反向转换: - 不需要手动配置 DNAT 规则 - 内核自动处理 3. 连接状态跟踪: - NEW: 新连接 - ESTABLISHED: 已建立 - RELATED: 相关连接 - INVALID: 无效
步骤 10: Egress 控制 (NetworkPolicy)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 默认情况下 Pod 可以访问任何外部 IP 限制 Egress: apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: egress-rule spec: podSelector: matchLabels: app: my-app policyTypes: - Egress egress: - to: - podSelector: {} # 只允许同命名空间 Pod 访问 - to: - namespaceSelector: {} # 只允许同命名空间 Service 访问
9.5 Service Mesh (Istio) Sidecar 通信 在 Service Mesh 架构中(如 Istio),每个 Pod 都有一个 Sidecar Proxy (Envoy) 拦截所有流量。
详细通信流程(15步) 步骤 1: Pod 部署时的 Sidecar 注入
1 2 3 4 5 kubectl label namespace default istio-injection=enabled istioctl kube-inject -f deployment.yaml
步骤 2: Init 容器配置 iptables
1 2 3 4 5 6 7 8 9 10 11 12 13 Init 容器 (istio-init) 设置 iptables: iptables -t nat -N ISTIO_INBOUND iptables -t nat -A ISTIO_INBOUND -p tcp --dport 80 -j ISTIO_IN_REDIRECT iptables -t nat -A ISTIO_IN_REDIRECT -j RETURN iptables -t nat -A OUTPUT -p tcp -j ISTIO_OUTPUT iptables -t nat -A ISTIO_OUTPUT -m owner --uid-owner 1337 -j RETURN iptables -t nat -A ISTIO_OUTPUT -j ISTIO_REDIRECT 结果: - 所有出站流量 redirect 到 Envoy (15001) - 所有入站流量 redirect 到 Envoy (15006)
步骤 3: Envoy Sidecar 启动
1 2 3 4 5 6 7 8 9 10 11 Envoy 配置文件: listeners: - address: 0.0.0.0:15006 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager - address: 0.0.0.0:15001 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager
步骤 4: xDS 配置获取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Envoy 通过 xDS 协议从 Istiod 获取配置: 1. CDS (Cluster Discovery Service): - 集群列表 - 每个集群的 endpoints 2. LDS (Listener Discovery Service): - 监听器配置 - 过滤器链 3. EDS (Endpoint Discovery Service): - 具体的后端 IP:Port 4. RDS (Route Discovery Service): - 路由规则 Istiod: - 发现服务 - 下发配置 - mTLS 证书分发
步骤 5: Pod A 应用发送请求
1 2 resp, err := http.Get("http://service-b:8080/api" )
步骤 6: iptables 重定向
1 2 3 4 5 6 7 8 9 10 11 12 应用发送请求到 service-b:8080 数据包原始: 目标 IP: service-b 的 ClusterIP 或 Pod IP 目标端口: 8080 Istio iptables 拦截: 所有 TCP 流量 redirect 到 15001 数据包变为: 目标 IP: 127.0.0.1 目标端口: 15001 (Envoy)
步骤 7: Envoy Outbound 接收
1 2 3 4 5 6 7 Envoy 15001 端口接收: - 从 iptables redirect 来的流量 - Envoy 作为代理 HTTP Connection Manager: - 解码 HTTP 请求 - 提取 Host/Path
步骤 8: Envoy 路由匹配
1 2 3 4 5 6 7 8 9 10 11 12 Envoy 配置 (来自 RDS): routes: - match: prefix: /api route: cluster: outbound|8080||service-b.namespace.svc.cluster.local cluster 定义 (来自 CDS/EDS): service-b.namespace.svc.cluster.local: - 10.1.1.20:8080 (Pod X) - 10.1.1.21:8080 (Pod Y)
步骤 9: 负载均衡选择 endpoint
1 2 3 4 5 6 7 负载均衡算法 (可配置): - ROUND_ROBIN: 轮询 - LEAST_REQUEST: 最少连接 - RANDOM: 随机 - consistent_hashing: 一致性哈希 假设选择: 10.1.1.20:8080 (Pod X)
步骤 10: mTLS 加密 (可选)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 如果启用 mTLS: 1. Envoy 使用客户端证书: - 证书来自 Istiod (通过 SDS) - 包含 ServiceAccount 身份 2. TLS 握手: Client Hello Server Certificate (Pod X Envoy) Key Exchange Finished 3. 加密通信: - Envoy <-> Envoy 加密 - 应用层不感知
步骤 11: 转发到 Pod X Envoy
1 2 3 4 5 6 7 请求从 Pod A Envoy 发出: 源: Pod A IP (10.1.1.11) <- Envoy 代理 目标: Pod X Envoy (15006 端口) Pod X Envoy 接收 (Inbound): - 验证客户端证书 (mTLS) - 确认调用方身份
步骤 12: Pod X Envoy 认证
1 2 3 4 Pod X Envoy inbound 配置: - 验证客户端证书 - 检查 peer 身份 (Pod A 的 ServiceAccount) - 应用 Authorization 策略
步骤 13: Pod X Envoy 转发到应用
1 2 3 4 5 6 通过 lo 接口转发到应用: localhost:15006 -> localhost:8080 (应用) Envoy 配置: - 15006 是 inbound listener - 转发给本地应用端口
步骤 14: Pod X 应用处理
1 2 3 4 5 6 7 应用收到请求: - 来源是 localhost:15006 - 应用不需要感知 Service Mesh 应用处理并响应: - 响应到 localhost:15006 - Envoy 收到响应
步骤 15: 响应返回
1 2 3 4 5 6 7 响应流程 (反向): Pod X Envoy -> Pod A Envoy -> Pod A 应用 Pod A Envoy 收到 Pod X 响应后: - 通过 outbound 发回 - Pod A iptables -> 应用
Istio 流量管理能力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 ┌─────────────────────────────────────────────────────────────────────────────┐ │ Istio 流量管理能力 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ 1. 金丝雀发布: │ │ ────────────── │ │ VirtualService: │ │ - 50% 流量到 v1 │ │ - 50% 流量到 v2 │ │ │ │ 2. A/B 测试: │ │ ────────── │ │ - 根据 header 分组 │ │ - header["X-User-ID"] 路由 │ │ │ │ 3. 流量镜像: │ │ ─────────── │ │ - 100% 到 v1 │ │ - 100% 镜像到 v2 (不返回给用户) │ │ │ │ 4. 超时重试: │ │ ────────── │ │ timeout: 5s │ │ retries: │ │ attempts: 3 │ │ perTryTimeout: 2s │ │ │ │ 5. 断路器: │ │ ─────── │ │ circuitBreakers: │ │ maxConnections: 100 │ │ maxPendingRequests: 50 │ │ │ │ 6. 限流: │ │ ──── │ │ rateLimit: │ │ requestsPerUnit: 100 │ │ unit: second │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
9.6 Node 本地通信 节点上的系统组件(kubelet、container runtime)之间的通信。
详细组件交互 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 ┌─────────────────────────────────────────────────────────────────────────────┐ │ Node 本地系统组件通信 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ Kubelet │ │ │ │ │ │ │ │ 端口: 10250 ( kubelet API, metrics, exec) │ │ │ │ 认证: 需要 Node 证书 │ │ │ │ │ │ │ │ 主要职责: │ │ │ │ - Pod 生命周期管理 │ │ │ │ - 容器运行时交互 │ │ │ │ - 网络插件调用 │ │ │ │ - 资源监控 │ │ │ │ │ │ │ │ ┌───────────────────────────────────────────────────────────────┐ │ │ │ │ │ CRI 接口调用 │ │ │ │ │ │ │ │ │ │ │ │ RunPodSandbox() │ │ │ │ │ │ CreateContainer() │ │ │ │ │ │ StartContainer() │ │ │ │ │ │ StopContainer() │ │ │ │ │ │ RemoveContainer() │ │ │ │ │ │ ContainerStats() │ │ │ │ │ │ │ │ │ │ │ └───────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ ┌───────────────────────────────────────────────────────────────┐ │ │ │ │ │ CNI 接口调用 │ │ │ │ │ │ │ │ │ │ │ │ ADD /etc/cni/net.d/10-flannel.conflist │ │ │ │ │ │ DEL │ │ │ │ │ │ │ │ │ │ │ └───────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ └──────────────────────────────┼──────────────────────────────────────┘ │ │ │ │ │ ┌──────────────────────────────┼──────────────────────────────────────┐ │ │ │ Containerd │ │ │ │ │ │ │ │ Socket: /run/containerd/containerd.sock │ │ │ │ Protocol: gRPC │ │ │ │ │ │ │ │ ┌───────────────────────────────────────────────────────────────┐ │ │ │ │ │ Containerd shim (runc-v2) │ │ │ │ │ │ │ │ │ │ │ │ containerd-shim-runc-v2 \ │ │ │ │ │ │ -address /run/containerd/containerd.sock \ │ │ │ │ │ │ -containerd-binary /usr/bin/containerd \ │ │ │ │ │ │ -namespace k8s.io │ │ │ │ │ │ │ │ │ │ │ │ 作用: │ │ │ │ │ │ - 充当 containerd 和 runc 的中介 │ │ │ │ │ │ - 允许容器不依赖 containerd 进程 │ │ │ │ │ │ - 处理容器重启 │ │ │ │ │ │ │ │ │ │ │ └───────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ └──────────────────────────────┼──────────────────────────────────────┘ │ │ │ │ │ ┌──────────────────────────────┼──────────────────────────────────────┐ │ │ │ runc │ │ │ │ │ │ │ │ 职责: │ │ │ │ - 创建容器进程 │ │ │ │ - 配置 namespace │ │ │ │ - 配置 cgroups │ │ │ │ - 配置 seccomp │ │ │ │ │ │ │ │ 创建容器流程: │ │ │ │ 1. runc create --pid-file /run/.../pid rootfs/ │ │ │ │ 2. 创建 container 进程 (pause 容器) │ │ │ │ 3. 配置网络 namespace (从 CNI 获取) │ │ │ │ 4. 设置 cgroups │ │ │ │ 5. runc start │ │ │ │ 6. 容器内执行ENTRYPOINT/CMD │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
9.7 完整通信场景汇总
通信场景
通信方式
是否经过 kube-proxy
是否经过 CNI
DNS 解析
Pod → Pod (同节点)
veth + bridge
否
是
否
Pod → Pod (跨节点)
路由/VXLAN
否
是
否
Pod → ClusterIP
iptables/ipvs
是
否
是
Pod → NodePort
iptables/ipvs
是
否
是
Pod → ExternalName
CNAME
否
否
是 (CNAME)
Pod → External IP
SNAT/NAT
否
是
否
Pod 内容器间
localhost
否
共享
否
Pod → API Server
HTTPS + Token
否
否
是
Istio Sidecar → Sidecar
mTLS
Envoy 拦截
iptables redirect
是
External → NodePort
iptables
是
是
否
External → LoadBalancer
云平台 LB
否
否
否