您可以将主机的默认网络接口绑定到容器中以读取网络统计信息吗?

Can you bind the default network interface of the host into the container to read network stats?

我有一个项目,我从容器内的主机读取系统信息。现在我可以使用 CPU、RAM 和存储,但事实证明网络有点困难。我正在使用 Node.js 库 https://systeminformation.io/network.html,它从 /sys/class/net/.

读取网络统计信息

我现在找到的唯一解决方案是使用 --network host,但这似乎不是最好的方法,因为它破坏了很多其他与网络相关的东西,而且我无法假设使用我的项目的每个人都很好。

我也试过 --add-host=host.docker.internal:host-gateway,但是虽然它确实出现在 /etc/hosts 中,但它没有向 /sys/class/net/ 添加网络接口。

我对Docker和Linux的了解很有限,不知道有没有其他的方法?

My workaround for now is, to use readlink -f /sys/class/net/$(ip addr show | awk '/inet.*brd/{print $NF; exit}') to get the final path to the network statistics of the default interface and mount it to a imaginary path in the container. Therefore I don't use the mentioned systeminformation library for that right now. I would still like to have something that is a bit more reliable and in the best case officially supported by docker. I am fine with something that is not compatible with systeminformation, though.

您可以将主机的 /sys/class/net/ 目录作为卷安装在您的容器中,并修补 systeminformation 包以读取自定义路径而不是默认路径的内容。需要在 lib/network.js 中进行更改。您可以在该文件中看到整个目录是如何硬编码的,只需在本地副本中执行 find/replace 即可更改默认路径的所有实例。

一个简单的方法是将主机的整个“/sys”文件系统挂载到容器中。将它们安装到新位置(例如 /sys_host)或 over-mount 容器中的原始“/sys”:

# docker run -it --rm -v /sys:/sys:ro debian:bullseye-slim bash
root@b84df3184dce:/# ls -l /sys/class/net/
lrwxrwxrwx 1 root root 0 Oct 25  2021 enp2s0 -> ../../devices/pci0000:00/0000:00:1c.1/0000:02:00.0/net/enp2s0
lrwxrwxrwx 1 root root 0 Oct 25  2021 enp3s0 -> ../../devices/pci0000:00/0000:00:1c.2/0000:03:00.0/net/enp3s0
[...]

root@b84df3184dce:/# ls -l /sys/class/net/enp2s0/
-r--r--r--  1 root root 4096 Oct 25  2021 addr_assign_type
-r--r--r--  1 root root 4096 Oct 25  2021 addr_len
-r--r--r--  1 root root 4096 Oct 25  2021 address
-r--r--r--  1 root root 4096 Oct 25  2021 broadcast
[...]

请注意,容器可以通过这种方式访问​​主机的整个“/sys”文件系统。从网络接口到pci设备的相关链接仍然有效。

如果您不需要写入,您应该通过将“:ro”附加到挂载路径来挂载它 read-only。

有一种方法可以在启动容器后进入主机网络命名空间。这可以用于 运行 容器网络命名空间中容器中的一个进程和主机网络命名空间中的另一个进程。进程之间的通信可以使用 unix 域套接字完成。

或者,您可以只安装一个指向主机网络命名空间的 sysfs 的新实例。如果我没理解错,这就是你真正需要的。

为此,您需要访问主机 net 命名空间(为此,我将 /proc/1/ns/net 挂载到容器)。此外还需要 CAP_SYS_PTRACE 和 CAP_SYS_ADMIN 功能。

### /proc/1 is the 'init' process of the host which is always running in host network namespace
# docker run -it --rm --cap-add CAP_SYS_PTRACE --cap-add CAP_SYS_ADMIN -v /proc/1/ns/net:/host_ns_net:ro debian:bullseye-slim bash
root@8b40f2f48808:/ ls -l /sys/class/net
lrwxrwxrwx 1 root root 0 Jun  2 21:09 eth0 -> ../../devices/virtual/net/eth0
lrwxrwxrwx 1 root root 0 Jun  2 21:09 lo -> ../../devices/virtual/net/lo

### enter the host network namespace
root@8b40f2f48808:/ nsenter --net=/host_ns_net bash

### now we are in the host network namespace and can see the host network interfaces
root@8b40f2f48808:/ mkdir /sys2
root@8b40f2f48808:/ mount -t sysfs nodevice /sys2
root@8b40f2f48808:/ ls -l /sys2/class/net/
lrwxrwxrwx 1 root root 0 Oct 25  2021 enp2s0 -> ../../devices/pci0000:00/0000:00:1c.1/0000:02:00.0/net/enp2s0
lrwxrwxrwx 1 root root 0 Oct 25  2021 enp3s0 -> ../../devices/pci0000:00/0000:00:1c.2/0000:03:00.0/net/enp3s0
[...]

root@8b40f2f48808:/ ls -l /sys2/class/net/enp2s0/
-r--r--r--  1 root root 4096 Oct 25  2021 addr_assign_type
-r--r--r--  1 root root 4096 Oct 25  2021 addr_len
-r--r--r--  1 root root 4096 Oct 25  2021 address
-r--r--r--  1 root root 4096 Oct 25  2021 broadcast
[...]

### Now you can switch back to the original network namespace
### of the container; the dir "/sys2" is still accessible
root@8b40f2f48808:/ exit

将其放在一起 non-interactive 用法:

使用带有以下参数的 docker run

docker run -it --rm --cap-add CAP_SYS_PTRACE --cap-add CAP_SYS_ADMIN -v /proc/1/ns/net:/host_ns_net:ro debian:bullseye-slim bash

在启动节点应用程序之前在容器中执行这些命令:

mkdir /sys2
nsenter --net=/host_ns_net mount -t sysfs nodevice /sys2

nsenter(和挂载)退出后,您将回到容器的网络名称空间中。理论上,您现在可以放弃扩展功能。

现在您可以访问/sys2/class/net下的网络设备了。