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