Linux 中的虛擬網路介面
獨立部落格地址:https://ryan4yin.space/posts/linux-virtual-network-interfaces/
本文用到的字元畫工具:vscode-asciiflow2
Linux 具有強大的虛擬網路能力,這也是 openstack 網路、docker 容器網路以及 kubernetes 網路等虛擬網路的基礎。
這裡介紹 Linux 常用的虛擬網路介面型別:TUN/TAP、bridge 以及 veth。
一、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 <[email protected]> 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 程式,可以參考
TUN 與 TAP 的區別
TUN 和 TAP 的區別在於工作的網路層次不同,使用者程式通過 TUN 裝置只能讀寫網路層的 IP 資料包,而 TAP 裝置則支援讀寫鏈路層的資料包(通常是乙太網資料包,帶有 Ethernet headers)。
TUN 與 TAP 的關係,就類似於 socket 和 raw socket.
TUN/TAP 應用最多的場景是 VPN 代理,比如:
二、veth
veth 介面總是成對出現,一對 veth 介面就類似一根網線,從一端進來的資料會從另一端出去。
同時 veth 又是一個虛擬網路介面,因此它和 TUN/TAP 或者其他物理網路介面一樣,也都能配置 mac/ip 地址(但是並不是一定得配 mac/ip 地址)。
其主要作用就是連線不同的網路,比如在容器網路中,用於將容器的 namespace 與 root namespace 的網橋 br0 相連。
容器網路中,容器側的 veth 自身設定了 ip/mac 地址並被重新命名為 eth0,作為容器的網路介面使用,而主機側的 veth 則直接連線在 docker0/br0 上面。
使用 veth 的容器網路的詳細示例圖,在下一節有提供。
三、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
四、其他虛擬網路介面的型別
除了上面介紹的這些,Linux 還支援 VLAN、VXLAN 等型別的虛擬網路介面,可通過 ip link help
檢視,因為我接觸的少,這裡就不介紹了。
五、虛擬網路介面的速率
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,但是感覺不太可能這麼慢,有時間可以再測一波看看。