深入 K8s 网络原理(一)- Flannel VXLAN 模式分析
- 1. 概述
- 2. TL;DR
- 3. Pod 間通信問題的由來
- 4. 測試環境準備
- 5. 從 veth 設備聊起
-
6. 網橋 cni0
- 6.1 在 Pod 內看網卡信息
- 6.2 在 host 上看網卡信息
- 7. VTEP flannel.1
- 8. 最后看下 Flannel 的配置
- 9. 總結
1. 概述
這周集中聊下 K8s 的集群網絡原理,我初步考慮分成3個方向:
-
Pod-to-Pod通信(同節點 or 跨節點),以 Flannel VXLAN 模式為例; -
Pod/External-to-Service通信,以 iptables 實現為例; -
Ingress原理,以 NGINX Ingress Controller 實現為例; - 其他:(到時候看心情)Flannel host-gw 模式,Calico,……
今天先介紹下 Flannel 實現 Pod 跨節點通信的原理。
2. TL;DR
我知道你們著急,這樣吧,先看圖:
一圖勝千言,下文都不知道咋展開了。哎。
此外,網絡這塊涉及的概念有點多,逐個細講感覺不合適。這樣,此處默認大家都熟悉 TCP/IP 協議族,下文該偷懶的地方我就偷懶。
3. Pod 間通信問題的由來
容器化以前,當需要將多個應用靈活部署到一些服務器上時,就不可避免地會遇到端口沖突問題,而協調這種沖突是很繁瑣的。K8s 體系的處理方式是將每個 Pod 丟到單獨的 netns 里,也就是 ip 和 port 都彼此隔離,這樣就不需要考慮端口沖突了。
不過這套網絡架構應該如何實現呢?整體來由需要解決下面這2個問題(結合上圖的 Pod1234):
- Pod1 如何和 Pod2 通信(同節點)
- Pod1 如何和 Pod3 通信(跨節點)
K8s 的網絡模型要求每個 Pod 都有一個唯一的 IP 地址,即使這些 Pod 分布在不同的節點上。為了實現這個網絡模型,CoreOS 團隊發起了 CNI 項目(后來 CNI 進了 CNCF 孵化)。CNI (Container Network Interface) 定義了實現容器之間網絡連通性和釋放網絡資源等相關操作的接口規范,這套接口進而由具體的 CNI 插件的去實現,CNI 插件負責為 Pod 分配 IP 地址,并處理跨節點的網絡路由等具體的工作。
行,接下來具體跟下 CNI 的 Flannel 實現是怎么工作的。
4. 測試環境準備
我在本地通過 Minikube 啟動一個3節點的 K8s 集群,3個節點的 IP 和 hostname 分別是:
- 192.168.49.2 minikube
- 192.168.49.3 minikube-m02
- 192.168.49.4 minikube-m03
此外在這個集群內創建了幾個 Pod,信息如下(主要留一下 Pod 的 IP 以及所在的節點):
kgpoowide
NAME READY STATUS RESTARTS AGE IP NODE
nginx-deployment-7fbb8f4b4c-89bds 1/1 Running 0 20h 10.244.2.4 minikube-m03
nginx-deployment-7fbb8f4b4c-d29zm 1/1 Running 0 20h 10.244.1.5 minikube-m02
nginx-deployment-7fbb8f4b4c-k5vh4 1/1 Running 0 102s 10.244.2.5 minikube-m03
nginx-deployment-7fbb8f4b4c-m4scr 1/1 Running 0 3s 10.244.1.6 minikube-m02
Pod 用的鏡像是帶有 ip 等命令的 NGINX,Dockerfile 如下:
FROM nginx:latest
RUN apt-get update && \
apt-get install -y iproute2 && \
rm -rf /var/lib/apt/lists/*
相應的 Deployment YAML 如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 4
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:test1
ports:
- containerPort: 80
5. 從 veth 設備聊起
不得不先提一句 veth(Virtual Ethernet Device)。veth 是一種在 Linux 中用于網絡虛擬化的技術,常用于容器網絡中。veth pair 可以看作是一對虛擬網絡接口設備,它們像管道的兩端一樣相連。在一個 veth 對中,數據從一端發送出去,可以在另一端被接收到,就像它們是通過一根以太網線連接的兩個獨立設備一樣。
在容器網絡中,veth 對經常被用來連接容器和主機。具體來說,veth 對的一個端點(通常稱為 veth 接口)位于容器的網絡命名空間內,好像是容器的網絡接口卡,而另一個端點位于主機的全局網絡命名空間內,通常會連接到一個 Linux 橋接或者其他網絡設備。
這種設置允許容器內的網絡流量通過 veth 接口流出容器,進入主機的網絡命名空間,并通過主機的網絡路由和策略進行進一步的處理或轉發。
如圖所示,Pod 內的 eth0 和 host 上的 veth1 其實就是一個 veth pair,Pod 和 host 在2個不同的網絡命名空間內,通過 veth 設備實現了2個 netns 之間的網絡互通。
而 veth-n 又會橋接到 cni0 這個網橋上,進而實現流量在主機上的路由過程。接下來我們具體看下 Pod 內外的網絡設備和路由規則等。
6. 網橋 cni0
接著來看網橋 cni0。
6.1 在 Pod 內看網卡信息
Pod 10.244.1.6 內的網卡信息如下:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
3: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN group default qlen 1000
link/tunnel6 :: brd :: permaddr 1622:c323:de90::
4: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65485 qdisc noqueue state UP group default
link/ether 4a:2c:84:bb:56:5e brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.244.1.6/24 brd 10.244.1.255 scope global eth0
valid_lft forever preferred_lft forever
可以看到 eth0@f11 設備,對應 IP 10.244.1.6/24,這里看著和一個普通的 vm 沒有大差別。此外留一下這里的 if11,這個 11 對應這個 veth pair 在主機上的另外一端的序號。
6.2 在 host 上看網卡信息
節點 minikube-m02 上的網卡信息如下:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
5: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65485 qdisc noqueue state UNKNOWN group default
link/ether 62:07:aa:05:13:c4 brd ff:ff:ff:ff:ff:ff
inet 10.244.1.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
6: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65485 qdisc noqueue state UP group default qlen 1000
link/ether de:07:f7:20:e0:70 brd ff:ff:ff:ff:ff:ff
inet 10.244.1.1/24 brd 10.244.1.255 scope global cni0
valid_lft forever preferred_lft forever
7: vetha7eec1e2@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65485 qdisc noqueue master cni0 state UP group default
link/ether da:ab:17:55:be:50 brd ff:ff:ff:ff:ff:ff link-netnsid 1
10: vethc9667243@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65485 qdisc noqueue master cni0 state UP group default
link/ether ce:dd:d3:ec:5e:d3 brd ff:ff:ff:ff:ff:ff link-netnsid 2
11: vethd26e8b95@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65485 qdisc noqueue master cni0 state UP group default
link/ether b2:64:95:13:2a:de brd ff:ff:ff:ff:ff:ff link-netnsid 3
24: eth0@if25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65535 qdisc noqueue state UP group default
link/ether 02:42:c0:a8:31:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 192.168.49.3/24 brd 192.168.49.255 scope global eth0
valid_lft forever preferred_lft forever
前面 Pod 內看到的 eth0@if11 對應這里的11號 vethd26e8b95@if4。別管這里的 if4,如果你是在 vm 里直接跑 Pod 就看不到 ifn 了。我這里因為用了 Docker Desktop 跑 K8s,所以 K8s 所在的 nodes 本質也是容器,這里多套了一層網絡嵌套而已。
先看下 cni0:
ip link show master cni0
7: vetha7eec1e2@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65485 qdisc noqueue master cni0 state UP mode DEFAULT group default
link/ether da:ab:17:55:be:50 brd ff:ff:ff:ff:ff:ff link-netnsid 1
10: vethc9667243@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65485 qdisc noqueue master cni0 state UP mode DEFAULT group default
link/ether ce:dd:d3:ec:5e:d3 brd ff:ff:ff:ff:ff:ff link-netnsid 2
11: vethd26e8b95@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65485 qdisc noqueue master cni0 state UP mode DEFAULT group default
link/ether b2:64:95:13:2a:de brd ff:ff:ff:ff:ff:ff link-netnsid 3
可以看到前面提到的 veth vethd26e8b95 被橋接到了 cni0 上。
繼續看 host 的路由表:
default via 192.168.49.1 dev eth0
10.244.0.0/24 via 10.244.0.0 dev flannel.1 onlink
10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1
10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
192.168.49.0/24 dev eth0 proto kernel scope link src 192.168.49.3
留意這里的 10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1
cni0 的信息是:
6: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65485 qdisc noqueue state UP group default qlen 1000
link/ether de:07:f7:20:e0:70 brd ff:ff:ff:ff:ff:ff
inet 10.244.1.1/24 brd 10.244.1.255 scope global cni0
valid_lft forever preferred_lft forever
結合起來看,也就是所有發往 10.244.1.0/24 段的數據包都會通過 cni0 傳輸,10.244.1.0/24 也就是 Flannel 分配給當前節點的 Pod IP 段。
7. VTEP flannel.1
上述路由表中還有這樣2條記錄:
10.244.0.0/24 via 10.244.0.0 dev flannel.1 onlink
10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink
當前集群是3個節點,也就是目的地址是當前節點內的 pods IP 段 10.244.1.0/24,流量交給 cni0 處理;而其他節點的 pods IP 段 10.244.0.0/24 和 10.244.2.0/24 則交給 flannel.1 來處理。
flannel.1 是 VXLAN 網絡的 VTEP 設備。簡單介紹下 VXLAN 和 VTEP:
VXLAN (Virtual Extensible LAN) 是一種覆蓋網絡技術,允許在現有的物理網絡基礎設施之上創建大量虛擬化的局域網(LAN)。它主要用于解決傳統 VLAN 技術的一些限制,如 VLAN ID 數量限制(只有4096個)。VXLAN 可以支持高達1600萬個虛擬網絡,極大地擴展了網絡的規模和靈活性。
VXLAN 相關的一些概念與原理:
- 封裝與隧道技術:VXLAN 通過封裝原始的以太網幀(Layer 2)到 UDP 數據包(Layer 3)中來工作。這意味著它可以跨越不同的網絡和子網,實現跨網絡邊界的通信。
- VXLAN 網絡標識符 (VNI):每個 VXLAN 網絡都有一個唯一的標識符,稱為 VNI(VXLAN Network Identifier),它提供了地址隔離,確保各個 VXLAN 網絡之間的數據包不會互相干擾。
- VTEP(VXLAN Tunnel Endpoint):VTEP 是 VXLAN 架構中的端點設備,負責封裝和解封裝數據包。每個通過 VXLAN 通信的網絡設備都有一個或多個 VTEP。
- 當數據包從虛擬網絡出發時,VTEP 會捕獲這些數據包,將它們封裝在 VXLAN 格式中(即加入 VNI 和 UDP 頭),然后通過物理網絡發送。
- 當 VXLAN 數據包到達目的地的 VTEP 時,該 VTEP 將對數據包進行解封裝,移除 VXLAN 頭部,然后將原始的以太網幀轉發到目標虛擬網絡中。
- VTEP 通常部署在數據中心的交換機(物理或虛擬)上,但也可以部署在其他網絡設備或服務器上。
- 在容器化環境(如 Kubernetes 使用 Flannel 等 CNI)中,VTEP 可以作為軟件組件運行,處理容器或 Pod 之間的 VXLAN 通信。
所以當數據包到達 flannel.1 的時候,就開始了 VXLAN 封包(MAC in UDP)過程,一個以太網幀被依次加上了 VXLAN 頭(VNI 信息)、UDP 頭、外部 IP 頭和 MAC 頭等。外部 IP 頭里包含了 VXLAN 隧道的源地址和目的地址(VTEP 地址),外部 MAC 頭則包含了以太網幀到達下一跳所需的 MAC 地址。
8. 最后看下 Flannel 的配置
在 minikube 環境中,Flannel 會被默認部署到 kube-flannel namespace 下。在這個 namespace 里有一個 ConfigMap 叫做 kube-flannel-cfg,里面包含這樣2個配置文件:
cni-conf.json
{
"name": "cbr0",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
net-conf.json
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
如果需要修改網絡模式或者 pods 網段,就可以在 net-conf.json 中靈活調整。早幾年用 Flannel 的時候,我就習慣將 10.244.0.0/16 改成 10.100.0.0/16。此外 vxlan 改成 host-gw 可以提高網絡傳輸性能,如果你的集群規模不是大幾百好幾千個節點,也可以考慮用 host-gw 模式。
9. 總結
從來不總結,下班。
算了,補張圖吧,前文提到的 Pod1 到 Pod2/Pod3 的流量怎么走:
總結
以上是生活随笔為你收集整理的深入 K8s 网络原理(一)- Flannel VXLAN 模式分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UNION ALL合表查询
- 下一篇: 微调baichuan2-7b遇到的显存坑