flannel流程解析
flannel是coreos貢獻給社區的一個kubernetes網絡插件。overlay。
1 VXLAN
1.1 VXLAN協議
二層數據中心網絡的一個關鍵特征就是它們的使用虛擬局域網(VLAN)提供廣播隔離,從而更好的為多租戶提供隔離。但隨著租戶數量越來越多,VLAN由于上限容量4096,越來越捉襟見肘。
vxlan(Virtual eXtensible Local Area Network)是一種隧道協議,用來解決IEEE 802.1q VLAN ID只能最多4096的限制。VXLAN網絡中的子網標識符擴展到了24位(成為VNI,VXLAN Network Identifier),其最大容量也達到了16777216。
vlan協議在IETF RFC 7348中定義,有多種實現,如linux kernel的vxlan模塊,OpenVswitch等。vxlan協議跑在UDP上,UDP連接的端口固定,如linux kernel vxlan的端口號為8472。
跟其他隧道不同,VXLAN并不是一個點到點的網絡,而是1 to N。VXLAN設備可以動態的學習IP地址,也可以從靜態配置的轉發表中學習。
VXLAN的配置管理使用iproute2包,這個工具是和VXLAN一起合入到內核的。
1.2 VXLAN配置
先來看看如何管理VXLAN接口。
1 創建VXLAN接口
ip link add vxlan0 type vxlan id 42 group 239.1.1.1 dev eth1 dstport 4789
這條命令會創建一個叫做vxlan0的新接口,它使用在eth1上的組播組239.1.1.1來通信。初始化時沒有轉發表。目的端口號是IANA規定的4789。在VXLAN中,一般將vxlan接口叫做VTEP(Vxlan tunnel endpoint),VXLAN子網的報文,都需要從VTEP出去。
多播組主要用來泛洪學習arp:vxlan子網內廣播ARP請求,對應VM響應。但并不是必須的。
如果網絡不復雜,可以認為某一Hypervisor上所有的子網IP的MAC,和Hypervisor上的VTEP的MAC一致,可以直接用VTEP MAC封裝報文;而VTEP的MAC,可以用bridge命令手工配置。
2 配置錯了可以刪除vxlan
ip link delete vxlan0
3 查看vxlan的信息
ip -d link show vxlan0
可以用bridge命令查看,刪除,查看VXLAN的轉發表。
1 創建一條轉發表項。MAC即對端VTEP的MAc,地址即對端VTEP的地址
bridge fdb add to 00:17:42:8a:b4:05 dst 192.19.0.2 dev vxlan0
2 刪除一條轉發表項
bridge fdb delete 00:17:42:8a:b4:05 dev vxlan0
3 查看VXLAN接口的轉發表
bridge fdb show dev vxlan0
一個典型的數據中心vxlan網絡:
一個典型的vxlan報文:
2 vxlan flannel
flannel的vxlan相對數據中心來說,是比較簡單的,因為其在Layer 3上只有1個Vxlan網絡,只有1個vxlan接口(flannel.[VNI],默認為flannel.1)。VTEP的MAC地址不是通過組播學習的,而是通過從apiserver的node接口watch到并靜態下發的。
2.1 flannel.1接口是怎么創建的?
一、main.go查找ExtIface(Hypervisor L3出接口,所有vxlan報文都要封裝后走ExtIface出去overlay)。flannel的出接口允許用戶按以下方式來選擇(LookupExtIface):
指定具體接口
指定接口的正則表達式來匹配查找
啥也不指定,由flannel根據默認網關找出接口
1.5.4版本默認kubeadm不會指定flannel的出接口,所以出接口選的是默認網關的接口。如果機器上網絡比較復雜,可能需要手工指定出接口。
二、確定subnet
flannel網絡中,以我們的集群為例,整個flannel網絡是10.244.0.0/16,每個node都是一個子網(subnet),如10.244.0.0/24, 10.244.1.0/24。
subnet的網段、長度是怎么確定的呢?k8s創建時會生成net-conf.json,其設置了Network和Backend的信息(configmap kube-flannel-cfg,以volume形式掛到flanneld容器里去)。
# flanneld容器中
cat /etc/kube-flannel/net-conf.json
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
ParseConfig中會確定subnet的配置項:SubnetMin, SubnetMax, SubnetLen, BackendType。如果net-conf.json沒有指定Backend,則默認使用udp。我這里使用了vxlan。注意,由于udp方式下,報文是通過tun從內核上送到用戶態的flanneld進程,在用戶態進程做udp封裝、解封裝,性能不佳,所以生產環境最好選用vxlan。
三、根據backend類型創建backend,然后調用be.RegisterNetwork函數去初始化flannel接口。步驟如下。
1 創建flannel接口。netlink下內核創建flannel.1接口,需要指定vni,出接口,源地址,目的端口,nolearning(netlink.Vxlan),并設置app_solicit為3。相當于如下命令:
ip link add $DEVNAME type vxlan id $VNI dev eth0 local $IP dstport $PORT nolearning
echo '3' > /proc/sys/net/ipv4/neigh/$DEVNAME/app_solicit
2 調用kube subnet manager獲取租約。ksm會去調用apiserver的node api,查詢node的PodCIDR,即該節點的pod overlay網絡,如10.244.1.0/24(kubeSubnetManager.AcquireLease, subnet/kube/kube.go:213)
3 如果從apiserver查詢node的backend Annotations跟實際node的信息不一致(例如VTEP MAC不同),則向node打patch,更新Anootations。此更新會被其他node watch到,從而更新其本地的fdb table(下面會提到)。
4 設置flannel.1接口地址,激活接口,并增加網段路由(vxlanDevice.Configure, backend/vxlan/device.go:125)
ip address add $VXSUBNET dev $DEVNAME
ip link set $DEVNAME up
ip route add $SUBNET dev $DEVNAME scope global
一個典型的flannel.1接口信息如下。
ip -d link show flannel.1
8: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT
link/ether 12:c0:e6:d3:0d:df brd ff:ff:ff:ff:ff:ff promiscuity 0
vxlan id 1 local 192.168.136.31 dev bond0 srcport 0 0 dstport 8472 nolearning ageing 300 addrgenmode eui64
至此,flannel.1接口配置完畢,接下來就是配置fdb靜態表項了。
2.2 fdb 轉發數據庫
flannel接口配置完畢后,接下來就是在集群中配置fdb表了。要做的其實也就是完成下面這條命令的下發,只是由flannel自動完成,用戶不需要手動操作。
bridge fdb add/append $mac-of-vtep-on-node-2 dev $DEVNAME dst $DESTIP
跟數據中心的vxlan不一樣,flannel網絡中的VTEP的MAC并不是通過組播學習的,而是通過apiserver去做的同步(或者是etcd)。前面在創建flannel.1接口時有提到,各個節點會將自己的VTEP信息上報給apiserver,而apiserver會再同步給各節點上正在watch node api的listener(flanneld),flanneld拿到了更新消息后,再通過netlink下發到內核,更新fdb表項,從而達到了整個集群的同步。
2.3 newKubeSubnetManager
flanneld會在啟動的時候,創建一個針對apiserver node api的informer(newKubeSubnetManager)。
flannel的子網管理器SubnetManager有2種:基于etcd、基于k8s的apiserver。我這里使用的是基于k8s的kubeSubnetManager(subnet/kube/kube.go, 下面簡稱ksm),它會去listWatch k8s apiserver的node api,通過ksm.events與backend通信。ksm.events是一個長度為5000的subnet.Event chan,ksm作為生產者,會將從k8s那里watch到的信息封裝(nodeToLease,節點信息轉為租約信息)后寫到ksm.events里去。
ksm.events的消費者是backend。backend/vxlan_network.go在run時,會拉起一個goroutine,調用subnet/watch/WatchLeases(),在這個函數里批量讀取ksm.events chan,并將批量事件推入backend的接收chan;backend會在其goroutine中處理這些事件:根據事件中帶的nodes信息,如IP,VtepMAC,下發到本機內核fdb table。
subnet/watch/WatchLeases()做了個優化:node比較活躍時,ksm.events里可能會有比較多的events;讀取ksm.events的時候,可以一次性讀出一組events,并且在處理這些events時,可以將同一SUBNET的lease合并,不過事件的總數不會減少。
第一批事件的處理函數是handleInitialSubnetEvents,進行初始化+事件處理。其獲取內核的fdbtable,之后會根據獲取的上面WatchLeases丟過來的event,對比fdbtable刷新內核fdb table:刪除內核多余的表項,增加內核缺少的表項,保持跟flannel網絡一致。這里有個疑問,initial的時候,怎么保證event都是Added的呢?如果是刪除的,刷新內核arp table的流程是有問題的。
后續事件的處理函數是handleSubnetEvents,它比較純粹,根據event.Type是Added還是Removed,修改內核fdb表現 和 flannel維護的routes(后面arp解析時會用到)
fdb更新下發內核相當于調用了如下命令:
bridge fdb add $mac-of-vtep-on-node dev $DEVNAME dst $DESTIP
一個典型的fdb表項如下。
bridge fdb show dev flannel.1
76:bc:c1:37:14:32 dst 192.168.136.35 self permanent
ca:07:82:ff:d1:6a dst 192.168.136.34 self permanent
0e:c4:9f:71:f3:56 dst 192.168.136.32 self permanent
66:73:1c:ea:76:58 dst 192.168.136.33 self permanent
至此,fdb靜態表項配置完成,并且可以達到與集群同步更新。
2.4 arp table
fdb完成后,報文轉發沒問題了,但在L2封裝時,還需要對端ip的arp表項。linux ARP解析是這樣的:
When there is no forward progress, ARP tries to reprobe. It first tries to ask a local arp daemon app_solicit times for an updated MAC address. If that fails and an old MAC address is known, a unicast probe is sent ucast_solicit times. If that fails too, it will broadcast a new ARP request to the network. Requests are sent only when there is data queued for sending.
即:
向用戶態arp daemon請求app_solicit次;
如果還有舊的MAC地址記錄,單播一下試試;
絕望了,怒而廣播之
(說起來好像跟某些事件有點類似)
所以,明白為啥前面創建flannel.1接口時,需要設置app_solicit為3了嗎?
在flannel網絡中,flanneld會在啟動時監聽RTM_GETNEIGH(vxlanDevice.MonitorMisses),flanneld相當于arp daemon;arp請求會在內核中通過netlink發送請求到用戶態的flanneld,由flanneld根據該arp請求ip所屬網段(該node上所有ip的MAC也就是VTEP的MAC),查詢其記錄的routes信息,然后netlink下發內核。
func (nw *network) handleL3Miss(miss *netlink.Neigh) {
route := nw.routes.findByNetwork(ip.FromIP(miss.IP))
err := nw.dev.AddL3(neighbor{IP: ip.FromIP(miss.IP), MAC: route.vtepMAC})
}
所以如果你先arp -d x.x.x.x -i flannel.1刪掉arp緩存,然后再ping想觸發一個arp請求,抓包是抓不到arp報文的。
arp table更新下發內核相當于調用了如下命令:
ip neighbor add/replace $ip-on-node-2 lladdr $mac-of-vtep-on-node-2 dev flannel.1
至此,vxlan的流程就走通了。
3 udp flannel
vxlan要求內核版本3.7+,最好3.9+,所以在一些舊版本的linux上就無法使用基于vxlan的flannel了,只能使用基于udp的flannel。
udp flannel原理與vxlan類似,都是package in udp,只是一個在內核做報文封裝,一個在用戶態做(通過tun, pkg/ip/tun.go)。可想而知,udp flannel的性能不會太好,所以除非是內核版本太低,一般還是用vxlan flannel性能會比較好。
ref:
Virtual eXtensible Local Area Networking documentation
vxlan在Flannel中的Overlay網絡的實現
Difference of VXLAN L3MISS between flannel and docker overlay implementation
總結
以上是生活随笔為你收集整理的flannel流程解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LINUX 设置 backspace为删
- 下一篇: 激活你的Chrome浏览器