引言

這篇算是數篇文章自己閱讀後的記錄,主要是關於 kubernetes 中的 br-netfilterbridge-nf-call-iptables 兩個在 node 網路上的設定,會影響到同個 node 的 pod 互通。

從 Kubernetes 在節點的設定開始

節錄裡面的段落

  1. 網路連線轉發 IPv4 位址並讓 iptables 查看橋接器的流量
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
  1. 請 Kubernetes (K8S) 引用載入 br_netfilter, overlay 二個核心模組
sudo modprobe overlay
sudo modprobe br_netfilter
  1. 啟用 br_netfilter, overlay 二個核心模組
# sysctl params required by setup, params persist across reboots
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF
  1. 在不重啟 node 下套用 sysctl 的參數
sudo sysctl --system
  1. 驗證 br_netfilter, overlay modules
lsmod | grep br_netfilter
lsmod | grep overlay
  1. 驗證 net.bridge.bridge-nf-call-iptables, net.bridge.bridge-nf-call-ip6tables, and net.ipv4.ip_forward system variables are set to 1
sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables net.ipv4.ip_forward

cni 中的參數簡述

CNI plugin 會建立自己的 bridge,不會沿用 node 本身的設定

  • br_netfilter 開啟,讓 iptables 的規則可以在二層的 linux bridge 上面運作,使經過的流量會經過 iptables chain
  • net.bridge.bridge-nf-call-iptables 開啟,讓二層的轉發仍會去使用 iptables 的三層規則(包含 conntrack)

br_netfilter

Linux 下 Netfilter 简介 : https://blog.csdn.net/qq_45571595/article/details/102769858

- br_netfiler作用:br_netfilter模块可以使 iptables 规则可以在 Linux Bridges 上面工作,用于将桥接的流量转发至iptables链
- 在基本使用过程中,如果没有加载br_netfilter模块,那么并不会影响不同node上的pod之间的通信,但是会影响同node内的pod之间通过service来通信

https://blog.csdn.net/qq_45571595/article/details/102769858

netfilter 框架是 Linux 中的包過濾框架,它提供了五個 hook 點,每個進入網路系統的包都會觸發這些 hook。

https://blog.csdn.net/qq_45571595/article/details/102769858

Iptables 與 netfilter 的關係: iptables是運行在 user namespace 的應用軟體,通過控制 Linux 核心 netfilter 模組,來管理網路封包的處理和轉發

iptable 的範例設定檔

iptables

  • 基於 netfilter 框架的防火牆工具,它可以用來控制網路流量。
  • 規則語法包括 table(表)、chain(鏈) 和 rules(規則) 三個部分。
  • 每個「表」指的是不同類型的封包處理流程
  • 每個表中又可以存在多個「鏈」,系統按照預訂的規則將封包通過某個內建鏈,例如將從本機發出的資料通過 OUTPUT chain
  • 在「鏈」中可以存在若干「規則」,這些規則會被逐一進行匹配,如果匹配,可以執行相應的動作,如修改封包,或者跳轉。

網路封包通過 Netfilter 時的工作流向

table 種類 - 封包處理流程

  • filter: 判斷是否允許 packet 通過
  • nat: network address translation,將 packet 轉送到無法直接訪問的網路
  • mangle: 修改 packet 的 IP
  • raw: iptables 會記錄狀態,conntrack 會記錄
  • security: SELinux 標記

Table 中會有不同的 chain - 通過指定的 chain

https://www.digitalocean.com/community/tutorials/a-deep-dive-into-iptables-and-netfilter-architecture

  • Incoming packets destined for the local system: PREROUTING -> INPUT
  • Incoming packets destined to another host: PREROUTING -> FORWARD -> POSTROUTING
  • Locally generated packets: OUTPUT -> POSTROUTING

Incoming packetes order:

  1. PREROUTLING chain:
    1. raw
    2. mangle
    3. nat
  2. INPUT chain
    1. mangle
    2. filter
    3. security
    4. nat
  3. nat table delivered to local socker

常用的 iptables 規則包括允許、拒絕、重定向和修改等

bridge-nf-call-iptables

Kubernetes学习(又是bridge-nf-call-iptables惹的祸): 發現同個 node 的 pod 無法互相溝通

服務的訪問路徑:service -> ClusterIP -> PodIP

  • coreDNS 負責將 service 解析成 ClusterIP

同個 node 上面 pod 互相打,不會經過 CNI plugin

clusterIP -> podIP 的可能問題

  • clusterIP 沒有正確的轉發到 podIP
  • 正確轉發後,卻沒有 response

image

如果 /proc/sys/net/bridge/bridge-nf-call-iptables = 0 則會導致 response 無法回來

虽然CNI使用的是flannel, 但flannel封装的也是linux bridge,linux bridge是虚拟的二层转发设备,而 iptables conntrack 是在三层上,所以如果直接访问同一网桥内的地址(ip同一网段),就会直接走二层转发,不经过 conntrack:

结合上面的图来看,同Node通过service访问pod的访问路径如下:

PodA 访问 service, 经过coreDNS解析成Cluster IP,不是网桥内的地址(ClusterIP一般跟PodIP不在一个网段),走Conntrack,进行DNAT,将ClusterIP转换成PodIP:Port
DNAT 后发现是要转发到了同节点上的 PodB,PodB 回包时发现目的 IP(此时是PodA的IP) 在同一网桥上(PodA与PodB的IP段一致),就直接走二层转发了,不会去调 conntrack,这样就导致回包时没有原路返回
没有返回包就导致请求方一直等直到超时退出.

这样也解释了为何访问在其它节点的应用的ClusterIP没有问题,因为目标PodIP与源PodIP不在同一个网段上,肯定要走conntrack.

kubernetes 訪問 service 都會走 DNAT,將 clusterIP:Port DNAT 成 endpoint (podIP:Port),然後登記在三層的 conntrack,這樣回傳時,conntrack 根據 table 反向 NAT

但若是關掉 bridge-nf-call-iptables 則會導致

  • 一開始是走 clusterIP 轉換,所以會走三層轉送,經過 DNAT 為 PodIP:Port
  • 回傳時發現是在同個 bridge,所以會直接走二層 bridge 轉發,沒有經過 conntrack,被視為不同的連接

結論

我自己是覺得,網路排障工具掌握得越多,知識累積得越多,通靈就會更準確🔮

就更有機會找到原因,不然一句「網路不通」其實超難解的啦

最近在公司一直遇到 cilium 的問題,特別是 iptables 中 cilium 的區塊整個不見,導致如上面 bridge-nf-call-iptables 沒開時無法 response,直到 timeout,所以很想研究看看裡面的玩意兒。也同時希望能把四個月前,筆記的 kube-proxy, coredns, nodelocaldns, cni plugin 能有機會轉化成有點條理的文章排序分享給大家 🫣

Reference: