当我在控制台中运行它时,有一个简单的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容器崩溃了,以及如何做到不崩溃?
提前谢谢!
答案 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.py
。 mp.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