Python多处理崩溃了docker容器

ila*_*lex 12 python python-3.x docker docker-compose

当我在控制台中运行它时,有一个简单的python多处理代码就像魅力一样:

# mp.py
import multiprocessing as mp


def do_smth():
    print('something')


if __name__ == '__main__':
    ctx = mp.get_context("spawn")
    p = ctx.Process(target=do_smth, args=tuple())
    p.start()
    p.join()
Run Code Online (Sandbox Code Playgroud)

结果:

> $ python3 mp.py
something
Run Code Online (Sandbox Code Playgroud)

然后我用Dockerfile创建了一个简单的Docker容器:

FROM python:3.6

ADD . /app
WORKDIR /app
Run Code Online (Sandbox Code Playgroud)

和docker-compose.yml:

version: '3.6'

services:
  bug:
    build:
      context: .
    environment:
      - PYTHONUNBUFFERED=1
    command: su -c "python3.6 forever.py"
Run Code Online (Sandbox Code Playgroud)

在哪里forever.py:

from time import sleep

if __name__ == '__main__':
    i = 0
    while True:
        sleep(1.0)
        i += 1
        print(f'hello {i:3}')
Run Code Online (Sandbox Code Playgroud)

现在我forever.py用docker compose 运行:

> $ docker-compose build && docker-compose up 
...
some output
...
Attaching to mpbug_bug_1
bug_1  | hello   1
bug_1  | hello   2
bug_1  | hello   3
bug_1  | hello   4
Run Code Online (Sandbox Code Playgroud)

到目前为止,一切都很好,可以理解.但是当我试图mp.py在docker容器中运行时,它崩溃而没有任何消息:

> $ docker exec -it mpbug_bug_1 /bin/bash
root@09779ec47f9d:/app# python mp.py 
something
root@09779ec47f9d:/app# % 
Run Code Online (Sandbox Code Playgroud)

可以在这里找到代码的要点:https://gist.github.com/ilalex/83649bf21ef50cb74a2df5db01686f18

你能解释为什么docker容器崩溃以及怎么做而不会崩溃?

先感谢您!

geo*_*xsh 8

为了快速修复,不要使用spawnstart方法,和/或不使用su -c ...,两者都是不必要的IMO.改成:

p = mp.Process(target=do_smth, args=tuple())
Run Code Online (Sandbox Code Playgroud)

或者您可以选择启动容器--init.

使用spawnstart方法,Python也会启动信号量跟踪器进程以防止信号量泄漏,你可以通过mp.py在中间暂停来看到这个过程,它看起来像:

472   463 /usr/local/bin/python3 -c from multiprocessing.semaphore_tracker import main;main(3)
Run Code Online (Sandbox Code Playgroud)

这个过程是从mp.py但是退出之后开始的mp.py,因此它不会被收获mp.py,但应该init由设计收获.

问题是init这个容器(命名空间)中没有,而不是initPID 1 su -c,因此采用了死信号量跟踪器进程su.

似乎su认为死子进程是命令进程(forever.py)错误地,没有检查关系,所以su盲目退出,当PID 1退出时,内核杀死容器中的所有其他进程,包括forever.py.

可以观察到这种行为strace:

docker run --security-opt seccomp:unconfined --rm -it ex_bug strace -e trace=process -f su -c 'python3 forever.py'
Run Code Online (Sandbox Code Playgroud)

将输出错误信息,如:

strace: Exit of unknown pid 14 ignored
Run Code Online (Sandbox Code Playgroud)

ref:Docker和PID 1僵尸收割问题(phusion.nl)