Linux 下實踐 VxLAN

本文首發於個人公衆號 Linux雲計算網絡(id: cloud_dev),專一於乾貨分享,號內有 10T 書籍和視頻資源,後臺回覆 「1024」 便可領取,歡迎你們關注,二維碼文末能夠掃。

在上篇文章中,咱們已經探討了 VxLAN 的概念和基本原理,本文就基於 Linux 對 VxLAN 作一個實踐。若是有相關概念不懂的能夠先看那篇文章。html

01 Linux 上對 VxLAN 的支持

首先,來看下 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

如下的實驗在以下的環境中完成:編程

  • 操做系統版本:CentOS Linux release 7.4.1708 (Core)
  • 內核版本:3.10.0-693.2.2.el7.x86_64
  • 雲虛機vm1 eth0網絡接口IP 172.31.0.106,雲虛機vm2 eth0網絡接口IP 172.31.0.107

02 實驗1:最簡單的點對點 VxLAN

建立簡單的點對點 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

  • id: VNI標識是1。
  • remote: 做爲一個VTEP設備來封裝和解封VXLAN報文,須要知道將封裝好的VXLAN報文發送到哪一個對端VTEP。Linux上能夠利用group指定組播組地址,或者利用remote指定對端單播地址。在實驗的雲環境中默認不支持組播,這裏利用remote指定點對點的對端IP地址爲172.31.0.107。
  • dstport: 指定目的端口爲4789。由於當Linux內核3.7版本首次實現VXLAN時,UDP端口還並無規定下來。不少廠商利用了8472這個端口,Linux也採用了相同的端口。後來IANA分配了4789做爲VXLAN的目的UDP端口。若是你須要使用IANA端口,須要用dstport指定。
  • dev: 指定VTEP經過哪一個物理device來通訊,這裏是使用eth0。

第二條命令讓 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。

03 實驗2:容器跨主機通訊

上面最簡單的點對點 VxLAN 實驗只是個簡答的演示,沒有太多實際工程意義,本節用容器通訊來演示一個更加完整的場景。

場景描述:在 vm1 和 vm2 上各部署一個 docker 容器,默認狀況下,一個容器宿主機上的容器可以直接用私網 IP 地址通訊,由於它們利用一個網橋接在一塊兒。而不一樣宿主機上的容器沒法直接用私網 IP 地址通訊。k8s 等 docker 部署軟件中的網絡組建實際上完成了這部分工做,讓不一樣宿主機的容器可以直接通訊。本節使用原生 docker,以及在宿主機上自建的 vxlan 網絡接口,來打通不一樣宿主機上容器,讓它們能夠直接利用內網IP通訊。

注意:由於實驗在雲上的虛擬機上完成,上面提到的容器宿主機,用的是雲上的虛擬機。容器宿主機也能夠是物理機,實驗效果不變。

3.1 準備 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 參數自定義 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
  • --net指定自定義網絡
  • --ip指定IP地址
  • centos指定image

查看容器 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

3.2 建立 VxLAN 接口接入 docker 網橋

先來梳理下docker及docker容器在Linux宿主機網絡模塊中作的操做,梳理清楚以後會發現打通不一樣宿主機上docker容器的方法很是簡單。從宿主Linux系統的視角看操做系統中的網絡設備,總結以下:

  • docker0接口:網橋,在安裝完docker後默認被建立,網段是172.17.0.0/16,網橋的默認IP地址爲172.17.0.1。
  • br-xxxx接口:網橋,在建立完自定義docker網絡完被建立,網段是被用戶指定的172.18.0.0/16,網橋的默認IP地址爲172.18.0.1。
  • vethxxxx接口:veth網絡接口,在建立一個具體的docker容器後被建立,若是有N個運行的容器,就會有N個veth網絡接口。容器中的eth0接口和宿主機的veth網絡接口是一個veth網絡對,Linux上的veth接口做爲一個端口鏈接入docker網橋,如docker0或其餘自定義網橋。這也是爲何一個宿主機上的docker容器可以默認通訊的緣由,由於它們建立後就被接入到了同一個網橋上。

爲了方便理解,在默認網段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
  • 第一條命令建立VNI爲200的VXLAN網絡接口,名稱爲vxlan_docker,參數設置和場景1中的各個參數相似。
  • 第三條命令把新建立的VXLAN接口vxlan_docker接入到docker網橋br-3231f89d69f6中。

在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

04 總結

最後說明,本節只是爲了演示 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++編程技術等內容,歡迎你們關注。

相關文章
相關標籤/搜索