1. 程式人生 > >基於Kubeadm的Flannel分析_Kubernetes中文社群

基於Kubeadm的Flannel分析_Kubernetes中文社群

Flannel概述

Flannel是將多個不同子網(基於主機Node)通過被Flannel維護的Overlay網路拼接成為一張大網來實現互聯的,通過官方的一張網路拓撲圖我們可以對其基本原理一目瞭然:

值得探討的是,flannel的這個overlay網路支援多種後端實現,除上圖中的UDP,還有VXLAN和host-gw等。此外,flannel支援通過兩種模式來維護隧道端點上FDB的資訊,其中一種是通過連線Etcd來實現,另外一種是直接對接K8S,通過K8S新增刪除Node來觸發更新。

Flannel部署常見問題

1. Node狀態顯示為“NotReady”

我的K8S環境使用kubeadm來容器化執行K8S的各個元件(除kubelet直接執行在裸機上外),當我使用kubeadm join命令加入新的Minion Node到K8S叢集中後,通過kubectl get node會發現所有的node都還是not ready狀態,這是因為還沒有配置好flannel網路。

2. 使用kube-flannel.yml無法建立DaemonSet

我使用的是K8S的1.6.4的版本,然後按照官方的說明,使用kube-flannel.yml來建立flannel deamon set,結果始終報錯。正確的姿勢是先使用kube-flannel-rbac.yml來建立flannel的ClusterRole並授權。該yml的主要作用是建立名叫flannel的ClusterRole,然後將該ClusterRole與ServiceAccount(flannel)繫結。接下來,當我們使用kube-flannel.yml來建立flannel daemon set的時候,該daemon set明確指定其Pod的ServiceAccount為flannel,於是通過它啟動起來的Pod就具有了flannel ClusterRole中指定的許可權。

3.flannel Pod狀態為Running,網路不通

我之前在我的Mac Pro上跑了三個VM,為了能夠訪問公網拉取映象,我為每個VM分配了一張網絡卡使用NAT模式,其分配到的IP地址可能重啟後發生變化。另外,為了我本機方便管理,我為每臺VM又分配了一張網絡卡使用Host-Only網路模式,每個網絡卡都有一個固定的IP地址方便SSH。然後,奇怪的事情就這樣發生了….

原因在與在kube-flannel.yml中,kube-flannel容器的command被指定為:

command: [ "/opt/bin/flanneld", "--ip-masq", "--kube-subnet-mgr"]

可見,其並沒有指定使用哪一張網絡卡作為flanneld對外通訊的物理網絡卡,於是,可能由於機器上面路由配置的差異,導致三臺機器並沒有一致通過Host-Only網路模式的網絡卡來打通Overlay網路。遇到這種情況,如果幾臺機器的配置一致,可以手動修改kube-flannel.yml檔案中kube-flannel的command的值,新增引數–iface=ethX,這裡的ethX就為我們希望flanneld通訊使用的網絡卡名稱。

4.flannel啟動異常,顯示install-cni錯誤

這個現象比較坑,遇到這種情況的第一反應就是去檢視install-cni容器到底做了什麼。我們開啟kube-flannel.yml可以看到,該容器的command欄位只有簡單的一行Shell:

command: [ "/bin/sh", "-c", "set -e -x; cp -f /etc/kube-flannel/cni-conf.json /etc/cni/net.d/10-flannel.conf; while true; do sleep 3600; done" ]

也就是將映象中做好的cni-conf.json拷貝到cni的netconf目錄下。由於容器的/etc/cni/net.d是掛載主機的對應的目錄,所以該操作主要目的是為CNI準備flannel環境,便於啟動容器的時候正確從netconf目錄中載入到flannel,從而使用flannel網路。

我發現進入主機的netconf目錄中能夠看到10-flannel.conf:

#ls /etc/cni/net.d/10-flannel.conf
/etc/cni/net.d/10-flannel.conf
# cat /etc/cni/net.d/10-flannel.conf
cat: /etc/cni/net.d/10-flannel.conf: No such file or directory

無法打出其內容,而且檔案顯示為紅色,說明其內容並沒有正確從容器中拷貝過來。

之前我懷疑該異常是因為kubelet所帶的檔案系統引數為systemd,而docker的檔案系統引數為cgroupfs所致,結果發現並非如此。當前能夠繞開該異常的workaround為手動進入到主機的netconf目錄,建立10-flannel.conf目錄,並寫入以下資料:

{
"name": "cbr0",
 "type": "flannel",
 "delegate": {
 "isDefaultGateway": true
 }
}

5.flannel網路啟動正常,能夠建立pod,但是網路不通

出現該現象一般會想到debug,而debug的思路當然是基於官方的那張網路拓撲圖。比如在我的機器上,通過參看網絡卡IP地址有如下資訊:

6: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN
link/ether 22:a0:ce:3c:bf:1f brd ff:ff:ff:ff:ff:ff
 inet 10.244.1.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
inet 10.244.2.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
7:cni0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1450 qdisc noqueue state DOWN qlen 1000
 link/ether 0a:58:0a:f4:01:01 brd ff:ff:ff:ff:ff:ff
inet 10.244.1.1/24 scope global cni0
valid_lft forever preferred_lft forever
inet6 fe80::4cb6:7fff:fedb:2106/64 scope link
valid_lft forever preferred_lft forever

很明顯,cni0為10.244.1.1/24網段,說明該主機應該位於10.244.1.0/16子網內;但是我們看flannel.1網絡卡,確有兩個IP地址,分別為於兩個不同的”/16”子網。所以,可以肯定的是,我們一定是在該太主機上部署了多次kubeadm,而kubeadm reset並不會清理flannel建立的flannel.1和cni0介面,這就導致環境上遺留下了上一次部署分配到的IP地址。這些IP地址會導致IP地址衝突,子網混亂,網路通訊異常。

解決的方法就是每次執行kubeadm reset的時候,手動執行以下命令來清楚對應的殘餘網絡卡資訊:

ip link del cni0
ip link del flannel.1

K8S如何使用Flannel網路

Flannel打通Overlay網路

當使用kubeadm來部署k8s,網路元件(如flannel)是通過Add-ons的方式來部署的。我們使用這個yml檔案來部署的flannel網路。
我截除了關鍵的一段內容(containers spec):

containers:
- name: kube-flannel
image: quay.io/coreos/flannel:v0.7.1-amd64
command: [ "/opt/bin/flanneld", "--ip-masq", "--kube-subnet-mgr" ]
securityContext:
privileged: true
env:
- name: POD_NAME
valueFrom:
fieldRef:
 fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
 fieldPath: metadata.namespace
 volumeMounts:
- name: run
mountPath: /run
- name: flannel-cfg
mountPath: /etc/kube-flannel/
 - name: install-cni
image: quay.io/coreos/flannel:v0.7.1-amd64
 command: [ "/bin/sh", "-c", "set -e -x; cp -f /etc/kube-flannel/cni-conf.json /etc/cni/net.d/10-flannel.conf; while true; do sleep 3600; done" ]
volumeMounts:
- name: cni
mountPath: /etc/cni/net.d
- name: flannel-cfg
 mountPath: /etc/kube-flannel/

通過分析該yaml檔案可以知道:該pod內有兩個container,分別為install-cni和 kube-flannel。

  • install-cni

主要負責將config-map中適用於該flannel的CNI netconf配置拷貝到主機的CNI netconf 路徑下,從而使得K8S在建立pod的時候可以遵照標準的CNI流程掛載網絡卡。

  • kube-flannel

主要啟動flanneld,該command中為flanneld的啟動指定了兩個引數: –ip-masq, –kube-subnet-mgr。第一個引數–ip-masq代表需要為其配置SNAT;第二個引數–kube-subnet-mgr代表其使用kube型別的subnet-manager。該型別有別於使用etcd的local-subnet-mgr型別,使用kube型別後,flannel上各Node的IP子網分配均基於K8S Node的spec.podCIDR屬性。可以參考下圖的方式來檢視(該示例中,k8s為node-1節點分配的podCIDR為:10.244.8.0/24):

#kubectl edit node node-1

apiVersion: v1
kind: Node
 ...
name: dev-1
resourceVersion: "2452057"
selfLink: /api/v1/nodesdev-1
uid: 31f6e4c3-57b6-11e7-a0a5-00163e122a49
spec:
 externalID: dev-1
podCIDR: 10.244.8.0/24

另外,flannel的subnet-manager通過監測K8S的node變化來維護了一張路由表,這張表裡面描述了要到達某個Pod子網需要先到達哪個EndPoint。

CNI掛載容器到隧道端點

如果說flannel為Pod打通了一張跨node的大網,那麼CNI就是將各個終端Pod掛載到這張大網上的安裝工人。

在剛部署好flannel網路並未在該Node上啟動任何Pod時,通過ip link我們只能夠看到flannel.1這張網絡卡,卻無法看到cni0。難道是flannel網路執行異常嗎?我們接下來就來分析flannel的CNI實現原理,就知道答案了。

通過傳統方式來部署flannel都需要通過指令碼來修改dockerd的引數,從而使得通過docker建立的容器能夠掛載到指定的網橋上。但是flannel的CNI實現並沒有採用這種方式。通過分析CNI程式碼,

我們可以瞭解flannel CNI的流程:

  • 讀取netconf配置檔案,並載入/run/flannel/subnet.env環境變數資訊。
  • 基於載入的資訊,生成適用於其delegate的ipam和CNI bridge的netconf檔案;其中指定ipam使用host-local,CNI bridge type為bridge。
  • 呼叫deletgate(CNI bridge type)對應的二進位制檔案來掛載容器到網橋上。

這裡的環境變數檔案/run/flannel/subnet.env是由flanneld生成的,裡面包含了該主機所能夠使用的IP子網網段,具體內容如下:

# cat /run/flannel/subnet.env
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.8.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true

這些資料生成ipam的配置檔案的依據,另外,flannel CNI外掛的程式碼中,預設指定delegate為bridge:

if !hasKey(n.Delegate, "type") {
 n.Delegate["type"] = "bridge"
}

所以,當flannel CNI外掛呼叫delegate,本質上就是呼叫bridge CNI外掛來將容器掛載到網橋上。分析bridge CNI 外掛的過程我們可以看到其指定了預設網橋名稱為cni0:

const defaultBrName = "cni0"

func loadNetConf(bytes []byte) (*NetConf, error) {
n := &NetConf{
 BrName: defaultBrName,
 }
 ...
 return n, nil
}

因此,現在我們可以將整個流程連起來了:flannel CNI外掛首先讀取netconf配置和subnet.env資訊,生成適用於bridge CNI外掛的netconf檔案和ipam(host-local)配置,並設定其delegate為bridge CNI外掛。然後呼叫走bridge CNI外掛掛載容器到bridge的流程。由於各個Pod的IP地址分配是基於host-local的Ipam,因此整個流程完全是分散式的,不會對API-Server造成太大的負擔。

至此,基於kubeadm的flannel分析就大致結束了,希望對您有幫助!