Kubernetes 网络高难度面试题 (Part 3)

20 阅读4分钟

五、高难度面试题及详细解答

问题 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 场景):

指标iptablesIPVS
规则查找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,连接正常!