Лучший способ перезапустить/перезагрузить Gunicorn (через Upstart) после 'git вытянуть мои проекты Django
Im ищет что-то лучше, чем sudo restart projectname
каждый раз, когда я вывожу git pull origin master
, который переносит мои последние изменения в проект Django. Эта команда restart
, я полагаю, связана с Upstart, которую я использую для запуска/завершения процесса сервера Gunicorn.
Этот перезапуск приводит к кратковременному отключению. Пользователи, попавшие на веб-сервер (nginx), получат 500, потому что Gunicorn все еще перезапускается. Фактически, он, кажется, перезапускается мгновенно, но загрузка страниц занимает несколько секунд.
Любые идеи о том, как сделать это без проблем? В идеале я хотел бы автоматически обновлять мои файлы git pull
и Gunicorn.
Ответы
Ответ 1
Для изящной перезагрузки вместо этого вы должны использовать команду Upstart reload
, например:
sudo reload jobname
В соответствии с initctl (Upstart) manpage, reload
отправит в процесс сигнал HUP
:
reload JOB [KEY=VALUE]...
Sends the SIGHUP signal to running process of the named JOB instance.
..., который для Gunicorn вызовет изящный перезапуск (см. FAQ).
Ответ 2
Вы можете сказать, что Gunicorn будет изящно перезагружаться с помощью сигнала HUP
следующим образом:
kill -HUP <pid>
(подробнее см. FAQ)
Я использую Supervisor для управления моим сервером Gunicorn, который позволяет мне использовать этот (слегка взломанный) способ перезагрузки Gunicorn после развертывания
supervisorctl status gunicorn | sed "s/.*[pid ]\([0-9]\+\)\,.*/\1/" | xargs kill -HUP
Очевидно, вы могли достичь чего-то подобного с помощью pidof
или ps
.
Это фактически выполняется из Fabric script, поэтому мне даже не нужно заходить на сервер вообще.
Ответ 3
Для тех, кто не использует superisord: что сказал Роб, он работает и с ps,
ps aux |grep gunicorn |grep projectname | awk '{ print $2 }' |xargs kill -HUP
Ответ 4
Мы запускаем Gunicorn под Supervisor, но это самый простой, самый чистый способ, который мы нашли, чтобы изящно перезагрузить Gunicorn, когда он запутался:
sudo pkill -HUP -f gunicorn.*master
Ответ 5
Systemd, gunicorn и Ubuntu
Вот один лайнер, если вы используете свою службу увольнения с помощью systemd.
systemctl status gunicorn | sed -n 's/.*Main PID: \(.*\)$/\1/g p' | cut -f1 -d' ' | xargs kill -HUP
Детали шаг за шагом
Поскольку gunicorn docs говорят, что правильный способ изящно перезагрузить рабочих - это использовать kill -HUP <Main PID>
, где <Main PID>
- это Идентификатор процесса основного процесса, мы извлекаем главный PID с помощью systemctl и запускаем kill -HUP <Main PID>
.
1) Получите информацию о процессе от systemd, используя имя службы
systemctl status gunicorn
где gunicorn
- имя службы, расположенное в /etc/systemd/system/
.
Пример вывода:
[email protected]:~$ systemctl status gunicorn
● gunicorn.service - Gunicorn server for yourproject.com
Loaded: loaded (/etc/systemd/system/gunicorn.service; enabled; vendor preset: enabled)
Active: active (running) since Sat 2017-11-04 19:16:24 UTC; 1h 15min ago
Main PID: 10673 (gunicorn)
CGroup: /system.slice/gunicorn.service
├─10673 /home/ubuntu/site/venv/bin/python3 /home/ubuntu/site/venv/bin/gunicorn --workers 3 --bind unix:/tmp/yourproject.socket config.wsgi:application
├─11069 /home/ubuntu/site/venv/bin/python3 /home/ubuntu/site/venv/bin/gunicorn --workers 3 --bind unix:/tmp/yourproject.socket config.wsgi:application
├─11070 /home/ubuntu/site/venv/bin/python3 /home/ubuntu/site/venv/bin/gunicorn --workers 3 --bind unix:/tmp/yourproject.socket config.wsgi:application
└─11071 /home/ubuntu/site/venv/bin/python3 /home/ubuntu/site/venv/bin/gunicorn --workers 3 --bind unix:/tmp/yourproject.socket config.wsgi:application
Nov 04 20:27:04 ip-10-4-12-247 gunicorn[10673]: [2017-11-04 20:27:04 +0000] [11047] [INFO] Booting worker with pid: 11047
Nov 04 20:27:04 ip-10-4-12-247 gunicorn[10673]: [2017-11-04 20:27:04 +0000] [11048] [INFO] Booting worker with pid: 11048
Nov 04 20:32:16 ip-10-4-12-247 gunicorn[10673]: [2017-11-04 20:32:16 +0000] [10673] [INFO] Handling signal: hup
Nov 04 20:32:16 ip-10-4-12-247 gunicorn[10673]: [2017-11-04 20:32:16 +0000] [10673] [INFO] Hang up: Master
Nov 04 20:32:16 ip-10-4-12-247 gunicorn[10673]: [2017-11-04 20:32:16 +0000] [11046] [INFO] Worker exiting (pid: 11046)
Nov 04 20:32:16 ip-10-4-12-247 gunicorn[10673]: [2017-11-04 20:32:16 +0000] [11047] [INFO] Worker exiting (pid: 11047)
Nov 04 20:32:16 ip-10-4-12-247 gunicorn[10673]: [2017-11-04 20:32:16 +0000] [11048] [INFO] Worker exiting (pid: 11048)
Nov 04 20:32:16 ip-10-4-12-247 gunicorn[10673]: [2017-11-04 20:32:16 +0000] [11069] [INFO] Booting worker with pid: 11069
Nov 04 20:32:16 ip-10-4-12-247 gunicorn[10673]: [2017-11-04 20:32:16 +0000] [11070] [INFO] Booting worker with pid: 11070
Nov 04 20:32:16 ip-10-4-12-247 gunicorn[10673]: [2017-11-04 20:32:16 +0000] [11071] [INFO] Booting worker with pid: 11071
2) Получить идентификатор процесса (PID) основного процесса стрельбы
Команда sed работает следующим образом: sed 's/<search this>/<replace with this>/g'
-
s
означает для подстановочной команды, а g
означает, что поиск всего входа глобально.
- Флаг
-n
сообщает sed не печатать каждую строку (или вообще ничего не печатать).
-
p
в конце говорит sed распечатать согласованную строку.
- Ищем
.*Main PID: \(.*\)$
, который является шаблоном регулярного выражения, который имеет следующие части: .*
соответствует любому символу (.
) ноль или более раз (*
). Затем мы ищем Main PID:
, за которым следуют любые символы, повторяющиеся ноль или более (.*
). Чтобы захватить все символы после Main PID:
-text, мы вставляем .*
в скобки, которые сбрасываются с обратными косыми чертами: \(.*\)
. $
указывает конец строки.
- "заменить на эту" часть команды sed просто
\1
, что означает первый захваченный набор символов.
Пример вывода:
[email protected]:~$ systemctl status gunicorn | sed -n 's/.*Main PID: \(.*\)$/\1/g p'
10673 (gunicorn)
3) Избавьтесь от лишних символов
Подключите выход к cut. cut -f1 -d' '
означает, что
- Строка разделена пробелом: здесь
-d
определяет разделитель, который является символом сразу после -d
. Поскольку разделитель является пространством, мы заключаем это в кавычки.
-
-f
означает, что резка производится с использованием разделителя (а не байтов), а -f1
означает, что мы хотим вынуть первый элемент списка.
Пример вывода:
[email protected]:~$ systemctl status gunicorn | sed -n 's/.*Main PID: \(.*\)$/\1/g p' | cut -f1 -d' '
10673
4) Используйте главный PID
Трубопровод до xargs означает просто запустить команду с аргументами из трубы в левой части. Поскольку мы соединяем только основной PID с xargs,
systemctl status gunicorn-django | sed -n 's/.*Main PID: \(.*\)$/\1/g p' | cut -f1 -d' ' | xargs kill -HUP
- это в основном то же самое, что
echo <Main PID > | xargs kill -HUP
который переводится в
kill -HUP <Main PID >
Изменить
Немногим более надежным решением было бы использовать cut -f1 -d$'\n'
или grep -m1 ""
перед cut -f1 -d' '
, чтобы выбрать только первую строку совпадения. Я не могу понять никаких обстоятельств, в которых было бы два совпадения для Main PID:
.