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:ffinet 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 1000link/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.namespacevolumeMounts: - name: run mountPath: /run - name: flannel-cfg mountPath: /etc/kube-flannel/ - name: install-cni image: quay.io/coreos/flannel:v0.7.1-amd64command: [ "/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-cfgmountPath: /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-1apiVersion: 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分析