虚拟网络技术的应知应会
学点网络总是没错。
TUN & TAP
TUN/TAP为用户态程序提供数据包接收和发送。它可以被视为一个简单的点对点或以太网设备,它不是从物理介质接收数据包,而是从用户空间程序接收数据包,并且不是通过物理介质发送数据包,而是将它们写入用户空间程序。
区别上就是
- TUN: 只支持 L3 的 IP 包
- TAP: 支持 Raw 以太网包
Try it
- 查下当前的 Interface
1 | $ ip addr |
- 创建一个 TUN 设备
1 | $ openvpn --mktun --dev tun0 |
- 启用并且为之分配 IP
1 | $ ip link set tun0 up # 在 kernels < 2.6.36 可以强制启动,对于当前 Linux ,设置也是无效的 |
- 接受 TUN 的请求
因为没有程序监听,就处于无法 UP 的状态,这里我们找一个开源工具 tuntap-packet-dumper
1 | $ git clone https://github.com/sjlongland/tuntap-packet-dumper.git |
如上文,程序就自动帮我们创建一个 tun
设备,不过就没有分配 IP
我们把上面的再执行一下
1 | $ ip a |
- 监听网卡
1 | $ tshark -i tun1 |
用途
- 因为我们可以获得数据的流量,所以我们可以用学习
TCP/IP
的协议栈,自己来实现一个TCP Stack
,这也是大多数的用户态网络协议栈的实现基础。 - 另外一个用法就是充当一个隧道,我们可以在
local
和remote
两者都创建一个tap/tun
设备,local
的请求通过tun
被程序劫持,然后进行转发到remote
上,remote
上的程序将数据再吐到tun
上,这样就形成一个虚拟的overlay
网络
可以参考的内容
Linux Namespace 命名空间
Linux Namespace 是 Linux 提供的一种内核级别环境隔离的方法。我们这里只考虑 Network Namespace
的情况
Try it
创建一个网络命名空间
1
2
3$ ip netns add <namespace name>
# 创建一个 peer 对
$ ip link add <namespace name> type veth peer name <namespace name>将网卡挂载到 namespace 中
1
$ ip link set <veth name> netns <namespace name>
在命名空间执行命令
1
$ ip netns exec <namespace name> <command>
用途
主要使用 namespace 隔离网络设备,避免网络设备互相干扰。
Bridge 网桥
Linux 网桥技术,就像真实的交换机一样,可以将多个网络设备连接在一起,但是值得注意的是 交换机
是一个二层设备,因此其实是通过 MAC
来进行通讯的,需要支持 STP
之类的功能。
Try it
创建
bridge
设备1
2
3# ip link add <bridge name> type bridge
$ ip link add br0 type bridge将网卡挂载到网桥上
1 | $ ip link set dev ens33 master br0 # 将 ens33 接到 br0 上 |
- 启用网桥
1
ip link set dev br0 up # 启用 br0 这时网络应该通了
用途
网桥的本质就是将互不连通的网卡接到一起。
为什么网桥有时候需要 IP
配置 IP 的目的只要是为了充当网关的角色,因为网桥内部的设备都是基于 MAC
来进行通讯的。因此如果我们想要将外部流量导入这个网桥的话,就需要 IP
地址了。
Linux Veth pair
veth
设备构建为成对连接的虚拟以太网接口,可以将其视为虚拟跳线。从一端进入的东西将从另一端出来。
veth pair
是成对出现的一种虚拟网络设备接口,一端连着网络协议栈,一端彼此相连。如下图所示:
Try it
创建
veth
设备对1
2
3
4
5
6
7
8# ip link add <veth name> type veth peer name <peer name>
$ ip link add veth0 type veth peer name veth1
$ ip a
9: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 32:c1:1e:0e:38:dc brd ff:ff:ff:ff:ff:ff
10: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether aa:13:47:de:c7:54 brd ff:ff:ff:ff:ff:ff给
veth
设置 IP1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17$ ip addr add 10.0.0.1/32 dev veth0
$ ip link set veth0 up
$ ip addr add 10.0.0.2/32 dev veth1
$ ip link set veth1 up
$ ip a
11: veth1@veth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 32:c1:1e:0e:38:dc brd ff:ff:ff:ff:ff:ff
inet 10.0.0.2/32 scope global veth1
valid_lft forever preferred_lft forever
inet6 fe80::30c1:1eff:fe0e:38dc/64 scope link
valid_lft forever preferred_lft forever
12: veth0@veth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether aa:13:47:de:c7:54 brd ff:ff:ff:ff:ff:ff
inet 10.0.0.1/32 scope global veth0
valid_lft forever preferred_lft forever
inet6 fe80::a813:47ff:fede:c754/64 scope link
valid_lft forever preferred_lft forever
用途
当我们集合上面的网桥,命名空间和veth 我们就可以构建一个容器网络了,我们在这里尝试展示一下,借鉴 通过实验学习 Linux VETH 和 Bridge
容器网络 Demo
网络拓扑
1 | +------------------------+ |
Try it
初始环境
1 | $ ip a |
准备网桥
1 | $ brctl addbr br01 |
分配 namespace
1 | $ ip netns add ns01 |
创建 veth pair & 分配到 namespace
1 | $ ip link add veth01 type veth peer name br-veth01 |
将一头放到网桥里面
1 | $ brctl addif br01 br-veth01 |
启用网卡
1 | $ ip link set dev br-veth01 up |
另外一头放入 namespace 中
1 | $ ip link set veth01 netns ns01 |
分配 IP 地址和网关
1 | $ ip netns exec ns01 ip link set dev veth01 up |
PING 抓包查看
1 | # 在网桥抓包 |
但是我们尝试 Ping 外网服务,会失败,抓包会发现只有发出去的包,并没有收到回复的包。
1 | $ ip netns exec ns01 ping 114.114.114.114 |
我们在 br0 上抓包发现包已经出去了
1 | $ tcpdump -i br01 -nn |
那我们在物理网卡上继续抓包,就没有任何数据。
1 | $ tcpdump -i ens33 -nn host 114.114.114.114 |
那说明我们网络的包被丢弃了,我们需要修改一下网络配置,这里主要是打开 ip forward
让 linux 可以转发
1 | $ sysctl net.ipv4.ip_forward |
我们继续抓包物理网卡就有数据了
1 | tcpdump: verbose output suppressed, use -v[v]... for full protocol decode |
但是并没有回复的报文,因为 Linux 会丢失不是本网卡的请求包,这里需要使用 NAT
技术了
1 | $ iptables -t nat -A POSTROUTING -s 10.1.0.0/24 -j MASQUERADE #让内核把来源是 10.1.0.0/24 的请求通过 `SNAT` 的方式转出去 |
这个时候我们就发现链路联调了
1 | $ ip netns exec ns01 ping 114.114.114.114 |
IPVS
IPVS(IP Virtual Server)是Linux内核自带的一个实现虚拟服务器的软件。
IPVS能够在Linux系统上实现负载均衡,将来自客户端的请求分发到多个后端服务器上,从而提高系统的可扩展性和可靠性。
IPVS的实现原理主要是基于虚拟IP和虚拟服务器,通过分析客户端的请求,将请求转发到后端服务器上,从而实现负载均衡的功能。
Try it
在上面的实验中,我们将 POD 的 CIDR 定义在 10.1.0.0/24
宿主机的 IP 是 172.x
那我们这里用一个 192.168.0.0/24
的网段给 SVC
- 创建一个虚拟地址
1 | # 增加一个虚拟服务 192.168.20.128:80,并指定调度算法为 wrr,即 weighted round robin |
- 通过抓包 POD
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19# 多 Curl 几次虚拟地址,让他可以访问到 0.1 地址
$ curl 192.168.20.128:80
# 从容器内抓包
$ ip netns exec ns01 tcpdump -i veth01 -nn -v
tcpdump: listening on veth01, link-type EN10MB (Ethernet), snapshot length 262144 bytes
15:06:21.537188 IP (tos 0x0, ttl 63, id 17209, offset 0, flags [DF], proto TCP (6), length 60)
172.16.140.134.58956 > 10.1.0.1.80: Flags [S], cksum 0x42c7 (incorrect -> 0xe8f4), seq 2924738622, win 64240, options [mss 1460,sackOK,TS val 3067927925 ecr 0,nop,wscale 7], length 0
15:06:21.537199 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 40)
10.1.0.1.80 > 172.16.140.134.58956: Flags [R.], cksum 0xe007 (correct), seq 0, ack 2924738623, win 0, length 0
15:06:24.905873 IP (tos 0x0, ttl 63, id 33240, offset 0, flags [DF], proto TCP (6), length 60)
172.16.140.134.48152 > 10.1.0.2.80: Flags [S], cksum 0x42c8 (incorrect -> 0xb3b0), seq 807127238, win 64240, options [mss 1460,sackOK,TS val 3067931293 ecr 0,nop,wscale 7], length 0
15:06:25.384265 IP (tos 0x0, ttl 63, id 45310, offset 0, flags [DF], proto TCP (6), length 60)
172.16.140.134.48166 > 10.1.0.1.80: Flags [S], cksum 0x42c7 (incorrect -> 0x5987), seq 3884018077, win 64240, options [mss 1460,sackOK,TS val 3067931772 ecr 0,nop,wscale 7], length 0
15:06:25.384275 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 40)
10.1.0.1.80 > 172.16.140.134.48166: Flags [R.], cksum 0x5fa1 (correct), seq 0, ack 3884018078, win 0, length 0
15:06:26.215791 IP (tos 0x0, ttl 63, id 59300, offset 0, flags [DF], proto TCP (6), length 60)
172.16.140.134.48178 > 10.1.0.1.80: Flags [S], cksum 0x42c7 (incorrect -> 0xc171), seq 3535541037, win 64240, options [mss 1460,sackOK,TS val 3067932603 ecr 0,nop,wscale 7], length 0
15:06:26.215803 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 40)
到这里对于单机的网络我们都已经搞定了,那我们再来看看分布式网络应该如何处理吧。