五、高难度面试题及详细解答
问题 1: 请详细描述一个 HTTP 请求从集群外部到达 Pod 的完整网络路径
答案:
┌─────────────────────────────────────────────────────────────────────────┐
│ 外部请求完整路径 (以 NodePort 为例) │
└─────────────────────────────────────────────────────────────────────────┘
客户端 (1.2.3.4)
│
│ 1. DNS 解析获取 Node IP
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 2. TCP 连接到 NodeIP:NodePort (192.168.1.10:30080) │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 3. 数据包到达 Node 网卡 eth0 │
│ src: 1.2.3.4:54321 dst: 192.168.1.10:30080 │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 4. PREROUTING 链 (nat 表) │
│ → KUBE-SERVICES 链 │
│ → KUBE-NODEPORTS 链 (匹配 NodePort) │
│ → KUBE-SVC-XXXX 链 (Service 链) │
│ → KUBE-SEP-YYYY 链 (随机选择 Endpoint) │
│ → DNAT: dst 改为 10.244.2.8:8080 (Pod IP) │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 5. 路由决策 │
│ 目标 10.244.2.8 属于 Pod CIDR │
│ 查路由表: 10.244.2.0/24 via flannel.1 或 BGP 路由 │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 6. POSTROUTING 链 (nat 表) │
│ KUBE-POSTROUTING 链 │
│ → MASQUERADE (SNAT): src 改为 Node IP │
│ (如果 externalTrafficPolicy=Local 则不做 SNAT) │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 7. 封装 (如果跨节点且使用 Overlay) │
│ VXLAN: 外层 UDP 头 + VXLAN 头 + 原始数据包 │
│ IPIP: 外层 IP 头 + 原始数据包 │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 8. 物理网络传输到目标 Node │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 9. 目标 Node 解封装,剥离 VXLAN/IPIP 头 │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 10. 通过 cni0 bridge 或直接路由到 Pod │
│ → veth pair → Pod Network Namespace │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 11. Pod 内 eth0 接收数据包,应用程序处理请求 │
└─────────────────────────────────────────────────────────────────────────┘
问题 2: externalTrafficPolicy: Local 和 Cluster 的区别?
答案:
┌─────────────────────────────────────────────────────────────────────────┐
│ externalTrafficPolicy: Cluster (默认) │
└─────────────────────────────────────────────────────────────────────────┘
Client (1.2.3.4)
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ Node 1 (无 Pod) │
│ │
│ 1. 接收请求 → 2. DNAT 到任意 Pod → 3. SNAT (改源 IP) │
│ │
│ 数据包: src=NodeIP dst=PodIP (源 IP 被改变!) │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ Node 2 (有 Pod) │
│ │
│ Pod 看到的源 IP: Node1 的 IP (不是真实客户端 IP) │
└─────────────────────────────────────────────────────────────────────────┘
优点: 负载均衡到所有 Pod,流量分布均匀
缺点:
- 丢失客户端真实 IP
- 可能产生跨节点流量,增加延迟
┌─────────────────────────────────────────────────────────────────────────┐
│ externalTrafficPolicy: Local │
└─────────────────────────────────────────────────────────────────────────┘
Client (1.2.3.4)
│
┌─────────────┴─────────────┐
▼ ▼
┌───────────────────────┐ ┌───────────────────────┐
│ Node 1 (无 Pod) │ │ Node 2 (有 Pod) │
│ │ │ │
│ 健康检查失败 │ │ 接收请求 │
│ LB 不会转发到此节点 │ │ 直接转发到本地 Pod │
│ │ │ 保留客户端真实 IP │
│ │ │ │
│ │ │ Pod 看到: │
│ │ │ src=1.2.3.4 (真实) │
└───────────────────────┘ └───────────────────────┘
优点:
- 保留客户端真实 IP
- 无跨节点流量,低延迟
缺点:
- 负载可能不均衡
- 需要 LB 支持健康检查
问题 3: 为什么 iptables 模式在大规模集群下性能会下降?IPVS 如何解决?
答案:
┌─────────────────────────────────────────────────────────────────────────┐
│ iptables 性能问题分析 │
└─────────────────────────────────────────────────────────────────────────┘
1. 规则数量爆炸:
假设: 1000 个 Service, 每个 Service 10 个 Pod
KUBE-SERVICES 链: ~1000 条规则
KUBE-SVC-* 链: ~1000 个链, 每个 ~10 条规则
KUBE-SEP-* 链: ~10000 个链
总规则数: 约 20000+ 条
2. 线性匹配 O(n):
每个数据包都要从 KUBE-SERVICES 链开始逐条匹配
最坏情况: 匹配到最后一个 Service 需要遍历 1000 条规则
平均情况: 遍历 500 条规则
每秒 10 万个数据包 × 500 次匹配 = 5000 万次规则匹配
3. 规则更新开销:
iptables-restore 是原子操作,需要:
1. 导出所有规则
2. 修改规则
3. 重新加载所有规则
20000 条规则的更新可能需要数秒,期间可能丢包
┌─────────────────────────────────────────────────────────────────────────┐
│ IPVS 解决方案 │
└─────────────────────────────────────────────────────────────────────────┘
1. 哈希查找 O(1):
数据包 dst=10.96.0.10:80
│
▼
Hash(10.96.0.10) ──► 直接定位到 Virtual Server
无论有多少 Service,查找时间恒定
2. 增量更新:
IPVS 支持单条规则的增删改
添加新 Service: ipvsadm -A -t 10.96.0.10:80 -s rr
添加后端: ipvsadm -a -t 10.96.0.10:80 -r 10.244.1.5:80 -m
不影响其他规则,无需全量更新
3. 内核级负载均衡:
支持多种调度算法:
- rr: 轮询
- lc: 最少连接
- dh: 目标地址哈希
- sh: 源地址哈希 (会话保持)
- sed: 最短期望延迟
- nq: 永不排队
性能对比 (1000 Service 场景):
| 指标 | iptables | IPVS |
|---|---|---|
| 规则查找 | O(n) | O(1) |
| 更新延迟 | 秒级 | 毫秒级 |
| CPU 占用 | 高 | 低 |
| 吞吐量 | 下降明显 | 稳定 |
问题 4: 解释 Hairpin NAT (发夹 NAT) 问题及解决方案
答案:
场景: Pod A 通过 Service ClusterIP 访问自己
┌─────────────────────────────────────────────────────────────────────────┐
│ Hairpin NAT 问题 │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ Node │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Pod A (10.244.1.5) │ │
│ │ │ │
│ │ 1. 发送请求到 Service: 10.96.0.10:80 │ │
│ │ src=10.244.1.5 dst=10.96.0.10 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ iptables DNAT │ │
│ │ │ │
│ │ 2. DNAT: dst 改为 10.244.1.5:80 (恰好选中自己) │ │
│ │ src=10.244.1.5 dst=10.244.1.5 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 问题发生! │ │
│ │ │ │
│ │ 3. 数据包返回时: src=10.244.1.5 dst=10.244.1.5 │ │
│ │ │ │
│ │ 4. Pod A 收到响应,但源 IP 是自己而不是 Service IP │ │
│ │ TCP 连接无法建立 (源 IP 不匹配) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ 解决方案: Hairpin SNAT │
└─────────────────────────────────────────────────────────────────────────┘
kube-proxy 添加的规则:
-A KUBE-SEP-XXXX -s 10.244.1.5/32 -j KUBE-MARK-MASQ
-A KUBE-POSTROUTING -m mark --mark 0x4000/0x4000 -j MASQUERADE
流程:
1. Pod A 发送: src=10.244.1.5 dst=10.96.0.10
2. DNAT: src=10.244.1.5 dst=10.244.1.5 (打上 MARK 标记)
3. MASQUERADE (SNAT): src=cni0-IP dst=10.244.1.5
4. Pod A 收到请求: src=cni0-IP dst=10.244.1.5
5. Pod A 响应: src=10.244.1.5 dst=cni0-IP
6. conntrack 反向 NAT: src=10.96.0.10 dst=10.244.1.5
7. Pod A 收到响应,源 IP 是 Service IP,连接正常!