如何在网络命名空间中使用绑定挂载?

sea*_*hub 4 namespace network-namespaces unshare

我有一个在网络命名空间中运行的应用程序。这效果很好。

我想在不同的命名空间中多次运行该应用程序。为了方便起见,我想将应用程序的工作目录绑定到命名空间内的 /tmp/nsX 之类的目录。

如果我只是mount --bind /tmp/nsX /var/lib/my-app在命名空间中执行此操作,那么当我退出命名空间时,挂载就会消失。

通过进入/退出命名空间,我的意思是ip netns exec bash

我正在看unsharensenter但我不知道该怎么做。

我想要:

  • 为命名空间配置网络
  • 在命名空间中为我的应用程序的工作目录创建绑定安装。
  • 在命名空间中生成我的应用程序。如果有帮助的话,它有一个“fork”选项。
  • 能够离开和进入命名空间,而不会出现任何东西消失或消失的情况。

如果我需要使用其他一些命名空间类型,那也没有问题。

A.B*_*A.B 5

为什么会发生这种情况?

  • 网络命名空间不会更改挂载设置:它处理网络

  • 但一些与网络命名空间相关的安装设置,最突出的是 /sys/class/net/proc/sys/net,如上一个链接中所述,确实依赖于网络命名空间

    这里的行为已经存在差异:当/proc/sys/net已经安装时,在进入新的命名空间时会动态更改,但/sys/class/net不会。这意味着当使用实际上仅更改网络名称空间的命令时:

    unshare -n -- sh -c 'ls -1d /proc/sys/net/*/conf/* /sys/class/net/*'
    
    Run Code Online (Sandbox Code Playgroud)

    人们会看到以前的网络接口已经消失/proc/sys/net/(只留下接口的新实例lo),但在以下位置仍然可见/sys/class/net/:防止与新网络命名空间的接口进行交互,并且仍然允许与以前的网络命名空间的接口进行交互(当这可能不是一个好主意时) 。

  • 为了解决这个与网络相关的问题,/sys必须从新的网络命名空间(重新)安装。为了避免影响专用于前一个(初始)网络命名空间的环境,必须在较新的安装命名空间中完成此操作。这就是这样做的原因ip netns exec:为了为应用程序准备一个一致的网络环境,它既进入现有的网络命名空间(通过绑定挂载创建并保持存在ip netns add),又取消共享新的挂载命名空间。

  • 仅当进程引用它时,该挂载命名空间才能保持存在。一旦没有进程离开,挂载命名空间以及在其中完成的任何挂载都会消失(只能从此类进程中看到)。

  • 因此,单独使用unshare -mor ip netns exec(甚至不打算首先处理挂载)不会在调用之间保留绑定挂载。

解决方案

这种挂载应该在使用之前一次性ip netns exec ...完成,而不是在创建和销毁(挂载)命名空间的单独步骤中完成。

/etc/netns

实际上ip netns exec已经在其自己的调用中管理此类绑定挂载,但在特定位置:/etc/netns。每次ip netns exec foo调用时,如果目录和/或文件存在且匹配,它将自动将挂载任何内容绑定/etc/netns/foo/*到其匹配的/etc/*. 由于此功能旨在促进在单独的命名空间中运行服务的多个实例,因此应优先将其作为解决方案。

/etc/例如,应用程序应该从 中的特定位置检索其配置,/etc/my-app/并且那里应该有一个 per-netns 不同的文件,该文件的内容将应用程序指向其工作目录,无论它在哪里,例如/var/lib/my-app/nsX(甚至/tmp/nsX)。

该目录/etc/my-app/应该存在,并且可能包含某种模板文件,供脚本使用以准备运行实例,但其内容将隐藏在其他名称空间中,因为它将成为绑定安装的目标。

mkdir -p /etc/netns # it is usually not provided by the distribution

for instance in foo bar baz; do
    mkdir "/etc/netns/$instance"
    cp -a /etc/my-app /etc/netns/$instance/
done
Run Code Online (Sandbox Code Playgroud)

然后使用脚本或手动,应该自定义每个实例(数据的位置、pid 文件的位置等),在其他地方添加相关目录(在/var/或中/run(可能在一些启动工具/配置的帮助下,如tmpfiles.d)。

一旦正确完成此操作,人们应该能够在不同的网络中运行应用程序的多个实例,如下所示:

ip netns exec foo my-app
ip netns exec bar my-app
ip netns exec baz my-app
Run Code Online (Sandbox Code Playgroud)

它们之间不会发生冲突(或者这意味着之前还需要做更多的事情)。

unshare -mip netns exec在一起

如果应用程序无法从包装器接收参数/etc或让包装器执行此操作/var/lib/my-app,并且坚持只使用 ,则紧随其后的绑定挂载ip netns exec也将起作用:

创建 ipnetns 命名空间:

ip netns add foo
ip netns add bar
ip netns add baz
Run Code Online (Sandbox Code Playgroud)

准备网络配置:

ip -n foo link ....
Run Code Online (Sandbox Code Playgroud)

运行应用程序:

unshare -m sh -c 'mount --bind /tmp/foo /var/lib/my-app; exec ip netns exec foo my-app'
unshare -m sh -c 'mount --bind /tmp/bar /var/lib/my-app; exec ip netns exec bar my-app'
unshare -m sh -c 'mount --bind /tmp/baz /var/lib/my-app; exec ip netns exec baz my-app'
Run Code Online (Sandbox Code Playgroud)

虽然这些绑定挂载稍后仍会消失,但它们将被现在实例化的my-app.

它或多或少地重现了已经内置的功能,ip netns exec并带有额外的中间挂载命名空间。

包装纸

您还可以简单地使用一个包装脚本,该脚本需要一个额外的参数来进行挂载,从而避免这个额外的挂载命名空间:

my-app.wrapper(不进行检查):

unshare -n -- sh -c 'ls -1d /proc/sys/net/*/conf/* /sys/class/net/*'
Run Code Online (Sandbox Code Playgroud)

并运行:

ip netns exec foo my-app.wrapper foo
Run Code Online (Sandbox Code Playgroud)

一体化

无论选择哪种方法,都应该将其集成到某些启动脚本中。例如,systemd实例化功能这是我的一个示例)可以与上述方法之一相结合,以创建并动态运行同一应用程序的新 netns 实例。