侧边栏壁纸
  • 累计撰写 32 篇文章
  • 累计创建 55 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

Linux Namespace:network

Testerfans
2022-06-23 / 0 评论 / 28 点赞 / 3,011 阅读 / 3,787 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-07-13,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

本文演示 CentOS 7.6:Linux testerfans 3.10.0-1160.45.1.el7.x86_64

前言

Linux network namespace在Linux 2.6.24加入到内核中,用来隔离网络设备,协议栈,端口等。每个network namespace在逻辑上是网络堆栈的另一个副本,具有自己的路由、防火墙规则和网络设备。默认情况下,进程从其父进程继承其网络命名空间。在没有新建网络命名空间情况下,所有进程都与 init 进程共享相同的默认网络命名空间。本章我们将重点通过演示来帮助大家理解什么是network namespace,如何实现不同network namespace之间的网络通信。

Linux ip netns介绍

在实际演示network namespace之前我们首先介绍一下Linux ip netns工具。ip netns工具可以实现给network namespace进行命名,然后通过名字对namespace进行操作。

按照惯例,named network namespace是 /var/run/netns/NAME 中可以打开的对象。打开 /var/run/netns/NAME 产生的文件描述符引用指定的网络命名空间。保持该文件描述符打开可以使网络命名空间保持活动状态。

ip netns命令

我们通过本机使用 man ip-netns或者ip-netns可以查看ip netns的使用说明。

CMD 描述
ip netns list 显示所有在 /var/run/netns的netns
ip netns add NAME 创建一个新的命名netns。如果 NAME 在 /var/run/netns 这个命令中可用则创建一个新的netns并分配 NAME。
ip netns attach NAME PID 创建一个新的命名netns。如果 NAME 在 /var/run/netns 这个命令中可用,将进程 PID 的netns附加到 NAME 就像它是用 ip netns 创建的一样。
ip [-all] netns delete [ NAME ] 删除命名netns。如果 NAME 存在于 /var/run/netns 中,它将被卸载并且挂载点被删除。
如果这是netns的最后一个用户 ,netns将被释放,所有物理设备都将移至默认设备。
否则网络命名空间会一直存在,直到它没有更多的用户。
如果挂载点被其他mnt namespace使用, ip netns delete 执行可能会失败。
如果指定了 -all 选项,则所有netns名称将被删除。
ip netns set NAME NETNSID 这个命令将一个ID分配给一个netns。
此 id 仅在当前网络命名空间中有效。如果指定了关键字“auto”,则内核会自动分配一个有效的nsid。
这个 id 将在某些netlink消息中被内核使用。如果内核在使用netns id的时候不存在,它将由内核自动分配。 一经分配,这个ID就无法更改。
ip netns pids NAME 查看命名netns内的进程。这个命令遍历proc并且找到所有拥有named netns作为他们的主netns的进程。
ip [-all] netns exec [ NAME ] cmd … 在指定的命名netns中执行命令。
ip netns monitor 监控对命名netns的操作。
ip netns list-id [target-nsid POSITIVE-INT] [nsid POSITIVE-INT] 以id形式显示所有的命名netns。

创建network namespace

  • 通过readlink /proc/$$/ns/net查看当前进程所在的netns。
[root@testerfans ~]# readlink /proc/$$/ns/net
net:[4026531956]
  • 通过ip netns add mynet创建一个mynet的网络命名空间。
  • 通过ls /var/run/netns查看在netns路径下多了一个mynet的文件对象。
[root@testerfans ~]# ip netns add mynet
[root@testerfans ~]# ls /var/run/netns
mynet
  • 通过ip netns exec mynet /bin/bash在mynet命名空间下启动一个bash进程。
  • 通过readlink /proc/$$/ns/net查看命名空间为net:[4026532292],说明bash运行在一个新的netns内。
[root@testerfans ~]# ip netns exec mynet /bin/bash
[root@testerfans ~]# readlink /proc/$$/ns/net
net:[4026532292]

我们在新的netns内启动了一个bash进程,那这个新的命名空间有什么不同呢?

  • 每个新创建的 network namespace 默认有一个本地环回接口 lo,并且这个接口是DOWN状态。
[root@testerfans ~]# ip addr
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
  • 我们启动这个lo接口,并查看被分配了IP:127.0.0.1。
[root@testerfans ~]# ip link set lo up
[root@testerfans ~]# ip addr
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这个IP可以正常ping通。
[root@testerfans ~]# 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.037 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.020 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.032 ms

--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.020/0.029/0.037/0.009 ms
  • 回收我们创建的mynet,确认被删除。
[root@testerfans ~]# ip netns delete mynet
[root@testerfans ~]# ls /var/run/netns

两个netns间通信

network namespace 之间是相互隔离的,我们可以使用 veth 设备把两个 network namespace 连接起来进行通信。veth 设备是虚拟的以太网设备。它们可以充当 network namespace 之间的通道,也可以作为独立的网络设备使用。veth 设备总是被成对的创建,并且这一对设备总是连接在一起的,所以一般把称之为 veth pair。需要注意的是,veth pair 无法单独存在,删除其中一个,另一个也会自动消失。接下来的示例我们就演示如何使用 veth pair 在两个 network namespace 直接通信。示例中我们使用 ip link 命令来创建和管理 veth pair。

  • 创建两个命名netns,分别为netns1和netns2。
[root@testerfans ~]# readlink /proc/$$/ns/net
net:[4026531956]
[root@testerfans ~]# ip netns add netns1 && ip netns add netns2
[root@testerfans ~]# ip netns exec netns1 bash
[root@testerfans ~]# readlink /proc/$$/ns/net
net:[4026532349]
[root@testerfans ~]# ip netns exec netns2 bash
[root@testerfans ~]# readlink /proc/$$/ns/net
net:[4026532406]
  • 创建一对命名的 veth 设备,分别为veth1和veth2。通过ip link ls查看生成了veth1和veth2两块网卡(返回结果中编号16、17)。
[root@testerfans ~]# ip link add veth1 type veth peer name veth2 && ip link ls
16: veth2@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 66:6d:31:22:94:de brd ff:ff:ff:ff:ff:ff
17: veth1@veth2: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 76:64:e7:59:6c:d3 brd ff:ff:ff:ff:ff:ff
  • 将veth1和veth2分别加入到netns1和netns2中,我们看到netns1和netns2分别多了veth1和veth2设备。
[root@testerfans ~]# ip link set veth1 netns netns1 && ip link set veth2 netns netns2 
[root@testerfans ~]# ip netns exec netns1 ip addr
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
17: veth1@if16: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 76:64:e7:59:6c:d3 brd ff:ff:ff:ff:ff:ff link-netnsid 1
[root@testerfans netns]# ip netns exec netns2 ip addr
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
16: veth2@if17: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 66:6d:31:22:94:de brd ff:ff:ff:ff:ff:ff link-netnsid 0
  • 将veth1和veth2加入到netns1和netns2中后,我们在当前主机不会再看到这两块网络设备。
[root@testerfans ~]# ip link ls
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: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:b5:c9:bc brd ff:ff:ff:ff:ff:ff

  • 我们分别给veth1和veth2增IP,IP地址为192.168.1.1/24和192.168.1.2/24。
[root@testerfans ~]# ip netns exec netns1 ip link set veth1 up
[root@testerfans ~]# ip netns exec netns1 ip addr add 192.168.1.1/24 dev veth1
[root@testerfans ~]# ip netns exec netns1 ip route
192.168.1.0/24 dev veth1 proto kernel scope link src 192.168.1.1 
[root@testerfans ~]# ip netns exec netns2 ip link set veth2 up
[root@testerfans ~]# ip netns exec netns2 ip addr add 192.168.1.2/24 dev veth2
[root@testerfans ~]# ip netns exec netns2 ip route
192.168.1.0/24 dev veth2 proto kernel scope link src 192.168.1.2 
  • 进入到netns1网络命名空间内ping netns2可以ping通,说明两个网络命名空间可以进行通信了。
[root@testerfans ~]# ip netns exec netns1 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.055 ms
64 bytes from 192.168.1.2: icmp_seq=2 ttl=64 time=0.035 ms
64 bytes from 192.168.1.2: icmp_seq=3 ttl=64 time=0.038 ms

--- 192.168.1.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.035/0.042/0.055/0.011 ms
  • 回收我们创建的两个netns和veth pair,确认都被删除。
[root@testerfans ~]# ip netns delete netns1 && ip netns delete netns2
[root@testerfans ~]# ls /var/run/netns
[root@testerfans ~]# ip link delete veth1 && ip link delete veth2

netns通过bridge通信

虽然 veth pair 可以实现两个 network namespace 之间的通信,但是当需要在多个 network namespace 之间通信的时候,光靠 veth pair 就不行了。我们可以使用 Linux 提供的虚拟交换机,来完成这样的功能。下面的示例演示如何通过虚拟交换机(这里就是一个虚拟网桥)连接多个 network namespace。

  • 创建一个名称为mybridge的网桥,查看网桥为up状态。
[root@testerfans ~]# ip link add mybridge type bridge
[root@testerfans ~]# ip link set mybridge up
[root@testerfans ~]# ip addr
18: mybridge: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ether ae:f0:10:89:bd:8f brd ff:ff:ff:ff:ff:ff
    inet6 fe80::acf0:10ff:fe89:bd8f/64 scope link 
       valid_lft forever preferred_lft forever
  • 创建一个叫netns1的命名空间。
[root@testerfans ~]# ip netns add netns1
  • 创建一对veth分别为veth1和veth1p。
[root@testerfans ~]# ip link add veth1 type veth peer name veth1p
  • 将veth1p加入到netns1中,并将名称修改成eth0。
[root@testerfans ~]# ip link set dev veth1p netns netns1
[root@testerfans ~]# ip netns exec netns1 ip link set dev veth1p name eth0
  • 分配IP并启动eth0,查看设备状态。
[root@testerfans ~]# ip netns exec netns1 ip addr add 192.168.1.1/24 dev eth0
[root@testerfans ~]# ip netns exec netns1 ip link set dev eth0 up
[root@testerfans ~]# ip netns exec netns1 ip addr
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
19: eth0@if20: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state LOWERLAYERDOWN group default qlen 1000
    link/ether b2:28:d2:70:ec:e2 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.1.1/24 scope global eth0
       valid_lft forever preferred_lft forever
  • 将veth1绑定到我们创建的mybridge网桥。
[root@testerfans ~]# ip link set dev veth1 master mybridge
[root@testerfans ~]# ip link set dev veth1 up
  • 重复执行上述步骤,分别创建netns2、netns3和对应的网络设备并绑定到网桥。
[root@testerfans ~]# ip netns add netns2
[root@testerfans ~]# ip link add veth2 type veth peer name veth2p
[root@testerfans ~]# ip link set dev veth2p netns netns2
[root@testerfans ~]# ip netns exec netns2 ip link set dev veth2p name eth0
[root@testerfans ~]# ip netns exec netns2 ip addr add 192.168.1.2/24 dev eth0
[root@testerfans ~]# ip netns exec netns2 ip link set dev eth0 up
[root@testerfans ~]# ip link set dev veth2 master mybridge
[root@testerfans ~]# ip link set dev veth2 up
[root@testerfans ~]# ip netns add netns3
[root@testerfans ~]# ip link add veth3 type veth peer name veth3p
[root@testerfans ~]# ip link set dev veth3p netns netns3
[root@testerfans ~]# ip netns exec netns3 ip link set dev veth3p name eth0
[root@testerfans ~]# ip netns exec netns3 ip addr add 192.168.1.3/24 dev eth0
[root@testerfans ~]# ip netns exec netns3 ip link set dev eth0 up
[root@testerfans ~]# ip link set dev veth3 master mybridge
[root@testerfans ~]# ip link set dev veth3 up
  • 停用网桥并给网桥分配IP,然后再启动。
[root@testerfans ~]# ip link set dev mybridge down
[root@testerfans ~]# ip addr add 192.168.1.0/24 dev mybridge
[root@testerfans ~]# ip link set dev mybridge up
[root@testerfans ~]# ip addr
18: mybridge: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 26:69:b9:5e:84:6d brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.4/24 scope global mybridge
       valid_lft forever preferred_lft forever
    inet6 fe80::2469:b9ff:fe5e:846d/64 scope link 
       valid_lft forever preferred_lft forever
  • 通过bridge link查看网桥信息。
[root@testerfans ~]# bridge link
20: veth1 state UP @(null): <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master mybridge state forwarding priority 32 cost 2 
22: veth2 state UP @(null): <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master mybridge state forwarding priority 32 cost 2 
24: veth3 state UP @(null): <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master mybridge state forwarding priority 32 cost 2 
  • 在三个网络命名空间内互ping,检查网络是否联通。
[root@testerfans ~]# ip netns exec netns1 ping -c 3 192.168.1.3
PING 192.168.1.3 (192.168.1.3) 56(84) bytes of data.

--- 192.168.1.3 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 1999ms

从上面结果上看我们发现在netns1中ping netns3,网络并未联通这是为什么呢?

iptables设置

产生无法ping通的原因是演示的CentOS系统为bridge开启了iptables功能,所有经过br0的数据包都要受iptables里面规则的限制。并且之前安装了docker修改了iptable的FORWARD策略导致。

docker为了安全性,将iptables里面filter表的FORWARD链的默认策略设置成了drop,于是所有不符合docker规则的数据包都不会被forward,导我们不同netns之间无法ping通

image-1656052354612

我们通过如下命令在iptable内增加一条mybridge的转发规则,然后再ping一下,此时我们发现可以ping通了。

[root@testerfans ~]# iptables -A FORWARD -i mybridge -j ACCEPT
[root@testerfans ~]# iptables -nvL
Chain INPUT (policy ACCEPT 260 packets, 987K bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain FORWARD (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination                
    0     0 ACCEPT     all  --  mybridge *       0.0.0.0/0            0.0.0.0/0   
[root@testerfans ~]# ip netns exec netns1 ping -c 3 192.168.1.3
PING 192.168.1.3 (192.168.1.3) 56(84) bytes of data.
64 bytes from 192.168.1.3: icmp_seq=1 ttl=64 time=0.047 ms
64 bytes from 192.168.1.3: icmp_seq=2 ttl=64 time=0.045 ms
64 bytes from 192.168.1.3: icmp_seq=3 ttl=64 time=0.045 ms

--- 192.168.1.3 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.045/0.045/0.047/0.007 ms

之后我又在一台裸机(没有安装docker)上进行了同样的测试,发现不需要进行iptable设置即可互通。

  • 回收演示数据。
[root@testerfans ~]# ip netns delete netns1
[root@testerfans ~]# ip netns delete netns2
[root@testerfans ~]# ip netns delete netns3
[root@testerfans ~]# ip link delete mybridge

总结

本文通过ip netns命令演示了network namespace的创建、通过veth pair实现两个netns通信和通过bridge实现多netns间的网络通信,这些试验均是为了加深对netns理解。并且容器间通过bridge进行通信也是docker容器间通信的实现原理。


本文参考:
Linux Namespace : Network

28

评论区