Python多处理使Docker容器崩溃

时间:2019-02-12 13:12:17

标签: 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()

结果:

> $ python3 mp.py
something

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

FROM python:3.6

ADD . /app
WORKDIR /app

还有docker-compose.yml:

version: '3.6'

services:
  bug:
    build:
      context: .
    environment:
      - PYTHONUNBUFFERED=1
    command: su -c "python3.6 forever.py"

forever.py在哪里:

from time import sleep

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

现在我用docker compose运行forever.py

> $ 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

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

> $ docker exec -it mpbug_bug_1 /bin/bash
root@09779ec47f9d:/app# python mp.py 
something
root@09779ec47f9d:/app# % 

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

您能解释一下为什么docker容器崩溃了,以及如何做到不崩溃?

提前谢谢!

2 个答案:

答案 0 :(得分:8)

要快速修复,请不要使用spawn启动方法,和/或请不要使用su -c ...,两者都是不必要的IMO。更改为:

p = mp.Process(target=do_smth, args=tuple())

或者您可以使用--init选项启动容器。

使用spawn启动方法,Python还将启动semaphore tracker process以防止信号量泄漏,您可以通过在中间暂停mp.py来看到此过程,如下所示:

472   463 /usr/local/bin/python3 -c from multiprocessing.semaphore_tracker import main;main(3)

此过程由mp.py开始,但在mp.py之后退出,因此该过程不会被mp.py收割,而应设计为init收割。

问题在于此容器(名称空间)中没有init,而不是init,PID 1是su -c,因此{{1 }}。

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

使用forever.py可以观察到此行为:

strace

将输出如下错误消息:

docker run --security-opt seccomp:unconfined --rm -it ex_bug strace -e trace=process -f su -c 'python3 forever.py'

ref:Docker and the PID 1 zombie reaping problem (phusion.nl)

答案 1 :(得分:1)

mp.py看起来不像forever.pymp.py将运行新的工作进程,该进程将仅打印something,然后将退出=> join(),在该工作进程完成后,主进程将立即退出。

等效于forever.py:工作进程在无限循环中打印问候消息,主进程将在join()-forever-mp.py中等待该工作进程退出:

import multiprocessing as mp
from time import sleep

def do_smth():
    i = 0
    while True:
        sleep(1.0)
        i += 1
        print(f'hello {i:3}')

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

更新了docker-compose.yml

version: '3.6'

services:
  bug:
    build:
      context: .
    environment:
      - PYTHONUNBUFFERED=1
    command: su -c "python3.6 forever-mp.py"

测试:

$ docker-compose build && docker-compose up 
...
some output
...
Attaching to multiprcs_bug_1_72681117a752
bug_1_72681117a752 | hello   1
bug_1_72681117a752 | hello   2
bug_1_72681117a752 | hello   3
bug_1_72681117a752 | hello   4

检查容器中的进程:

$ docker top multiprcs_bug_1_72681117a752
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                38235               38217               0                   21:36               ?                   00:00:00            su -c python3.6 forever-mp.py
root                38297               38235               0                   21:36               ?                   00:00:00            python3.6 forever-mp.py
root                38300               38297               0                   21:36               ?                   00:00:00            /usr/local/bin/python3.6 -c from multiprocessing.semaphore_tracker import main;main(3)
root                38301               38297               0                   21:36               ?                   00:00:00            /usr/local/bin/python3.6 -c from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=4, pipe_handle=6) --multiprocessing-fork