9.30更新:这本书应该不会再看下去了。豆瓣评价不多且差评蛮多,有几个好评才决定看的,看了一部分后还是及时止损吧。把时间花在更难更有价值的书籍上吧,官网学习也是正确的道路。
容器网络虚拟化基础
macOS M1下使用docker运行linux(centos):
1
|
docker run -it -v ~/Documents/docker/:/datas centos
|
报错:mount --make-shared /var/run/netns failed: Operation not permitted
解决方法:加上--privileged
,即:
1
|
docker run --privileged -it -v ~/Documents/docker/:/datas centos
|
Linux网络虚拟化
network namespace
配置network namespace
Linux的namespace:隔离内核资源
network namespace分类:
- Mount namespace:文件系统挂载点
- UTS namespace:主机名
- IPC namespace:POSIX进程间通信消息队列
- PID namespace:进程PID数字空间
- network namespace:IP地址
- user namespace:user ID数字空间
1
2
3
4
5
6
7
8
9
10
11
12
|
docker run --privileged -it -v ~/Documents/docker/:/datas centos
# ip netns add netns1
# ip netns exec netns1 ip link list
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
3: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/tunnel6 :: brd :: permaddr 9a0c:8a2c:6c02::
# ip netns list
netns1
# ip netns delete netns1
|
ip netns add NAME - create a new named network namespace
If NAME is available in /var/run/netns this command creates a new network namespace and assigns NAME.
当ip命令创建了一个network namespace时,系统会在/var/run/netns路径下面生成一个挂载点。
1
2
3
4
5
6
7
8
|
# ip netns exec netns1 ping 127.0.0.1
connect: Network is unreachable
# ip netns exec netns1 ip link set dev lo up
# ip netns exec netns1 ping 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.043 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.123 ms
...
|
1
2
|
# ip link add veth0 type veth peer name veth1
# ip link set veth1 netns netns1
|
创建了veth0和veth1这么一对虚拟以太网卡。
在默认情况下,它们都在主机的根network namespce中,将其中一块虚拟网卡veth1通过ip link set命令移动到netns1 network namespace。
报错:exec of "ifconfig" failed: No such file or directory
解决方法:
How to fix “bash: /usr/sbin/ifconfig: No such file or directory” on Linux
centos下运行:
yum install net-tools -y
再次运行ifconfig:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.2 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:ac:11:00:02 txqueuelen 0 (Ethernet)
RX packets 27688 bytes 133307537 (127.1 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 26844 bytes 1814858 (1.7 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (Local Loopback)
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
|
1
2
|
# ip netns exec netns1 ifconfig veth1 10.1.1.1/24 up
# ifconfig veth0 10.1.1.2/24 up
|
在主机根network namespace中的网卡veth0绑定IP地址10.1.1.2/24。
在主机上ping 10.1.1.1(netns1 network namespace里的网卡):
1
2
3
4
5
|
# ping 10.1.1.1
PING 10.1.1.1 (10.1.1.1) 56(84) bytes of data.
64 bytes from 10.1.1.1: icmp_seq=1 ttl=64 time=0.946 ms
64 bytes from 10.1.1.1: icmp_seq=2 ttl=64 time=0.295 ms
...
|
进入netns1 network namespace去ping主机上的虚拟网卡:
1
2
3
4
5
|
# ip netns exec netns1 ping 10.1.1.2
PING 10.1.1.2 (10.1.1.2) 56(84) bytes of data.
64 bytes from 10.1.1.2: icmp_seq=1 ttl=64 time=0.317 ms
64 bytes from 10.1.1.2: icmp_seq=2 ttl=64 time=0.387 ms
...
|
不同network namespace之间的路由表和防火墙规则等也是隔离的。
1
2
3
4
5
6
7
8
9
10
|
# route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default _gateway 0.0.0.0 UG 0 0 0 eth0
10.1.1.0 0.0.0.0 255.255.255.0 U 0 0 0 veth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
# ip netns exec netns1 route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
10.1.1.0 0.0.0.0 255.255.255.0 U 0 0 0 veth1
|
连接因特网的若干解决方法。例如:
- 主机的根network namespace创建一个Linux网桥并绑定veth pair的一端到网桥上
- 通过适当的NAT(网络地址转换)规则并辅以Linux的IP转发功能(配置net.ipv4.ip_forward=1)
用户可以随意将虚拟网络设备分配到自定义的network namespace里,而连接真实硬件的物理设备则只能放在系统的根network namesapce中。
1
|
# ip netns exec netns1 ip link set veth1 netns 1
|
ip link set veth1 netns 1
把netns1 network namespace下的veth1网卡挪到PID为1
的进程(即init进程
)所在的network namespace。
有两种途径索引network namespace:
- 名字(例如netns1)
- 属于该namespace的进程PID(例如1)
network namespace API
clone()、unshare()和setns()系统调用会使用CLONE_NEW*常量来区别要操作的namespace类型。
CLONE_NEW*常量一共有6个:CLONE_NEWIPC、CLONE_NEWNS、CLONE_NEWNET、CLONE_NEWPID、CLONE_NEW USER和CLONE_NEWUTS,分别代表6种不同的namespace类型。
创建namespace:clone系统调用
1
|
int clone(int (&child_func)(void *), void *child_stack, int flags, void *arg);
|
函数指针child_func:指定一个由新进程执行的函数。当这个函数返回时,子进程终止。该函数返回一个整数(表示子进程的退出代码)。
指针child_stack:传入子进程使用的栈空间,也就是把用户态堆栈指针
赋给子进程的esp寄存器
。调用clone()的进程应该总是为子进程分配新的堆栈。
int flags:表示CLONE_*标志位(可以多个)。
void *args:用户的自定义参数。
维持namespace存在:/proc/PID/ns
目录
每个Linux进程都拥有一个属于自己的/proc/PID/ns,这个目录下的每个文件都代表一个类型的namespace。
在Linux内核3.8版本以前,/proc/PID/ns目录下的文件都是硬链接(hard link)
,而且只有ipc、net和uts这三个文件。
从Linux内核3.8版本开始,每个文件都是一个特殊的符号链接文件
,这些文件提供了操作进程关联namespace的一种方式。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# ls -l /proc/$$/ns
total 0
lrwxrwxrwx 1 root root 0 Sep 19 00:51 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0 Sep 19 00:51 ipc -> 'ipc:[4026532305]'
lrwxrwxrwx 1 root root 0 Sep 19 00:51 mnt -> 'mnt:[4026532303]'
lrwxrwxrwx 1 root root 0 Sep 19 00:51 net -> 'net:[4026532308]'
lrwxrwxrwx 1 root root 0 Sep 19 00:51 pid -> 'pid:[4026532306]'
lrwxrwxrwx 1 root root 0 Sep 19 00:51 pid_for_children -> 'pid:[4026532306]'
lrwxrwxrwx 1 root root 0 Sep 19 00:51 time -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0 Sep 19 00:51 time_for_children -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0 Sep 19 00:51 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 Sep 19 00:51 uts -> 'uts:[4026532304]'
注:$$是shell进程的PID
|
符号链接的其中一个用途是确定某两个进程是否属于同一个namespace
。
如果两个进程在同一个namespace中,那么这两个进程/proc/PID/ns目录下对应符号链接文件的inode数字
(即上文例子中[]内的数字,例如4026532308;也可以通过stat()系统调用
获取返回结构体的st_ino字段
)会是一样的。
/proc/PID/ns目录下的文件还有一个作用——当我们打开这些文件时,只要文件描述符保持open状态
,对应的namespace就会一直存在,哪怕这个namespace里的所有进程都终止运行了。
1
2
|
# touch /my/net
# mount --bind /proc/$$/ns/net /my/net
|
把/proc/PID/ns目录下的文件挂载起来就能起到打开文件描述符的作用,而且这个network namespace会一直存在,直到/proc/self/ns/net被卸载。
往namespace里添加进程:setns系统调用
1
|
int setns(int fd,int nstype);
|
int fd:进程待加入的namespace对应的文件描述符。它是一个指向/proc/PID/ns目录下符号链接的文件描述符。
int nstype:让调用者检查第一个参数fd指向的namespace类型是否符合实际要求,0表示不检查。
让进程离开namespace:unshare系统调用
1
|
int unshare(int flags);
|
unshare()系统调用的工作机制是:
- 先通过指定的flags(即CLONE_NEW*bit位的组合)创建相应的namespace
- 再把这个进程挪到新创建的namespace中
于是也就完成了进程从原先namespace的撤离。unshare()提供的功能其实很像clone(),区别在于unshare()作用在一个已存在的进程上,而clone()会创建一个新的进程。
大部分Linux发行版自带的unshare命令就是基于unshare()系统调用的,它的作用就是在当前shell所在的namespace外执行一条命令。
1
|
# unshare [options] program [arguments]
|
Linux会为需要执行的命令(在上面的例子中,即program)启动一个新进程,然后在另外一个namespace中执行操作,这样就可以起到执行结果和原(父)进程隔离的效果。
veth pair
veth是虚拟以太网卡(Virtual Ethernet)的缩写。veth设备总是成对的,因此称之为veth pair。
容器与host veth pair的关系
经典容器组网模型就是veth pair+bridge的模式。
方法1:
1
2
3
4
5
|
# ip link show eth0
18: eth0@if19: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
# ip link show | grep 19
18: eth0@if19: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
|
18: eth0@if19,其中18是eth0接口的index,19是和它成对的veth的index。
方法2:
1
2
3
4
|
[root@d3eff7a40411 /]# ethtool -S eth0
NIC statistics:
peer_ifindex: 19
...
|
可以通过ethtool -S命令列出veth pair对端的网卡index。
Linux bridge
Linux bridge就是Linux系统中的网桥,但是Linux bridge的行为更像是一台虚拟的网络交换机,任意的真实物理设备(eth0)和虚拟设备(veth pair和tap设备)都可以连接到Linux bridge上。Linux bridge不能跨机
连接网络设备。
Linux bridge有多个端口,数据可以从任何端口进来,进来之后从哪个口出去取决于目的MAC地址,原理和物理交换机差不多。
创建一个bridge:
1
2
|
[root@d3eff7a40411 /]# ip link add name br0 type bridge
[root@d3eff7a40411 /]# ip link set br0 up
|
创建一对veth设备,并配置IP地址:
1
2
3
4
5
|
[root@d3eff7a40411 /]# ip link add veth0 type veth peer name veth1
[root@d3eff7a40411 /]# ip addr add 1.2.3.101/24 dev veth0
[root@d3eff7a40411 /]# ip addr add 1.2.3.102/24 dev veth1
[root@d3eff7a40411 /]# ip link set veth0 up
[root@d3eff7a40411 /]# ip link set veth1 up
|
将veth0连接到br0上:
1
2
|
# ip link set dev veth0 master br0
# brctl addif br0 veth0
|
tun/tap 设备
iptables
ipip
VXLAN
Macvlan
IPvlan
Docker容器网络
Linux容器
Docker的四大网络模式
CNM
容器组网
Kubernetes网络
Istio网络
Author
Anjana
LastMod
2022-08-20
License
原创文章,如需转载请注明作者和出处。谢谢!