您现在的位置是:首页 > 技术教程 正文

Linux network namespace(网络命名空间)认知

admin 阅读: 2024-03-16
后台-插件-广告管理-内容页头部广告(手机)

写在前面


  • 整理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
┌──[liruilong@liruilongs.github.io]-[~] └─$sudo -i [sudo] password for liruilong:
  • 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 的使用

  1. 创建 namespace 的黑科技:clone 系统调用

用户可以使用 clone()系统调用创建一个 namespace。

  1. 维持 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 里的所有进程都终止运行了

  1. 往 namespace setns() 系统调用

Linux 系统调用 setns() 把一个进程加入一个已经存在的 namespace 中。

  1. 帮助进程逃离 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

后台-插件-广告管理-内容页尾部广告(手机)
关注我们

扫一扫关注我们,了解最新精彩内容

搜索