【问题标题】:Start multiprocessing.process in namespace在命名空间中启动 multiprocessing.process
【发布时间】:2015-05-08 15:31:15
【问题描述】:

我正在尝试从已创建的命名空间(名为“test”)中启动一个新进程。

我研究了一些方法,包括 nsenter:

import subprocess
from nsenter import Namespace

with Namespace(mypid, 'net'):
    # output network interfaces as seen from within the mypid's net NS:
    subprocess.check_output(['ip', 'a'])

但我似乎无法找到在哪里可以找到 var、mypid...的参考!

理想情况下,我希望将 nsenter 之类的依赖项保持在最低限度(出于可移植性考虑),因此我可能希望沿 ctypes 路线走下去,例如(尽管 netns 没有系统调用...):

nsname = 'test'
netnspath = '%s%s' % ('/run/netns/', nsname)
netnspath = netnspath.encode('ascii')

libc = ctypes.CDLL('libc.so.6')

printdir(libc)

fd = open(netnspath)
print libc.syscall(???, fd.fileno())

OR(取自http://tech.zalando.com/posts/entering-kernel-namespaces-with-python.html

import ctypes
libc = ctypes.CDLL('libc.so.6')
# replace MYPID with the container's PID
fd = open('/proc/<MYPID>/ns/net')
libc.setns(fd.fileno(), 0)
# we are now inside MYPID's network namespace

不过,我还是要知道 PID,再加上我的 libc 没有 setns!

任何关于我如何获得 PID 的想法都会很棒!

TIA!

【问题讨论】:

    标签: python namespaces ctypes nsenter


    【解决方案1】:

    nsenter 模块的问题是您需要向它提供已在目标命名空间内运行的进程的 PID。这意味着您实际上不能使用此模块来使用您使用 ip netns add 之类的东西创建的网络命名空间。

    内核的setns() 系统调用采用文件描述符而不是PID。如果你愿意用ctypes解决它,你可以这样做:

    from ctypes import cdll
    libc = cdll.LoadLibrary('libc.so.6')
    _setns = libc.setns
    
    CLONE_NEWIPC = 0x08000000
    CLONE_NEWNET = 0x40000000
    CLONE_NEWUTS = 0x04000000
    
    def setns(fd, nstype):
        if hasattr(fd, 'fileno'):
            fd = fd.fileno()
    
        _setns(fd, nstype)
    
    def get_netns_path(nspath=None, nsname=None, nspid=None):
        '''Generate a filesystem path from a namespace name or pid,
        and return a filesystem path to the appropriate file.  Returns
        the nspath argument if both nsname and nspid are None.'''
    
        if nsname:
            nspath = '/var/run/netns/%s' % nsname
        elif nspid:
            nspath = '/proc/%d/ns/net' % nspid
    
        return nspath
    

    如果您的 libc 没有 setns() 调用,您可能会不走运 (虽然你在哪里运行你有一个足够新的内核 支持网络命名空间,但 libc 不支持?)。

    假设您有一个名为“blue”的命名空间可用 (ip netns add blue),您可以运行:

    with open(get_netns_path(nsname="blue")) as fd:
        setns(fd, CLONE_NEWNET)
        subprocess.check_call(['ip', 'a'])
    

    请注意,您必须以root 运行此代码。

    【讨论】:

    • 这对我来说似乎是一个解决方案,我将对其进行测试...仅供参考,我实际上是在 Kali 上进行测试,它显然支持 netns 但没有 setns()...!你知道如何测试内核是否是用 setns 构建的吗?可能会尝试启动虚拟机....
    • hmmm Debian Wheezy 也没有 setns...你在运行什么?!
    • 我已经在 Fedora 21 和 Ubuntu Trusty (14.04) 上测试过了。
    • 您需要内核 >= 3.0 和 glibc >= 2.14 才能使用 setns()。你可以通过执行你的 libc.so.6 来检查你的 glibc 版本。
    【解决方案2】:

    这可行,但是我不确定 0 作为系统调用的一部分做了什么。所以如果有人能启发我,那就太好了!

    import ctypes
    
    nsname = 'test'
    netnspath = '%s%s' % ('/run/netns/', nsname)
    netnspath = netnspath.encode('ascii')
    
    libc = ctypes.CDLL('libc.so.6')
    
    fd = open(netnspath)
    print libc.syscall(308, fd.fileno(), 0)
    

    【讨论】:

    • 0 表示你不在乎你输入的是什么类型的命名空间:
    【解决方案3】:

    在找到这个问题后,我们更新了python-nsenter,因此它现在除了提供 pid 外,还可以通过任意路径进入命名空间。

    例如,如果您想输入由ip netns add 创建的命名空间,您现在可以执行以下操作:

    with Namespace('/var/run/netns/foo', 'net'):
        # do something in the namespace
        pass
    

    Version 0.2 is now available via PyPi 更新。

    【讨论】:

    • 谢谢@cnelson!我有一段时间没有看这个项目,因为这是一个主要的绊脚石(我开始使用 ctypes 方法,但它给了我奇怪的结果)我会去反馈!非常感谢
    • 作为一个附带问题,我将如何将整个过程无限期地设置到命名空间中?我们总是必须使用'with'吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-16
    • 1970-01-01
    • 1970-01-01
    • 2021-02-26
    • 2018-10-11
    • 1970-01-01
    • 2014-12-14
    相关资源
    最近更新 更多