分析Kube-Proxy在默认工作模式(iptable mode)下,那些眼花缭乱的自定义规则,并绘制成图,希望后续能为定位集群的网络问题提供帮助。

1. KubeProxy:IPtables Mode

1. IPtables

简单回顾 IPtables 的工作原理:IPtables 在TCP/IP协议栈特定位置(Chain),根据一些设定的规则(Rule)过滤数据包,并对符合条件的数据包执行某些操作(ACTION)。

当数据包匹配某条Rule时,我们可以对数据包执行特定ACTION,以下是常用ACTION:

ACTION 说明 后续操作
ACCEPT 允许数据包通过 next Chain
DROP 丢弃数据包 中断
REJECT 丢弃数据包,发送拒绝信息 中断
SNAT 源地址转换 next Chain
DNAT 目标地址转换 next Chain
REDIRECT 目标端口转换 next rule
MARK 将数据包打上标记 next rule
RETURN parent Chain
MASQUERADE 改写SRC IP为当前IP next Chain
LOG 记录信息到/var/log next rule
MIRROR 调换源IP和目的IP,将包发回 中断
QUEUE 封包放入队列,交给其它程序处理 中断

在 IPtables 的体系中根据 Rule 的作用,将其分成4类(或者认为将其分为4个不同的表):

  • filter:负责过滤功能
  • nat:网络地址转换功能,典型的比如 SNAT、DNAT
  • mangle:解包报文、修改并封包
  • raw:关闭 nat 表上启用的连接追踪机制(conntrack)

在 netfilter 内核中的 Hook 点上,一串顺序执行的规则称为链。netfilter 提供了以下5个默认链:

chain 允许表规则
PreRouting raw, mangle, nat
Forward mangle, filter
Input mangle, filter
Output raw, mangle, nat, filter
PostRouting mangle, nat

数据包进入 IPtables 的流程如下:

更细致的流程图:

2. kube-proxy

在 Kubernetes 中,kube-proxy 是一个 L4(TCP/UDP/SCTP)负载均衡器,它使用 DNAT 将入站流量从Service IP重定向到后端 pod,可以说是除 CNI 之外 Kubernetes 网络体系中最重要的组成部分。

Kubernetes 中支持创建 ClusterIP、NodePort、LoadBalancer、ExternalName 四种类型的 Service ,下面详细叙述一下对于 ClusterIP 和 NodePort 的流量,kube-proxy 如何通过自定义 chain 实现流量转发。

kube-proxy 自定义以下 Chain 处理目的地址为 ClusterIP 或者 local:NodePort 的数据包。

  • KUBE-SERVICES(nat.PREROUTING/nat.OUTPUT):安装在 PREROUTING 和 OUTPUT 链的最开始
    • 将目标地址为 SVCIP:Port 的数据包分派到相应的 KUBE-SVC-xxx 链
    • 将目标地址为本地网卡的数据包分派到 KUBE-NODEPORTS 链
  • KUBE-NODEPORTS:根据 dst-port 匹配NodePort端口
    • 数据包分派到相应的 KUBE-SVC-xxx 链(externalTrafficPolicy=Cluster)
    • 数据包分派到相应的 KUBE-XLB-xxx 链(externalTrafficPolicy=Local)
  • KUBE-SVC-xxx: 对应 service,数据包将随机进入 KUBE-SEP-xxx 链
  • KUBE-XLB-xxx: 对应 service,数据包可能进入 KUBE-SEP-xxx 链或者被丢弃
  • KUBE-SEP-xxx: 对应 endpoint 中的IP地址,数据包将 DNAT 到 Pod IP
  • KUBE-FIREWALL(filter.INPUT/filter.OUTPUT):丢弃 0x8000 的包,主要用在 externalTrafficPolicy=Local的场景
  • KUBE-MARK-MASQ:标记数据包为0x4000(需要SNAT)
  • KUBE-MARK-DROP:标记数据包为0x8000(DROP包)
  • KUBE-POSTROUTING(nat.POSTROUTING):MASQUERADE 0x4000 的包

数据包在iptable中的流向如下:

几个注意点:

  • 物理访问 ClusterIP/NodePort 时,流量从output进入;本地容器中访问 ClusterIP/NodePort 时,流量从 PREROUTING 进入
  • 那些流量会被 SNAT:
    • 访问 ClusterIP:
      • MasqueradeAll=true:所有Client的流量被 SNAT(PS:在POD中访问 ClusterIP,服务端也会丢失真实IP)
      • MasqueradeAll=false:Pod以外的流量被 SNAT (PS:在POD中访问 ClusterIP,服务端可以获取POD的真实IP)
    • 访问 host-ip:NodePort:
      • externalTrafficPolicy=Cluster:必定被SNAT
      • externalTrafficPolicy=Local:
        • 本地HOST-IP:无论当前节点是否有 Pod 后端都进行 SNAT,剩余逻辑按照 externalTrafficPolicy=Cluster 时处理
        • 其他HOST-IP(K8S集群其他Node的ip):不会进行 SNAT
    • 容器通过ClusterIP访问自身(PS:访问的后端是自己,即POD通过 ClusterIP 访问自己,请求也可能丢失真实IP)

参考文档