Python многопроцессорный сбой док-контейнера
Существует простой многопроцессорный код 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
Затем я создал простой Docker-контейнер с Dockerfile:
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}')
Теперь я запускаю 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
До этого момента все хорошо и понятно. Но когда я пытаюсь запустить mp.py
в контейнере mp.py
он вылетает без какого-либо сообщения:
> $ docker exec -it mpbug_bug_1 /bin/bash
[email protected]:/app# python mp.py
something
[email protected]:/app# %
Суть кода можно найти здесь: https://gist.github.com/ilalex/83649bf21ef50cb74a2df5db01686f18
Можете ли вы объяснить, почему разбился контейнер Docker и как это сделать без сбоев?
Заранее спасибо!
Ответы
Ответ 1
для быстрого исправления не используйте метод запуска spawn
и/или не используйте su -c...
, оба из которых являются ненужными IMO. изменить на:
p = mp.Process(target=do_smth, args=tuple())
или вы можете запустить контейнер с --init
.
с помощью метода запуска spawn
Python также запустит процесс отслеживания семафора, чтобы предотвратить утечку семафора, вы можете увидеть этот процесс, приостановив mp.py
в середине, он выглядит следующим образом:
472 463 /usr/local/bin/python3 -c from multiprocessing.semaphore_tracker import main;main(3)
этот процесс запускается с помощью mp.py
но mp.py
после mp.py
, поэтому он не будет mp.py
помощью mp.py
, но предполагается, что он будет mp.py
с помощью init
.
проблема в том, что в этом контейнере (пространстве имен) нет init
, вместо init
PID 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'
выведет сообщение об ошибке как:
strace: Exit of unknown pid 14 ignored
ссылка: Докер и проблема пожирания зомби PID 1 (phusion.nl)
Ответ 2
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