在git pulling我的Django项目之后重启/重载Gunicorn(通过Upstart)的更好方法
我寻找的东西比sudo restart projectname
每次我发出一个git pull origin master
,这拉下了我的最新变化Django项目。 我相信这个restart
命令与Upstart有关,我用它来启动/顶部我的Gunicorn服务器进程。
此重新启动会导致短暂的中断。 用户点击Web服务器(nginx)将得到500,因为Gunicorn仍在重新启动。 实际上,它似乎立即重启,但页面加载需要几秒钟的时间。
任何想法如何使这无缝? 理想情况下,我想要发出我的git pull
而Gunicorn会自动重新加载。
对于优雅的重载,你应该使用Upstart的reload
命令,例如:
sudo reload jobname
根据initctl(Upstart)手册页 , reload
将发送一个HUP
信号给进程:
reload JOB [KEY=VALUE]... Sends the SIGHUP signal to running process of the named JOB instance.
这对于Gunicorn来说会触发一个平稳的重启(参见FAQ )。
您可以告诉Gunicorn使用HUP
信号优雅地重新载入,如下所示:
kill -HUP <pid>
(详情请参阅FAQ )
我使用Supervisor来控制我的Gunicorn服务器,它允许我在部署完成后使用这个(稍微黑客)的方式重新加载Gunicorn:
supervisorctl status gunicorn | sed "s/.*[pid ]\([0-9]\+\)\,.*/\1/" | xargs kill -HUP
你显然可以用pidof
或者ps
实现类似的function。
这实际上是从Fabric脚本运行的,所以我甚至不需要login到服务器。
对于那些不使用supervisord的人来说:Rob说的是,它也适用于ps,
ps aux |grep gunicorn |grep projectname | awk '{ print $2 }' |xargs kill -HUP
我们在Supervisor下运行Gunicorn,但是这是最简单,最清洁的方式,我们发现当Gunicorn变得困惑的时候,
sudo pkill -HUP -f gunicorn.*master
Systemd,gunicorn和Ubuntu
如果你正在使用systemd来运行你的gunicorn服务,那么这里就是单行程。
systemctl status gunicorn | sed -n 's/.*Main PID: \(.*\)$/\1/gp' | cut -f1 -d' ' | xargs kill -HUP
细节一步一步
由于gunicorn文档告诉我们正确地重新加载worker的方法是使用kill -HUP <Main PID>
,其中<Main PID>
是主进程的进程ID,我们使用systemctl提取主PID,并运行kill -HUP <Main PID>
。
1)使用服务名称获取有关systemd进程的信息
systemctl status gunicorn
其中gunicorn
是服务的名称,位于/etc/systemd/system/
。
示例输出:
ubuntu@ip-10-4-12-247:~$ 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)获得主炮进程的进程ID(PID)
sed命令的工作原理如下: sed 's/<search this>/<replace with this>/g'
-
s
代表替代命令,g
全局search整个input。 -
-n
标志告诉sed 不要打印每一行(或者实际上不打印任何内容)。 - 最后的
p
告诉sed 打印匹配的行 。 -
.*Main PID: \(.*\)$
,它是一个正则expression式模式,它有以下几部分:.*
匹配任何字符(.
)零次或多次(*
)。 然后我们searchMain PID:
后跟任何字符,重复零次或多次(.*
)。 要捕获Main PID:
-text后面的所有字符,我们将.*
括起来,并用反斜线转义:\(.*\)
。$
表示行结束。 - sed命令的“replace”部分就是
\1
,这意味着第一个被捕获的字符集。
示例输出:
ubuntu@ip-10-4-12-247:~$ systemctl status gunicorn | sed -n 's/.*Main PID: \(.*\)$/\1/gp' 10673 (gunicorn)
3)摆脱多余的字符
pipe输出剪切 。 cut -f1 -d' '
表示这个意思
- string是空格分隔的:这里的
-d
决定了分隔符,这是-d
后面的字符。 由于分隔符是空格,我们用引号括起来。 -
-f
意味着使用分隔符(而不是字节)进行剪切,而-f1
意味着我们要取出列表的第一个元素。
示例输出:
ubuntu@ip-10-4-12-247:~$ systemctl status gunicorn | sed -n 's/.*Main PID: \(.*\)$/\1/gp' | cut -f1 -d' ' 10673
4)使用主PID
对xargs进行配pipe意味着只需使用左侧pipe道的参数运行该命令。 由于我们只是把主要的PID传给xargs,
systemctl status gunicorn-django | sed -n 's/.*Main PID: \(.*\)$/\1/gp' | cut -f1 -d' ' | xargs kill -HUP
基本上是一样的东西
echo <Main PID > | xargs kill -HUP
翻译成
kill -HUP <Main PID >
编辑
一个更强大的解决scheme是在cut -f1 -d$'\n'
grep -m1 ""
前面使用cut -f1 -d$'\n'
或grep -m1 ""
来select匹配的第一行。 但是我无法弄清楚任何情况,哪里会有Main PID:
两个匹配Main PID:
但是。