VXLAN協(xié)議VXLAN是Virtual eXtensible Local Area Network的縮寫,RFC 7348的標題“A Framework for Overlaying Virtualized Layer 2 Networks over Layer 3 Networks”,說明了VXLAN是一個在傳統(tǒng)Layer 3網絡上架設出來的Layer 2 overlay網絡。RFC Abstract如下:
在下面的場景中有兩服務器,由三層物理網絡連接。這兩臺服務器可能在同一個機架上,或者在不同的機架上,亦或在物理距離相距甚遠的不同數(shù)據(jù)中心里。有4個VxLAN overlay網絡,標識分別為VNI 22, 34, 74, 98。來看看Server 1上的虛擬機VM1-1和Server 2上的虛擬機VM2-4,它們屬于相同的標識為VNI 22的VXLAN overlay網絡。虛擬機并不知道overlay網絡,也不知道VxLAN報文的封裝和解封過程,因為這些過程都由物理服務器上的VTEP完成??吹竭@里,即使你沒聽說過VTEP這個概念,也大概知道它的作用了吧,VTEP (VXLAN Tunnel End Point), An entity that originates and/or terminates VXLAN tunnels,VTEP負責VxLAN的封裝和解封裝。 這種部署方法是基于物理機能夠感知VXLAN協(xié)議,從而可以作為VxLAN的封裝和解封點(VTEP) 。另外一種部署方法是把VTEP設備的位置放在網絡設備中,比如交換機,可以作為一個VXLAN網絡來負責VxLAN報文的封裝和解封。在數(shù)據(jù)中心的部署中,物理機作為VTEP的部署方式能更好地和物理網絡設備解耦,并且更加方便運維。 Linux上對VXLAN的支持快速搭建和理解VXLAN的方法之一就是利用Linux。從內核3.7版本開始,Linux就開始支持VXLAN。到了內核3.12版本,Linux對VXLAN的支持已經完備,支持單播和組播,IPv4和IPv6。利用man查看ip的link子命令,可以查看是否有vxlan type,如下: $ man ip-link
搜索vxlan,可以看到如下描述,可以利用ip link add增加類型為VXLAN的link。
下面的實驗在如下環(huán)境中完成:
場景1: 最簡單的點對點VXLAN創(chuàng)建簡單的點對點VXLAN環(huán)境非常簡單。如下圖所示,只需要在兩個機器(物理機或者虛擬機都可以,本實驗中是云上的虛擬機環(huán)境)中各創(chuàng)建一個vxlan類型的網絡接口即可,vxlan類型的接口vxlan1可以作為上文中提到的VTEP。 在上面的環(huán)境中,注意我們將vxlan網絡接口配置上IP地址,在10.0.0.0/24網段內。在IP地址分配后,Linux系統(tǒng)的路由表就會創(chuàng)建一條路由,去往10.0.0.0/24網段的報文走網絡接口vxlan1出去。vm1上去往10.0.0.0/24的報文,在vxlan1上會做VXLAN封裝,內層地址是10.0.0.106,外層地址是172.31.0.106。VXLAN報文通過物理網絡達到對端vm2上的VETP vxlan1,在vm2的vxlan1接口上做VXLAN協(xié)議的解封裝,從而結束整個過程。 上圖是一個物理上的示意圖,在邏輯上形成的VXLAN overlay網絡環(huán)境如下圖,虛線部分示意出來的Overlay Network和VXLAN Tunnel都是邏輯上的概念。如果有容器和虛機被接入邏輯上的Overlay網絡10.0.0.0/24,它們完全不用感知底層物理網絡,看起來對端是和自己在同一個二層環(huán)境里,就是像是在VTEP設備的上面直接構建了一條VXLAN Tunnel,把Overlay網絡里的網絡接口直接在二層打通。 具體的配置只需要3條命令。如下,在vm1上執(zhí)行如下命令: # ip link add vxlan1 type vxlan id 1 remote 172.31.0.107 dstport 4789 dev eth0 # ip link set vxlan1 up # ip addr add 10.0.0.106/24 dev vxlan1 上面的第一條命令創(chuàng)建了一個Linux上類型為vxlan的網絡接口,名為vxlan1。
第二條命令讓vxlan1接口up起來。第三條命令給設備分配IP地址10.0.0.106, 子網掩碼為24 (255.255.255.0)。 在vm2上,利用類似方法創(chuàng)建名為vxlan1的網絡接口。 # ip link add vxlan1 type vxlan id 1 remote 172.31.0.106 dstport 4789 dev eth0 # ip link set vxlan1 up # ip addr add 10.0.0.107/24 dev vxlan1 以上簡單的命令就完成了所有配置。用ifconfig可以看到vxlan1網絡接口,如下: # ifconfig vxlan1 vxlan1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450 inet 10.0.0.106 netmask 255.255.255.0 broadcast 0.0.0.0 ether 22:2d:c4:f0:c7:29 txqueuelen 1000 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 看下vm1的如下路由表,去往目的網段10.0.0.0/24的報文將走vxlan1接口。 # route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 172.31.0.253 0.0.0.0 UG 0 0 0 eth0 10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 vxlan1 169.254.0.0 0.0.0.0 255.255.0.0 U 1002 0 0 eth0 在vm1上ping overlay網絡的對端IP地址10.0.0.107,可以ping通。 # ping 10.0.0.107 -c 3 PING 10.0.0.107 (10.0.0.107) 56(84) bytes of data. 64 bytes from 10.0.0.107: icmp_seq=1 ttl=64 time=0.447 ms 64 bytes from 10.0.0.107: icmp_seq=2 ttl=64 time=0.361 ms 64 bytes from 10.0.0.107: icmp_seq=3 ttl=64 time=0.394 ms --- 10.0.0.107 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2000ms rtt min/avg/max/mdev = 0.361/0.400/0.447/0.042 ms 在ping包的同時,用tcpdump抓vm1 eth0網卡的包。因為報文到達eth0前經過了網絡接口vxlan1, 完成了VXLAN的封裝,所以在抓包結果里應該能看到完整的VXLAN報文。 抓包時可以只抓和對端172.31.0.107通信的報文,如下: # tcpdump -i eth0 host 172.31.0.107 -s0 -v -w vxlan_vni_1.pcap 抓包結果如下,wireshark自動將UDP目的端口為4789的報文識別成VXLAN報文,直接顯示內層的報文,protocol為ICMP協(xié)議。如果使用Linux默認接口8472,顯示的應該是UDP協(xié)議,還需要修改wireshark的協(xié)議設置,讓其識別成VXLAN。 場景2: 容器跨主機通信上面最簡單的點對點VXLAN實驗只是個簡答的演示,沒有太多實際工程意義,本節(jié)用容器通信來演示一個更加完整的場景。 場景描述:在vm1和vm2上各部署一個docker容器,默認情況下,一個容器宿主機上的容器能夠直接用私網IP地址通信,因為它們利用一個網橋接在一起。而不同宿主機上的容器無法直接用私網IP地址通信。k8s等docker部署軟件中的網絡組建實際上完成了這部分工作,讓不同宿主機的容器能夠直接通信。本節(jié)使用原生docker,以及在宿主機上自建的vxlan網絡接口,來打通不同宿主機上容器,讓它們可以直接利用內網IP通信。 注意:因為實驗在云上的虛擬機上完成,上面提到的容器宿主機,用的是云上的虛擬機。容器宿主機也可以是物理機,實驗效果不變。 準備docker容器安裝docker的過程不展開了,docker官方文檔有詳細的描述。在Linux安裝了docker后,可以看到多了一個docker0的網絡接口,默認在172.17.0.0/16網段。這個是連接本地多個容器的網橋。 # ifconfig docker0 docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450 inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255 ether 02:42:44:e8:74:e8 txqueuelen 0 (Ethernet) RX packets 6548 bytes 360176 (351.7 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 7489 bytes 40249455 (38.3 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 使用默認172.17.0.0/16網段,docker容器的IP地址都會從172.17.0.2開始分配。為了能使vm1和vm2上的容器使用不同的IP地址,在利用docker run啟動容器的時候需要能自定義IP地址,而利用--ip參數(shù)自定義IP地址的功能只能在自定網絡中支持,所以先創(chuàng)建一個自定義網絡,指定網段172.18.0.0/16。 # docker network create --subnet 172.18.0.0/16 mynetwork 3231f89d69f6b3fbe2550392ebe4d00daa3d19e251f66ed2d81f61f2b9184362 # docker network ls NETWORK ID NAME DRIVER SCOPE 1cb284a6cb33 bridge bridge local 069538be0246 host host local 3231f89d69f6 mynetwork bridge local 0b7934996485 none null local 利用docker network ls查看,可以看到一個新的bridge網絡被創(chuàng)建,名稱為我指定的mynetwork。利用ifconfig可以看到多了一個網絡接口,名字不是dockerXX,而直接以br開頭,是一個網橋。 br-3231f89d69f6: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 inet 172.18.0.1 netmask 255.255.0.0 broadcast 172.18.255.255 ether 02:42:97:22:a5:f9 txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 創(chuàng)建一個新的容器,如下: # docker run -itd --net mynetwork --ip 172.18.0.2 centos 16bbaeaaebfccd2a497e3284600f5c0ce230e89678e0ff92f6f4b738c6349f8d
查看容器ID和狀態(tài),并且登錄SHELL,如下: # docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 16bbaeaaebfc centos "/bin/bash" 2 minutes ago Up 2 minutes condescending_swartz # docker exec -it 16bbaeaaebfc /bin/bash [root@16bbaeaaebfc /]# ifconfig bash: ifconfig: command not found 注意:docker為了創(chuàng)建容器的效率,通常都用了size很小的image,意味著很多常用工具需要安裝,比如centos image里面的ifconfig??梢岳脃um whatprovides ifconfig命令查看ifconfig輸入哪個包,查到屬于net-tools-2.0-0.22.20131004git.el7.x86_64包,直接用yum install net-tools -y安裝即可。再執(zhí)行ifconfig命令,可以看到容器eth0網卡的IP地址為172.18.0.2。 [root@16bbaeaaebfc /]# ifconfig eth0 eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.18.0.2 netmask 255.255.0.0 broadcast 172.18.255.255 ether 02:42:ac:12:00:02 txqueuelen 0 (Ethernet) RX packets 3319 bytes 19221325 (18.3 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 2015 bytes 132903 (129.7 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 在vm2上執(zhí)行同樣的操作,在創(chuàng)建新容器的時候,指定IP地址為172.18.0.3,容器的環(huán)境即準備完畢。在vm1上的centos 容器中ping 172.18.0.3,和預期一致,是無法ping通的。 [root@16bbaeaaebfc /]# ping 172.18.0.3 -c 2 PING 172.18.0.3 (172.18.0.3) 56(84) bytes of data. From 172.18.0.2 icmp_seq=1 Destination Host Unreachable From 172.18.0.2 icmp_seq=2 Destination Host Unreachable --- 172.18.0.3 ping statistics --- 2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 1000ms pipe 2 [root@16bbaeaaebfc /]# ping 172.18.0.1 -c 2 PING 172.18.0.1 (172.18.0.1) 56(84) bytes of data. 64 bytes from 172.18.0.1: icmp_seq=1 ttl=64 time=0.060 ms 64 bytes from 172.18.0.1: icmp_seq=2 ttl=64 time=0.079 ms --- 172.18.0.1 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 999ms rtt min/avg/max/mdev = 0.060/0.069/0.079/0.012 ms 創(chuàng)建VXLAN接口接入docker網橋先來梳理下docker及docker容器在Linux宿主機網絡模塊中做的操作,梳理清楚之后會發(fā)現(xiàn)打通不同宿主機上docker容器的方法非??簡單。從宿主Linux系統(tǒng)的視角看操作系統(tǒng)中的網絡設備,總結如下:
為了方便理解,在默認網段172.17.0.0/16中創(chuàng)建2個容器,在自定義網段中上文已經創(chuàng)建了1個docker容器,利用btctl查看網橋及其接口,如下: # brctl show bridge name bridge id STP enabled interfaces br-3231f89d69f6 8000.02429722a5f9 no veth2fa4c50 docker0 8000.024244e874e8 no vethc7cd982 vethd3d0c18 從上面的輸出結果可以看到,默認網橋docker0上,有vethc7cd982和vethd3d0c18兩個網絡接口接入。在定義網絡網橋br-3231f89d69f6一個端口上,veth2fa4c50網絡接口接入。這三個veth網絡接口分別連接著一個docker容器的eth0網絡接口,連接著同一個網橋的veth網絡接口vethc7cd982和vethd3d0c18默認二層能通。 有了上面的梳理和本文第一節(jié)VXLAN網絡接口的基礎知識,想必打通不同宿主機上docker容器的方法也比較清晰了。思路就是在兩個容器宿主機上各創(chuàng)建一個VXLAN接口,并且將VXLAN接口接入docker網橋的端口上,如下圖: 有了VXLAN接口的連接后,從vm1上docker容器發(fā)出的包到達docker網橋后,可以從網橋的VXLAN接口出去,從而報文在VETP(VXLAN接口)處被封裝成VXLAN報文,再從物理網絡上到達對端VETP所在的主機vm2。對端VTEP能正確解包VXLAN報文的話,隨后即可將報文通過vm2上的docker網橋送到上層的docker容器中。 具體的配置如下,在vm1上: # ip link add vxlan_docker type vxlan id 200 remote 172.31.0.107 dstport 4789 dev eth0 # ip link set vxlan_docker up # brctl addif br-3231f89d69f6 vxlan_docker
在vm2上,輸入如下命令: # ip link add vxlan_docker type vxlan id 200 remote 172.31.0.106 dstport 4789 dev eth0 # ip link set vxlan_docker up # brctl addif br-f4b35af34313 vxlan_docker 在vm1的docker容器上再ping 172.18.0.3,結果如下,ping可以通。注意RTT的時間,ping 172.18.0.3的RTT在10^(-1)毫秒級別,ping 172.18.0.1的RTT在10^(-2)毫秒級別,前者是走物理網絡的延遲,后者是協(xié)議棧的延遲,兩者有量級上的差別。 # docker exec -it 16bbaeaaebfc ifconfig eth0 eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.18.0.2 netmask 255.255.0.0 broadcast 172.18.255.255 ether 02:42:ac:12:00:02 txqueuelen 0 (Ethernet) RX packets 3431 bytes 19230266 (18.3 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 2132 bytes 141908 (138.5 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 # docker exec -it 16bbaeaaebfc ping 172.18.0.3 -c 2 PING 172.18.0.3 (172.18.0.3) 56(84) bytes of data. 64 bytes from 172.18.0.3: icmp_seq=1 ttl=64 time=0.544 ms 64 bytes from 172.18.0.3: icmp_seq=2 ttl=64 time=0.396 ms --- 172.18.0.3 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1001ms rtt min/avg/max/mdev = 0.396/0.470/0.544/0.074 ms # # docker exec -it 16bbaeaaebfc ping 172.18.0.1 -c 2 PING 172.18.0.1 (172.18.0.1) 56(84) bytes of data. 64 bytes from 172.18.0.1: icmp_seq=1 ttl=64 time=0.072 ms 64 bytes from 172.18.0.1: icmp_seq=2 ttl=64 time=0.072 ms --- 172.18.0.1 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 999ms rtt min/avg/max/mdev = 0.072/0.072/0.072/0.000 ms 最后說明,本節(jié)只是為了演示Linux VXLAN的用于而構造了這個簡單但沒有實際用處的場景,在跨主機環(huán)境的容器之間利用VXLAN從二層打通。在工程中做容器跨主機通信時有很多方面需要考慮,也有很多項目在致力于這方面的研究。比如Flannel,通過給每臺宿主機分配一個子網的方式為容器提供虛擬網絡,它基于Linux TUN/TAP,使用UDP封裝IP包來實現(xiàn)L3 overlay網絡,并借助etcd維護網絡的分配情況。Github上有項目的文檔。 Linux公社的RSS地址:https://www./rssFeed.aspx 本文永久更新鏈接地址:https://www./Linux/2019-03/157820.htm |
|
來自: 點點閱 > 《通信協(xié)議》