kube-proxy iptables 模式的原理

这是一个理解 Kubernetes Service 网络核心机制的关键知识点。总的来说,kube-proxy iptables 模式的核心原理是:监听 Kubernetes API 中 Service 和 Endpoint 的变化,然后动态地在每个节点的 iptables 中设置相应的规则,将访问 Service VIP(ClusterIP)的流量透明地转发到后端健康的 Pod 上。

下面我们分步骤进行分解:

核心设计思想

Kubernetes Service 是一个抽象层,它定义了一组 Pod 的访问策略。Service 有一个固定的虚拟 IP(ClusterIP)和端口。kube-proxy 的任务就是解决“如何将对这个虚拟IP:端口的请求,实际送达背后某个真实的Pod” 这个问题。在 iptables 模式下,它不扮演任何代理角色,而是通过配置 Linux 内核的 netfilter(通过 iptables 工具)来完成这个任务。


详细工作流程

步骤1:监听与感知

kube-proxy 作为一个 DaemonSet 运行在每个节点上。它启动后,会持续地监听(Watch) Kubernetes API Server,关注两类对象的变化:

  1. Service 对象:当有新的 Service 被创建、更新或删除时,kube-proxy 会得到通知。

  2. Endpoints 或 EndpointSlice 对象:当 Service 对应的后端 Pod 集合发生变化时(例如 Pod 重启、扩容、缩容),kube-proxy 也会得到通知。

步骤2:生成 iptables 规则

当 kube-proxy 感知到上述变化后,它会在本机内存中根据最新的 Service 和 Endpoint 信息,生成一套完整的 iptables 规则。它并不是简单地增删单条规则,而是为了确保一致性,会重新生成整个规则集。

步骤3:将规则写入内核

生成规则后,kube-proxy 会通过调用 iptables-restore 命令,将整套规则一次性、原子性地加载到 Linux 内核的 netfilter 框架中。这保证了规则的更新是快速且一致的,避免了在增量更新过程中出现流量中断。


iptables 规则链的详细解析

这是最核心的部分。我们通过一个具体的例子来看数据包是如何被转发的。

假设我们有一个:

  • Service:my-svc,ClusterIP 是 10.96.1.10,端口是 80

  • 后端有两个 Pod:

    • Pod A: 10.244.1.5:8080

    • Pod B: 10.244.2.10:8080

kube-proxy 会创建几条以 KUBE- 为前缀的自定义链来组织规则,逻辑非常清晰。

1. 入口点:KUBE-SERVICES 链
这是所有 Service 流量的总入口。kube-proxy 会在 nat 表的 PREROUTING 和 OUTPUT 链中插入规则,将目标地址是 ClusterIP 或端口是 NodePort 的数据包 JUMP 到 KUBE-SERVICES 链进行处理。

text
*nat
-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES

2. 服务匹配:KUBE-SVC-XXXXX 链
在 KUBE-SERVICES 链中,会有规则匹配到 my-svc 的 ClusterIP 和端口,然后跳转到一个专为 my-svc 创建的链,命名为 KUBE-SVC-<HASH>(HASH 由 Service 信息计算得出)。

text
-A KUBE-SERVICES -d 10.96.1.10/32 -p tcp -m tcp --dport 80 -m comment --comment "default/my-svc: cluster IP" -j KUBE-SVC-XXXXXXXX

3. 负载均衡:KUBE-SEP-XXXXX 链
KUBE-SVC-XXXXXXXX 链负责负载均衡。默认情况下,kube-proxy 使用随机(random) 算法。它会为每个后端 Pod 创建一个对应的链 KUBE-SEP-YYYYY 和 KUBE-SEP-ZZZZZ,并在 KUBE-SVC-XXX 链中通过概率规则来实现负载均衡。

对于两个后端 Pod,规则大致如下:

text
-A KUBE-SVC-XXXXXXXX -m comment --comment "default/my-svc:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-YYYYY
-A KUBE-SVC-XXXXXXXX -m comment --comment "default/my-svc:" -j KUBE-SEP-ZZZZZ
  • 第一条规则:有 50% 的概率跳转到 Pod A 的链。

  • 第二条规则:剩下的 50% 概率(100% - 50%)跳转到 Pod B 的链。
    如果有更多 Pod,概率会相应调整。

4. 最终目的地址转换(DNAT)
在 Pod 专用的链(如 KUBE-SEP-YYYYY)中,会进行最关键的一步:DNAT。它将数据包的目标 IP 和端口从 Service 的 10.96.1.10:80 修改为实际 Pod 的 10.244.1.5:8080

text
-A KUBE-SEP-YYYYY -p tcp -m tcp -m comment --comment "default/my-svc:" -j DNAT --to-destination 10.244.1.5:8080

完整的数据包流向(以从节点外访问为例)

  1. 一个数据包到达节点,目标地址是 10.96.1.10:80

  2. 进入 nat 表的 PREROUTING 链。

  3. 被跳转到 KUBE-SERVICES 链。

  4. 匹配到 my-svc 的规则,被跳转到 KUBE-SVC-XXXXXXXX 链。

  5. 根据概率,被随机跳转到 KUBE-SEP-YYYYY 链(假设选中 Pod A)。

  6. 在 KUBE-SEP-YYYYY 链中,执行 DNAT,目标地址被修改为 10.244.1.5:8080

  7. 内核根据修改后的目标地址进行路由,发现 10.244.1.5 在另一个 Pod 网络(可能在同一节点或不同节点)。

  8. 如果 Pod 在同一节点,数据包通过 cni0 网桥等直接送达 Pod。

  9. 如果 Pod 在不同节点,数据包会被转发到对应节点(这需要 CNI 网络插件负责,如 Flannel, Calico等)。


其他服务类型的处理

  • NodePort:原理类似。kube-proxy 会在 KUBE-SERVICES 链中加入规则,匹配到目标端口是 NodePort 的数据包,然后后续流程与 ClusterIP 完全一样。

  • LoadBalancer:通常是 Cloud Provider 在 NodePort 之上又提供了一层负载均衡器,到达 NodePort 后的流程与上述一致。

  • ExternalName:通过 CNAME 记录返回外部域名,不涉及 iptables DNAT。

  • Headless Service(无头服务):因为没有 ClusterIP,kube-proxy 不会配置任何 iptables 规则。DNS 查询会直接返回所有 Pod IP,由客户端自行选择。


优缺点

优点:

  • 高性能:由于是在内核层面进行数据包转发,绕过了用户空间的上下文切换,性能远高于最初的 userspace 模式。

  • 简单可靠:利用成熟稳定的 Linux netfilter 机制,系统资源占用少。

缺点:

  • 规则线性增长:iptables 规则是顺序匹配的。当 Service 数量非常多时(例如数千个),规则链会变得非常长,导致匹配效率下降,增加延迟。

  • 可扩展性差:无法提供更高级的负载均衡算法(如最少连接数)。

  • 排障复杂:iptables 规则链复杂,在问题排查时对运维人员要求较高。

正因为这些缺点,在大型集群中,IPVS 模式 正在逐渐取代 iptables 模式。IPVS 专门为负载均衡设计,使用哈希表,性能不受后端数量影响,并支持更丰富的负载均衡算法。

总结

kube-proxy 的 iptables 模式本质上是一个动态的、分布式的 DNAT 规则管理器。它通过监听 Kubernetes API,将抽象的 Service 定义翻译成每个节点上具体的、可执行的 iptables 规则,从而实现了服务发现和负载均衡的功能。这种设计巧妙地将控制平面(kube-proxy)与数据平面(Linux kernel)分离开,既保证了灵活性,又获得了不错的性能。

 

我们来看这两条iptables规则:

  1. -A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES

  2. -A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES

它们分别作用于两个不同的链:PREROUTING和OUTPUT。这两个链属于nat表。

作用链的区别:

  • PREROUTING链:当数据包到达网络接口并进入系统时,在路由决策之前(即决定数据包是发给本机还是转发给其他机器之前)会经过PREROUTING链。因此,所有进入本机的数据包都会经过这个链。

  • OUTPUT链:当数据包由本机进程产生并从本机发出时,在路由决策之后(即已经确定数据包从哪个接口出去)会经过OUTPUT链。因此,本机产生的数据包会经过这个链。

 
posted @ 2025-11-25 13:32  滴滴滴  阅读(14)  评论(0)    收藏  举报