Linux network namespace(网络命名空间)认知
写在前面
-
整理K8s网络相关笔记 -
博文内容涉及 Linux network namespace 认知以及彼此通信Demo,实际中的应用 -
理解不足小伙伴帮忙指正
不必太纠结于当下,也不必太忧虑未来,当你经历过一些事情的时候,眼前的风景已经和从前不一样了。——村上春树
network namespace 是什么?
可以通过 ip-netns
帮助文档简单了解
┌──[liruilong@liruilongs.github.io]-[~]
└─$man ip-netns | cat
网络命名空间
在逻辑上是网络堆栈
的另一个副本,具有自己的路由、防火墙规则和网络设备
。
默认情况下,进程
从其父进程
继承其网络名称空间
。 最初,所有进程共享来自 init 进程的相同默认网络命名空间
。即 Linux 进程
处在和主机相同的 namespace
,即初始的根 namespace
里,默认享有全局系统资源。
按照约定,命名网络命名空间是位于 /var/run/netns/NAME
的可以打开的对象。 打开 /var/run/netns/NAME
产生的文件描述符引用指定的网络名称空间。 保持文件描述符打开可以使网络命名空间保持活动状态。 文件描述符可以与 setns(2)
系统调用一起使用来更改与任务关联的网络命名空间。
对于了解网络命名空间的应用程序,惯例是首先在 /etc/netns/NAME/
中查找全局网络配置文件,然后在 /etc/
中查找。 例如,如果您想要不同的用于隔离 VPN
的网络命名空间的 /etc/resolv.conf
版本,您可以将其命名为 /etc/netns/myvpn/resolv.conf
。
ip netns exec
通过创建安装命名空间
并绑定安装所有每个网络命名空间,自动处理此配置、网络命名空间
不感知应用程序的文件约定将文件配置到 /etc
中的传统位置。
network namespace
可以说是整个 Linux 网络虚拟化技术的基石,其作用就是隔离内核资源
Linux 内核自2.4.19
版本接纳第一个 namespace:Mount namespace(用于隔离文件系统挂载点)
起,到 3.8 版本的user namespace(用于隔离用户权限)
,总共实现了 6 种不同类型的 namespace (Mount namespace、UTS namespace(主机名)、IPC namespace(进程通信mq)、PID namespace、network namespace和user namespace)
。
默认情况下 network namespace
在 Linux 内核 2.6
版本引入,作用是隔离 Linux 系统的设备,以及 IP 地址、端口、路由表、防火墙规则等网络资源
。因此,每个网络 namespace 里都有自己的网络设备(如 IP 地址、路由表、端口范围、/proc/net 目录等)。
初识 network namespace
network namespace
可以通过系统调用来创建, 当前 network namespace
的增删改查功能已经集成到 Linux 的 ip 工具的 netns
子命令中。
帮助文档中的 Demo
ip netns add vpn
创建名为vpn的网络名称空间
ip netns exec vpn ip link set lo up
在vpn网络名称空间中激活回环接口。
创建一个名为 ns_lrl
的 network namespace
┌──[root@liruilongs.github.io]-[~]
└─$ip netns list
┌──[root@liruilongs.github.io]-[~]
└─$ip netns add ns_lrl
┌──[root@liruilongs.github.io]-[~]
└─$ip netns list
ns_lrl
创建了一个 network namespace
时,系统会在 /var/run/netns
路径下面生成一个挂载点
┌──[root@vms105.liruilongs.github.io]-[~]
└─$ls /var/run/netns/ns_lrl
/var/run/netns/ns_lrl
┌──[root@vms105.liruilongs.github.io]-[~]
└─$
挂载点的作用一方面是方便对 namespace
的管理,另一方面是使 namespace
即使没有进程运行也能继续存在。
命令空间通过下面的方式激活回环接口后
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec ns_lrl ip link set lo up
可以看到像默认 Linux namespace 一样的本地回环地址
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec ns_lrl ip a
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
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
做简单的 ping
测试
┌──[root@vms105.liruilongs.github.io]-[~]
└─$ip netns exec ns_lrl ping 127.0.0.1 -c 3
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.044 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.033 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.031 ms
--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2024ms
rtt min/avg/max/mdev = 0.031/0.036/0.044/0.005 ms
┌──[root@vms105.liruilongs.github.io]-[~]
└─$
配置 network namespace 之间通信
虚拟以太网对(veth pair)
我们来看一个 Demo,两个网络命名空间如何通信,在这之前,先看一点理论,
veth pair
是Virtual Ethernet(虚拟以太网)接口在Linux中的一种实现方式。veth
接口具有以下特点:
veth是一对接口,分别位于两个不同的network namespace
中。这一对接口通过内核实现软链接
,可将不同namespace中的数据连接起来。数据可以直接在这一对接口间进行传输,实现了namespace间的数据通信。
创建一个网络命名空间
┌──[liruilong@liruilongs.github.io]-[~]
└─$ip netns add net1
mkdir /var/run/netns failed: Permission denied
需要 root 才行,切一下 root,这里需要说明
-
非 root 进程被分配到 network namespace 后只能访问和配置已经存在于该 network namespace 的设备 -
root 进程可以在 network namespace 里创建新的网络设备 -
network namespace 里的 root 进程还能把本 network namespace 的虚拟网络设备分配到其他 network namespac
┌──[liruilong@liruilongs.github.io]-[~]
└─$sudo -i
[sudo] password for liruilong:
创建两个 网络命名空间
┌──[root@liruilongs.github.io]-[~]
└─$ip netns add net1
┌──[root@liruilongs.github.io]-[~]
└─$ip netns add net2
┌──[root@liruilongs.github.io]-[~]
└─$ip netns list
net2
net1
仅有一个本地回环设备是没法与外界通信的。如果我们想与外界(比如主机上的网卡)进行通信,就需要在 namespace
里再 创建一对虚拟的以太网卡
,即所谓的 veth pair
。顾名思义,veth pair
总是成对出现且相互连接,它就像 Linux 的双向管道(pipe)
,报文从 veth pair
一端进去就会由另一端收到.
┌──[root@liruilongs.github.io]-[~]
└─$ip link add veth1 netns net1 type veth peer name veth2 netns net2
┌──[root@liruilongs.github.io]-[~]
└─$ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether 00:0c:29:93:51:67 brd ff:ff:ff:ff:ff:ff
altname enp3s0
-
ip link add
: 创建一对接口 -
veth1
: 设置本地端接口名 -
netns net1
: 将 veth1 移动到名为 net1 的命名空间 -
type veth: 指定接口类型为 veth
虚拟接口,veth是virtual ethernet
的缩写,即虚拟以太网
接口。 -
peer name veth2
: 设置远端接口名 -
netns net2
: 将veth2
移动到名为 net2 的命名空间
上面的操作创建了 veth1
和 veth2
这么一对虚拟以太网卡。在默认情况下,它们都在主机的根 network namespce
中,将其中一块虚拟网卡 veth1
通过 netns net1
命令移动到 net1 network namespace
,另一块网卡通过 netns net2
命令移动到 命令移动到
net2 network namespace`
执行这个命令后,会在两个不同的命名空间net1和net2
内各自创建一根接口:
-
在 net1 命名空间内创建接口 veth1
-
在 net2 命名空间内创建接口 veth2
这两根接口通过 peer 自动连接成一对,形成跨命名空间的虚拟链路。
在每个命名空间执行 ip link
命令,可以看到详细的信息
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec net1 ip link
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: veth1@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether b2:ae:39:9e:50:4b brd ff:ff:ff:ff:ff:ff link-netns net2
net1
命名空间虚拟网卡 veth1
,与名称为 net2
的命名空间相关联
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec net2 ip link
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: veth2@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 7a:1b:8e:91:41:79 brd ff:ff:ff:ff:ff:ff link-netns net1
net2
命名空间虚拟网卡 veth2
,与名称为 net1
的命名空间相关联
通过 ip netns exec net1 bash
这个命令进入指定命名空间的 shell 环境,在当前 shell 中执行的命名对当前命名空间生效
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec net1 bash
分配 IP 地址以及配置掩码,指定对应的虚拟网卡, 这里分配IP 192.168.20.1/24
┌──[root@liruilongs.github.io]-[~]
└─$ip address add 192.168.20.1/24 dev veth1
激活本地回环网卡
┌──[root@liruilongs.github.io]-[~]
└─$ip link set dev lo up
激活虚拟网卡
┌──[root@liruilongs.github.io]-[~]
└─$ip link set dev veth1 up
因为另一端 veth1
还没有打开,所以链接状态仍然显示为关闭 state DOWN
┌──[root@liruilongs.github.io]-[~]
└─$ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: veth1@if2: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default qlen 1000
link/ether b2:ae:39:9e:50:4b brd ff:ff:ff:ff:ff:ff link-netns net2
查看路由信息,可以发现,命令空间路由相互独立,但是由于接口当前 down,这条路由实际不可用
┌──[root@liruilongs.github.io]-[~]
└─$ip route
192.168.20.0/24 dev veth1 proto kernel scope link src 192.168.20.1 linkdown
查看命名空间IP信息
┌──[root@liruilongs.github.io]-[~]
└─$ip a
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
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: veth1@if2: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
link/ether b2:ae:39:9e:50:4b brd ff:ff:ff:ff:ff:ff link-netns net2
inet 192.168.20.1/24 scope global veth1
valid_lft forever preferred_lft forever
┌──[root@liruilongs.github.io]-[~]
└─$exit
exit
退出第一个命名空间的 shell
环境,我们进入第二个命名空间的 shell 环境,做相同的配置 这里分配IP 192.168.20.2/24
┌──[root@liruilongs.github.io]-[~]
└─$ ip netns exec net2 bash
┌──[root@liruilongs.github.io]-[~]
└─$ip address add 192.168.20.2/24 dev veth2
┌──[root@liruilongs.github.io]-[~]
└─$ip link set dev veth2 up
┌──[root@liruilongs.github.io]-[~]
└─$ip link set dev lo up
这个时候,我们在看链接,状态,会发现,veth2 虚拟网卡状态为 UP 状态 state UP
┌──[root@liruilongs.github.io]-[~]
└─$ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: veth2@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 7a:1b:8e:91:41:79 brd ff:ff:ff:ff:ff:ff link-netns net1
查看分配IP的虚拟网卡也为 UP 状态
┌──[root@liruilongs.github.io]-[~]
└─$ip a
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
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: veth2@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 7a:1b:8e:91:41:79 brd ff:ff:ff:ff:ff:ff link-netns net1
inet 192.168.20.2/24 scope global veth2
valid_lft forever preferred_lft forever
inet6 fe80::781b:8eff:fe91:4179/64 scope link
valid_lft forever preferred_lft forever
独立的路由信息
┌──[root@liruilongs.github.io]-[~]
└─$ip route
192.168.20.0/24 dev veth2 proto kernel scope link src 192.168.20.2
回到 net1,net1名称空间中veth1的链接状态也显示UP (state UP)
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec net1 ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: veth1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether b2:ae:39:9e:50:4b brd ff:ff:ff:ff:ff:ff link-netns net2
┌──[root@liruilongs.github.io]-[~]
└─$exit
exit
根
命名空间不知道net1
和net2
命名空间的IP配置
,三者彼此隔离。
┌──[root@liruilongs.github.io]-[~]
└─$ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether 00:0c:29:93:51:67 brd ff:ff:ff:ff:ff:ff
altname enp3s0
路由信息也为独立的路由信息
┌──[root@liruilongs.github.io]-[~]
└─$ip route
default via 192.168.26.2 dev ens160 proto dhcp src 192.168.26.149 metric 100
192.168.26.0/24 dev ens160 proto kernel scope link src 192.168.26.149 metric 100
从根网络命名空间 ping 测试到veth1 IP失败。这是因为IP 192.168.20.1 属于独立的网络命名空间 net1。
┌──[root@liruilongs.github.io]-[~]
└─$ping 192.168.20.1 -c 1
PING 192.168.20.1 (192.168.20.1) 56(84) bytes of data.
--- 192.168.20.1 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms
从 net1
和 net2
测试网络通信,命名空间使用 ping 命令。
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec net1 ping -c 1 192.168.20.2
PING 192.168.20.2 (192.168.20.2) 56(84) bytes of data.
64 bytes from 192.168.20.2: icmp_seq=1 ttl=64 time=0.060 ms
--- 192.168.20.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.060/0.060/0.060/0.000 ms
输出确认 net1
名称空间中的 veth1
接口能够成功地与 net2
名称空间中的 veth2
接口通信。
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec net2 ping -c 1 192.168.20.1
PING 192.168.20.1 (192.168.20.1) 56(84) bytes of data.
64 bytes from 192.168.20.1: icmp_seq=1 ttl=64 time=0.027 ms
--- 192.168.20.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.027/0.027/0.027/0.000 ms
┌──[root@liruilongs.github.io]-[~]
└─$
在实际使用中,更多的是 当前的根网络命名空间
和 某个网络命名空间
组成 veth pair
进行通信。
# 创建一个网络命名空间
┌──[root@liruilongs.github.io]-[~]
└─$ip netns add pod_ns
# 在根网络命名空间和"pod_ns"命名空间之间创建一个veth pair,并将其中一个端口放入"pod_ns"命名空间:
┌──[root@liruilongs.github.io]-[~]
└─$ip link add veth0 type veth peer name veth1
┌──[root@liruilongs.github.io]-[~]
└─$ip link set veth1 netns pod_ns
# 在根网络命名空间中配置veth0的IP地址和其他网络参数
┌──[root@liruilongs.github.io]-[~]
└─$ip addr add 192.168.1.1/24 dev veth0
#激活虚拟网卡
┌──[root@liruilongs.github.io]-[~]
└─$ip link set veth0 up
# 查看连接信息 link-netns pod_ns
┌──[root@liruilongs.github.io]-[~]
└─$ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether 00:0c:29:93:51:67 brd ff:ff:ff:ff:ff:ff
altname enp3s0
4: veth0@if3: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state LOWERLAYERDOWN mode DEFAULT group default qlen 1000
link/ether 6e:62:83:49:71:90 brd ff:ff:ff:ff:ff:ff link-netns pod_ns
# 在"pod_ns"命名空间中配置veth1的IP地址和其他网络参数,激活网卡
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec pod_ns ip addr add 192.168.1.2/24 dev veth1
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec pod_ns ip link set veth1 up
查看链接状态
┌──[root@liruilongs.github.io]-[~]
└─$ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether 00:0c:29:93:51:67 brd ff:ff:ff:ff:ff:ff
altname enp3s0
4: veth0@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 6e:62:83:49:71:90 brd ff:ff:ff:ff:ff:ff link-netns pod_ns
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec pod_ns ip link
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
3: veth1@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 7e:60:b2:6e:d8:55 brd ff:ff:ff:ff:ff:ff link-netnsid 0
┌──[root@liruilongs.github.io]-[~]
└─$ip netns exec pod_ns ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
3: veth1@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 7e:60:b2:6e:d8:55 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 192.168.1.2/24 scope global veth1
valid_lft forever preferred_lft forever
inet6 fe80::7c60:b2ff:fe6e:d855/64 scope link
valid_lft forever preferred_lft forever
在根网络命名空间对 pod_ns
网络命名空间分配IP进行ping 测试
┌──[root@liruilongs.github.io]-[~]
└─$ping -c 3 192.168.1.2
PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.
64 bytes from 192.168.1.2: icmp_seq=1 ttl=64 time=0.380 ms
64 bytes from 192.168.1.2: icmp_seq=2 ttl=64 time=0.043 ms
64 bytes from 192.168.1.2: icmp_seq=3 ttl=64 time=0.117 ms
--- 192.168.1.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2047ms
rtt min/avg/max/mdev = 0.043/0.180/0.380/0.144 ms
┌──[root@liruilongs.github.io]-[~]
└─$
network namespace 实际中的应用
接触过 k8s
的小伙伴通过ip a
命令打印网络接口信息的时候(CNI使用Calico),经常会看到下面的一些接口信息
┌──[root@vms100.liruilongs.github.io]-[~/ansible]
└─$ansible etcd -m shell -a "ip a | grep -A 4 cali"
192.168.26.102 | CHANGED | rc=0 >>
5: cali6f956c2ada9@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
link/ether 6a:65:54:1a:19:e6 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::6865:54ff:fe1a:19e6/64 scope link
valid_lft forever preferred_lft forever
192.168.26.100 | CHANGED | rc=0 >>
5: cali0b7f49da20a@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
link/ether 9e:da:0e:cc:b3:7e brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::9cda:eff:fecc:b37e/64 scope link
valid_lft forever preferred_lft forever
192.168.26.101 | CHANGED | rc=0 >>
5: calib6f7ddae7e3@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
link/ether 1e:e6:16:ae:f0:91 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::1ce6:16ff:feae:f091/64 scope link
valid_lft forever preferred_lft forever
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$
实际上,上面的接口信息即为生成的 虚拟以太网对veth pair
,也就是刚刚做的Demo,一端在根网络命名空间,一端在 Pod对应的网络命名空间中。从而实现 k8s Pod
之间的通信,当然其中还要涉及工作节点的路由信息。
容器与 host veth pair 的关系
经典容器组网
模型就是 veth pair+bridge
的模式。容器中的 eth0
实际上和外面根网络命名空间上的某个 veth 是成对的(pair)关系.(这里的 bridge 主要用于 和因特网通信)
可以通过下面两种方式来获取对应关系
方法一
容器里面看 /sys/class/net/eth0/iflink
┌──[root@liruilongs.github.io]-[/]
└─$ docker exec -it 6471704fd03a sh
/ # cat /sys/class/net/eth0/if
ifalias ifindex iflink
/ # cat /sys/class/net/eth0/iflink
95
/ # exit
然后,在主机上遍历 /sys/claas/net
下面的全部目录,查看子目录 ifindex 的值和容器里查出来的 iflink 值相当的 veth 名字,这样就找到了容器和主机的 veth pair
关系。
┌──[root@liruilongs.github.io]-[/]
└─$ grep -c 95 /sys/class/net/*/ifindex | grep 1
/sys/class/net/veth2e08884/ifindex:1
方法二
在目标容器里执行以下命令,获取网卡索引为 94
,其中 94 是 eth0 接口的index,95 是和它成对的veth的index。
┌──[root@liruilongs.github.io]-[/]
└─$ docker exec -it 6471704fd03a sh
/ # ip link show eth0
94: eth0@if95: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
/ # exit
通过 95 index 来定位主机上对应的虚拟网卡
┌──[root@liruilongs.github.io]-[/]
└─$ ip link show | grep 95
95: veth2e08884@if94: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT
┌──[root@liruilongs.github.io]-[/]
└─$
network namespace API 的使用
-
创建 namespace 的黑科技: clone
系统调用
用户可以使用 clone()系统调用创建一个 namespace。
-
维持 namespace 存在: /proc/PID/ns
目录的奥秘
每个 Linux 进程都拥有一个属于自己的/proc/PID/ns,这个目录下的每个文件都代表一个类型的 namespace。
┌──[root@liruilongs.github.io]-[~]
└─$ls -l /proc/$$/ns
total 0
lrwxrwxrwx. 1 root root 0 Feb 11 02:27 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx. 1 root root 0 Feb 11 02:27 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx. 1 root root 0 Feb 11 02:27 mnt -> 'mnt:[4026531841]'
lrwxrwxrwx. 1 root root 0 Feb 11 02:27 net -> 'net:[4026531840]'
lrwxrwxrwx. 1 root root 0 Feb 11 02:27 pid -> 'pid:[4026531836]'
lrwxrwxrwx. 1 root root 0 Feb 11 02:27 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx. 1 root root 0 Feb 11 02:27 time -> 'time:[4026531834]'
lrwxrwxrwx. 1 root root 0 Feb 11 02:27 time_for_children -> 'time:[4026531834]'
lrwxrwxrwx. 1 root root 0 Feb 11 02:27 user -> 'user:[4026531837]'
lrwxrwxrwx. 1 root root 0 Feb 11 02:27 uts -> 'uts:[4026531838]'
┌──[root@liruilongs.github.io]-[~]
└─$
在 Linux 内核 3.8
版本以前,/proc/PID/ns
目录下的文件都是硬链接(hard link)
,而且只有 ipc、net 和 uts 这三个文件,从 Linux 内核 3.8
版本开始,每个文件都是一个特殊的符号链接
文件
符号链接的其中一个用途是确定某两个进程是否属于同一个 namespace。如果两个进程在同一个 namespace
中,那么这两个进程/proc/PID/ns
目录下对应符号链接文件的 inode
数字(即上文例子中[]内的数字,例如 4026531839
会是一样的。
/proc/PID/ns
目录下的文件还有一个作用当我们打开这些文件时,只要文件描述符保持 open
状态,对应的 namespace 就会一直存在,哪怕这个 namespace 里的所有进程都终止运行了
-
往 namespace setns()
系统调用
Linux 系统调用 setns() 把一个进程加入一个已经存在的 namespace 中。
-
帮助进程逃离 namespace: unshare
系统调用
通过 Linux 的 network namespace 技术可以自定义一个独立的网络栈
,简单到只有 loopback
设备,复杂到具备系统完整的网络能力,这就使得 network namespace 成为 Linux 网络虚拟化技术的基石——不论是虚拟机还是容器时代。
network namespace 的另一个隔离功能在于,系统管理员一旦禁用 namespace
中的网络设备,即使里面的进程拿到了一些系统特权,也无法和外界通信。
# NETNSNAME 是命名空间的名称,DEVNAME 是要禁用的网络设备的名称。
ip netns exec NETNSNAME ip link set DEVNAME down
最后,网络对安全较为敏感,即使 network namespace 能够提供网络资源隔离的机制,用户还是会结合其他类型的 namespace 一起使用,以提供更好的安全隔离能力。
博文部分内容参考
© 文中涉及参考链接内容版权归原作者所有,如有侵权请告知 :)
https://man7.org/linux/man-pages/man7/network_namespaces.7.html
© 2018-2024 liruilonger@gmail.com, All rights reserved. 保持署名-非商用-相同方式共享(CC BY-NC-SA 4.0)
- 点赞
- 收藏
- 关注作者
评论(0)