flannel vxlan 實現原理【轉】
flannel是coreos為kubernets提供的網路解決方案,主要為打通跨節點的容器通訊,其中vxlan模式為flannel實現的一種後端模式,其他模式還包括udp, host-gw等,可以通過flannel官網瞭解更多資訊。
linux vxlan工作原理
flannel的vxlan模式使用的是原生的linux vxlan實現,因此瞭解linux vxlan工作原理對於理解flannel的程式碼實現很有幫助。
在linux vxlan中,主要術語:
- L3 Miss: 目標IP在鄰居表中未找到 (IP Miss,所以才叫L3 Miss嗎?)
- L2 Miss: 目標MAC在vxlan FDB中未找到對應項 (2層的Miss)
- NOLEARNING: 禁止洪泛資料包 (在FDB中未找到相應表項)
vxlan fdb 主要對映目標MAC到vtep IP。
如圖
建立vxlan device
$ip link add vxlan0 type vxlan id 42 group 239.1.1.1 dev eth0 $ip link set vxlan0 address 54:8:20:0:0:A $ip address add 10.10.10.1 dev vxlan0 $ip link set up vxlan0
host-A 10.10.10.1 ping host-B 10.10.11.1
Host-A $ping 10.10.11.1
- host-A vxlan0介面生成arp請求10.10.11.1的mac地址
- vxlan 驅動封裝新增VNI header,沒有已知的目的mac,使用多播地址
- eth0 發出資料包
在host-B上,
- host-B收到資料包然後轉發給相應的udp埠(vxlan)
- vxlan驅動解封裝,vxlan0接收到arp request包,並生成相應的arp reply包,新增相應的vxlan header,目的mac為56:bb:01:0f:cb:A
這樣在host-B的fdb中,會學到56:bb:01:0f:cb:A到轉發規則,如下:
Host-B $bridge fdb show dev vxlan0 56:bb:01:0f:cb:A dev vxlan0 dst 192.168.1.10 self 0:0:0:0:0:0 dev vxlan0 dst 239.1.1.1 via eth0 self permanent
第二條規則就是在目的mac為止時使用多播地址的相應規則。
flannel實現方式
因為flannel是為k8s提供的網路解決方案,而在k8s中,每一臺host會分配一個網段,該網段所有啟動的容器均在這臺機器上,所以,對於flnanel來說,很多資訊都是已知的,可以簡化flannel的vxlan fdb(不需要處理未知的MAC地址情況)以及相應的程式碼實現。
在flannel中,flannel會在每一臺啟動了flannel agentd的機器上建立一個vxlan device(上述的vxlan0),名稱是flannel.1(1為vni號),flannel agent會根據分配的網段資訊和vxlan device資訊(vxlan device的mac地址),動態的修改host上的鄰居表,並結合vxlan device的fdb實現跨主機的docker容器的通訊。
一個例子
flannel的網段分配資訊是通過etcd 記錄的,在etcd中設定相應資訊:
etcd $etcdctl get /flannel/network/config
{ "Network": "10.10.0.0/16", "Backend": { "Type": "vxlan", "VNI": 1 } }
在host-A上執行flannel agent,agent在etcd中分配網段10.10.10.0/24,agent建立flannel.1裝置介面,配置IP 10.10.10.0 MAC 56:bb:01:0f:cb:A,配置路由,整個大段通過flannel.1, 這樣overlay網路流量通過flannel.1轉發處理,然後啟動docker0,通過指定bip 10.10.10.1/24啟動,這樣在host-A上的容器使用網段10.10.10.1/24。
同理在host-B上執行flannel agent,agent進行的相應配置過程類似。
整個例子如圖
flannel vxlan相應工作流程
由於flannel agentd知道所有的網段分配資訊以及每臺host上的flannel.1裝置的IP,MAC,因此每一個網段在進行vxlan fdb轉發時,可以使用host上flannel.1的MAC地址。
在host-A上,執行
host-A# bridge fdb show dev flannel.1 56:bb:01:0f:cb:B dst 192.168.1.11 self permanent
在host-B上,執行
host-B# bridge fdb show dev flannel.1 56:bb:01:0f:cb:A dst 192.168.1.10 self permanent
如圖c1 ping c2時,如果容器c1 IP(10.10.10.2), 容器c2 IP(10.10.11.2), 因為host-A 的鄰居表裡沒有c2 IP到MAC表項,flannel agent會收到相應的l3 miss(netlink)訊息,然後flannel agent會反應式的設定c2 的IP到MAC表項為10.10.11.2-56:bb:01:0f:cb:B,這樣在fdb中MAC 56:bb:01:0f:cb:B就對應到host-B的flannel.1。
因為flannel知道必要的網路資訊,所以flannel直接按段處理了L3 miss的訊息,L2的fdb直接在啟動時根據etcd資訊靜態配置好,這樣整個網路就連通了。
L3 miss程式碼
如程式碼,在L3 miss程式碼中,通過miss的IP在所有段裡匹配然後設定對應的vtepMac,即: 上述的c2 IP是對應到網段10.10.11.0/24的,然後相應的vtepMAC就對應到host-B的flannel.1的MAC,程式碼中是通過路由資訊記錄的,也儲存了裝置的MAC。
func (n *network) handleL3Miss(miss *netlink.Neigh) {
log.Infof("L3 miss: %v", miss.IP)
rt := n.rts.findByNetwork(ip.FromIP(miss.IP))
if rt == nil {
log.Infof("Route for %v not found", miss.IP) return } if err := n.dev.AddL3(neigh{IP: ip.FromIP(miss.IP), MAC: rt.vtepMAC}); err != nil { log.Errorf("AddL3 failed: %v", err) } else { log.Info("AddL3 succeeded") } }
L2的程式碼也可以在該函式所在檔案中找到。