引言
這篇算是數篇文章自己閱讀後的記錄,主要是關於 kubernetes 中的 br-netfilter
與 bridge-nf-call-iptables
兩個在 node 網路上的設定,會影響到同個 node 的 pod 互通。
從 Kubernetes 在節點的設定開始
- 官方的設定:https://kubernetes.io/docs/setup/production-environment/container-runtimes/#forwarding-ipv4-and-letting-iptables-see-bridged-traffic
- Kubernetes (K8S) 地端伺服器建置實錄 – RedHat 篇 : https://blog.jks.coffee/on-premise-self-host-kubernetes-k8s-setup-redhat/
節錄裡面的段落
- 網路連線轉發 IPv4 位址並讓 iptables 查看橋接器的流量
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
- 請 Kubernetes (K8S) 引用載入 br_netfilter, overlay 二個核心模組
sudo modprobe overlay
sudo modprobe br_netfilter
- 啟用 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
- 在不重啟 node 下套用 sysctl 的參數
sudo sysctl --system
- 驗證
br_netfilter
,overlay
modules
lsmod | grep br_netfilter
lsmod | grep overlay
- 驗證
net.bridge.bridge-nf-call-iptables
,net.bridge.bridge-nf-call-ip6tables
, andnet.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来通信
netfilter 框架是 Linux 中的包過濾框架,它提供了五個 hook 點,每個進入網路系統的包都會觸發這些 hook。
Iptables 與 netfilter 的關係: iptables是運行在 user namespace 的應用軟體,通過控制 Linux 核心 netfilter 模組,來管理網路封包的處理和轉發
- A Deep Dive into Iptables and Netfilter Architecture - DigitalOcean: 介紹 iptables 跟 netfilter 架構
- iptables - wiki
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
- 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:
- PREROUTLING chain:
- raw
- mangle
- nat
- INPUT chain
- mangle
- filter
- security
- nat
- 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
如果 /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 能有機會轉化成有點條理的文章排序分享給大家 🫣