我有一个在网络命名空间中运行的应用程序。这效果很好。
我想在不同的命名空间中多次运行该应用程序。为了方便起见,我想将应用程序的工作目录绑定到命名空间内的 /tmp/nsX 之类的目录。
如果我只是mount --bind /tmp/nsX /var/lib/my-app
在命名空间中执行此操作,那么当我退出命名空间时,挂载就会消失。
通过进入/退出命名空间,我的意思是ip netns exec bash
我正在看unshare
,nsenter
但我不知道该怎么做。
我想要:
- 为命名空间配置网络
- 在命名空间中为我的应用程序的工作目录创建绑定安装。
- 在命名空间中生成我的应用程序。如果有帮助的话,它有一个“fork”选项。
- 能够离开和进入命名空间,而不会出现任何东西消失或消失的情况。
如果我需要使用其他一些命名空间类型,那也没有问题。
答案1
为什么会发生这种情况?
A网络命名空间不更改安装设置:它处理网络
但一些与网络命名空间相关的安装设置,最突出的是
/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/*'
人们会看到以前的网络接口已经消失
/proc/sys/net/
(只留下接口的新实例lo
),但在以下位置仍然可见/sys/class/net/
:防止与新网络命名空间的接口进行交互,并且仍然允许与以前的网络命名空间的接口进行交互,这可能不是一个好主意。为了解决这个与网络相关的问题,
/sys
必须从新的网络命名空间(重新)安装。为了避免影响专用于前一个(初始)网络名称空间的环境,必须在较新的网络名称空间中完成此操作山名称空间。这就是为什么ip netns exec
这样做:为应用程序准备一个连贯的网络环境,它既进入现有的网络命名空间(通过绑定挂载创建并保持存在ip netns add
)和取消共享新的挂载命名空间。仅当进程引用它时,该挂载命名空间才能保持存在。一旦没有进程离开,挂载命名空间以及在其中完成的任何挂载都会消失(只能从此类进程中看到)。
因此,单独使用
unshare -m
orip 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
然后使用脚本或手动,应该自定义每个实例(数据的位置、pid 文件的位置等),在其他地方添加相关目录(在/var/
或中/run
(可能在一些启动工具/配置的帮助下,例如tmp文件.d)。
一旦正确完成此操作,人们应该能够在不同的网络中运行应用程序的多个实例,如下所示:
ip netns exec foo my-app
ip netns exec bar my-app
ip netns exec baz my-app
它们之间不会发生冲突(或者这意味着之前还需要做更多的事情)。
unshare -m
加ip netns exec
在一起
如果应用程序无法从包装器接收参数/etc
或让包装器执行此操作,并且坚持只使用/var/lib/my-app
,则紧随其后的绑定挂载ip netns exec
也将起作用:
创建 ipnetns 命名空间:
ip netns add foo
ip netns add bar
ip netns add baz
准备网络配置:
ip -n foo link ....
运行应用程序:
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'
虽然这些绑定挂载稍后仍会消失,但它们将被现在实例化的my-app
.
它或多或少地重现了已经内置的功能,ip netns exec
并带有额外的中间挂载命名空间。
包装纸
您还可以简单地使用一个包装脚本,该脚本需要一个额外的参数来进行挂载,从而避免这个额外的挂载命名空间:
my-app.wrapper
(不进行检查):
#!/bin/sh
mount --bind /tmp/"$1" /var/lib/my-app
shift
exec my-app "$@"
并运行:
ip netns exec foo my-app.wrapper foo
一体化
无论选择哪种方法,都应该将其集成到某些启动脚本中。举些systemd
例子实例化特征(这是我的一个例子)可以与上述方法之一结合使用,以动态创建并运行同一应用程序的新 netns 实例。