use*_*651 6 linux ip namespaces linux-networking network-namespace
似乎无法从网络名称空间创建网络名称空间。它导致“错误:对等网络引用无效。”。
这是一个错误还是有某种我不知道的限制?
以下是我对错误的 cmd 跟踪。
# ip netns add foo1
# ip netns exec foo1 ip netns add foo2
# ip netns
Error: Peer netns reference is invalid.
Error: Peer netns reference is invalid.
foo2
foo1
# ip netns exec foo2 /bin/bash
setting the network namespace "foo2" failed: Invalid argument
Run Code Online (Sandbox Code Playgroud)
TL;DR:虽然看起来很奇怪,但这实际上不是网络命名空间问题,而是挂载命名空间问题,这是意料之中的。
您应该创建所有新的“ip netns 命名空间”(含义见后文),即ip netns add ...
从初始(主机)“ip netns 命名空间”运行所有命令,而不是从使用ip netns exec ...
. 只要您不创建它们,您就可以随意在它们之间切换,包括将命令从一个嵌套到另一个,使用ip netns exec ...
.
以下是分步示例的详细说明...
ip netns
专门用于网络命名空间,但为了处理所有功能,还必须与挂载命名空间混合,原因有两个(至少,我知道):
绑定挂载/etc/netns/FOO/SOMESERVICE
以/etc/SOMESERVICE
管理备用服务/守护程序配置
一个可以方便地在其他网络命名空间中轻松运行一些(网络相关)守护进程的功能,但除此之外,它仍然是“主机”的一部分。您可以在 UL 上查看我对有关它的问题的回答:Namespace management with ip netns (iproute2)。它的使用需要和下面这个特性一样的处理,这里不再赘述。
重新安装/sys
以在其层次结构中公开新网络命名空间的网络设备
这是一项强制性功能。暴露问题的例子:
从“初始主机”:
# ip link add dev dummy9 type dummy
# ip -br link show dummy9
dummy9 DOWN f6:f6:48:9c:12:b9 <BROADCAST,NOARP>
# ls -l /sys/class/net/dummy9
lrwxrwxrwx. 1 root root 0 Apr 4 22:09 /sys/class/net/dummy9 -> ../../devices/virtual/net/dummy9
Run Code Online (Sandbox Code Playgroud)
使用较低级别的工具更改为其他(临时)网络命名空间:
# unshare --net ip -br link show dummy9
Device "dummy9" does not exist.
# unshare --net ls -l /sys/class/net/dummy9
lrwxrwxrwx. 1 root root 0 Apr 4 22:13 /sys/class/net/dummy9 -> ../../devices/virtual/net/dummy9
Run Code Online (Sandbox Code Playgroud)
这就是问题所在:/sys
仍然公开初始主机的接口,而不是新网络命名空间的接口。这就是网络命名空间和 mount 之间存在交互的地方/sys
:如果/sys
从新的网络命名空间挂载,它将切换到在选择的目录层次结构(例如/sys/class/net
和/sys/devices/virtual/net
)中公开新的网络接口。这仅在安装时完成,而不是动态完成。一些高级网络设置只需在那里读取或写入即可轻松获得,因此必须提供它们,反之亦然:在新网络环境中运行的隔离进程不应该能够看到或更改初始主机的接口。
所以,ip netns exec FOO ...
(但不是ip netns add FOO
)也通过解决这个取消共享的安装空间和重新安装/sys/
里面,不破坏初始主机的网络命名空间。但重要的是,这个挂载命名空间本身是短暂的:当你分别运行两个ip netns exec FOO ...
命令时,它们最终不会出现在同一个挂载命名空间中。他们每个人都有自己的,/sys
重新安装在那里指向相同的网络命名空间。
到现在为止,没有问题。发生这种情况时,我将其称为“ip netns 命名空间”,因为现在涉及两种类型的命名空间。到目前为止,我们有:
第一学期:
# ip netns add FOO
# ls -l /proc/$$/ns/{mnt,net}
lrwxrwxrwx. 1 root root 0 Apr 4 22:28 /proc/1712/ns/mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 root root 0 Apr 4 22:28 /proc/1712/ns/net -> net:[4026531992]
# ip netns exec FOO bash
# ls -l /proc/$$/ns/{mnt,net}
lrwxrwxrwx. 1 root root 0 Apr 4 22:33 /proc/1864/ns/mnt -> mnt:[4026532618]
lrwxrwxrwx. 1 root root 0 Apr 4 22:33 /proc/1864/ns/net -> net:[4026532520]
Run Code Online (Sandbox Code Playgroud)
学期2:
# ls -l /proc/$$/ns/{mnt,net}
lrwxrwxrwx. 1 root root 0 Apr 4 22:32 /proc/1761/ns/mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 root root 0 Apr 4 22:32 /proc/1761/ns/net -> net:[4026531992]
# ip netns exec FOO bash
# ls -l /proc/$$/ns/{mnt,net}
lrwxrwxrwx. 1 root root 0 Apr 4 22:33 /proc/1866/ns/mnt -> mnt:[4026532821]
lrwxrwxrwx. 1 root root 0 Apr 4 22:33 /proc/1866/ns/net -> net:[4026532520]
Run Code Online (Sandbox Code Playgroud)
请注意,在更改 ip netns 命名空间后,虽然term1和term2的新网络命名空间相同,但新的挂载命名空间彼此不同(以及与初始主机不同)。
现在当你在term1创建一个新的 ip netns 命名空间时会发生什么?让我们来看看:
第一学期:
# ip netns add BAR
# ip netns ls
BAR
FOO
Run Code Online (Sandbox Code Playgroud)
学期2:
# ip netns ls
Error: Peer netns reference is invalid.
Error: Peer netns reference is invalid.
BAR
FOO
Run Code Online (Sandbox Code Playgroud)
那是因为较新的命名空间 BAR 在没有进程的情况下保持存在,与其他命名空间一样,安装在(新创建的空文件)上/var/run/netns/BAR
(同样,请参见前面的链接以获取示例)。虽然挂载命名空间不同,但它们具有相同的根目录:初始主机的根目录。所以当然这个新创建的空文件在创建时/var/run/netns/BAR
随处可见(初始,term1的 mount ns,term2的 mount ns)。
唉,在term1的 FOO 的挂载命名空间上完成的挂载只能在term1上看到,不能在term2或其他任何地方看到,因为它是一个不同的挂载命名空间。因此,虽然在 term1(的 FOO ip netns 命名空间)中/var/run/netns/BAR
是一个属于nsfs
伪文件系统的伪文件:
第一学期:
# stat -f -c %T /var/run/netns/BAR
nsfs
Run Code Online (Sandbox Code Playgroud)
它是其他任何地方tmpfs
(来自实际/run
挂载)的空文件:
学期2:
# stat -f -c %T /var/run/netns/BAR
tmpfs
Run Code Online (Sandbox Code Playgroud)
任何其他终端:
$ stat -f -c %T /var/run/netns/BAR
tmpfs
Run Code Online (Sandbox Code Playgroud)
只要不退出当前的“ip netns namespace”,在term1中仍然可以看到。如果从term1仍然切换 ip netns namespaces ,它仍然会很好,因为新的非共享临时挂载命名空间是以前的副本,包括所有挂载。
如果退出,该挂载点将丢失(这意味着如果不再有进程或文件描述符使用它,BAR 相应的网络命名空间将消失,因为它仅由该挂载点持有)。在此之后,任何ip netns ls
命令都会在任何地方抱怨。您可以删除陈旧且现在无用的文件/run/netns/BAR
来修复它。
这一步一步的解释后,有什么要记住的是,你不应该创建新的命名空间ip netns add
内的命名空间目前与进入ip netns exec
。您应该从初始(主机)命名空间创建它们,然后您可以从任何 ip netns 命名空间在它们之间随意切换。
当然,如果/var/run/netns/
(即挂载点/run
)在(保持模糊)命名空间之间是不同的,那么就没有交互,并且每次ip netns
调用都将与其他调用隔离,看不到其他调用,也无法与其他调用进行交互。这通常发生在哪里?在完整的容器中,挂载和网络命名空间是分开的,并且从一开始就指向不同的资源。
更新:正如评论中所问,我检查了如何“修复”这个问题,但找不到任何简单的解决方案。
首先有一个先决条件:如上所述,一旦在 FOO 内部创建了新的“ip netns”命名空间 BAR,并且留下 FOO,则对 BAR 的唯一引用将消失,从而使 BAR 也消失。还需要一些东西。
ip netns
):允许在没有任何进程的情况下保留名称空间,可以在名称空间内仅包含网络设置(接口、网桥、tc 规则、防火墙规则等)我们可以使用第一种或第三种方法。在找到有用的东西之前,这里有各种失败的尝试......
如前所述,行不通:
# ip netns add FOO
# ip netns exec FOO ip netns add BAR
Run Code Online (Sandbox Code Playgroud)
只需让一个进程在第一个“ip netns”命名空间中临时运行,作为其临时挂载命名空间部分,以保留对新“ip netns”命名空间的网络命名空间所需的引用,并稍后从外部(从初始命名空间)重用它。
也行不通:
# ip netns add FOO
# ip netns exec FOO sh -c 'ip netns add BAR; sleep 999 < /var/run/netns/BAR & echo $!'
28344
# strace -e trace=readlink,mount mount --bind /proc/6295/fd/0 /var/run/netns/BAR
readlink("/proc/6295/fd/0", "/run/netns/BAR", 4095) = 14
readlink("/var/run", "/run", 4095) = 4
mount("/run/netns/BAR", "/run/netns/BAR", 0x55c88c9cccb0, MS_BIND, NULL) = 0
+++ exited with 0 +++
# stat -f -c %T /run/netns/BAR
tmpfs
Run Code Online (Sandbox Code Playgroud)
正如在这个用例中不应该使用strace
的mount
命令后面的符号链接所见(注意:挂载仍然以某种方式链接到睡眠进程,必须杀死它才能卸载它)。
这(进入sleep
的挂载命名空间,访问隐藏在那里的 BAR 挂载的网络命名空间)有效,但依赖于继续存在sleep
或任何进程的继续使用:
# ip netns add FOO
# ip netns exec FOO sh -c 'ip netns add BAR; ip -n BAR link add dummy8 type dummy; sleep 999 & echo $!'
12916
# nsenter --target=12916 --mount ip -n -brief BAR link show
lo DOWN 00:00:00:00:00:00 <LOOPBACK>
dummy8 DOWN 8e:ce:b3:d1:9c:bb <BROADCAST,NOARP>
Run Code Online (Sandbox Code Playgroud)
奇怪的是,这(使用 mount 命名空间快捷方式/proc/pid/root/
)不起作用(我真的不知道为什么):
# stat -f -c %T /proc/12916/root/var/run/netns/BAR
tmpfs
Run Code Online (Sandbox Code Playgroud)
最后什么会起作用:
# ip netns add FOO
# ip netns exec FOO sh -c 'ip netns add BAR; ip -n BAR link add dummy8 type dummy; ip netns exec BAR sh -c '\''sleep 999 & echo $!'\'
14124
# mount --bind /proc/14124/ns/net /var/run/netns/BAR
# ip -n BAR -brief link show
lo DOWN 00:00:00:00:00:00 <LOOPBACK>
dummy8 DOWN 3a:48:65:20:68:c1 <BROADCAST,NOARP>
Run Code Online (Sandbox Code Playgroud)
所以最终可以使用这样的东西。如果您尝试在 sleep 命令结束之前立即删除它们,则可能存在竞争条件。
# ip netns add FOO
# mount --bind /proc/$(ip netns exec FOO sh -c 'ip netns add BAR; ip netns exec BAR bash -c '\''sleep 5 </dev/null >/dev/null 2>&1 & echo $!; disown'\')/ns/net /var/run/netns/BAR
Run Code Online (Sandbox Code Playgroud)
如何使用这样的构造?我不知道,因为没有给出遇到嵌套“ip netns”问题之前的原始问题。也许更简单的解决方案是可用的,而无需尝试创建“嵌套网络命名空间”。