Docker网络命名空间在ip netns列表中不可见

71

当我创建一个新的Docker容器时,就像使用

docker run -it -m 560m --cpuset-cpus=1,2 ubuntu sleep 120

并检查其命名空间,我可以看到已创建新的命名空间(以pid 7047为例)。

root@dude2:~# ls /proc/7047/ns -la
total 0
dr-x--x--x 2 root root 0 Jul  7 12:17 .
dr-xr-xr-x 9 root root 0 Jul  7 12:16 ..
lrwxrwxrwx 1 root root 0 Jul  7 12:17 ipc -> ipc:[4026532465]
lrwxrwxrwx 1 root root 0 Jul  7 12:17 mnt -> mnt:[4026532463]
lrwxrwxrwx 1 root root 0 Jul  7 12:17 net -> net:[4026532299]
lrwxrwxrwx 1 root root 0 Jul  7 12:17 pid -> pid:[4026532466]
lrwxrwxrwx 1 root root 0 Jul  7 12:17 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Jul  7 12:17 uts -> uts:[4026532464]
root@dude2:~# ls /proc/self/ns -la

当我使用 ip netns list 命令查看时,我无法看到新的网络命名空间。

dude@dude2:~/docker/testroot$ ip netns list
dude@dude2:~/docker/testroot$ 
任何想法为什么?
4个回答

81
那是因为 Docker 没有创建所需的符号链接:
# (as root)
pid=$(docker inspect -f '{{.State.Pid}}' ${container_id})
mkdir -p /var/run/netns/
ln -sfT /proc/$pid/ns/net /var/run/netns/$container_id

然后,可以使用ip netns ${container_id}查看容器的netns命名空间,例如:

# e.g. show stats about eth0 inside the container 
ip netns exec "${container_id}" ip -s link show eth0

2
您还可以挂载 /var/run/docker/netns,以便 ip 命令能够找到它。命令如下:docker run -it --rm -v /var/run/docker/netns:/var/run/netns --privileged=true nicolaka/netshoot ip netns list - Jarek Przygódzki
8
请进行挂载(网络)命名空间引用,因为符号链接可能会稍后指向重用的PID和完全不同的命名空间。相反,在/run/netns/内创建一个空文件“foo”,然后将/proc/.../ns/net绑定到/run/netns/foo。这将确保即使原始进程死亡并且PID被重用,旧PID的网络命名空间仍保持开放,直到您卸载或重新启动,但它永远不会错误地指向新进程的网络命名空间。 - TheDiveO
检查Docker是否在错误的目录中创建了映射,很容易进行链接(删除/保存原始文件):cd /var/run; sudo ln -s docker/netns netns - Alec Istomin
@TheDiveO 很遗憾,这似乎是不可能的,因为命名空间不是常规文件,在 ls -l 中它们显示为 net -> 'net:[4026532543]'。这会导致错误 mount: /run/netns/abc: mount(2) system call failed: Not a directory. - Paul
当然,这是可能的,请参见此评论末尾的链接。确保在文件上进行绑定挂载mount --bind ...),而不是目录,这与指向nsfs文件系统的文件路径完美配合。还可以在我的Linux内核命名空间发现Go包的单元测试之一中查看示例,https://github.com/TheDiveO/lxkns/blob/42457374d7d786e43244423bda7414677e48a525/discovery_bindmount_test.go#L52。 - TheDiveO
1
警告:永远不要使用符号链接创建命名空间,因为这会通过可能消失的路径引用命名空间inode。更糟糕的是:替换为具有相同路径的不同命名空间。始终将命名空间引用绑定到另一个位置,以正确跟踪底层inode引用。 - TheDiveO

55

正如 @jary 所指出的那样,ip netns 命令只能使用 /var/run/netns 中的命名空间符号链接。然而,如果您有可用的 nsenter 命令(属于 util-linux 包的一部分),您可以通过您的 Docker 容器的 PID 来实现同样的效果。

要获取 Docker 容器的 PID,请执行以下操作:

docker inspect --format '{{.State.Pid}}' <container_name_or_Id>

在容器的网络命名空间内执行指令:

nsenter -t <contanier_pid> -n <command>

E.g:

$ docker inspect --format '{{.State.Pid}}' weechat
4432
$ sudo nsenter -t 4432 -n ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 
    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
75: eth0@if76: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:1b brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.27/16 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:acff:fe11:1b/64 scope link 
       valid_lft forever preferred_lft forever

上述内容等同于运行 ip netns exec <some_namespace> ip addr show
如您所见,您需要使用 root 权限运行 nsenter

17

与 @jary 的答案类似但有所不同。
无需介绍 /proc/<pid>/netster,只需要以下一步即可实现您想要的操作。因此,您可以像在主机上手动创建容器网络命名空间一样操作容器的网络命名空间。

一步操作:

ln -s /var/run/docker/netns  /var/run/netns 

结果:

启动一个容器:

docker run -tid ubuntu:18.04 
  • List 容器:
root@Light-G:/var/run# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
972909a27ea1        ubuntu:18.04        "/bin/bash"         19 seconds ago      Up 18 seconds                           peaceful_easley

列出此容器的网络命名空间:

root@Light-G:/var/run# ip netns list
733443afef58 (id: 0)

删除容器:

root@Light-G:/var/run# docker rm -f 972909a27ea1
972909a27ea1

再次列出网络命名空间:

root@Light-G:/var/run# ip netns list
root@Light-G:/var/run#

1
我完全不同意。ln -s /var/run/docker/netns /var/run/netns并不能达到我们需要的效果。它只是创建了一个符号链接/var/run/netns/netns,而当新容器被创建后,ip netns list并不会显示其命名空间。 - Alex
1
@Alex 我已在Ubuntu和CentOS上测试过了。你正在使用哪个平台?也许我们可以进一步研究一下这个问题。 - Light.G
6
通常情况下,你需要使用 ln -Ts 而不是仅使用 ln -s 。这样做可以避免出现双重嵌套的 /var/run/netns/netns。 - kubanczyk

0

注意:这种方法似乎比符号链接更安全,但仍可能导致严重的安全问题。

我认为我们可能正在违反容器应该具有的特性-一个封闭的环境。

这是因为Docker没有创建所需的符号链接:

# (as root)
pid=$(docker inspect -f '{{.State.Pid}}' "${container_id}")
mkdir -p /var/run/netns/
touch /var/run/$container_id
mount -o ro,bind "/proc/$pid/ns/net" "/var/run/netns/$container_id"

然后,可以使用ip netns "${container_id}"来检查容器的netns命名空间,例如:

# e.g. show stats about eth0 inside the container 
ip netns exec "${container_id}" ip -s link show eth0

感谢 @TheDiveO 的提醒。


我认为你应该将这个加到原始答案中,这样会使它更加显眼。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接