Docker 容器网络命名空间不可见
本文将探讨 Docker 容器中网络命名空间文件的一个问题。我们将研究为什么网络命名空间文件对 ip netns ls 命令不可见。
在继续之前,让我们先简要概述一下 Docker、容器和网络命名空间。
容器化
容器化类似于虚拟化,其中应用程序及其所有依赖项和库都打包到一个容器中;它可以在任何计算环境中运行。当操作系统内核以及所有必要的库和依赖项都包含在容器中时,任何处理该应用程序的人员都可以仅使用该容器来处理它,而不是在虚拟机上设置适当的计算环境。容器没有客户机操作系统,并在主机上运行。因此,它们根据需要共享相关的库和资源。
由于特定于容器的二进制文件和库在主机内核上运行,因此应用程序的处理和执行速度非常快。
容器在几分之一秒内启动,并且容器比虚拟机更轻量级且速度更快。
Docker
Docker 是一个软件平台,可实现应用程序的快速开发、测试和部署。Docker 将软件组织成称为容器的标准化单元,其中包含软件运行所需的一切,例如库、系统工具、代码和运行时。Docker 允许您将应用程序快速部署和扩展到任何环境,同时确信您的代码将运行。
网络命名空间
Linux 网络命名空间是内核的一项功能,允许我们虚拟化和隔离网络环境。例如,使用网络命名空间,您可以创建独立的网络接口和路由表,这些接口和路由表与系统其余部分隔离。
Linux 中的命名空间分为六种类型:pid、net、uts、mnt、ipc 和 user。
示例
如果您运行命令“lsns”,它将显示系统中所有现有的命名空间,如下所示。
$ lsns
输出
NS TYPE NPROCS PID USER COMMAND 4026531836 pid 2 7358 sachin -bash 4026531837 user 2 7358 sachin -bash 4026531838 uts 2 7358 sachin -bash 4026531839 ipc 2 7358 sachin -bash 4026531840 mnt 2 7358 sachin -bash 4026532185 net 2 7358 sachin -bash
不可见的 Docker 网络命名空间
当我们创建 Docker 容器时,守护进程将为容器进程生成命名空间伪文件。然后,这些文件将放置在 /proc/pid/ns 目录中,其中 pid 是容器的进程 ID。考虑以下场景
pi@TTP:sudo docker run --rm -d ubuntu:latest sleep infinity c5503724a3ab4339e9246f0ef4cddf475e1f4f98964df834bfacaf5ff1 70fb03
pi@TTP:docker inspect --format '{{.State.Pid}}' c5503724a3ab 1946
查看 /proc/1946/ns 目录,我们可以看到所有不同类型的命名空间都已创建。
pi@TTP:sudo ls -la /proc/1946/ns total 0 dr-x--x--x 2 root root 0 Oct 6 17:25 . dr-xr-xr-x 9 root root 0 Oct 6 17:25 .. lrwxrwxrwx 1 root root 0 Oct 6 17:28 ipc -> ipc:[4026532177] lrwxrwxrwx 1 root root 0 Oct 6 17:25 mnt -> mnt:[4026532175] lrwxrwxrwx 1 root root 0 Oct 6 17:25 net -> net:[4026532180] lrwxrwxrwx 1 root root 0 Oct 6 17:28 pid -> pid:[4026532178] lrwxrwxrwx 1 root root 0 Oct 6 17:28 user -> user:[4026531837] lrwxrwxrwx 1 root root 0 Oct 6 17:28 uts -> uts:[4026532176]
在命名空间伪文件列表中可以看到此进程的 net 文件的存在。我们可以预期在列出所有网络命名空间时 net 文件会出现,因为它对应于 Linux 网络命名空间。但是,很明显情况并非如此。例如,现在运行 ip netns ls 会产生 0 个结果。
pi@TTP:ip netns ls pi@TTP:
缺少文件引用
我们必须了解 ip netns ls 命令在 /var/run/netns 目录中搜索网络命名空间文件。但是,在创建网络命名空间文件后,Docker 守护进程不会在 /var/run/netns 目录中创建对它的引用。因此,ip netns ls 无法解析网络命名空间文件。
pi@TTP:mkdir -p /var/run/netns pi@TTP:touch /var/run/netns/c5503724a3ab
要绑定挂载 net 文件,请使用 mount -o bind 命令 -
pi@TTP:mount -o bind /proc/1946/ns/net /var/run/netns/c5503724a3ab
再次运行相同的 ip netns ls 命令将按预期显示网络命名空间。
pi@TTP:ip netns ls c5503724a3ab (id: 1)
在建立到网络命名空间文件的文件引用后,我们可以使用 ip netns exec 执行任何 ip 命令。例如,我们可以使用 ip addr list 命令检查网络命名空间中的接口 -
pi@TTP:ip netns exec c5503724a3ab ip addr list 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 10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.17.0.3/16 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::42:acff:fe11:3/64 scope link valid_lft forever preferred_lft forever
结论
我们在本教程的开始简要概述了 Linux 命名空间、Docker 和容器。然后,我们演示了 docker run 创建的网络命名空间文件在运行 ip netns ls 时不会出现的问题。我们后来发现,这是因为在 /var/run/netns 中没有创建文件引用,而 ip netns ls 命令在该目录中查找任何网络命名空间。
最后,我们在文章中给出了一个简单的解决方法:将文件绑定挂载到 /var/run/netns,以便可以使用 ip netns ls 找到它。