kube-proxy 服务代理通信流程

目标

这篇文档只回答一个核心问题:kube-proxy 如何实现 Service 的负载均衡和网络代理?

重点放在 kube-proxy 与 API Server 的交互、规则同步机制以及 iptables/IPVS 模式的差异。

一句话摘要

kube-proxy 通过 watch API Server 中的 Service 和 EndpointSlice 对象,在本地维护 iptables/IPVS 规则,将到达 Service ClusterIP 的流量转发到后端 Pod。


1. 流程总览

从通信视角看,这条链路可以拆成 3 个阶段:

  1. 配置获取阶段:kube-proxy watch Service 和 EndpointSlice 对象。
  2. 规则计算阶段:根据 Service 类型计算需要生成的代理规则。
  3. 规则同步阶段:将规则写入 iptables/IPVS,实现流量转发。

架构总览图

flowchart TB
    subgraph 控制面["控制面"]
        API[kube-apiserver]
        SVC[Service 对象]
        EPS[EndpointSlice 对象]
    end

    subgraph KubeProxy["kube-proxy"]
        direction TB
        INFORMER[Informer<br/>watch Service/EndpointSlice]
        HANDLER[EventHandler<br/>处理变更]
        PROXIER[Proxier<br/>规则计算]
        SYNC[SyncRunner<br/>规则同步]
    end

    subgraph 规则引擎["规则引擎"]
        direction LR
        IPT[iptables 模式]
        IPVS[ipvs 模式]
        NFPROXY[nftables 模式<br/>Alpha]
    end

    subgraph 数据面["数据面"]
        direction TB
        CLIP[ClusterIP<br/>服务虚拟IP]
        NODEIP[NodePort<br/>节点端口]
        EXTIP[ExternalIP<br/>外部IP]
    end

    API --> INFORMER
    SVC --> API
    EPS --> API

    INFORMER --> HANDLER
    HANDLER --> PROXIER
    PROXIER --> SYNC
    SYNC --> IPT
    SYNC --> IPVS
    SYNC --> NFPROXY

    CLIP --> IPT
    NODEIP --> IPT
    EXTIP --> IPT

kube-proxy 内部架构图

flowchart TB
    subgraph 输入层["配置输入"]
        direction LR
        S1[Service Informer]
        S2[EndpointSlice Informer]
        S3[Node Informer]
    end

    subgraph 处理层["事件处理"]
        direction TB
        QUEUE[Event Queue<br/>变更队列]
        HANDLE[HandleServiceUpdate<br/>HandleEndpointSliceUpdate]
    end

    subgraph 计算层["规则计算"]
        direction TB
        BUILD[buildServiceMap<br/>buildEndpointMap]
        DIFF[computeDiff<br/>计算差异]
        PLAN[planning<br/>规则规划]
    end

    subgraph 执行层["规则执行"]
        direction TB
        SYNC[SyncRules<br/>同步规则]
        IPT[iptables interface]
        IPVS[ipvs interface]
        NFT[nftables interface]
    end

    S1 --> QUEUE
    S2 --> QUEUE
    S3 --> QUEUE
    QUEUE --> HANDLE
    HANDLE --> BUILD
    BUILD --> DIFF
    DIFF --> PLAN
    PLAN --> SYNC
    SYNC --> IPT
    SYNC --> IPVS
    SYNC --> NFT

这张图想表达什么

  • kube-proxy 是纯观察者,不主动创建 Service。
  • 规则同步是定期执行的,不是每次变更都立即同步。
  • 支持多种后端模式,iptables 是默认模式。

2. 分阶段通信流程


阶段一:配置获取

这个阶段的本质是:kube-proxy 通过 Informer 机制获取 Service 和 EndpointSlice 的变更。

步骤 1.1:启动 Informer

kube-proxy 启动时,会创建三个 Informer:

  • Service Informer:watch Service 对象
  • EndpointSlice Informer:watch EndpointSlice 对象
  • Node Informer:watch Node 对象(用于检测本节点网络变化)

通信方向:kube-proxy → API Server(通过 informer/watch)

步骤 1.2:收到 Service 变更事件

当 Service 被创建、更新或删除时,Informer 会收到事件,并触发 EventHandler。

通信方向:API Server → kube-proxy(watch 推送)

步骤 1.3:收到 EndpointSlice 变更事件

当后端 Pod 发生变化时,EndpointSlice Controller 会更新 EndpointSlice,kube-proxy 的 Informer 会收到事件。

通信方向:EndpointSlice Controller → API Server → kube-proxy(watch 推送)

步骤 1.4:事件入队

EventHandler 不会立即处理变更,而是将变更信息放入队列,等待 SyncRunner 定期处理。

通信方向:EventHandler → Queue(内部队列)


阶段二:规则计算

这个阶段的本质是:根据 Service 配置计算需要生成的 iptables/IPVS 规则。

步骤 2.1:构建服务映射

Proxier 会构建两个核心数据结构:

  • serviceMap:Service UID → ServiceInfo(ClusterIP、Port、Protocol 等)
  • endpointsMap:Service UID → []Endpoint(Pod IP、Port、Ready 状态等)

通信方向:Proxier 内部计算

步骤 2.2:计算规则差异

Proxier 会对比当前规则和期望规则,计算需要:

  • 添加哪些规则
  • 修改哪些规则
  • 删除哪些规则

通信方向:Proxier 内部 diff 计算

步骤 3.3:生成规则指令

对于每个 Service,Proxier 会生成对应的规则指令,包括:

  • ClusterIP 规则(KUBE-SERVICES 链)
  • NodePort 规则(KUBE-NODEPORTS 链)
  • 外部 IP 规则
  • 负载均衡规则(KUBE-SVC-xxx 链)
  • 会话亲和规则(KUBE-SEP-xxx 链)

通信方向:Proxier 内部规划


阶段三:规则同步

这个阶段的本质是:将计算好的规则写入内核网络栈。

步骤 3.1:执行 Sync 规则

SyncRunner 定期(默认 1 分钟)或在收到变更时触发 Sync() 方法。

通信方向:SyncRunner → Proxier.Sync()

步骤 3.2:调用 iptables/ipvs 命令

Proxier 调用 iptables 或 ipvs 命令,将规则写入内核:

  • iptables 模式:调用 iptables-restore 批量更新
  • ipvs 模式:调用 ipvsadm 配置虚拟服务器

通信方向:kube-proxy → Linux 内核(通过系统调用)

步骤 3.3:清理过期规则

同步完成后,Proxier 会清理不再需要的规则,避免规则堆积。

通信方向:kube-proxy → Linux 内核


3. iptables 模式详解

iptables 规则链结构

flowchart TB
    subgraph nat表["iptables nat 表"]
        PREROUTING[PREROUTING 链]
        OUTPUT[OUTPUT 链]
        POSTROUTING[POSTROUTING 链]

        KS[KUBE-SERVICES 链<br/>所有 Service 入口]
        KN[KUBE-NODEPORTS 链<br/>NodePort 规则]
        KEXT[KUBE-EXTERNAL-SERVICES 链<br/>ExternalIP 规则]

        KSVC[KUBE-SVC-xxx 链<br/>Service 负载均衡]
        KSEP[KUBE-SEP-xxx 链<br/>后端端点]
        KMARK[KUBE-MARK-MASQ 链<br/>标记需要 SNAT]
    end

    PREROUTING --> KS
    OUTPUT --> KS
    KS --> KN
    KS --> KEXT
    KS --> KSVC

    KSVC --> KSEP
    KSVC --> KMARK
    KSEP --> KMARK

    POSTROUTING --> KMARK

ClusterIP 流量路径

sequenceDiagram
    autonumber
    participant Client as 客户端 Pod
    participant IPT as iptables/nat
    participant EP as 后端 Pod
    participant RESP as 响应路径

    Client->>IPT: 发送到 ClusterIP:Port
    Note over IPT: PREROUTING → KUBE-SERVICES

    IPT->>IPT: 匹配 KUBE-SVC-xxx 链
    Note over IPT: 概率选择后端

    IPT->>IPT: 进入 KUBE-SEP-xxx 链
    Note over IPT: DNAT: ClusterIP → PodIP

    IPT->>EP: 转发到 PodIP:Port
    EP->>RESP: 处理请求

    RESP->>Client: 响应 (源 IP: PodIP)
    Note over RESP: conntrack 反向 NAT

NodePort 流量路径

sequenceDiagram
    autonumber
    participant Ext as 外部客户端
    participant NODE as 节点网络栈
    participant IPT as iptables/nat
    participant EP as 后端 Pod

    Ext->>NODE: 发送到 NodeIP:NodePort
    Note over NODE: 监听在所有接口

    NODE->>IPT: 进入 PREROUTING
    IPT->>IPT: KUBE-SERVICES → KUBE-NODEPORTS

    IPT->>IPT: 匹配 NodePort 规则
    Note over IPT: DNAT: NodePort → ClusterIP

    IPT->>IPT: KUBE-SVC-xxx 负载均衡
    IPT->>IPT: KUBE-SEP-xxx 选择后端

    IPT->>EP: 转发到 PodIP:Port

    EP-->>Ext: 响应
    Note over Ext,EP: SNAT: 源 IP 可能被改写

4. IPVS 模式详解

IPVS vs iptables 对比

特性 iptables 模式 IPVS 模式
规则更新 全量更新 (iptables-restore) 增量更新 (netlink)
负载均衡算法 随机 (概率规则) 多种算法 (rr/lc/dh/sh/sed/nq等)
性能 O(n) 规则匹配 O(1) 哈希查找
规则数量 随 Service 数量线性增长 与 Service 数量无关
健康检查 无内置支持 支持后端健康检查
内核依赖 所有 Linux 内核 需要加载 ip_vs 模块

IPVS 规则结构

flowchart TB
    subgraph IPVS["IPVS 虚拟服务"]
        VS1[Virtual Service<br/>ClusterIP:Port]
        RS1[Real Server 1<br/>PodIP1:Port]
        RS2[Real Server 2<br/>PodIP2:Port]
        RS3[Real Server 3<br/>PodIP3:Port]
    end

    subgraph iptables["iptables (辅助)"]
        MASQ[KUBE-MARK-MASQ<br/>SNAT 标记]
        CLUSTER[KUBE-CLUSTER-IP<br/>ClusterIP 跳转]
    end

    VS1 --> RS1
    VS1 --> RS2
    VS1 --> RS3

    CLUSTER --> VS1
    RS1 --> MASQ
    RS2 --> MASQ
    RS3 --> MASQ

IPVS 负载均衡算法

算法 代码 说明
轮询 rr Round Robin,依次分发
加权轮询 wrr Weighted Round Robin,按权重分发
最少连接 lc Least Connections,选连接数最少的
加权最少连接 wlc Weighted Least Connections
源地址哈希 sh Source Hashing,相同源 IP 到同一后端
目标地址哈希 dh Destination Hashing
短期期望延迟 sed Shortest Expected Delay
最少队列 nq Never Queue

5. 详细时序图

5.1 Service 创建完整时序图

sequenceDiagram
    autonumber
    participant User as 用户
    participant API as kube-apiserver
    participant EP as EndpointSlice Controller
    participant KP as kube-proxy
    participant IPT as iptables/ipvs
    participant KERNEL as Linux 内核

    User->>API: kubectl apply -f service.yaml
    API->>API: 创建 Service 对象

    Note over API,KP: Informer watch 触发

    API-->>KP: watch Service ADDED
    KP->>KP: ServiceHandler.OnServiceAdd()
    KP->>KP: 入队 serviceUpdate

    Note over API,EP: EndpointSlice 创建

    EP->>EP: watch Service 创建
    EP->>EP: 匹配 Pod labels
    EP->>API: 创建 EndpointSlice

    API-->>KP: watch EndpointSlice ADDED
    KP->>KP: EndpointSliceHandler.OnAdd()
    KP->>KP: 入队 endpointUpdate

    Note over KP: SyncRunner 触发

    KP->>KP: Sync()
    KP->>KP: buildServiceMap()
    KP->>KP: buildEndpointsMap()
    KP->>KP: computeDifferences()
    KP->>KP: generateRules()

    KP->>IPT: iptables-restore / ipvsadm
    IPT->>KERNEL: 写入网络规则
    KERNEL-->>IPT: 确认
    IPT-->>KP: 成功

    Note over KP: Service 现在可达

5.2 后端 Pod 变更时序图

sequenceDiagram
    autonumber
    participant KL as Kubelet
    participant API as kube-apiserver
    participant EP as EndpointSlice Controller
    participant KP as kube-proxy
    participant IPT as iptables/ipvs

    Note over KL: Pod Ready 状态变化

    KL->>API: 更新 Pod status
    API-->>EP: watch Pod UPDATE

    EP->>EP: 检查 Pod 是否匹配 Service
    EP->>EP: 更新 EndpointSlice

    alt Pod 变为 Ready
        EP->>API: 添加 Endpoint 到 EndpointSlice
    else Pod 变为 NotReady
        EP->>API: 从 EndpointSlice 移除 Endpoint
    end

    API-->>KP: watch EndpointSlice UPDATE

    KP->>KP: EndpointSliceHandler.OnUpdate()
    KP->>KP: 入队更新

    Note over KP: 等待 SyncRunner

    KP->>KP: Sync()
    KP->>KP: 重新计算 endpointsMap
    KP->>KP: 计算规则差异

    KP->>IPT: 更新 iptables/ipvs 规则
    IPT-->>KP: 成功

    Note over KP: 流量现在路由到新后端

5.3 规则同步失败重试时序图

sequenceDiagram
    autonumber
    participant KP as kube-proxy
    participant IPT as iptables/ipvs
    participant KERNEL as Linux 内核

    KP->>KP: SyncRunner 触发
    KP->>KP: generateRules()

    KP->>IPT: iptables-restore < rules
    IPT->>KERNEL: 写入规则

    alt 规则写入失败
        KERNEL-->>IPT: Error (如资源不足)
        IPT-->>KP: 失败

        KP->>KP: 记录错误
        KP->>KP: 保留旧规则

        Note over KP: 等待下次 Sync

        KP->>KP: SyncRunner 再次触发
        KP->>IPT: 重试 iptables-restore
        IPT->>KERNEL: 写入规则
        KERNEL-->>IPT: 成功
        IPT-->>KP: 成功
    else 规则写入成功
        KERNEL-->>IPT: 成功
        IPT-->>KP: 成功
    end

6. 故障排查指南

6.1 常见问题与诊断方法

问题 1:Service ClusterIP 无法访问

症状:通过 Service ClusterIP 访问服务超时或拒绝连接

排查流程图

flowchart TD
    A[ClusterIP 不可达] --> B{检查 Service 是否存在}

    B -->|不存在| B1[创建 Service]
    B -->|存在| C{检查 EndpointSlice}

    C -->|无后端| C1[检查 Pod labels]
    C1 --> C2[检查 Pod Ready 状态]
    C2 --> C3[检查 selector 匹配]

    C -->|有后端| D{检查 iptables 规则}

    D -->|无规则| D1[检查 kube-proxy 日志]
    D1 --> D2[重启 kube-proxy]

    D -->|有规则| E{检查 conntrack}
    E --> E1[conntrack -L]
    E --> E2[检查连接状态]

    A --> F{检查网络策略}
    F --> F1[检查 NetworkPolicy]
    F --> F2[检查 CNI 配置]

诊断命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 检查 Service
kubectl get svc -n <namespace>
kubectl describe svc <service-name> -n <namespace>

# 检查 EndpointSlice
kubectl get endpointslices -n <namespace> -l kubernetes.io/service-name=<service-name>

# 检查 iptables 规则
sudo iptables -t nat -L KUBE-SERVICES -n
sudo iptables -t nat -L KUBE-SVC-<hash> -n

# 检查 IPVS 规则
sudo ipvsadm -Ln

# 检查 conntrack
sudo conntrack -L | grep <cluster-ip>
sudo conntrack -F # 清理 conntrack (谨慎使用)

# 检查 kube-proxy 日志
kubectl logs -n kube-system <kube-proxy-pod>
journalctl -u kube-proxy

问题 2:NodePort 外部无法访问

症状:从集群外部无法通过 NodeIP:NodePort 访问服务

排查流程图

flowchart TD
    A[NodePort 不可达] --> B{检查防火墙}

    B --> B1[检查节点防火墙规则]
    B1 --> B2[iptables -L INPUT]
    B1 --> B3[firewalld/ufw 状态]

    B --> C{检查 NodePort 范围}
    C --> C1[kubectl get svc]
    C1 --> C2[NodePort 是否在 30000-32767]

    A --> D{检查 kube-proxy}
    D --> D1[检查 KUBE-NODEPORTS 链]
    D --> D2[检查 IPVS 虚拟服务]

    A --> E{检查网络连通性}
    E --> E1[从节点内测试 curl localhost:NodePort]
    E --> E2[从节点外测试 curl NodeIP:NodePort]

    A --> F{检查 local 部流量策略}
    F --> F1[externalTrafficPolicy]
    F1 --> F2[Local 模式只转发到本节点 Pod]

诊断命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 检查 NodePort 范围
kubectl cluster-info dump | grep -i "service-node-port-range"

# 检查 iptables NodePort 规则
sudo iptables -t nat -L KUBE-NODEPORTS -n

# 检查节点防火墙
sudo iptables -L INPUT -n
sudo firewall-cmd --list-all # CentOS/RHEL

# 从节点内测试
curl -v http://localhost:<nodeport>
curl -v http://127.0.0.1:<nodeport>

# 检查 externalTrafficPolicy
kubectl get svc <service-name> -o jsonpath='{.spec.externalTrafficPolicy}'

问题 3:Session Affinity 不生效

症状:相同客户端请求被分发到不同后端

排查流程图

flowchart TD
    A[Session Affinity 失效] --> B{检查 Service 配置}

    B --> B1[检查 sessionAffinity]
    B --> B2[检查 sessionAffinityConfig.timeoutSeconds]

    A --> C{检查负载均衡模式}
    C --> C1[iptables 模式: 检查概率规则]
    C --> C2[IPVS 模式: 检查调度算法]

    A --> D{检查 conntrack}
    D --> D1[conntrack 状态]
    D --> D2[连接超时时间]

    A --> E{检查客户端 IP}
    E --> E1[是否经过 SNAT]
    E --> E2[externalTrafficPolicy 设置]

诊断命令

1
2
3
4
5
6
7
8
9
10
11
# 检查 Session Affinity 配置
kubectl get svc <service-name> -o yaml | grep -A 5 sessionAffinity

# 检查 iptables 规则中的 affinity
sudo iptables -t nat -L KUBE-SEP-<hash> -n -v | grep recent

# 检查 conntrack
sudo conntrack -L -p tcp --dport <service-port>

# 检查 IPVS 持久化
sudo ipvsadm -Ln --persistent-conn

6.2 关键日志关键词

场景 日志关键词 含义
规则同步 Sync rules 开始同步规则
规则更新 Setting iptables 更新 iptables
Service 变更 Adding new service 新增 Service
Endpoint 变更 Updating endpoints 更新后端
同步失败 Failed to execute 规则执行失败
IPVS 模式 Using ipvs Proxier IPVS 模式启动

6.3 配置参数参考

参数 默认值 说明
--proxy-mode iptables 代理模式 (iptables/ipvs/userspace)
--cluster-cidr Pod 网段,用于 masquerade
--ipvs-sync-period 1m IPVS 规则同步周期
--iptables-sync-period 1m iptables 规则同步周期
--masquerade-all false 是否对所有流量 SNAT
--nodeport-addresses 所有 NodePort 监听地址

6.4 一键诊断命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# kube-proxy 问题诊断脚本
SVC_NAME=${1:-"<service-name>"}
NAMESPACE=${2:-"default"}

echo "=== Service Info ===" && \
kubectl get svc $SVC_NAME -n $NAMESPACE -o wide && \
echo -e "\n=== Endpoints ===" && \
kubectl get endpoints $SVC_NAME -n $NAMESPACE && \
echo -e "\n=== EndpointSlice ===" && \
kubectl get endpointslices -n $NAMESPACE -l kubernetes.io/service-name=$SVC_NAME && \
echo -e "\n=== Service iptables Rules ===" && \
echo "Run on node: sudo iptables -t nat -L KUBE-SERVICES -n | grep $SVC_NAME" && \
echo -e "\n=== IPVS Services ===" && \
echo "Run on node: sudo ipvsadm -Ln | grep -A 5 <cluster-ip>"

7. 版本差异说明

1.27 → 1.28 变化

变化点 说明
nftables 模式 (Alpha) 新增 nftables 后端,未来将替代 iptables
Dual-stack Service 改进 IPv4/IPv6 双栈支持更完善

1.28 → 1.29 变化

变化点 说明
Service 内部流量策略 internalTrafficPolicy: Local 支持
nftables 模式 (Beta) nftables 模式更稳定

8. 代码入口(精简版)

如果读者想从流程跳回实现,可从下面几个入口开始:


9. 面试题与详细解答

问题 1:kube-proxy 的三种模式(userspace/iptables/IPVS)有什么区别?生产环境如何选择?

回答要点

三种模式对比

特性 userspace iptables IPVS
性能 最差(用户态转发) 中等(内核态 O(n)) 最好(内核态 O(1))
规则更新 立即生效 全量更新 增量更新
负载均衡 随机 概率规则 多种算法
可靠性 需加载 ip_vs 模块

选择建议

  1. 生产环境推荐 IPVS

    • 大规模集群(Service > 1000)必须使用
    • 需要高级负载均衡算法(如最少连接)
    • 规则更新频繁场景
  2. 中小规模可用 iptables

    • 默认模式,无需额外配置
    • 所有 Linux 内核都支持
    • 规则数量可控时性能足够
  3. 避免使用 userspace

    • 仅用于兼容旧系统
    • 性能瓶颈明显

代码位置

  • iptables Proxier:pkg/proxy/iptables/proxier.go
  • IPVS Proxier:pkg/proxy/ipvs/proxier.go

问题 2:kube-proxy 如何保证 Service 后端 Pod 变更后规则及时更新?更新延迟是多少?

回答要点

更新机制

  1. Informer Watch 机制

    • kube-proxy watch API Server 的 EndpointSlice 对象
    • 变更事件实时推送到 kube-proxy
  2. 事件处理流程

    1
    EndpointSlice 变更 → Informer 收到事件 → EventHandler 入队 → SyncRunner 定期处理 → 规则计算 → 规则同步
  3. 同步周期(默认配置):

    • --iptables-sync-period:1 分钟(定期全量同步)
    • --ipvs-sync-period:1 分钟
    • 但变更事件会立即触发增量更新

延迟分析

阶段 延迟
EndpointSlice 更新 1-2 秒(kubelet 上报 Pod Ready)
Informer Watch 延迟 < 1 秒
队列等待 0 - sync-period
规则写入 < 1 秒
总延迟 通常 < 5 秒

代码位置

  • EndpointSlice 缓存:pkg/proxy/endpointslicecache.go
  • SyncRunner:pkg/proxy/sync_runner.go

问题 3:iptables 模式下的 ClusterIP 流量转发完整流程是什么?为什么需要 SNAT?

回答要点

流量转发流程

1
2
3
4
5
6
7
8
1. Pod 发送请求到 ClusterIP:Port
2. 进入节点网络栈 PREROUTING 链
3. 跳转到 KUBE-SERVICES 链
4. 匹配 KUBE-SVC-xxx 链(根据 ClusterIP:Port)
5. 概率选择进入 KUBE-SEP-xxx 链(负载均衡)
6. 执行 DNAT:ClusterIP → PodIP
7. 转发到后端 Pod
8. 响应包通过 conntrack 反向 NAT 返回

iptables 链关系

1
2
3
4
5
PREROUTING → KUBE-SERVICES → KUBE-SVC-xxx → KUBE-SEP-xxx

DNAT

后端 Pod

SNAT 的必要性

  1. 场景:Pod 访问自身 Service(hairpin 模式)
  2. 问题:如果不 SNAT,响应包直接返回给客户端 Pod,源 IP 是后端 Pod IP,客户端无法识别
  3. 解决
    • KUBE-MARK-MASQ 链标记需要 SNAT 的流量
    • POSTROUTING 链执行 MASQUERADE

关键规则示例

1
2
3
4
5
6
7
# KUBE-SVC-xxx 链(概率负载均衡)
-A KUBE-SVC-xxx -m statistic --mode random --probability 0.5 -j KUBE-SEP-xxx1
-A KUBE-SVC-xxx -m statistic --mode random --probability 1.0 -j KUBE-SEP-xxx2

# KUBE-SEP-xxx 链(DNAT)
-A KUBE-SEP-xxx -s 10.244.1.2/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-xxx -p tcp -m tcp -j DNAT --to-destination 10.244.1.2:8080

问题 4:Service 的 SessionAffinity 是如何实现的?为什么有时候不生效?

回答要点

实现原理

  1. iptables 模式

    • 使用 recent 模块记录源 IP
    • 后续请求优先匹配已建立连接的后端
      1
      2
      3
      # 示例规则
      -A KUBE-SEP-xxx -m recent --set --name KUBE-SEP-xxx
      -A KUBE-SEP-xxx -m recent --rcheck --seconds 10800 --name KUBE-SEP-xxx -j DNAT
  2. IPVS 模式

    • 使用 IPVS 的持久化连接(persistent connection)
    • ipvsadm -p 参数设置持久化时间
    • 支持 source hashing 调度算法

不生效的常见原因

  1. 客户端 IP 变化

    • 经过 SNAT 后,源 IP 被改写
    • 解决:设置 externalTrafficPolicy: Local
  2. 超时时间过期

    • 默认 3 小时,可在 sessionAffinityConfig 调整
      1
      2
      3
      4
      sessionAffinity: ClientIP
      sessionAffinityConfig:
      clientIP:
      timeoutSeconds: 3600
  3. 负载均衡算法

    • IPVS 模式需要使用 sh(source hashing)算法
    • 配置:--ipvs-scheduler=sh
  4. 后端 Pod 数量变化

    • Pod 增减导致哈希重新计算
    • 解决:使用一致性哈希或减少 Pod 变化频率

调试命令

1
2
3
4
5
6
7
8
# 检查 SessionAffinity 配置
kubectl get svc -o yaml | grep -A 5 sessionAffinity

# 检查 iptables recent 规则
sudo iptables -t nat -L KUBE-SEP-xxx -n -v | grep recent

# 检查 IPVS 持久化
sudo ipvsadm -Ln --persistent-conn

问题 5:NodePort 服务的外部流量策略(externalTrafficPolicy)有什么作用?Local 和 Cluster 模式的区别?

回答要点

两种模式对比

特性 Cluster(默认) Local
流量转发 可转发到任意节点 Pod 只转发到本节点 Pod
源 IP 保留 否(SNAT) 是(保留真实客户端 IP)
负载均衡 集群级别均匀 取决于外部负载均衡
可用性 高(所有节点都可用) 低(只有 Pod 所在节点可用)

Cluster 模式流程

1
2
外部请求 → NodeIP:NodePort → KUBE-NODEPORTS → KUBE-SVC-xxx
→ 任意节点 Pod(可能 SNAT 两次)

Local 模式流程

1
外部请求 → NodeIP:NodePort → KUBE-XLB-xxx → 仅本节点 Pod(保留源 IP)

使用场景

  1. Cluster 模式

    • 对源 IP 无要求
    • 需要高可用性
    • 后端 Pod 分布不均匀
  2. Local 模式

    • 需要保留源 IP(如安全策略、日志分析)
    • 配合外部负载均衡使用
    • 后端 Pod 在每个节点都有部署

配置示例

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: NodePort
externalTrafficPolicy: Local # 或 Cluster
ports:
- port: 80
targetPort: 8080
nodePort: 30080

注意事项

  • Local 模式下,如果本节点没有 Pod,请求会被拒绝
  • 建议配合 DaemonSet 使用 Local 模式

问题 6:kube-proxy 如何处理 Service 的 headless 类型?DNS 解析与普通 Service 有什么区别?

回答要点

Headless Service 特点

  1. 定义spec.clusterIP: None
  2. kube-proxy 行为:不创建任何 iptables/IPVS 规则
  3. 负载均衡:完全依赖 DNS 轮询或客户端实现

DNS 解析区别

类型 DNS 返回 示例
ClusterIP Service 单个 ClusterIP 10.96.0.1
Headless Service 所有 Ready Pod IP 10.244.1.2, 10.244.1.3, ...

Headless Service DNS 查询结果

1
2
3
4
5
6
7
8
9
10
11
# 普通 Service
$ dig my-service.default.svc.cluster.local
;; ANSWER SECTION:
my-service.default.svc.cluster.local. 30 IN A 10.96.0.1

# Headless Service
$ dig my-service.default.svc.cluster.local
;; ANSWER SECTION:
my-service.default.svc.cluster.local. 30 IN A 10.244.1.2
my-service.default.svc.cluster.local. 30 IN A 10.244.1.3
my-service.default.svc.cluster.local. 30 IN A 10.244.1.4

使用场景

  1. StatefulSet:每个 Pod 有稳定网络标识
  2. 自定义负载均衡:客户端自行选择后端
  3. 服务发现:获取所有后端 IP 列表
  4. 数据库集群:需要区分主从节点

实现原理

  1. EndpointSlice Controller

    • 仍然创建 EndpointSlice 对象
    • 包含所有 Ready Pod 的 IP
  2. CoreDNS

    • 读取 EndpointSlice
    • 为每个 IP 返回 A 记录
    • 支持 SRV 记录(带端口)

代码位置

  • EndpointSlice Controller:pkg/controller/endpointslice/controller.go

问题 7:如何排查 Service 无法访问的问题?请给出完整的排查思路和命令。

回答要点

排查流程图

1
2
3
4
5
6
7
8
9
10
11
Service 无法访问

检查 Service 是否存在

检查 EndpointSlice 是否有后端

检查 iptables/IPVS 规则

检查网络连通性

检查 conntrack 状态

详细排查步骤

步骤 1:检查 Service 配置

1
2
3
4
5
6
# 查看 Service
kubectl get svc -n <namespace>
kubectl describe svc <service-name> -n <namespace>

# 检查 ClusterIP 和 Port
kubectl get svc <service-name> -o jsonpath='{.spec.clusterIP}:{.spec.ports[0].port}'

步骤 2:检查 EndpointSlice

1
2
3
4
5
# 查看后端 Pod
kubectl get endpointslices -n <namespace> -l kubernetes.io/service-name=<service-name>

# 检查后端是否 Ready
kubectl get pods -n <namespace> -l <selector> -o wide

步骤 3:检查 iptables 规则(在节点上)

1
2
3
4
5
6
7
8
# 查看 KUBE-SERVICES 链
sudo iptables -t nat -L KUBE-SERVICES -n

# 查看特定 Service 的规则
sudo iptables -t nat -L KUBE-SVC-<hash> -n

# 查看 NodePort 规则
sudo iptables -t nat -L KUBE-NODEPORTS -n

步骤 4:检查 IPVS 规则(在节点上)

1
2
3
4
5
# 查看所有虚拟服务
sudo ipvsadm -Ln

# 查看特定服务的后端
sudo ipvsadm -Ln -t <cluster-ip>:<port>

步骤 5:检查 conntrack 状态

1
2
3
4
5
# 查看连接追踪
sudo conntrack -L | grep <cluster-ip>

# 清理 conntrack(谨慎使用)
sudo conntrack -D -p tcp --dport <port>

步骤 6:网络连通性测试

1
2
3
4
5
6
7
8
# 从 Pod 内测试
kubectl exec -it <pod-name> -- curl -v http://<cluster-ip>:<port>

# 从节点测试
curl -v http://<cluster-ip>:<port>

# DNS 解析测试
kubectl exec -it <pod-name> -- nslookup <service-name>

常见问题及解决

问题 原因 解决方法
无 ClusterIP Service 配置错误 检查 yaml 配置
无后端 Pod selector 不匹配 修正 labels
后端 NotReady Pod 未就绪 检查 Pod 状态
无 iptables 规则 kube-proxy 故障 重启 kube-proxy
conntrack 满了 连接数超限 增大 conntrack 表

一键诊断脚本

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
SVC_NAME=${1}
NAMESPACE=${2:-default}

echo "=== Service Info ===" && \
kubectl get svc $SVC_NAME -n $NAMESPACE -o wide && \
echo -e "\n=== EndpointSlice ===" && \
kubectl get endpointslices -n $NAMESPACE -l kubernetes.io/service-name=$SVC_NAME && \
echo -e "\n=== Backend Pods ===" && \
kubectl get pods -n $NAMESPACE -o wide | grep -E "NAME|$(kubectl get endpointslices -n $NAMESPACE -l kubernetes.io/service-name=$SVC_NAME -o jsonpath='{.items[0].endpoints[*].ip}' | tr ' ' '|')" && \
echo -e "\n=== Service Events ===" && \
kubectl get events -n $NAMESPACE --field-selector involvedObject.name=$SVC_NAME