Gunicorn官方文档已经阐述了如何正确部署,不过对于使用虚拟环境的部署只有寥寥数语。由于大多数时候Python都会有单独的虚拟环境,如此反而导致走了许多弯路,在此记录下来正确部署方法。

配置Systemd相关文件

Gunicorn官方文档使用Unix socket与Nginx通讯,理论上能有更好的性能,因此我们也按照它的步骤走,分为两个文件,首先配置/etc/systemd/system/gunicorn.service::

[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target mysql.service

[Service]
PIDFile=/run/gunicorn/pid
User=someuser
Group=someuser
RuntimeDirectory=gunicorn
WorkingDirectory=/home/someuser/applicationroot
ExecStart=/home/someuser/.local/share/virtualenvs/nomox-E5eq2naN/bin/gunicorn\
 --pid /run/gunicorn/pid   --bind unix:/run/gunicorn/socket nomox.wsgi
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

只需要将ExecStart的目录替换为虚拟环境地址就行了,如果记不清可以在pipenv shell加载环境时查看。要注意Gunicorn是直接安装在虚拟环境中的。

接下来配置/etc/systemd/system/gunicorn.socket,不需要改变:

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn/socket

[Install]
WantedBy=sockets.target

Systemd的两个文件已经配置完毕,但还需要给运行目录权限,配置/etc/tmpfiles.d/gunicorn.conf:

d /run/gunicorn 0755 someuser somegroup -

检查是否配置正确

开始启动gunicorn:

systemctl enable gunicorn.socket
systemctl start gunicorn.socket

使用curl查看是否有内容curl --unix-socket /run/gunicorn/socket http来判断是不是正常运行。

Nginx配置

配置如下:

...
http {
    ...
    server {
        listen          8000;
        server_name     127.0.0.1;
        # path for static files
        root /home/someuser/applicationroot/staticfile;

        location / {
          # checks for static file, if not found proxy to app
          try_files $uri @proxy_to_app;
        }

        location @proxy_to_app {
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header X-Forwarded-Proto $scheme;
          proxy_set_header Host $http_host;
          # we don't want nginx trying to do something clever with
          # redirects, we set the Host: header above already.
          proxy_redirect off;
          proxy_pass http://unix:/run/gunicorn/socket;
        }

    }
    ...
}
...

保存后使用nginx -s reload命令重新加载配置文件就应该部署完成了。

总结

由于Pipenv底层虚拟环境使用的还是Virtualenv,所以配置的方法和Virtualenv一致,只需要找准环境目录即可。这里还有一个小问题没有弄清楚,到底该用什么用户和用户组运行Gunicorn?就我个人而言,还是使用部署代码时的非root用户和用户组来直接运行,避免不必要的麻烦,但是在安全性方面可能有所欠缺。