Proxy IPTable Proxier

Iptables

References

Packet Flow

图片来源:维基百科 iptables

Table & Chain

Chain 如上图中虚线方框所示,代表了包处理过程中,实际的规则执行路径。Table 是可简单理解为规则的分类:

  • Raw:Connection Tracking 相关操作

  • Mangle:修改 IP 包

  • Nat:地址转换

  • Filter:过滤

Proxier

Monitoring Routine

创建 Proxier 结构时,启动了一个协程,用于确保上图的 Table、Chain 都是存在的,然后执行同步规则方法。如果检查存在性时失败,则删除全部的 Table 及 Chain。创建 Table、Chain 成功后,才会执行 syncProxyRules 方法。 Proxier 的 SyncLoop 启动时,注册了 syncProxyRules 的 BoundedFrequencyRunner 也同时启动,可以通过 Sync 方法,触发 syncProxyRules 方法执行,需要注意 BoundedFrequencyRunner 通过令牌桶算法,限制其运行方法的执行频率。

Service Change Handling

ServiceMap

Service 到 ServiceMap 过程如上图所示,LoadBalancer 部分没有在图中标注。如果需要定制化操作,可以通过自定义 makeServiceInfo 方法来实现。ServiceMap 是 iptables 模式下的核心结构之一。

Service Change Track

Service Informer 触发后,由 proxy.Provider 来处理。在 iptables 模式下,处理最终落在 ServiceChangeTracker 的 OnServiceAdd/Update/Delete 方法上。三个方法使用简单的技巧,统一为 Update:

  • Create 时调用 OnServiceUpdate(nil, service)

  • Delete 时调用 OnServiceDelete(service, nil)

在 ServiceChangeTracker 的 Update 方法中,将当前 Service 对象与前次的 Service 对象分别对应的 ServiceMap 做比较,决定其在 ServiceMap 中的去留,规则如下

  • 同一 Service 对应的 NamespacedName 对象相同

  • 如果 previous 与 current 保存的 ServiceMap 内容相同,则删除,否则更新

  • Create 时,current 为 Service 对应的 ServiceMap,previous 为 nil

  • Update 时,更新 current,不改变 previous

  • Delete 时,更新 current,不改变 previous

Endpoint Change Handling

Endpoints 处理逻辑与 Service 基本一致,不同指出在于 Endpoints 中包含的 Ports 与 Addresses 可自由组合。同样的,可以通过自定义 makeEndpointInfo 获取 Endpoint 接口对象,这里的 Endpoint 接口,是 Proxy 中使用的,不是 Kubernetes 的资源对象。

处理 Endpoints 变更方法与 Service 并无本质区别,只是将 previous 与 current 指向的对象更根为 EndpointsMap。

Node Change Handling

Node 资源变更处理相对简单,只要变更 Proxier 的 nodeLabels 即可,以 OnNodeAdd 为例

OnNodeUpdate 使用新 Node 对象的 Labels;OnNodeDelete 则将 nodeLabels 设置为 nil。

Sync Proxy Rules

原则上,Service、Endpoints(EndpointSlice) 任何一个的变更都会触发 syncProxyRules 的执行,但是,不要忘记 BoundedFrequencyRunner 限制调用频率的存在,因此,在执行规则同步时,有可能存在 Service、Endpoints(EnpointSlice) 同时变更的可能性。 Service、Endpoints、Node 的变更,全部 kube-proxy 都会收到,后续的处理如果没有特殊说明,每个 Node 都会处理。

ServiceMap

将检测到的 Service 变更情况更新至 ServiceMap 中,已删除的 Service 如果包含 UDP 协议的端口,保留下来。 最终得到了所有 Created、Updated 的 ServiceMap、已删除的 UDP 端口及到目前为止仍然存活的 Service 的健康检查端口。

EndpoinsMap

将 Endpoints 变更应用到 EndpointsMap 上,上图是没有开启 EndpointSlice 特性时的情况。不同于 ServiceMap 处理之处在于保留的是本地健康的 Endpoint IP 数量。

然后,将 staleServiceNames 合并至由 Service 变更引起的 staleServicesClusterIP 中,这样,全部变更的 Cluster IP 获取完毕。

Jumping Chains

创建了如下的自定义链并将默认链处理对应至自定义链

  • Nat

    • KUBE-SERVICES

    • KUBE-POSTROUTING

  • Filter

    • KUBE-SERVICES

    • KUBE-EXTERNAL-SERVICES

    • KUBE-FORWARD

Iptables Save Format

# Generated by iptables-save v1.3.1 on Sun Apr 23 05:32:09 2006
*filter
:INPUT ACCEPT [273:55355]
:FORWARD ACCEPT [0:0]
:LOGNDROP - [0:0]
:OUTPUT ACCEPT [92376:20668252]
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -j LOGNDROP
-A LOGNDROP -p tcp -m limit --limit 5/min -j LOG --log-prefix "Denied TCP: " --log-level 7
-A LOGNDROP -p udp -m limit --limit 5/min -j LOG --log-prefix "Denied UDP: " --log-level 7
-A LOGNDROP -p icmp -m limit --limit 5/min -j LOG --log-prefix "Denied ICMP: " --log-level 7
-A LOGNDROP -j DROP
COMMIT
# Completed on Sun Apr 23 05:32:09 2006

Prepare Chains & Rules

在根据 ServiceMap 处理规则前,先使用 iptables-save 格式确保以下的 Chain 存在

  • Filter

    • KUBE-SERVICES

    • KUBE-EXTERNAL-SERVICES

    • KUBE-FORWARD

  • Nat

    • KUBE-SERVICES

    • KUBE-NODEPORTS

    • KUBE-POSTROUTING

    • KUBE-MARK-MASQ

添加 nat 规则

Handling ServiceMaps

Cluster IP

无 Endpoints 的服务,Node 在接收到请求后,直接拒绝,因此规则添加在 Filter 表;Cluster IP 要根据配置进行 NAT 转化。

External IPs

Load Balancer Ingress

是否只有本节点 Endpoints 判断代码如下所示

Node Ports

处理至此,与服务相关的基本规则均已建立,如果本次循环处理的 Service 没有 Endpoints,那么继续处理下一个 Service;如果有,则继续向下建立 Endpoints 规则。

Endpoint Chains

处理到此处,Service 对应的 Endpoints 如果不是 NodeOnly,则处理下一个 Service。后续处理,仅对 NodeOnly 的服务起作用。

然后,对 Node 上每一个网络接口地址设置如下规则

最后,设置 KUBE-FORWARD 上规则如下

最后更新于