一起誤刪cni0虛擬網絡卡引發的k8s事故

語言: CN / TW / HK

起因:

由於生產K8S叢集需要踢出一個已存在的節點後重新加入,在清理node節點環境的過程中,誤將需要在node節點上執行的刪除cni0虛擬網絡卡的操作在master節點上執行了。由於資源問題,我們的master節點也部署了很多的業務pod。這個操作導致master節點上非hostnetwork的pod的網路通訊全部中斷。很多業務受到了影響,告警也很及時。

誤操作命令:

ip link del cni0

緊急修復:

為了儘快恢復業務,我直接將master節點強制排水了。將節點上的pod驅逐到了其它節點上,使得業務儘快得以恢復。

kubectl drain master01 --ignore-daemonsets --delete-emptydir-data
// 注意排水後,觀察pod重排程部署情況。確認所有pod正常啟動恢復業務!

後續:

業務是恢復了,但是master節點上的pod網路還是不通的。我想到了兩種恢復手段。第一,直接將節點踢出叢集,然後重新加入。第二,嘗試手工恢復cni0虛擬網絡卡,將pod網路接回來。第一種簡單粗暴些,考慮到重新加入叢集執行的指令碼中,因為迭代過的原因,可能有些引數或者核心引數變更過。為了避免出現修改影響到master元件。我傾向於嘗試第二種方式。

要將pod網路接回來,就要了解K8S flannel元件的通訊原理和連線方式。需要理解pod虛擬veth pair對是如何工作的?這裡主要涉及flannel的工作原理,網路名稱空間的基本機制和操作。

首先,flannel(vxlan模式)的工作原理如下:

如下是pod進出網路流量大致流程:

    • pod中產生資料,根據pod的路由資訊,將資料傳送到cni0;

    • cni0 根據node節點的路由表,將資料傳送到隧道裝置flannel.1;

    • flannel.1檢視資料包的目的ip,從flanneld獲得對端隧道裝置的必要資訊,封裝資料包;

    • flannel.1將資料包傳送到對端裝置。對端節點的網絡卡接收到資料包,發現數據包為overlay資料包,解開外層封裝,併發送內層封裝到flannel.1裝置;

    • 資料達到node節點的flannel.1裝置檢視資料包,根據路由表匹配,將資料傳送給cni0裝置;

    • cni0匹配路由表,傳送資料給網橋上對應的埠。

從通訊過程可以知道,pod的網路需要連線到cni0網橋,而cni0和flannel.1網橋之間是沒有連線的,通過node節點的路由表來實現轉發通訊的。所以,這裡只需要將node節點所有的pod的網路虛擬對(veth pair)找到,然後將其中一端連線到重新建立的cni0虛擬網橋應該就可以了。變成了如下兩個問題:

第一:如何建立cni0網橋並配置正確對應的引數?

由於flannel使用的是vxlan模式,所以建立cni0網橋的時候需要注意mtu值的設定。如下,建立cni0網橋:

// 建立cni0裝置,指定型別為網橋
# ip link add cni0 type bridge
# ip link set dev cni0 up
// 為cni0設定ip地址,這個地址是pod的閘道器地址,需要和flannel.1對應網段
# ifconfig cni0 172.28.0.1/25
// 為cni0設定mtu為1450
# ifconfig cni0 mtu 1450 up


// 檢視建立情況
# ifconfig cni0
cni0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
inet 172.28.0.1 netmask 255.255.255.128 broadcast 172.28.0.127
ether 0e:5e:b9:62:0d:60 txqueuelen 1000 (Ethernet)
RX packets 487334 bytes 149990594 (149.9 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 629306 bytes 925100055 (925.1 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
// 此時檢視路由表,也已經有了去往本機pod網段的cni0資訊
# route -n | grep cni0
172.28.0.0 0.0.0.0 255.255.255.128 U 0 0 0 cni0

第二:如何準確找出每個pod的對應的veth pair虛擬對,是否和名稱空間有關係?

在一臺只有兩個pod的節點上檢視虛擬網絡卡的情況,發現在node主機上可以看到兩個veth字首的虛擬網絡卡,它們的另一端在pod中,並且pod的netns也可以通過show命令看到。值得注意的是,在node節點同網路名稱空間下的vethf0978d0c和vethb9525687的連線設定master為cni0。所以,找到所有的veth字首的虛擬網絡卡,並將其掛載到cni0上即可。

// 這裡通過一個簡單的指令碼批量將veth的虛擬網絡卡掛載到cni0網橋上
for veth in $(ip addr | grep veth | grep -v master | awk -F'[@|:]' '{print $2}' | sed 's/ //g')
do
ip link set dev $veth master cni0
done

通過以上兩步操作後,失聯的pod已經可以ping通,並和其它節點的pod正常通訊了。測試新建刪除pod都是正常的。

至此,手工恢復cni0算是完成了。如果還有下次 ip link del cni0的操作,可以不做節點排水操作。通過這個方式以最快,影響最小的方式恢復pod網路通訊。這裡也暴露了一個問題,那就是master節點的操作規範問題,對於生產環境,我們應該儘可能避免直接登入到master節點上進行操作,應該在其它管理機上授權k8s許可權去操作。然後master節點儘量和業務分開獨立部署,以保證master節點的穩定性。

K8s培訓推薦

10月底開課

Kubernetes 線上直播班

快速加 K8s學習交流群,與大佬共卷!