雲原生虛擬網絡之 VXLAN 協議

轉載請聲明出處哦~,本篇文章發佈於luozhiyun的博客:https://www.luozhiyun.com/archives/687

第一次認識 VXLAN 是在看 k8s 裏面用到的叫 flannel 的網絡插件有個 VXLAN 模式,利用它實現了 Overlay Network(覆蓋網絡),可以把所有容器連通在一起。所以本篇文章,我們一起來看看 VXLAN 是怎麼將不同容器之間的網絡進行打通的。

概述

在看 VXLAN 之前,我們先來看看它的前輩 VLAN。VLAN 的全稱是“虛擬局域網”(Virtual Local Area Network),它是一個二層(數據鏈路層)的網絡,用來分割廣播域,因爲隨着計算機的增多,如果僅有一個廣播域,會有大量的廣播幀(如 ARP 請求、DHCP、RIP 都會產生廣播幀)轉發到同一網絡中的所有客戶機上。

這樣造成了沒有必要的浪費,一方面廣播信息消耗了網絡整體的帶寬,另一方面,收到廣播信息的計算機還要消耗一部分CPU時間來對它進行處理。造成了網絡帶寬和CPU運算能力的大量無謂消耗。

在這種情況下出現了 VLAN 技術。這種技術可以把一個 LAN 劃分成多個邏輯的 VLAN ,每個 VLAN 是一個廣播域,VLAN 內的主機間通信就和在一個 LAN 內一樣,而 VLAN 間則不能直接互通,廣播報文就被限制在一個 VLAN 內。如下圖所示。

vlan

然而 VLAN 有兩個明顯的缺陷,第一個缺陷在於 VLAN Tag 的設計,定義 VLAN 的 802.1Q規範是在 1998 年提出的,只給 VLAN Tag 預留了 32 Bits 的存儲空間,其中只有12 Bits 才能用來存儲 VLAN ID。當雲計算數據中心出現後,VLAN ID只有12 Bits 意味着雲廠商只能提供給 4096 個租戶使用,這個數量限制了雲廠商的客戶數量和營收。

VLAN 第二個缺陷在於它本身是一個二層網絡技術,但是在兩個獨立數據中心之間信息只能夠通過三層網絡傳遞,雲計算的發展普及很多業務有跨數據中心運作的需求,所以數據中心間傳遞 VLAN Tag 又是一件比較麻煩的事情;並且在虛擬網絡中,一臺物理機會有多個容器,容器與 VM 相比也是呈數量級增長,每個虛擬機都有獨立的 IP 地址和 MAC 地址,這樣帶給交換機的壓力也是成倍增加。

基於上面種種原因,VXLAN 也就呼之欲出了。

VXLAN 協議

協議報文

VXLAN(Virtual eXtensible LAN)虛擬可擴展局域網採用 L2 over L4 (MAC in UDP)的報文封裝模式,把原本在二層傳輸的以太幀放到四層 UDP 協議的報文體內,同時加入了自己定義的 VXLAN Header。在 VXLAN Header 裏直接就有 24 Bits 的 VLAN ID,同樣可以存儲 1677 萬個不同的取值,VXLAN 讓二層網絡得以在三層範圍內進行擴展,不再受數據中心間傳輸的限制。VXLAN 工作在二層網絡( IP 網絡層),只要是三層可達(能夠通過 IP 互相通信)的網絡就能部署 VXLAN 。VXLAN 的整個報文結構如圖:

vxlan

上圖我們可以看到 VXLAN 報文對原始 Original Layer2 Frame 進行了包裝:

  • VXLAN Header 8字節,其中包含24Byte 的 VNI 字段,用來定義VXLAN網絡中不同的租戶,可以存儲 1677 萬個不同的取值;
  • UDP Header,其中 VXLAN 頭和原始以太幀一起作爲 UDP 的數據,頭中目的端口號(VXLAN Port)固定爲4789,源端口按流隨機分配(通過 MAC,IP,四層端口號進行 hash 操作), 這樣可以更好的做 ECMP。;
  • Outer IP Header 封裝外層IP頭,封裝了目的IP地址和源IP地址,這裏 IP 指的是宿主機的 IP 地址;
  • Outer MAC Header 封裝外層以太頭,封裝了源MAC地址,目的MAC地址,這裏 MAC 地址指的是宿主機 MAC 地址;

工作模型

vxlan2

從上面圖 VXLAN 網絡網絡模型中我們可以發現 VXLAN 網絡中出現了以下幾個組件:

  • VTEP(VXLAN Tunnel Endpoints,VXLAN隧道端點):VXLAN 網絡的邊緣設備,是 VXLAN 隧道的起點和終點,負責 VXLAN 協議報文的封包和解包,也就是在虛擬報文上封裝 VTEP 通信的報文頭部。。VTEP 可以是網絡設備(比如交換機),也可以是一臺機器(比如虛擬化集羣中的宿主機);
  • VNI(VXLAN Network Identifier):前文提到,以太網數據幀中VLAN只佔了12比特的空間,這使得VLAN的隔離能力在數據中心網絡中力不從心。而 VNI 的出現,就是專門解決這個問題的。一般每個 VNI 對應一個租戶,並且它是個 24 位整數,也就是說使用 VXLAN 搭建的公有云可以理論上可以支撐最多1677萬級別的租戶
  • VXLAN 隧道:隧道是一個邏輯上的概念,在 VXLAN 模型中並沒有具體的物理實體想對應。隧道可以看做是一種虛擬通道,VXLAN 通信雙方(圖中的虛擬機)認爲自己是在直接通信,並不知道底層網絡的存在。從整體來說,每個 VXLAN 網絡像是爲通信的虛擬機搭建了一個單獨的通信通道,也就是隧道;

通信過程

VXLAN 網絡中通常 VTEP 可能會有多條隧道,VTEP 在進行通信前會通過查詢轉發表 FDB 來確定目標 VTEP 地址,轉發表 FDB 用於保存遠端虛擬機/容器的 MAC 地址,遠端 VTEP IP,以及 VNI 的映射關係,而轉發表通過泛洪和學習機制來構建。目標MAC地址在轉發表中不存在的流量稱爲未知單播(Unknown unicast)。VXLAN 規範要求使用 IP 多播進行洪泛,將數據包發送到除源 VTEP 外的所有 VTEP。目標 VTEP 發送迴響應數據包時,源 VTEP 從中學習 MAC 地址、VNI 和 VTEP 的映射關係,並添加到轉發表中。

下面我們看看首次通信過程看看 VTEP 是如何學習的:

vxlan3

  1. 由於是首次進行通信,VM-A 上沒 VM-B 的 MAC 地址,所以會發送 ARP 廣播報文請求 VM-B 的 MAC 地址。VM-A 發送源 MAC 爲 VM-A 、目的 MAC 爲全F、源 IP 爲 IP-A、目的 IP 爲 IP-B 的 ARP 廣播報文,請求VM-B 的 MAC 地址;
  2. VTEP-1 收到 ARP 請求後,根據二層子接口上的配置判斷報文需要進入 VXLAN 隧道。VTEP-1 會對報文進行封裝,封裝的外層源 IP 地址爲本地 VTEP(VTEP-1)的 IP 地址,外層目的 IP 地址爲對端 VTEP(VTEP-2 和VTEP-3)的 IP 地址;外層源 MAC 地址爲本地 VTEP 的 MAC 地址,而外層目的 MAC 地址爲去往目的 IP 的網絡中下一跳設備的 MAC 地址;
  3. 報文到達VTEP-2和VTEP-3後,VTEP對報文進行解封裝,得到VM-A發送的原始報文。然後 VTEP-2 和 VTEP-3 根據二層子接口上的配置對報文進行相應的處理並在對應的二層域內廣播。VM-B 和 VM-C 接收到 ARP 請求後,比較報文中的目的IP地址是否爲本機的IP地址。VM-C 發現目的IP不是本機IP,故將報文丟棄;VM-B 發現目的IP是本機IP,則對ARP請求做出應答;
  4. VM-B 會根據請求的 ARP 包進行 ARP 應答報文爲單播報文,報文源 MAC 爲MAC-B,目的 MAC 爲 MAC-A,源 IP 爲 IP-B 、目的 IP 爲 IP-A;
  5. VTEP-2 接收到 VM-B 發送的 ARP 應答報文後,識別報文所屬的 VNI,VTEP-2 對報文進行封裝。封裝的外層源IP地址爲本地 VTEP(VTEP-2)的 IP 地址,外層目的IP地址爲對端 VTEP(VTEP-1)的IP地址;外層源MAC地址爲本地 VTEP 的 MAC 地址,而外層目的MAC地址爲去往目的IP的網絡中下一跳設備的MAC地址;
  6. 報文到達 VTEP-1 後,VTEP-1 對報文進行解封裝,得到 VM_B 發送的原始報文。同時,VTEP-1 學習VM_B 的MAC地址、VNI 和遠端 VTEP 的IP地址(IP-2)的對應關係,並記錄在本地 MAC 表中。之後,VTEP-1 將解封裝後的報文發送給VM-A;
  7. 至此,VM-A 就收到了 ARP 廣播報文響應 VM-B 的 MAC 地址;

除了上面這種多播的方式進行學習的方式來獲取 MAC <--> VNI <--> VTEP IP這一組映射關係以外還有一種方式,就是分佈式的控制中心。

例如 Flannel 的 VXLAN 模式網絡中的 VTEP 的 MAC 地址並不是通過多播學習的,而是通過 apiserver 去做的同步(或者是etcd)。每個節點在創建 Flannel 的時候,各個節點會將自己的VTEP信息上報給 apiserver,而apiserver 會再同步給各節點上正在 watch node api 的 listener(Flanneld),Flanneld 拿到了更新消息後,再通過netlink下發到內核,更新 FDB(查詢轉發表) 表項,從而達到了整個集羣的同步。這個 apiserver 就起到了分佈式的控制中心的作用, 不再需要發送多餘的請求去滿網絡訪問獲取對應的映射信息。

一個例子

下面,我們自己動手弄一個 VXLAN 網絡,然後抓包看一下,是不是和我們上面長篇大論講述的結論是一致的。需要注意的是,在自己虛擬機上實驗的時候,爲了避免不必要的麻煩,記得關防火牆,centos命令是:systemctl stop firewalld

下面我們打算用 docker 來進行實驗,思路就是在兩個容器宿主機上各創建一個VXLAN接口,並且將VXLAN接口接入docker網橋的端口上,如下圖:

vxlan4

對於 docker 來說,是無法直接跨節點通信的,我們這裏使用 VXLAN 來模擬跨節點通信。

docker 默認使用的是 172.17.0.0/16 網段,docker容器的IP地址都會從 172.17.0.2 開始分配。爲了能利用--ip參數自定義IP地址的功能,需要先創建一個自定義網絡,指定網段172.18.0.0/16。

[root@localhost ~]# docker network create --subnet 172.18.0.0/16 mynetwork

## mynetwork 新的bridge網絡被創建
[root@localhost ~]# docker network ls
NETWORK ID     NAME        DRIVER    SCOPE
eb07bfe03ee3   bridge      bridge    local
7014433d34cf   host        host      local
87133e370c6c   mynetwork   bridge    local
82472e531205   none        null      local

我們還可以看到 docker 爲我們新的網絡創建了一個新的網橋:

[root@localhost ~]# brctl show
bridge name     bridge id               STP enabled     interfaces
br-87133e370c6c         8000.0242233b251a       no              veth385f866
                                                        vxlan_docker
docker0         8000.024213087f4b       no

創建一個新的容器,如下:

## VM1
[root@localhost ~]# docker run -itd --net mynetwork --ip 172.18.0.10 centos
## VM2
[root@localhost ~]# docker run -itd --net mynetwork --ip 172.18.0.11 centos

--net指定自定義網絡
--ip指定IP地址
centos指定image

上面我們雖然創建好了網絡,但是我們直接進去是無法通信的:

[root@localhost ~]#  docker exec -it 5a2e519610bb /bin/bash

[root@5a2e519610bb /]# ping  172.18.0.11
PING 172.18.0.11 (172.18.0.11) 56(84) bytes of data.
From 172.18.0.10 icmp_seq=1 Destination Host Unreachable

--- 172.18.0.11 ping statistics ---
11 packets transmitted, 0 received, +8 errors, 100% packet loss, time 10007ms
pipe 4

下面我們在兩個容器宿主機上各創建一個VXLAN接口,並且將VXLAN接口接入docker網橋的端口上:

## VM1
[root@localhost ~]#  ip link add vxlan_docker type vxlan id 200 remote 192.168.13.132 dstport 4789 dev ens33
[root@localhost ~]#  ip link set vxlan_docker up
[root@localhost ~]#  brctl addif br-87133e370c6c vxlan_docker

## VM2
[root@localhost ~]#  ip link add vxlan_docker type vxlan id 200 remote 192.168.13.131 dstport 4789 dev ens33
[root@localhost ~]#  ip link set vxlan_docker up
[root@localhost ~]#  brctl addif br-26d918129b18 vxlan_docker

上面我們分別使用 ip link add爲 VM1 和 VM2 分別創建了創建 VNI 爲200的 VXLAN 網絡接口,名稱爲vxlan_docker;然後使用 brctl addif 把新創建的VXLAN接口vxlan_docker接入到 docker 網橋中。

然後我們進入到容器中發現可以 ping 通了:

[root@5a2e519610bb /]# ping  172.18.0.11
PING 172.18.0.11 (172.18.0.11) 56(84) bytes of data.
64 bytes from 172.18.0.11: icmp_seq=1 ttl=64 time=1.14 ms
64 bytes from 172.18.0.11: icmp_seq=2 ttl=64 time=0.620 ms
^C
--- 172.18.0.11 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 0.620/0.879/1.139/0.261 ms

下面在宿主機上抓包看看:

[root@localhost ~]#  tcpdump -i ens33 host 192.168.13.131 -s0 -v -w vxlan_vni_1.pcap

image-20220724203245594

上面我們看到,首先是發送出 ARP 請求獲取 MAC 地址,外層是 UDP 報文,目的端口是4789,目的 IP 是宿主機 VM2 的 IP;VXLAN 報文頭 VNI 是200 ;ARP 請求源 MAC 地址是 VM1 裏面發送消息的容器 MAC 地址,目的地址沒有獲取到,爲 ff:ff:ff:ff:ff:ff

在收到回包之後,172.18.0.11回覆 ARP 響應包告知 MAC 地址是 02:42:ac:12:00:0b,然後就可以正常發送 ICMP 包了。

總結

本篇內容,從介紹 VLAN 開始講述 VLAN 有哪些缺點,以及爲什麼會有 VXLAN。然後講了 VXLAN 的協議報文是如何封裝的,整體的工作模型是怎樣的,以及 VXLAN 通信過程熟悉了它是怎麼運作的,最後通過一個例子實戰自己動手在兩個節點上實現容器間的相互通信。相信到了這裏,對 VXLAN 應該有了不少了解。

資料

https://zhuanlan.zhihu.com/p/109349917

https://zhuanlan.zhihu.com/p/35616289

https://forum.huawei.com/enterprise/zh/thread-334207.html

http://icyfenix.cn/immutable-infrastructure/network/linux-vnet.html

https://www.ciscolive.com/c/dam/r/ciscolive/emea/docs/2019/pdf/DEVWKS-1445.pdf

http://just4coding.com/2017/05/21/vxlan/

https://www.linuxidc.com/Linux/2019-03/157820.htm

https://zhuanlan.zhihu.com/p/130277008

https://juejin.cn/post/6994825163757846565

https://ieevee.com/tech/2017/08/12/k8s-flannel-src.html

掃碼_搜索聯合傳播樣式-白色版 1

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章