??蘇州程式大白決議Linux 中的虛擬網路介面??《??記得收藏??》
- 目錄
-
🏳??🌈開講啦!!!!🏳??🌈蘇州程式大白🏳??🌈 - 🌟博主介紹
- 前言
- tun/tap 虛擬網路介面
- C 語言編程測驗 TUN 設備
- TUN 與 TAP 的區別
- veth
- bridge
- 虛擬機場景(橋接模式)
- 跨 namespace 通信場景(容器網路,NAT 模式)
- macvlan
- ipvlan
- vlan
- vxlan/geneve
- 比組播更高效的 vxlan 實作
- 虛擬網路介面的速率
- 網路性能實測
- 🌟作者相關的文章、資源分享🌟
目錄
| 🏳??🌈開講啦!!!!🏳??🌈蘇州程式大白🏳??🌈 |

🌟博主介紹
💂 個人主頁:蘇州程式大白
🤟作者介紹:中國DBA聯盟(ACDU)成員,CSDN全國各地程式猿(媛)聚集地管理員,目前從事工業自動化軟體開發作業,擅長C#、Java、機器視覺、底層演算法等語言,2019年成立柒月軟體作業室,
💬如果文章對你有幫助,歡迎關注、點贊、收藏(一鍵三連)和C#、Halcon、python+opencv、VUE、各大公司面試等一些訂閱專欄哦
🎗? 承接各種軟體開發專案
💅 有任何問題歡迎私信,看到會及時回復
👤 微信號:stbsl6,微信公眾號:蘇州程式大白
🎯 想加入技術交流群的可以加我好友,群里會分享學習資料
前言

注意: 本文中使用 ip 命令創建或修改的任何網路配置,都是未持久化的,主機重啟即消失, ?
Linux 具有強大的虛擬網路能力,這也是 openstack 網路、docker 容器網路以及 kubernetes 網路等虛擬網路的基礎,
這里介紹 Linux 常用的虛擬網路介面型別:TUN/TAP、bridge、veth、ipvlan/macvlan、vlan 以及 vxlan/geneve,
tun/tap 虛擬網路介面
tun/tap 是作業系統內核中的虛擬網路設備,他們為用戶層程式提供資料的接收與傳輸,
普通的物理網路介面如 eth0,它的兩端分別是內核協議堆疊和外面的物理網路,
而對于 TUN/TAP 虛擬介面如 tun0,它的一端一定是連接的用戶層程式,另一端則視配置方式的不同而變化,可以直連內核協議堆疊,也可以是某個 bridge(后面會介紹), Linux 通過內核模塊 TUN 提供 tun/tap 功能,該模塊提供了一個設備介面 /dev/net/tun 供用戶層程式讀寫,用戶層程式通過 /dev/net/tun 讀寫主機內核協議堆疊的資料,
> modinfo tun
filename: /lib/modules/5.13.6-1-default/kernel/drivers/net/tun.ko.xz
alias: devname:net/tun
alias: char-major-10-200
license: GPL
author: (C) 1999-2004 Max Krasnyansky <maxk@qualcomm.com>
description: Universal TUN/TAP device driver
...
> ls /dev/net/tun
/dev/net/tun
一個 TUN 設備的示例圖如下: ?
+----------------------------------------------------------------------+
| |
| +--------------------+ +--------------------+ |
| | User Application A | | User Application B +<-----+ |
| +------------+-------+ +-------+------------+ | |
| | 1 | 5 | |
|...............+......................+...................|...........|
| ↓ ↓ | |
| +----------+ +----------+ | |
| | socket A | | socket B | | |
| +-------+--+ +--+-------+ | |
| | 2 | 6 | |
|.................+.................+......................|...........|
| ↓ ↓ | |
| +------------------------+ +--------+-------+ |
| | Network Protocol Stack | | /dev/net/tun | |
| +--+-------------------+-+ +--------+-------+ |
| | 7 | 3 ^ |
|................+...................+.....................|...........|
| ↓ ↓ | |
| +----------------+ +----------------+ 4 | |
| | eth0 | | tun0 | | |
| +-------+--------+ +-----+----------+ | |
| 10.32.0.11 | | 192.168.3.11 | |
| | 8 +---------------------+ |
| | |
+----------------+-----------------------------------------------------+
↓
Physical Network
因為 TUN/TAP 設備的一端是內核協議堆疊,顯然流入 tun0 的資料包是先經過本地的路由規則匹配的,
路由匹配成功,資料包被發送到 tun0 后,tun0 發現另一端是通過 /dev/net/tun 連接到應用程式 B,就會將資料丟給應用程式 B,
應用程式對資料包進行處理后,可能會構造新的資料包,通過物理網卡發送出去,比如常見的 VPN 程式就是把原來的資料包封裝/加密一遍,再發送給 VPN 服務器,
C 語言編程測驗 TUN 設備
為了使用 tun/tap 設備,用戶層程式需要通過系統呼叫打開 /dev/net/tun 獲得一個讀寫該設備的檔案描述符(FD),并且呼叫 ioctl() 向內核注冊一個 TUN 或 TAP 型別的虛擬網卡(實體化一個 tun/tap 設備),其名稱可能是 tun0/tap0 等,
此后,用戶程式可以通過該 TUN/TAP 虛擬網卡與主機內核協議堆疊(或者其他網路設備)互動,當用戶層程式關閉后,其注冊的 TUN/TAP 虛擬網卡以及自動生成的路由表相關條目都會被內核釋放,
可以把用戶層程式看做是網路上另一臺主機,他們通過 tun/tap 虛擬網卡相連,
一個簡單的 C 程式示例如下,它每次收到資料后,都只單純地列印一下收到的位元組數:
#include <linux/if.h>
#include <linux/if_tun.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include<stdlib.h>
#include<stdio.h>
int tun_alloc(int flags)
{
struct ifreq ifr;
int fd, err;
char *clonedev = "/dev/net/tun";
// 打開 tun 檔案,獲得 fd
if ((fd = open(clonedev, O_RDWR)) < 0) {
return fd;
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = flags;
// 向內核注冊一個 TUN 網卡,并與前面拿到的 fd 關聯起來
// 程式關閉時,注冊的 tun 網卡及自動生成的相關路由策略,會被自動釋放
if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0) {
close(fd);
return err;
}
printf("Open tun/tap device: %s for reading...\n", ifr.ifr_name);
return fd;
}
int main()
{
int tun_fd, nread;
char buffer[1500];
/* Flags: IFF_TUN - TUN device (no Ethernet headers)
* IFF_TAP - TAP device
* IFF_NO_PI - Do not provide packet information
*/
tun_fd = tun_alloc(IFF_TUN | IFF_NO_PI);
if (tun_fd < 0) {
perror("Allocating interface");
exit(1);
}
while (1) {
nread = read(tun_fd, buffer, sizeof(buffer));
if (nread < 0) {
perror("Reading from interface");
close(tun_fd);
exit(1);
}
printf("Read %d bytes from tun/tap device\n", nread);
}
return 0;
}
接下來開啟三個終端視窗來測驗上述程式,分別運行上面的 tun 程式、tcpdump 和 iproute2 指令,
首先通過編譯運行上述 c 程式,程式會阻塞住,等待資料到達:
# 編譯,請忽略部分 warning
> gcc mytun.c -o mytun
# 創建并監聽 tun 設備需要 root 權限
> sudo mytun
Open tun/tap device: tun0 for reading...
現在使用 iproute2 查看下鏈路層設備:
# 能發現最后面有列出名為 tun0 的介面,但是狀態為 down
? ip addr ls
......
3: wlp4s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether c0:3c:59:36:a4:16 brd ff:ff:ff:ff:ff:ff
inet 192.168.31.228/24 brd 192.168.31.255 scope global dynamic noprefixroute wlp4s0
valid_lft 41010sec preferred_lft 41010sec
inet6 fe80::4ab0:130f:423b:5d37/64 scope link noprefixroute
valid_lft forever preferred_lft forever
7: tun0: <POINTOPOINT,MULTICAST,NOARP> mtu 1500 qdisc noop state DOWN group default qlen 500
link/none
# 為 tun0 設定 ip 地址,注意不要和其他介面在同一網段,會導致路由沖突
> sudo ip addr add 172.21.22.23/24 dev tun0
# 啟動 tun0 這個介面,這一步會自動向路由表中添加將 172.21.22.23/24 路由到 tun0 的策略
> sudo ip link set tun0 up
#確認上一步添加的路由策略是否存在
? ip route ls
default via 192.168.31.1 dev wlp4s0 proto dhcp metric 600
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
172.21.22.0/24 dev tun0 proto kernel scope link src 172.21.22.23
192.168.31.0/24 dev wlp4s0 proto kernel scope link src 192.168.31.228 metric 600
# 此時再查看介面,發現 tun0 狀態為 unknown
> ip addr ls
......
8: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 500
link/none
inet 172.21.22.23/24 scope global tun0
valid_lft forever preferred_lft forever
inet6 fe80::3d52:49b5:1cf3:38fd/64 scope link stable-privacy
valid_lft forever preferred_lft forever
# 使用 tcpdump 嘗試抓下 tun0 的資料,會阻塞在這里,等待資料到達
> tcpdump -i tun0
現在再啟動第三個視窗發點資料給 tun0,持續觀察前面 tcpdump 和 mytun 的日志:
# 直接 ping tun0 的地址,貌似有問題,資料沒進 mytun 程式,而且還有回應
? ping -c 4 172.21.22.23
PING 172.21.22.23 (172.21.22.23) 56(84) bytes of data.
64 bytes from 172.21.22.23: icmp_seq=1 ttl=64 time=0.167 ms
64 bytes from 172.21.22.23: icmp_seq=2 ttl=64 time=0.180 ms
64 bytes from 172.21.22.23: icmp_seq=3 ttl=64 time=0.126 ms
64 bytes from 172.21.22.23: icmp_seq=4 ttl=64 time=0.141 ms
--- 172.21.22.23 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3060ms
rtt min/avg/max/mdev = 0.126/0.153/0.180/0.021 ms
# 但是 ping 該網段下的其他地址,流量就會被轉發給 mytun 程式,因為 mytun 啥資料也沒回,自然丟包率 100%
# tcpdump 和 mytun 都會列印出相關日志
? ping -c 4 172.21.22.26
PING 172.21.22.26 (172.21.22.26) 56(84) bytes of data.
--- 172.21.22.26 ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 3055ms
下面給出 mytun 的輸出:
Read 84 bytes from tun/tap device
Read 84 bytes from tun/tap device
Read 84 bytes from tun/tap device
Read 84 bytes from tun/tap device
以及 tcpdump 的輸出:
00:22:03.622684 IP (tos 0x0, ttl 64, id 37341, offset 0, flags [DF], proto ICMP (1), length 84)
172.21.22.23 > 172.21.22.26: ICMP echo request, id 11, seq 1, length 64
00:22:04.633394 IP (tos 0x0, ttl 64, id 37522, offset 0, flags [DF], proto ICMP (1), length 84)
172.21.22.23 > 172.21.22.26: ICMP echo request, id 11, seq 2, length 64
00:22:05.653356 IP (tos 0x0, ttl 64, id 37637, offset 0, flags [DF], proto ICMP (1), length 84)
172.21.22.23 > 172.21.22.26: ICMP echo request, id 11, seq 3, length 64
00:22:06.677341 IP (tos 0x0, ttl 64, id 37667, offset 0, flags [DF], proto ICMP (1), length 84)
172.21.22.23 > 172.21.22.26: ICMP echo request, id 11, seq 4, length 64
TUN 與 TAP 的區別
TUN 和 TAP 的區別在于作業的網路層次不同,用戶程式通過 TUN 設備只能讀寫網路層的 IP 資料包,而 TAP 設備則支持讀寫鏈路層的資料包(通常是以太網資料包,帶有 Ethernet headers),
TUN 與 TAP 的關系,就類似于 socket 和 raw socket,
TUN/TAP 應用最多的場景是 VPN 代理,比如:
1、clash: 一個支持各種規則的隧道,也支持 TUN 模式,
2、tun2socks: 一個全域透明代理,和 VPN 的作業模式一樣,它通過創建虛擬網卡+修改路由表,在第三層網路層代理系統流量,
veth
veth 介面總是成對出現,一對 veth 介面就類似一根網線,從一端進來的資料會從另一端出去,
同時 veth 又是一個虛擬網路介面,因此它和 TUN/TAP 或者其他物理網路介面一樣,也都能配置 mac/ip 地址(但是并不是一定得配 mac/ip 地址),
其主要作用就是連接不同的網路,比如在容器網路中,用于將容器的 namespace 與 root namespace 的網橋 br0 相連, 容器網路中,容器側的 veth 自身設定了 ip/mac 地址并被重命名為 eth0,作為容器的網路介面使用,而主機側的 veth 則直接連接在 docker0/br0 上面,
使用 veth 實作容器網路,需要結合下一小節介紹的 bridge,在下一小節將給出容器網路結構圖,
bridge
Linux Bridge 是作業在鏈路層的網路交換機,由 Linux 內核模塊 brige 提供,它負責在所有連接到它的介面之間轉發鏈路層資料包,
添加到 Bridge 上的設備被設定為只接受二層資料幀并且轉發所有收到的資料包到 Bridge 中, 在 Bridge 中會進行一個類似物理交換機的查MAC埠映射表、轉發、更新MAC埠映射表這樣的處理邏輯,從而資料包可以被轉發到另一個介面/丟棄/廣播/發往上層協議堆疊,由此 Bridge 實作了資料轉發的功能,
如果使用 tcpdump 在 Bridge 介面上抓包,可以抓到網橋上所有介面進出的包,因為這些資料包都要通過網橋進行轉發,
與物理交換機不同的是,Bridge 本身可以設定 IP 地址,可以認為當使用 brctl addbr br0 新建一個 br0 網橋時,系統自動創建了一個同名的隱藏 br0 網路介面,br0 一旦設定 IP 地址,就意味著這個隱藏的 br0 介面可以作為路由介面設備,參與 IP 層的路由選擇(可以使用 route -n 查看最后一列 Iface),因此只有當 br0 設定 IP 地址時,Bridge 才有可能將資料包發往上層協議堆疊,
但被添加到 Bridge 上的網卡是不能配置 IP 地址的,他們作業在資料鏈路層,對路由系統不可見,
它常被用于在虛擬機、主機上不同的 namepsaces 之間轉發資料,
虛擬機場景(橋接模式)
以 qemu-kvm 為例,在虛擬機的橋接模式下,qemu-kvm 會為每個虛擬機創建一個 tun/tap 虛擬網卡并連接到 br0 網橋, 虛擬機內部的網路介面 eth0 是 qemu-kvm 軟體模擬的,實際上虛擬機內網路資料的收發都會被 qemu-kvm 轉換成對 /dev/net/tun 的讀寫,
以發送資料為例,整個流程如下:
-
虛擬機發出去的資料包先到達 qemu-kvm 程式,
-
資料被用戶層程式 qemu-kvm 寫入到 /dev/net/tun,到達 tap 設備,
-
tap 設備把資料傳送到 br0 網橋,
-
br0 把資料交給 eth0 發送出去,
整個流程跑完,資料包都不需要經過宿主機的協議堆疊,效率高,
+------------------------------------------------+-----------------------------------+-----------------------------------+
| Host | VirtualMachine1 | VirtualMachine2 |
| | | |
| +--------------------------------------+ | +-------------------------+ | +-------------------------+ |
| | Network Protocol Stack | | | Network Protocol Stack | | | Network Protocol Stack | |
| +--------------------------------------+ | +-------------------------+ | +-------------------------+ |
| ↑ | ↑ | ↑ |
|.......................|........................|................|..................|.................|.................|
| ↓ | ↓ | ↓ |
| +--------+ | +-------+ | +-------+ |
| | .3.101 | | | .3.102| | | .3.103| |
| +------+ +--------+ +-------+ | +-------+ | +-------+ |
| | eth0 |<--->| br0 |<--->|tun/tap| | | eth0 | | | eth0 | |
| +------+ +--------+ +-------+ | +-------+ | +-------+ |
| ↑ ↑ ↑ +--------+ ↑ | ↑ |
| | | +------|qemu-kvm|-----------+ | | |
| | ↓ +--------+ | | |
| | +-------+ | | | |
| | |tun/tap| | | | |
| | +-------+ | | | |
| | ↑ | +--------+ | | |
| | +-------------------------------------|qemu-kvm|-------------|-----------------+ |
| | | +--------+ | |
| | | | |
+---------|--------------------------------------+-----------------------------------+-----------------------------------+
↓
Physical Network (192.168.3.0/24)
跨 namespace 通信場景(容器網路,NAT 模式)
由于容器運行在自己單獨的 network namespace 里面,所以和虛擬機一樣,它們也都有自己單獨的協議堆疊,
容器網路的結構和虛擬機差不多,但是它改用了 NAT 網路,并把 tun/tap 換成了 veth,導致 docker0 過來的資料,要先經過宿主機協議堆疊,然后才進入 veth 介面,
多了一層 NAT,以及多走了一層宿主機協議堆疊,都會導致性能下降,
示意圖如下:
+-----------------------------------------------+-----------------------------------+-----------------------------------+
| Host | Container 1 | Container 2 |
| | | |
| +---------------------------------------+ | +-------------------------+ | +-------------------------+ |
| | Network Protocol Stack | | | Network Protocol Stack | | | Network Protocol Stack | |
| +----+-------------+--------------------+ | +-----------+-------------+ | +------------+------------+ |
| ^ ^ | ^ | ^ |
|........|.............|........................|................|..................|.................|.................|
| v v ↓ | v | v |
| +----+----+ +-----+------+ | +-----+-------+ | +-----+-------+ |
| | .31.101 | | 172.17.0.1 | +------+ | | 172.17.0.2 | | | 172.17.0.3 | |
| +---------+ +-------------<---->+ veth | | +-------------+ | +-------------+ |
| | eth0 | | docker0 | +--+---+ | | eth0(veth) | | | eth0(veth) | |
| +----+----+ +-----+------+ ^ | +-----+-------+ | +-----+-------+ |
| ^ ^ | | ^ | ^ |
| | | +------------------------+ | | |
| | v | | | |
| | +--+---+ | | | |
| | | veth | | | | |
| | +--+---+ | | | |
| | ^ | | | |
| | +------------------------------------------------------------------------------+ |
| | | | |
| | | | |
+-----------------------------------------------+-----------------------------------+-----------------------------------+
v
Physical Network (192.168.31.0/24)
每創建一個新容器,都會在容器的 namespace 里新建一個 veth 介面并命令為 eth0,同時在主 namespace 創建一個 veth,將容器的 eth0 與 docker0 連接,
可以在容器中通過 iproute2 查看到, eth0 的介面型別為 veth:
? docker run -it --rm debian:buster bash
root@5facbe4ddc1e:/# ip --details addr ls
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0 minmtu 0 maxmtu 0 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
20: eth0@if21: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 0 minmtu 68 maxmtu 65535
veth numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
同時在宿主機中能看到對應的 veth 設備是系結到了 docker0 網橋的:
? sudo brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242fce99ef5 no vethea4171a
macvlan
目前 docker/podman 都支持創建基于 macvlan 的 Linux 容器網路,
注意 macvlan 和 WiFi 存在兼容問題,如果使用筆記本測驗,可能會遇到麻煩,
?
macvlan 是比較新的 Linux 特性,需要內核版本 >= 3.9,它被用于在主機的網路介面(父介面)上配置多個虛擬子介面,這些子介面都擁有各自獨立的 mac 地址,也可以配上 ip 地址進行通訊,
macvlan 下的虛擬機或者容器網路和主機在同一個網段中,共享同一個廣播域,macvlan 和 bridge 比較相似,但因為它省去了 bridge 的存在,所以配置和除錯起來比較簡單,而且效率也相對高,除此之外,macvlan 自身也完美支持 VLAN,
如果希望容器或者虛擬機放在主機相同的網路中,享受已經存在網路堆疊的各種優勢,可以考慮 macvlan,
ipvlan
linux 網路虛擬化: ipvlan
cilium 1.9 已經提供了基于 ipvlan 的網路(beta 特性),用于替換傳統的 veth+bridge 容器網路,詳見 IPVLAN based Networking (beta) - Cilium 1.9 Docs
ipvlan 和 macvlan 的功能很類似,也是用于在主機的網路介面(父介面)上配置出多個虛擬的子介面,但不同的是,ipvlan 的各子介面沒有獨立的 mac 地址,它們和主機的父介面共享 mac 地址,
因為 mac 地址共享,所以如果使用 DHCP,就要注意不能使用 mac 地址做 DHCP,需要額外配置唯一的 clientID,
如果你遇到以下的情況,請考慮使用 ipvlan:
-
父介面對 mac 地址數目有限制,或者在 mac 地址過多的情況下會造成嚴重的性能損失,
-
作業在 802.11(wireless)無線網路中(macvlan 無法和無線網路共同作業),
-
希望搭建比較復雜的網路拓撲(不是簡單的二層網路和 VLAN),比如要和 BGP 網路一起作業,
-
基于 ipvlan/macvlan 的容器網路,比 veth+bridge+iptables 的性能要更高,
vlan
vlan 即虛擬局域網,是一個鏈路層的廣播域隔離技術,可以用于切分局域網,解決廣播泛濫和安全性問題,被隔離的廣播域之間需要上升到第三層才能完成通訊,
常用的企業路由器如 ER-X 基本都可以設定 vlan,Linux 也直接支持了 vlan.
以太網資料包有一個專門的欄位提供給 vlan 使用,vlan 資料包會在該位置記錄它的 VLAN ID,交換機通過該 ID 來區分不同的 VLAN,只將該以太網報文廣播到該 ID 對應的 VLAN 中,
vxlan/geneve
rfc8926 - Geneve: Generic Network Virtualization Encapsulation rfc7348 - Virtual eXtensible Local Area Network (VXLAN)
Linux 上實作 vxlan 網路 ?
在介紹 vxlan 前,先說明下兩個名詞的含義: ?
-
underlay 網路:即物理網路,
-
overlay 網路:指在現有的物理網路之上構建的虛擬網路,其實就是一種隧道技術,將原生態的二層資料幀報文進行封裝后通過隧道進行傳輸,
vxlan 與 geneve 都是 overlay 網路協議,它倆都是使用 UDP 包來封裝鏈路層的以太網幀,
vxlan 在 2014 年標準化,而 geneve 在 2020 年底才通過草案階段,目前尚未形成最終標準,但是目前 linux/cilium 都已經支持了 geneve,
geneve 相對 vxlan 最大的變化,是它更靈活——它的 header 長度是可變的,
目前所有 overlay 的跨主機容器網路方案,幾乎都是基于 vxlan 實作的(例外:cilium 也支持 geneve),
單機的容器網路時,不需要接觸到 vxlan,但是在學習跨主機容器網路方案如 flannel/calico/cilium 時,那 vxlan(overlay) 及 BGP(underlay) 就不可避免地要接觸了,
介紹下 vxlan 的資料包結構:

? 在創建 vxlan 的 vtep 虛擬設備時,我們需要手動設定圖中的如下屬性:
-
VXLAN 目標埠:即接收方 vtep 使用的埠,這里 IANA 定義的埠是 4789,但是只有 calico 的vxlan 模式默認使用該埠 calico,而 cilium/flannel 的默認埠都是 Linux 默認的 8472, -
VNID: 每個 VXLAN 網路介面都會被分配一個獨立的 VNID,
一個點對點的 vxlan 網路架構圖如下: ?

可以看到每臺虛擬機 VM 都會被分配一個唯一的 VNID,然后兩臺物理機之間通過 VTEP 虛擬網路設備建立了 VXLAN 隧道,所有 VXLAN 網路中的虛擬機,都通過 VTEP 來互相通信,
有了上面這些知識,我們就可以通過如下命令在兩臺 Linux 機器間建立一個點對點的 VXLAN 隧道:
# 在主機 A 上創建 VTEP 設備 vxlan0
# 與另一個 vtep 介面 B(192.168.8.101)建立隧道
# 將 vxlan0 自身的 IP 地址設為 192.168.8.100
# 使用的 VXLAN 目標埠為 4789(IANA 標準)
ip link add vxlan0 type vxlan \
id 42 \
dstport 4789 \
remote 192.168.8.101 \
local 192.168.8.100 \
dev enp0s8
# 為我們的 VXLAN 網路設定虛擬網段,vxlan0 就是默認網關
ip addr add 10.20.1.2/24 dev vxlan0
# 啟用我們的 vxlan0 設備,這會自動生成路由規則
ip link set vxlan0 up
# 現在在主機 B 上運行如下命令,同樣創建一個 VTEP 設備 vxlan0,remote 和 local 的 ip 與前面用的命令剛好相反,
# 注意 VNID 和 dstport 必須和前面完全一致
ip link add vxlan0 type vxlan \
id 42 \
dstport 4789 \
remote 192.168.8.100 \
local 192.168.8.101 \
dev enp0s8
# 為我們的 VXLAN 網路設定虛擬網段,vxlan0 就是默認網關
ip addr add 10.20.1.3/24 dev vxlan0
ip link set vxlan0 up
# 到這里,兩臺機器就完成連接,可以通信了,可以在主機 B 上 ping 10.20.1.2 試試,應該能收到主機 A 的回應,
ping 10.20.1.2
點對點的 vxlan 隧道實際用處不大,如果集群中的每個節點都互相建 vxlan 隧道,代價太高了,
一種更好的方式,是使用 「組播模式」的 vxlan 隧道,這種模式下一個 vtep 可以一次與組內的所有 vtep 建立隧道, 示例命令如下(這里略過了如何設定組播地址 239.1.1.1 的資訊):
ip link add vxlan0 type vxlan \
id 42 \
dstport 4789 \
group 239.1.1.1 \
dev enp0s8
ip addr add 10.20.1.2/24 dev vxlan0
ip link set vxlan0 up
可以看到,只需要簡單地把 local_ip/remote_ip 替換成一個組播地址就行,組播功能會將收到的資料包發送給組里的所有 vtep 介面,但是只有 VNID 能對上的 vtep 會處理該報文,其他 vtep 會直接丟棄資料,
接下來,為了能讓所有的虛擬機/容器,都通過 vtep 通信,我們再添加一個 bridge 網路,充當 vtep 與容器間的交換機,架構如下:

使用 ip 命令創建網橋、網路名字空間、veth pairs 組成上圖中的容器網路:
# 創建 br0 并將 vxlan0 系結上去
ip link add br0 type bridge
ip link set vxlan0 master bridge
ip link set vxlan0 up
ip link set br0 up
# 模擬將容器加入到網橋中的操作
ip netns add container1
## 創建 veth pair,并把一端加到網橋上
ip link add veth0 type veth peer name veth1
ip link set dev veth0 master br0
ip link set dev veth0 up
## 配置容器內部的網路和 IP
ip link set dev veth1 netns container1
ip netns exec container1 ip link set lo up
ip netns exec container1 ip link set veth1 name eth0
ip netns exec container1 ip addr add 10.20.1.11/24 dev eth0
ip netns exec container1 ip link set eth0 up
然后在另一臺機器上做同樣的操作,并創建新容器,兩個容器就能通過 vxlan 通信啦~
比組播更高效的 vxlan 實作
組播最大的問題在于,因為它不知道資料的目的地,所以每個 vtep 都發了一份,如果每次發資料時,如果能夠精確到對應的 vtep,就能節約大量資源,
另一個問題是 ARP 查詢也會被組播,要知道 vxlan 本身就是個 overlay 網路,ARP 的成本也很高,
上述問題都可以通過一個中心化的注冊中心(如 etcd)來解決,所有容器、網路的注冊與變更,都寫入到這個注冊中心,然后由程式自動維護 vtep 之間的隧道、fdb 表及 ARP 表,
虛擬網路介面的速率
Loopback 和本章講到的其他虛擬網路介面一樣,都是一種軟體模擬的網路設備, 他們的速率是不是也像物理鏈路一樣,存在鏈路層(比如以太網)的帶寬限制呢?
比如目前很多老舊的網路設備,都是只支持到百兆以太網,這就決定了它的帶寬上限, 即使是較新的設備,目前基本也都只支持到千兆,也就是 1GbE 以太網標準,那本文提到的虛擬網路介面單純在本機內部通信,是否也存在這樣的制約呢?是否也只能跑到 1GbE?
使用 ethtool 檢查:
# docker 容器的 veth 介面速率
> ethtool vethe899841 | grep Speed
Speed: 10000Mb/s
# 網橋看起來沒有固定的速率
> ethtool docker0 | grep Speed
Speed: Unknown!
# tun0 設備的默認速率貌似是 10Mb/s ?
> ethtool tun0 | grep Speed
Speed: 10Mb/s
# 此外 ethtool 無法檢查 lo 以及 wifi 的速率
網路性能實測
接下來實際測驗一下,先給出機器引數:
? cat /etc/os-release
NAME="openSUSE Tumbleweed"
# VERSION="20210810"
...
? uname -a
Linux legion-book 5.13.8-1-default #1 SMP Thu Aug 5 08:56:22 UTC 2021 (967c6a8) x86_64 x86_64 x86_64 GNU/Linux
? lscpu
Architecture: x86_64
CPU(s): 16
Model name: AMD Ryzen 7 5800H with Radeon Graphics
...
# 記憶體,單位 MB
? free -m
total used free shared buff/cache available
Mem: 27929 4482 17324 249 6122 22797
Swap: 2048 0 2048
使用 iperf3 測驗: ?
# 啟動服務端
iperf3 -s
-------------
# 新視窗啟動客戶端,通過 loopback 介面訪問 iperf3-server,大概 49Gb/s
? iperf3 -c 127.0.0.1
Connecting to host 127.0.0.1, port 5201
[ 5] local 127.0.0.1 port 48656 connected to 127.0.0.1 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 4.46 GBytes 38.3 Gbits/sec 0 1.62 MBytes
[ 5] 1.00-2.00 sec 4.61 GBytes 39.6 Gbits/sec 0 1.62 MBytes
[ 5] 2.00-3.00 sec 5.69 GBytes 48.9 Gbits/sec 0 1.62 MBytes
[ 5] 3.00-4.00 sec 6.11 GBytes 52.5 Gbits/sec 0 1.62 MBytes
[ 5] 4.00-5.00 sec 6.04 GBytes 51.9 Gbits/sec 0 1.62 MBytes
[ 5] 5.00-6.00 sec 6.05 GBytes 52.0 Gbits/sec 0 1.62 MBytes
[ 5] 6.00-7.00 sec 6.01 GBytes 51.6 Gbits/sec 0 1.62 MBytes
[ 5] 7.00-8.00 sec 6.05 GBytes 52.0 Gbits/sec 0 1.62 MBytes
[ 5] 8.00-9.00 sec 6.34 GBytes 54.5 Gbits/sec 0 1.62 MBytes
[ 5] 9.00-10.00 sec 5.91 GBytes 50.8 Gbits/sec 0 1.62 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 57.3 GBytes 49.2 Gbits/sec 0 sender
[ 5] 0.00-10.00 sec 57.3 GBytes 49.2 Gbits/sec receiver
# 客戶端通過 wlp4s0 wifi 網卡(192.168.31.228)訪問 iperf3-server,實際還是走的本機,但是速度要比 loopback 快一點,可能是默認設定的問題
? iperf3 -c 192.168.31.228
Connecting to host 192.168.31.228, port 5201
[ 5] local 192.168.31.228 port 43430 connected to 192.168.31.228 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 5.12 GBytes 43.9 Gbits/sec 0 1.25 MBytes
[ 5] 1.00-2.00 sec 5.29 GBytes 45.5 Gbits/sec 0 1.25 MBytes
[ 5] 2.00-3.00 sec 5.92 GBytes 50.9 Gbits/sec 0 1.25 MBytes
[ 5] 3.00-4.00 sec 6.00 GBytes 51.5 Gbits/sec 0 1.25 MBytes
[ 5] 4.00-5.00 sec 5.98 GBytes 51.4 Gbits/sec 0 1.25 MBytes
[ 5] 5.00-6.00 sec 6.05 GBytes 52.0 Gbits/sec 0 1.25 MBytes
[ 5] 6.00-7.00 sec 6.16 GBytes 52.9 Gbits/sec 0 1.25 MBytes
[ 5] 7.00-8.00 sec 6.08 GBytes 52.2 Gbits/sec 0 1.25 MBytes
[ 5] 8.00-9.00 sec 6.00 GBytes 51.6 Gbits/sec 0 1.25 MBytes
[ 5] 9.00-10.00 sec 6.01 GBytes 51.6 Gbits/sec 0 1.25 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 58.6 GBytes 50.3 Gbits/sec 0 sender
[ 5] 0.00-10.00 sec 58.6 GBytes 50.3 Gbits/sec receiver
# 從容器中訪問宿主機的 iperf3-server,速度幾乎沒區別
? docker run -it --rm --name=iperf3-server networkstatic/iperf3 -c 192.168.31.228
Connecting to host 192.168.31.228, port 5201
[ 5] local 172.17.0.2 port 43436 connected to 192.168.31.228 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 4.49 GBytes 38.5 Gbits/sec 0 403 KBytes
[ 5] 1.00-2.00 sec 5.31 GBytes 45.6 Gbits/sec 0 544 KBytes
[ 5] 2.00-3.00 sec 6.14 GBytes 52.8 Gbits/sec 0 544 KBytes
[ 5] 3.00-4.00 sec 5.85 GBytes 50.3 Gbits/sec 0 544 KBytes
[ 5] 4.00-5.00 sec 6.14 GBytes 52.7 Gbits/sec 0 544 KBytes
[ 5] 5.00-6.00 sec 5.99 GBytes 51.5 Gbits/sec 0 544 KBytes
[ 5] 6.00-7.00 sec 5.86 GBytes 50.4 Gbits/sec 0 544 KBytes
[ 5] 7.00-8.00 sec 6.05 GBytes 52.0 Gbits/sec 0 544 KBytes
[ 5] 8.00-9.00 sec 5.99 GBytes 51.5 Gbits/sec 0 544 KBytes
[ 5] 9.00-10.00 sec 6.12 GBytes 52.5 Gbits/sec 0 544 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 58.0 GBytes 49.8 Gbits/sec 0 sender
[ 5] 0.00-10.00 sec 58.0 GBytes 49.8 Gbits/sec receiver
把 iperf3-server 跑在容器里再測一遍:
# 在容器中啟動 iperf3-server,并映射到宿主機埠 6201
> docker run -it --rm --name=iperf3-server -p 6201:5201 networkstatic/iperf3 -s
> docker inspect --format "{{ .NetworkSettings.IPAddress }}" iperf3-server
172.17.0.2
-----------------------------
# 測驗容器之間互訪的速度,ip 為 iperf3-server 的容器 ip,速度要慢一些,
# 畢竟過了 veth -> veth -> docker0 -> veth -> veth 五層虛擬網路介面
? docker run -it --rm networkstatic/iperf3 -c 172.17.0.2
Connecting to host 172.17.0.2, port 5201
[ 5] local 172.17.0.3 port 40776 connected to 172.17.0.2 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 4.74 GBytes 40.7 Gbits/sec 0 600 KBytes
[ 5] 1.00-2.00 sec 4.48 GBytes 38.5 Gbits/sec 0 600 KBytes
[ 5] 2.00-3.00 sec 5.38 GBytes 46.2 Gbits/sec 0 600 KBytes
[ 5] 3.00-4.00 sec 5.39 GBytes 46.3 Gbits/sec 0 600 KBytes
[ 5] 4.00-5.00 sec 5.42 GBytes 46.6 Gbits/sec 0 600 KBytes
[ 5] 5.00-6.00 sec 5.39 GBytes 46.3 Gbits/sec 0 600 KBytes
[ 5] 6.00-7.00 sec 5.38 GBytes 46.2 Gbits/sec 0 635 KBytes
[ 5] 7.00-8.00 sec 5.37 GBytes 46.1 Gbits/sec 0 667 KBytes
[ 5] 8.00-9.00 sec 6.01 GBytes 51.7 Gbits/sec 0 735 KBytes
[ 5] 9.00-10.00 sec 5.74 GBytes 49.3 Gbits/sec 0 735 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 53.3 GBytes 45.8 Gbits/sec 0 sender
[ 5] 0.00-10.00 sec 53.3 GBytes 45.8 Gbits/sec receiver
# 本機直接訪問容器 ip,走的是 docker0 網橋,居然還挺快
? iperf3 -c 172.17.0.2
Connecting to host 172.17.0.2, port 5201
[ 5] local 172.17.0.1 port 56486 connected to 172.17.0.2 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 5.01 GBytes 43.0 Gbits/sec 0 632 KBytes
[ 5] 1.00-2.00 sec 5.19 GBytes 44.6 Gbits/sec 0 703 KBytes
[ 5] 2.00-3.00 sec 6.46 GBytes 55.5 Gbits/sec 0 789 KBytes
[ 5] 3.00-4.00 sec 6.80 GBytes 58.4 Gbits/sec 0 789 KBytes
[ 5] 4.00-5.00 sec 6.82 GBytes 58.6 Gbits/sec 0 913 KBytes
[ 5] 5.00-6.00 sec 6.79 GBytes 58.3 Gbits/sec 0 1007 KBytes
[ 5] 6.00-7.00 sec 6.63 GBytes 56.9 Gbits/sec 0 1.04 MBytes
[ 5] 7.00-8.00 sec 6.75 GBytes 58.0 Gbits/sec 0 1.04 MBytes
[ 5] 8.00-9.00 sec 6.19 GBytes 53.2 Gbits/sec 0 1.04 MBytes
[ 5] 9.00-10.00 sec 6.55 GBytes 56.3 Gbits/sec 0 1.04 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 63.2 GBytes 54.3 Gbits/sec 0 sender
[ 5] 0.00-10.00 sec 63.2 GBytes 54.3 Gbits/sec receiver
# 如果走本機 loopback 地址 + 容器埠映射,速度就慢了好多
# 或許是因為用 iptables 做埠映射導致的?
? iperf3 -c 127.0.0.1 -p 6201
Connecting to host 127.0.0.1, port 6201
[ 5] local 127.0.0.1 port 48862 connected to 127.0.0.1 port 6201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 2.71 GBytes 23.3 Gbits/sec 0 1.37 MBytes
[ 5] 1.00-2.00 sec 3.64 GBytes 31.3 Gbits/sec 0 1.37 MBytes
[ 5] 2.00-3.00 sec 4.08 GBytes 35.0 Gbits/sec 0 1.37 MBytes
[ 5] 3.00-4.00 sec 3.49 GBytes 30.0 Gbits/sec 0 1.37 MBytes
[ 5] 4.00-5.00 sec 5.50 GBytes 47.2 Gbits/sec 2 1.37 MBytes
[ 5] 5.00-6.00 sec 4.06 GBytes 34.9 Gbits/sec 0 1.37 MBytes
[ 5] 6.00-7.00 sec 4.12 GBytes 35.4 Gbits/sec 0 1.37 MBytes
[ 5] 7.00-8.00 sec 3.99 GBytes 34.3 Gbits/sec 0 1.37 MBytes
[ 5] 8.00-9.00 sec 3.49 GBytes 30.0 Gbits/sec 0 1.37 MBytes
[ 5] 9.00-10.00 sec 5.51 GBytes 47.3 Gbits/sec 0 1.37 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 40.6 GBytes 34.9 Gbits/sec 2 sender
[ 5] 0.00-10.00 sec 40.6 GBytes 34.9 Gbits/sec receiver
# 可走 wlp4s0 + 容器埠映射,速度也不慢啊
? iperf3 -c 192.168.31.228 -p 6201
Connecting to host 192.168.31.228, port 6201
[ 5] local 192.168.31.228 port 54582 connected to 192.168.31.228 port 6201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 4.34 GBytes 37.3 Gbits/sec 0 795 KBytes
[ 5] 1.00-2.00 sec 4.78 GBytes 41.0 Gbits/sec 0 834 KBytes
[ 5] 2.00-3.00 sec 6.26 GBytes 53.7 Gbits/sec 0 834 KBytes
[ 5] 3.00-4.00 sec 6.30 GBytes 54.1 Gbits/sec 0 875 KBytes
[ 5] 4.00-5.00 sec 6.26 GBytes 53.8 Gbits/sec 0 875 KBytes
[ 5] 5.00-6.00 sec 5.75 GBytes 49.4 Gbits/sec 0 875 KBytes
[ 5] 6.00-7.00 sec 5.49 GBytes 47.2 Gbits/sec 0 966 KBytes
[ 5] 7.00-8.00 sec 5.72 GBytes 49.1 Gbits/sec 2 966 KBytes
[ 5] 8.00-9.00 sec 4.81 GBytes 41.3 Gbits/sec 2 966 KBytes
[ 5] 9.00-10.00 sec 5.98 GBytes 51.4 Gbits/sec 0 966 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 55.7 GBytes 47.8 Gbits/sec 4 sender
[ 5] 0.00-10.00 sec 55.7 GBytes 47.8 Gbits/sec receiver
總的來看,loopback、bridge、veth 這幾個介面基本上是沒被限速的,veth 有查到上限為 10000Mb/s(10Gb/s) 感覺也是個假數字, 實際上測出來的資料基本在 35Gb/s 到 55Gb/s 之間,視情況浮動,
性能的變化和虛擬網路設備的鏈路和型別有關,或許和默認配置的區別也有關系,
另外 TUN 設備這里沒有測,ethtool tun0 查到的值是比較離譜的 10Mb/s,但是感覺不太可能這么慢,有時間可以再測一波看看,
🌟作者相關的文章、資源分享🌟
🌟讓天下沒有學不會的技術🌟
學習C#不再是難問題
🌳《C#入門到高級教程》🌳
有關C#實戰專案
👉C#RS232C通訊原始碼👈
👉C#委托資料傳輸👈
👉C# Modbus TCP 源代碼👈
👉C# 倉庫管理系統原始碼👈
👉C# 歐姆龍通訊Demo👈
👉C#+WPF+SQL目前在某市上線的車管所攝像系統👈
👉2021C#與Halcon視覺通用的框架👈
👉2021年視覺專案中利用C#完成三菱PLC與上位機的通訊👈
👉VP聯合開源深度學習編程(WPF)👈
?有關C#專案歡迎各位查看個人主頁?
🌟機器視覺、深度學習🌟
學習機器視覺、深度學習不再是難問題
🌌《Halcon入門到精通》🌌
🌌《深度學習資料與教程》🌌
有關機器視覺、深度學習實戰
👉2021年C#+HALCON視覺軟體👈
👉2021年C#+HALCON實作模板匹配👈
👉C#集成Halcon的深度學習軟體👈
👉C#集成Halcon的深度學習軟體,帶[MNIST例子]資料集👈
👉C#支持等比例縮放拖動的halcon WPF開源表單控制元件👈
👉2021年Labview聯合HALCON👈
👉2021年Labview聯合Visionpro👈
👉基于Halcon及VS的動車組制動閘片厚度自動識別模塊👈
?有關機器視覺、深度學習實戰歡迎各位查看個人主頁?
🌟Java、資料庫教程與專案🌟
學習Java、資料庫教程不再是難問題
🍏《JAVA入門到高級教程》🍏
🍏《資料庫入門到高級教程》🍏
有關Java、資料庫專案實戰
👉Java經典懷舊小霸王網頁游戲機原始碼增強版👈
👉js+css類似網頁版網易音樂原始碼👈
👉Java物業管理系統+小程式原始碼👈
👉JavaWeb家居電子商城👈
👉JAVA酒店客房預定管理系統的設計與實作SQLserver👈
👉JAVA圖書管理系統的研究與開發MYSQL👈
?有關Java、資料庫教程與專案實戰歡迎各位查看個人主頁?
🌟分享Python知識講解、分享🌟
學習Python不再是難問題
🥝《Python知識、專案專欄》🥝
🥝《Python 檢測抖音關注賬號是否封號程》🥝
🥝《手把手教你Python+Qt5安裝與使用》🥝
🥝《用一萬字給小白全面講解python編程基礎問答》🥝
🥝《Python 繪制Android CPU和記憶體增長曲線》🥝
有關Python專案實戰
👉Python基于Django圖書管理系統👈
👉Python管理系統👈
👉2021年9個常用的python爬蟲原始碼👈
👉python二維碼生成器👈
?有關Python教程與專案實戰歡迎各位查看個人主頁?
🌟分享各大公司面試題、面試流程🌟
面試成功不是難事
🍏《2021年金九銀十最新的VUE面試題??《??記得收藏??》》🍏
🍏《只要你認真看完一萬字??Linux作業系統基礎知識??分分鐘鐘都吊打面試官《??記得收藏??》》🍏
🍏《??用一萬字給小白全面講解python編程基礎問答??《😀記得收藏不然看著看著就不見了😀》》🍏
?有關各大公司面試題、面試流程歡迎各位查看個人主頁?

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/319873.html
標籤:其他
