在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:但是。