flannel流程解析
flannel是coreos貢獻給社區(qū)的一個kubernetes網(wǎng)絡插件。overlay。
1 VXLAN
1.1 VXLAN協(xié)議
二層數(shù)據(jù)中心網(wǎng)絡的一個關(guān)鍵特征就是它們的使用虛擬局域網(wǎng)(VLAN)提供廣播隔離,從而更好的為多租戶提供隔離。但隨著租戶數(shù)量越來越多,VLAN由于上限容量4096,越來越捉襟見肘。
vxlan(Virtual eXtensible Local Area Network)是一種隧道協(xié)議,用來解決IEEE 802.1q VLAN ID只能最多4096的限制。VXLAN網(wǎng)絡中的子網(wǎng)標識符擴展到了24位(成為VNI,VXLAN Network Identifier),其最大容量也達到了16777216。
vlan協(xié)議在IETF RFC 7348中定義,有多種實現(xiàn),如linux kernel的vxlan模塊,OpenVswitch等。vxlan協(xié)議跑在UDP上,UDP連接的端口固定,如linux kernel vxlan的端口號為8472。
跟其他隧道不同,VXLAN并不是一個點到點的網(wǎng)絡,而是1 to N。VXLAN設備可以動態(tài)的學習IP地址,也可以從靜態(tài)配置的轉(zhuǎn)發(fā)表中學習。
VXLAN的配置管理使用iproute2包,這個工具是和VXLAN一起合入到內(nèi)核的。
1.2 VXLAN配置
先來看看如何管理VXLAN接口。
1 創(chuàng)建VXLAN接口
ip link add vxlan0 type vxlan id 42 group 239.1.1.1 dev eth1 dstport 4789
這條命令會創(chuàng)建一個叫做vxlan0的新接口,它使用在eth1上的組播組239.1.1.1來通信。初始化時沒有轉(zhuǎn)發(fā)表。目的端口號是IANA規(guī)定的4789。在VXLAN中,一般將vxlan接口叫做VTEP(Vxlan tunnel endpoint),VXLAN子網(wǎng)的報文,都需要從VTEP出去。
多播組主要用來泛洪學習arp:vxlan子網(wǎng)內(nèi)廣播ARP請求,對應VM響應。但并不是必須的。
如果網(wǎng)絡不復雜,可以認為某一Hypervisor上所有的子網(wǎng)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的轉(zhuǎn)發(fā)表。
1 創(chuàng)建一條轉(zhuǎn)發(fā)表項。MAC即對端VTEP的MAc,地址即對端VTEP的地址
bridge fdb add to 00:17:42:8a:b4:05 dst 192.19.0.2 dev vxlan0
2 刪除一條轉(zhuǎn)發(fā)表項
bridge fdb delete 00:17:42:8a:b4:05 dev vxlan0
3 查看VXLAN接口的轉(zhuǎn)發(fā)表
bridge fdb show dev vxlan0
一個典型的數(shù)據(jù)中心vxlan網(wǎng)絡:
一個典型的vxlan報文:
2 vxlan flannel
flannel的vxlan相對數(shù)據(jù)中心來說,是比較簡單的,因為其在Layer 3上只有1個Vxlan網(wǎng)絡,只有1個vxlan接口(flannel.[VNI],默認為flannel.1)。VTEP的MAC地址不是通過組播學習的,而是通過從apiserver的node接口watch到并靜態(tài)下發(fā)的。
2.1 flannel.1接口是怎么創(chuàng)建的?
一、main.go查找ExtIface(Hypervisor L3出接口,所有vxlan報文都要封裝后走ExtIface出去overlay)。flannel的出接口允許用戶按以下方式來選擇(LookupExtIface):
指定具體接口
指定接口的正則表達式來匹配查找
啥也不指定,由flannel根據(jù)默認網(wǎng)關(guān)找出接口
1.5.4版本默認kubeadm不會指定flannel的出接口,所以出接口選的是默認網(wǎng)關(guān)的接口。如果機器上網(wǎng)絡比較復雜,可能需要手工指定出接口。
二、確定subnet
flannel網(wǎng)絡中,以我們的集群為例,整個flannel網(wǎng)絡是10.244.0.0/16,每個node都是一個子網(wǎng)(subnet),如10.244.0.0/24, 10.244.1.0/24。
subnet的網(wǎng)段、長度是怎么確定的呢?k8s創(chuàng)建時會生成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從內(nèi)核上送到用戶態(tài)的flanneld進程,在用戶態(tài)進程做udp封裝、解封裝,性能不佳,所以生產(chǎn)環(huán)境最好選用vxlan。
三、根據(jù)backend類型創(chuàng)建backend,然后調(diào)用be.RegisterNetwork函數(shù)去初始化flannel接口。步驟如下。
1 創(chuàng)建flannel接口。netlink下內(nèi)核創(chuàng)建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 調(diào)用kube subnet manager獲取租約。ksm會去調(diào)用apiserver的node api,查詢node的PodCIDR,即該節(jié)點的pod overlay網(wǎng)絡,如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接口地址,激活接口,并增加網(wǎng)段路由(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靜態(tài)表項了。
2.2 fdb 轉(zhuǎn)發(fā)數(shù)據(jù)庫
flannel接口配置完畢后,接下來就是在集群中配置fdb表了。要做的其實也就是完成下面這條命令的下發(fā),只是由flannel自動完成,用戶不需要手動操作。
bridge fdb add/append $mac-of-vtep-on-node-2 dev $DEVNAME dst $DESTIP
跟數(shù)據(jù)中心的vxlan不一樣,flannel網(wǎng)絡中的VTEP的MAC并不是通過組播學習的,而是通過apiserver去做的同步(或者是etcd)。前面在創(chuàng)建flannel.1接口時有提到,各個節(jié)點會將自己的VTEP信息上報給apiserver,而apiserver會再同步給各節(jié)點上正在watch node api的listener(flanneld),flanneld拿到了更新消息后,再通過netlink下發(fā)到內(nèi)核,更新fdb表項,從而達到了整個集群的同步。
2.3 newKubeSubnetManager
flanneld會在啟動的時候,創(chuàng)建一個針對apiserver node api的informer(newKubeSubnetManager)。
flannel的子網(wǎng)管理器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作為生產(chǎn)者,會將從k8s那里watch到的信息封裝(nodeToLease,節(jié)點信息轉(zhuǎn)為租約信息)后寫到ksm.events里去。
ksm.events的消費者是backend。backend/vxlan_network.go在run時,會拉起一個goroutine,調(diào)用subnet/watch/WatchLeases(),在這個函數(shù)里批量讀取ksm.events chan,并將批量事件推入backend的接收chan;backend會在其goroutine中處理這些事件:根據(jù)事件中帶的nodes信息,如IP,VtepMAC,下發(fā)到本機內(nèi)核fdb table。
subnet/watch/WatchLeases()做了個優(yōu)化:node比較活躍時,ksm.events里可能會有比較多的events;讀取ksm.events的時候,可以一次性讀出一組events,并且在處理這些events時,可以將同一SUBNET的lease合并,不過事件的總數(shù)不會減少。
第一批事件的處理函數(shù)是handleInitialSubnetEvents,進行初始化+事件處理。其獲取內(nèi)核的fdbtable,之后會根據(jù)獲取的上面WatchLeases丟過來的event,對比fdbtable刷新內(nèi)核fdb table:刪除內(nèi)核多余的表項,增加內(nèi)核缺少的表項,保持跟flannel網(wǎng)絡一致。這里有個疑問,initial的時候,怎么保證event都是Added的呢?如果是刪除的,刷新內(nèi)核arp table的流程是有問題的。
后續(xù)事件的處理函數(shù)是handleSubnetEvents,它比較純粹,根據(jù)event.Type是Added還是Removed,修改內(nèi)核fdb表現(xiàn) 和 flannel維護的routes(后面arp解析時會用到)
fdb更新下發(fā)內(nèi)核相當于調(diào)用了如下命令:
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靜態(tài)表項配置完成,并且可以達到與集群同步更新。
2.4 arp table
fdb完成后,報文轉(zhuǎn)發(fā)沒問題了,但在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.
即:
向用戶態(tài)arp daemon請求app_solicit次;
如果還有舊的MAC地址記錄,單播一下試試;
絕望了,怒而廣播之
(說起來好像跟某些事件有點類似)
所以,明白為啥前面創(chuàng)建flannel.1接口時,需要設置app_solicit為3了嗎?
在flannel網(wǎng)絡中,flanneld會在啟動時監(jiān)聽RTM_GETNEIGH(vxlanDevice.MonitorMisses),flanneld相當于arp daemon;arp請求會在內(nèi)核中通過netlink發(fā)送請求到用戶態(tài)的flanneld,由flanneld根據(jù)該arp請求ip所屬網(wǎng)段(該node上所有ip的MAC也就是VTEP的MAC),查詢其記錄的routes信息,然后netlink下發(fā)內(nèi)核。
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想觸發(fā)一個arp請求,抓包是抓不到arp報文的。
arp table更新下發(fā)內(nèi)核相當于調(diào)用了如下命令:
ip neighbor add/replace $ip-on-node-2 lladdr $mac-of-vtep-on-node-2 dev flannel.1
至此,vxlan的流程就走通了。
3 udp flannel
vxlan要求內(nèi)核版本3.7+,最好3.9+,所以在一些舊版本的linux上就無法使用基于vxlan的flannel了,只能使用基于udp的flannel。
udp flannel原理與vxlan類似,都是package in udp,只是一個在內(nèi)核做報文封裝,一個在用戶態(tài)做(通過tun, pkg/ip/tun.go)。可想而知,udp flannel的性能不會太好,所以除非是內(nèi)核版本太低,一般還是用vxlan flannel性能會比較好。
ref:
Virtual eXtensible Local Area Networking documentation
vxlan在Flannel中的Overlay網(wǎng)絡的實現(xiàn)
Difference of VXLAN L3MISS between flannel and docker overlay implementation
總結(jié)
以上是生活随笔為你收集整理的flannel流程解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LINUX 设置 backspace为删
- 下一篇: 激活你的Chrome浏览器