本文首發於個人公衆號 Linux雲計算網絡(id: cloud_dev),專一於乾貨分享,號內有 10T 書籍和視頻資源,後臺回覆 「1024」 便可領取,歡迎你們關注,二維碼文末能夠掃。
在上篇文章中,咱們已經探討了 VxLAN 的概念和基本原理,本文就基於 Linux 對 VxLAN 作一個實踐。若是有相關概念不懂的能夠先看那篇文章。html
首先,來看下 Linux 對 VxLAN 的支持,Linux 對 VxLAN 協議的支持時間並不久,2012 年 Stephen Hemminger 才把相關的工做合併到 kernel 中,並最終出如今 kernel 3.7.0 版本。爲了穩定性和不少的功能,你能夠會看到某些軟件推薦在 3.9.0 或者 3.10.0 之後版本的 kernel 上使用 VxLAN。git
這些版本的 Linux 對 VxLAN 的支持已經完備,支持單播和組播,IPv4 和 IPv6。利用 man 查看 ip 的 link 子命令,能夠查看是否有 vxlan type,以下:docker
man ip link
如下的實驗在以下的環境中完成:編程
建立簡單的點對點 VxLAN 環境很是簡單。以下圖所示,只須要在兩個機器(物理機或者虛擬機均可以,本實驗中是雲上的虛擬機環境)中各建立一個 vxlan 類型的網絡接口便可,vxlan 類型的接口 vxlan1 能夠做爲的 VTEP。centos
在上面的環境中,注意咱們將 vxlan 網絡接口配置上 IP 地址,在 10.0.0.0/24 網段內。在 IP 地址分配後,Linux 系統的路由表就會建立一條路由,去往 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 協議的解封裝,從而結束整個過程。bash
上圖是一個物理上的示意圖,在邏輯上造成的 VxLAN overlay 網絡環境以下圖,虛線部分示意出來的 Overlay Network 和 VxLAN Tunnel 都是邏輯上的概念。若是有容器和虛機被接入邏輯上的 Overlay 網絡 10.0.0.0/24,它們徹底不用感知底層物理網絡,看起來對端是和本身在同一個二層環境裏,就是像是在 VTEP 設備的上面直接構建了一條 VxLAN Tunnel,把 Overlay 網絡裏的網絡接口直接在二層打通。網絡
具體的配置只須要 3 條命令。以下,在 vm1 上執行以下命令:tcp
# 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
上面的第一條命令建立了一個 Linux 上類型爲 vxlan 的網絡接口,名爲 vxlan1。ide
第二條命令讓 vxlan1 接口 up 起來。第三條命令給設備分配 IP 地址 10.0.0.106, 子網掩碼爲 24 (255.255.255.0)。工具
在 vm2 上,利用相似方法建立名爲 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 172.31.0.0 0.0.0.0 255.255.255.0 U 0 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. bytes from 10.0.0.107: icmp_seq=1 ttl=64 time=0.447 ms bytes from 10.0.0.107: icmp_seq=2 ttl=64 time=0.361 ms bytes from 10.0.0.107: icmp_seq=3 ttl=64 time=0.394 ms --- 10.0.0.107 ping statistics --- 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 協議。若是使用 Linux 默認接口 8472,顯示的應該是 UDP 協議,還須要修改 wireshark 的協議設置,讓其識別成 VxLAN。
上面最簡單的點對點 VxLAN 實驗只是個簡答的演示,沒有太多實際工程意義,本節用容器通訊來演示一個更加完整的場景。
場景描述:在 vm1 和 vm2 上各部署一個 docker 容器,默認狀況下,一個容器宿主機上的容器可以直接用私網 IP 地址通訊,由於它們利用一個網橋接在一塊兒。而不一樣宿主機上的容器沒法直接用私網 IP 地址通訊。k8s 等 docker 部署軟件中的網絡組建實際上完成了這部分工做,讓不一樣宿主機的容器可以直接通訊。本節使用原生 docker,以及在宿主機上自建的 vxlan 網絡接口,來打通不一樣宿主機上容器,讓它們能夠直接利用內網IP通訊。
注意:由於實驗在雲上的虛擬機上完成,上面提到的容器宿主機,用的是雲上的虛擬機。容器宿主機也能夠是物理機,實驗效果不變。
安裝 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
參數自定義 IP 地址的功能只能在自定網絡中支持,因此先建立一個自定義網絡,指定網段 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 網絡被建立,名稱爲我指定的 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
建立一個新的容器,以下:
# docker run -itd --net mynetwork --ip 172.18.0.2 centos 16bbaeaaebfccd2a497e3284600f5c0ce230e89678e0ff92f6f4b738c6349f8d
查看容器 ID 和狀態,而且登陸 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 爲了建立容器的效率,一般都用了 size 很小的 image,意味着不少經常使用工具須要安裝,好比 centos image 裏面的 ifconfig。能夠利用 yum whatprovides ifconfig 命令查看 ifconfig 輸入哪一個包,查到屬於 net-tools-2.0-0.22.20131004git.el7.x86_64包,直接用 yum install net-tools -y 安裝便可。再執行 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 上執行一樣的操做,在建立新容器的時候,指定 IP 地址爲 172.18.0.3,容器的環境即準備完畢。在 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 --- 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. bytes from 172.18.0.1: icmp_seq=1 ttl=64 time=0.060 ms bytes from 172.18.0.1: icmp_seq=2 ttl=64 time=0.079 ms --- 172.18.0.1 ping statistics --- packets transmitted, 2 received, 0% packet loss, time 999ms rtt min/avg/max/mdev = 0.060/0.069/0.079/0.012 ms
先來梳理下docker及docker容器在Linux宿主機網絡模塊中作的操做,梳理清楚以後會發現打通不一樣宿主機上docker容器的方法很是簡單。從宿主Linux系統的視角看操做系統中的網絡設備,總結以下:
爲了方便理解,在默認網段172.17.0.0/16中建立2個容器,在自定義網段中上文已經建立了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默認二層能通。
有了上面的梳理和本文第一節VXLAN網絡接口的基礎知識,想必打通不一樣宿主機上docker容器的方法也比較清晰了。思路就是在兩個容器宿主機上各建立一個VXLAN接口,而且將VXLAN接口接入docker網橋的端口上,以下圖:
有了VXLAN接口的鏈接後,從vm1上docker容器發出的包到達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)毫秒級別,前者是走物理網絡的延遲,後者是協議棧的延遲,二者有量級上的差異。
# 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. bytes from 172.18.0.3: icmp_seq=1 ttl=64 time=0.544 ms bytes from 172.18.0.3: icmp_seq=2 ttl=64 time=0.396 ms --- 172.18.0.3 ping statistics --- 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. bytes from 172.18.0.1: icmp_seq=1 ttl=64 time=0.072 ms bytes from 172.18.0.1: icmp_seq=2 ttl=64 time=0.072 ms --- 172.18.0.1 ping statistics --- packets transmitted, 2 received, 0% packet loss, time 999ms rtt min/avg/max/mdev = 0.072/0.072/0.072/0.000 ms
最後說明,本節只是爲了演示 Linux VxLAN 的用於而構造了這個簡單但沒有實際用處的場景,在跨主機環境的容器之間利用 VxLAN 從二層打通。在工程中作容器跨主機通訊時有不少方面須要考慮,也有不少項目在致力於這方面的研究。好比 Flannel,經過給每臺宿主機分配一個子網的方式爲容器提供虛擬網絡,它基於 Linux TUN/TAP,使用 UDP 封裝 IP 包來實現 L3 overlay 網絡,並藉助 etcd 維護網絡的分配狀況。
參考來源: https://www.cnblogs.com/wipan...
公衆號後臺回覆「加羣」,帶你進入高手如雲交流羣
個人公衆號 「Linux雲計算網絡」(id: cloud_dev) ,號內有 10T 書籍和視頻資源,後臺回覆 「1024」 便可領取,分享的內容包括但不限於 Linux、網絡、雲計算虛擬化、容器Docker、OpenStack、Kubernetes、工具、SDN、OVS、DPDK、Go、Python、C/C++編程技術等內容,歡迎你們關注。