1. 程式人生 > >flannel vxlan 實現原理【轉】

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。

如圖flannel-ovs

建立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
  1. host-A vxlan0介面生成arp請求10.10.11.1的mac地址
  2. vxlan 驅動封裝新增VNI header,沒有已知的目的mac,使用多播地址
  3. eth0 發出資料包

在host-B上,

  1. host-B收到資料包然後轉發給相應的udp埠(vxlan)
  2. 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 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的程式碼也可以在該函式所在檔案中找到。

參考資料

  1. http://events.linuxfoundation.org/sites/events/files/slides/2013-linuxcon.pdf
  2. http://lartc.org/howto/lartc.kernel.obscure.html
  3. http://hustcat.github.io/vxlan-l3miss-problem/