Linux network namespace(网络命名空间)认知
后台-插件-广告管理-内容页头部广告(手机) |
写在前面
- 整理K8s网络相关笔记
- 博文内容涉及 Linux network namespace 认知以及彼此通信Demo,实际中的应用
- 理解不足小伙伴帮忙指正
不必太纠结于当下,也不必太忧虑未来,当你经历过一些事情的时候,眼前的风景已经和从前不一样了。——村上春树
network namespace 是什么?
可以通过 ip-netns 帮助文档简单了解
┌──[liruilong@liruilongs.github.io]-[~] └─$man ip-netns | cat- 1
- 2
网络命名空间在逻辑上是网络堆栈的另一个副本,具有自己的路由、防火墙规则和网络设备。
默认情况下,进程从其父进程继承其网络名称空间。 最初,所有进程共享来自 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网络名称空间中激活回环接口。- 1
- 2
- 3
- 4
- 5
创建一个名为 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- 1
- 2
- 3
- 4
- 5
- 6
- 7
创建了一个 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]-[~] └─$- 1
- 2
- 3
- 4
- 5
挂载点的作用一方面是方便对 namespace 的管理,另一方面是使 namespace 即使没有进程运行也能继续存在。
命令空间通过下面的方式激活回环接口后
┌──[root@liruilongs.github.io]-[~] └─$ip netns exec ns_lrl ip link set lo up- 1
- 2
可以看到像默认 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- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
做简单的 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]-[~] └─$- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
配置 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- 1
- 2
- 3
需要 root 才行,切一下 root,这里需要说明
- 非 root 进程被分配到 network namespace 后只能访问和配置已经存在于该 network namespace 的设备
- root 进程可以在 network namespace 里创建新的网络设备
- network namespace 里的 root 进程还能把本 network namespace 的虚拟网络设备分配到其他 network namespac
- 1
- 2
- 3
创建两个 网络命名空间
┌──[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- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
仅有一个本地回环设备是没法与外界通信的。如果我们想与外界(比如主机上的网卡)进行通信,就需要在 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- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 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- 1
- 2
- 3
- 4
- 5
- 6
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- 1
- 2
- 3
- 4
- 5
- 6
net2 命名空间虚拟网卡 veth2 ,与名称为 net1 的命名空间相关联
通过 ip netns exec net1 bash 这个命令进入指定命名空间的 shell 环境,在当前 shell 中执行的命名对当前命名空间生效
┌──[root@liruilongs.github.io]-[~] └─$ip netns exec net1 bash- 1
- 2
分配 IP 地址以及配置掩码,指定对应的虚拟网卡, 这里分配IP 192.168.20.1/24
┌──[root@liruilongs.github.io]-[~] └─$ip address add 192.168.20.1/24 dev veth1- 1
- 2
激活本地回环网卡
┌──[root@liruilongs.github.io]-[~] └─$ip link set dev lo up- 1
- 2
激活虚拟网卡
┌──[root@liruilongs.github.io]-[~] └─$ip link set dev veth1 up- 1
- 2
因为另一端 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- 1
- 2
- 3
- 4
- 5
- 6
查看路由信息,可以发现,命令空间路由相互独立,但是由于接口当前 down,这条路由实际不可用
┌──[root@liruilongs.github.io]-[~] └─$ip route 192.168.20.0/24 dev veth1 proto kernel scope link src 192.168.20.1 linkdown- 1
- 2
- 3
查看命名空间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- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
退出第一个命名空间的 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- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
这个时候,我们在看链接,状态,会发现,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- 1
- 2
- 3
- 4
- 5
- 6
查看分配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- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
独立的路由信息
┌──[root@liruilongs.github.io]-[~] └─$ip route 192.168.20.0/24 dev veth2 proto kernel scope link src 192.168.20.2- 1
- 2
- 3
回到 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- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
根命名空间不知道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- 1
- 2
- 3
- 4
- 5
- 6
- 7
路由信息也为独立的路由信息
┌──[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- 1
- 2
- 3
- 4
从根网络命名空间 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- 1
- 2
- 3
- 4
- 5
- 6
从 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- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
输出确认 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]-[~] └─$- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
在实际使用中,更多的是 当前的根网络命名空间和 某个网络命名空间组成 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- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
查看链接状态
┌──[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- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
在根网络命名空间对 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]-[~] └─$- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
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] └─$- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
实际上,上面的接口信息即为生成的 虚拟以太网对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- 1
- 2
- 3
- 4
- 5
- 6
- 7
然后,在主机上遍历 /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- 1
- 2
- 3
方法二
在目标容器里执行以下命令,获取网卡索引为 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- 1
- 2
- 3
- 4
- 5
- 6
通过 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]-[/] └─$- 1
- 2
- 3
- 4
- 5
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]-[~] └─$- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
在 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- 1
- 2
最后,网络对安全较为敏感,即使 network namespace 能够提供网络资源隔离的机制,用户还是会结合其他类型的 namespace 一起使用,以提供更好的安全隔离能力。
博文部分内容参考
© 文中涉及参考链接内容版权归原作者所有,如有侵权请告知
1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。
在线投稿:投稿 站长QQ:1888636
后台-插件-广告管理-内容页尾部广告(手机) |