Остановка Gunicorn, работающего в сеансе schroot
Я управляю сервером Gunicorn в сеансе schroot через супервизора. Моя проблема заключается в том, что служба останавливается не полностью при остановке с помощью "supervisorctl stop".
Это скрипт (упрощенный), управляющий моим сервером, он запускает gunicorn на переднем плане:
# gunicorn.sh
schroot -c gunicorn -r -- bash -c "gunicorn --workers=1 myapp.wsgi:application"
Это мой конфиг супервизора для запуска этого скрипта:
[program:gunicorn]
command=/home/test/gunicorn.sh
stderr_logfile=/var/log/gunicorn.err.log
stdout_logfile=/var/log/gunicorn.out.log
Когда я запускаю сервис через "supervisorctl start", моё дерево процессов выглядит так:
supervisord(7175)---gunicorn.sh(8061)---schroot(8067)---gunicorn(8068)---gunicorn(8073)---{gunicorn}(8078)
Теперь, когда я останавливаю службу с помощью "supervisorctl stop", соответствующий процесс supervisor и его прямой дочерний объект gunicorn.sh завершаются. Но сам процесс schroot продолжает жить и теперь является потомком процесса init:
schroot(8067)---gunicorn(8068)---gunicorn(8073)---{gunicorn}(8078)
Все это поведение, по-видимому, связано как с тем, как работают Шруот и Гантикорн.
Как я могу позволить супервизору правильно остановить процесс, размещенный в schroot?
2 ответа
Пришлось начинать Gunicorn с exec:
# gunicorn.sh
exec schroot -c gunicorn -r -- bash -c "gunicorn --workers=1 myapp.wsgi:application"
Сегодня я решил точно такую же проблему, но принятое решение не сработало. Та же проблема упоминается, например, здесь.
Для запуска программы с использованием супервизора обычно требуется, чтобы все, что вы выполняете, выполнялось с помощью системного вызова exec() - поскольку он заменяет исходный процесс самим собой, оставляя PID (что важно) для управления супервизором (без отсоединения от терминала, конечно).
Запуск gunicorn в сеансе schroot всегда приводит к двум процессам - schroot, на который ссылается супервизор, и master of gunicorn (рабочие не важны).
Вызов stop в супервизоре просто убивает шрута и заставляет мастера gunicorn мигрировать в init (PID=1), оставляя его фактически запущенным.
Без chroot самый простой способ - просто выполнить exec gunicorn / любой другой /.
Обходной путь, который я нашел, который, кажется, в порядке, таков:
- Запустите процесс schroot gunicorn с помощью вспомогательной программы pidproxy (из дистрибутива supervisor, /usr/bin/pidproxy)
- Запишите PID процесса gunicorn в файл (обычно путем изменения запуска вашего приложения)
- Правильно используйте exec, когда это возможно.
Объявление 1. Используйте pidproxy
[program:xxx]
command = pidproxy /path/to/pid/file /path/to/xxx
Объявление 2. Используйте все средства, чтобы записать pid в файл. Я просто использовал следующий фрагмент кода при запуске приложения:
pid_file = os.path.join(conf.data_dir, "eventaid.pid")
with open(pid_file, "w") as fout:
fout.write(str(os.getpid()))
Объявление 3. Используйте exec в вашем скрипте /path/to/xxx
# activate venv
. $HOME/.virtualenvs/python3-xxx/bin/activate
# start within schroot
exec schroot -c jessie -- sh -c "exec gunicorn <whatever>"
Теперь отправка SIGTERM/SIGKILL/... изнутри супервизора фактически отправит SIGTERM/SIGKILL/... на pidproxy, который перенаправит сигнал PID воина-убийцы, фактически убив его.