用nginx 和 gunicorn 部署Django

本文介绍在linux (ubuntu20) 上用nginx 和 gunicorn 部署 Django 的方法。 我的原机上安装了LEMP上的wordpress, 参见 在ubuntu 20 上安装 wordpress lemp ,主页面是worpress 同时也安装了django的应用。当然 wordpress 不是必要的。

本文没有介绍 django 工程的建立,只是介绍其部署,当然在部署前还是需要验证django 应用的。

参考原文是 How To Set Up Django with Postgres, Nginx, and Gunicorn on Ubuntu 20.04  原文介绍得很多,也比较难看懂,但本文只是集中在django 的部署。

软件安装和工程准备

执行下面的安装命令:

sudo apt update
sudo apt install python3-pip python3-dev libpq-dev  nginx curl

libpq-dev 在我主机有点问题,就不安装他了,用下面命令:

sudo apt install python3-pip python3-dev  nginx curl

建立虚拟空间,先安装软件:

sudo -H pip3 install --upgrade pip
sudo -H pip3 install virtualenv

建立工程目录 :

mkdir ~/mysite
cd ~/mysite

建立虚拟空间:

virtualenv mysiteenv

激活虚拟空间:

source mysiteenv/bin/activate

界面是这样的:

leon@ubuntu-s-1vcpu-1gb-tor1-01:~$ source myproject/myprojectenv/bin/activate
(myprojectenv) leon@ubuntu-s-1vcpu-1gb-tor1-01:~$

这样提示行有虚拟环境名。

在虚拟环境里安装 Django gunicorn 。注意这里安装用pip 而不是pip3:

pip install django gunicorn 

建立django 和应用

建立工程,设置等等,这里就不多说。可以参看:python 的web框架 django 的入门教程 1

确保防火墙打开8000端口:

sudo ufw allow 8000

启动应用:

~/mysite/manage.py runserver 0.0.0.0:8000

验证,在浏览器输入你的应用名,比如:http://127.0.0.1:8000/polls/

或者:http://138.197.144.170/covid/

这里应该看到你的应用运行正常。

测试Gunicorn服务的功能

先看看工程mysite 目录下的内容:

leon@ubuntu-s-1vcpu-1gb-tor1-01:~/mysite/mysite$ ls
__init__.py  __pycache__  asgi.py  settings.py  urls.py  views.py  views.py.save  wsgi.py

我们用到 wsgi.py 这个文件

guniorn 的服务验证是这样的:

cd ~/mysite
gunicorn --bind 0.0.0.0:8000 mysite.wsgi
这样用gunicorn 启动django 应用服务。和上面启动django 应用效果一样的。

在浏览器输入你的应用名,比如:http://127.0.0.1:8000/polls/

或者:http://138.197.144.170/covid/

这里应该看到你的应用同样运行正常。

退出虚拟空间用下面命令:

deactivate

为Gunicorn创建systemd套接字和服务文件

sudo nano /etc/systemd/system/gunicorn.socket

在这里,我们将创建一个[Unit]节来描述套接字,一个[Socket]节来定义套接字位置,并创建一个[Install]节以确保在正确的时间创建了套接字:

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

接下来,在文本编辑器中创建并使用sudo权限打开Gunicorn的systemd service文件。service文件名应与socket文件名匹配,但扩展名不一样:

sudo nano /etc/systemd/system/gunicorn.service

从[Unit]部分开始,该部分用于指定元数据和相关性。 我们将在此处对我们的服务进行描述,并告诉init系统仅在达到网络目标后才启动此服务。 因为我们的服务依赖于套接字文件中的套接字,所以我们需要包含一个Requires指令来指示这种关系:

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

接下来,我们将打开[ Servicce]部分。 这里指定要运行的用户和组。 由于该过程需要拥有所有相关文件,因此需要将授予该过程的常规用户帐户所有权。 我们会将群组所有权赋予www-data群组,以便Nginx可以轻松地与Gunicorn通信。

然后,我们将映射出工作目录并指定用于启动服务的命令。 在这种情况下,我们必须指定Gunicorn可执行文件的完整路径,该路径已安装在我们的虚拟环境中。 我们会将进程绑定到在/ run目录中创建的Unix套接字,以便进程可以与Nginx通信。 我们将所有数据记录到标准输出,以便日记处理可以收集Gunicorn日志。 我们还可以在此处指定任何可选的Gunicorn调整项。 例如,在这种情况下,我们指定了3个工作进程:

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

[Service]
User=leon
Group=www-data
WorkingDirectory=/home/leon/mysite
ExecStart=/home/leon/mysite/mysiteenv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          mysite.wsgi:application

在这里我们看到 user =leon 这是拥有root 权限的用户名

workingDirecctory = /home/leon/mysite  这是django 工程目录的绝对目录名

ExecStart = /home/leon/mysite/mysiteenv/bin/gunicorn   这是我们虚拟环境安装的gunicorn 的绝对路径

mysite.wsgi:appliation    可以看到上面目录里的wsgi.py 里有个application = get_wsgi_application()

--bind unix:/run/guniorn.sok 与上面gunicorn.socket 文件里的ListenStream 对应。

 最后,我们将添加一个[Install]部分。 如果需要启用该服务以在启动时启动,它将告诉systemd该服务链接到什么。 我们希望该服务在常规多用户系统启动并运行时启动:

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

[Service]
User=leon
Group=www-data
WorkingDirectory=/home/leon/myprojectdir
ExecStart=/home/leon/myprojectdir/myprojectenv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          myproject.wsgi:application

[Install]
WantedBy=multi-user.target

这样,我们的systemd service文件就完成了。保存并关闭。

现在,我们可以启动并使能Gunicorn套接字。以后在启动时,也会在/run/gunicorn.sock处创建套接字文件。 与该套接字建立连接后,systemd将自动启动gunicorn.service来处理它:

sudo systemctl start gunicorn.socket
sudo systemctl enable gunicorn.socket

检查Gunicorn套接字文件

检查进程的状态,以了解它是否能成功启动:

sudo systemctl status gunicorn.socket

输出应该是下面这个样子:

● gunicorn.socket - gunicorn socket
     Loaded: loaded (/etc/systemd/system/gunicorn.socket; enabled; vendor preset: enabled)
     Active: active (listening) since Sat 2021-02-06 22:55:01 UTC; 43s ago
   Triggers: ● gunicorn.service
     Listen: /run/gunicorn.sock (Stream)
      Tasks: 0 (limit: 1137)
     Memory: 0B
     CGroup: /system.slice/gunicorn.socket

Feb 06 22:55:01 ubuntu-s-1vcpu-1gb-tor1-01 systemd[1]: Listening on gunicorn socket.

接下来,检查/ run目录中是否存在gunicorn.sock文件:

file /run/gunicorn.sock

应该有这样的输出:

/run/gunicorn.sock: socket

如果systemctl status命令指示发生错误,或者在目录中找不到gunicorn.sock文件,则表明无法正确创建Gunicorn套接字。 输入以下内容,查看Gunicorn套接字的日志:

sudo journalctl -u gunicorn.socket

在继续操作之前,如有出错误,需要查看/etc/systemd/system/gunicorn.socket文件以解决所有问题。

测试套接字激活

如果只是启动了gunicorn.socket单元,但套接字尚未收到任何连接,那么gunicorn.service将不会处于活动状态。 可以通过输入以下内容进行检查:

sudo systemctl status gunicorn

应该是如下的输出:

 gunicorn.service - gunicorn daemon
   Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
   Active: inactive (dead)

为了测试套接字激活机制,可以通过输入以下内容通过curl发送到套接字的连接:

curl --unix-socket /run/gunicorn.sock localhost

应该从终端中的应用程序接收HTML输出。 这表明Gunicorn已启动,并且能够为Django应用程序提供服务。 可以通过键入以下命令来验证Gunicorn服务是否正在运行:

sudo systemctl status gunicorn

应该有这样的输出

gunicorn.service - gunicorn daemon
     Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
     Active: active (running) since Fri 2020-06-26 18:52:21 UTC; 2s ago
TriggeredBy: ● gunicorn.socket
   Main PID: 22914 (gunicorn)
      Tasks: 4 (limit: 1137)
     Memory: 89.1M
     CGroup: /system.slice/gunicorn.service
             ├─22914 /home/sammy/myprojectdir/myprojectenv/bin/python /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunico>
             ├─22927 /home/sammy/myprojectdir/myprojectenv/bin/python /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunico>
             ├─22928 /home/sammy/myprojectdir/myprojectenv/bin/python /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunico>
             └─22929 /home/sammy/myprojectdir/myprojectenv/bin/python /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunico>

Jun 26 18:52:21 django-tutorial systemd[1]: Started gunicorn daemon.
Jun 26 18:52:21 django-tutorial gunicorn[22914]: [2020-06-26 18:52:21 +0000] [22914] [INFO] Starting gunicorn 20.0.4
Jun 26 18:52:21 django-tutorial gunicorn[22914]: [2020-06-26 18:52:21 +0000] [22914] [INFO] Listening at: unix:/run/gunicorn.sock (22914)
Jun 26 18:52:21 django-tutorial gunicorn[22914]: [2020-06-26 18:52:21 +0000] [22914] [INFO] Using worker: sync
Jun 26 18:52:21 django-tutorial gunicorn[22927]: [2020-06-26 18:52:21 +0000] [22927] [INFO] Booting worker with pid: 22927
Jun 26 18:52:21 django-tutorial gunicorn[22928]: [2020-06-26 18:52:21 +0000] [22928] [INFO] Booting worker with pid: 22928
Jun 26 18:52:21 django-tutorial gunicorn[22929]: [2020-06-26 18:52:21 +0000] [22929] [INFO] Booting worker with pid: 22929

如果curl的输出或systemctl status的输出指示发生问题,请检查日志以获取其他详细信息:

sudo journalctl -u gunicorn

检查/etc/systemd/system/gunicorn.service文件中的问题。 如果对/etc/systemd/system/gunicorn.service文件进行了更改,请重新加载守护程序以重新读取服务定义并通过键入以下命令重新启动Gunicorn进程:

sudo systemctl daemon-reload
sudo systemctl restart gunicorn

在继续前,确保这里没有问题。

配置Nginx代理传递给Gunicorn

现在已经建立了Gunicorn,我们需要配置Nginx来将流量传递给该进程。

首先在Nginx的sites-available目录(/etc/nginx/sites-available)中创建并打开一个新的服务器块,我只是一个域名,所以直接修改default 文件。我的主机原来运行了wordpress 服务,我这只是特别指定提供2个应用的django 服务。

这样修改的目的是,域名/polls  和 /covid 下的服务都指向djiango的Gunicorn服务 ,其他就由 wordpress 管,这个其余部分这里就没有显示了。

修改的内容如下:

server {
    ...

     location /polls/ {
     include proxy_params;
     proxy_pass http://unix:/run/gunicorn.sock;
     }

     location /covid/ {
     include proxy_params;
     proxy_pass http://unix:/run/gunicorn.sock;
     }
}

检查nginx 没有配置错误:

sudo nginx -t

如果没有什么错误的话,重启nginx 使配置生效:

sudo systemctl restart nginx

Nginx和Gunicorn的故障排除

如果最后一步没有显示您的应用程序,则需要对安装进行故障排除。

Nginx显示默认页面而不是Django应用程序
如果Nginx显示默认页面而不是代理到应用程序,则通常意味着需要调整/ etc / nginx / sites-available / myproject文件中的server_name以指向服务器的IP地址或域名。

Nginx使用server_name来确定使用哪个服务器块来响应请求。如果收到默认的Nginx页面,则表明Nginx无法将请求明确匹配到服务器块,因此它会退回到/ etc / nginx / sites-available / default中定义的默认块。

项目的服务器块中的server_name必须比要选择的默认服务器块中的server_name更具体。

Nginx显示502 Bad Gateway错误,而不是Django应用程序
502错误表示Nginx无法成功代理请求。各种各样的配置问题都以502错误表示出来,因此需要更多信息以进行适当的故障排除。

寻找更多信息的主要地方是在Nginx的错误日志中。通常,这将告诉您什么情况导致代理事件期间出现问题。通过输入以下内容来关注Nginx错误日志:

sudo tail -F /var/log/nginx/error.log


现在,在浏览器中再次发出请求以生成新的错误(尝试刷新页面)。您应该会收到一条新的错误消息,将其写入日志。如果查看该消息,它应该可以帮助缩小问题范围。

可能会收到以下消息:

connect() to unix:/run/gunicorn.sock failed (2: No such file or directory)

这表明Nginx无法在给定位置找到gunicorn.sock文件。应该将/ etc / nginx / sites-available / myproject文件中定义的proxy_pass位置与gunicorn.socket系统单元生成的gunicorn.sock文件的实际位置进行比较。

如果在/ run目录中找不到gunicorn.sock文件,则通常意味着systemd套接字文件无法创建它。返回有关检查Gunicorn套接字文件的部分,以逐步完成Gunicorn的故障排除步骤。

connect() to unix:/run/gunicorn.sock failed (13: Permission denied)

这表明由于权限问题,Nginx无法连接到Gunicorn套接字。当使用root用户而不是sudo用户执行该过程时,可能会发生这种情况。虽然systemd可以创建Gunicorn套接字文件,但Nginx无法访问它。

如果根目录(/)gunicorn.sock文件之间的任何位置权限有限,则可能发生这种情况。通过将套接字文件的绝对路径传递给namei命令,我们可以查看套接字文件及其每个父目录的权限和所有权值:

namei -l /run/gunicorn.sock

输出类似这样:

f: /run/gunicorn.sock
drwxr-xr-x root root /
drwxr-xr-x root root run
srw-rw-rw- root root gunicorn.sock

输出显示每个目录组件的权限。 通过查看权限(第一列),所有者(第二列)和组所有者(第三列),我们可以确定允许对套接字文件进行哪种类型的访问。

在上面的示例中,套接字文件和通向该套接字文件的每个目录都具有全局的读取和执行权限(目录的权限列以r-x而不是---结尾)。 Nginx进程应该能够成功访问套接字。

如果通向套接字的任何目录都不具有全局的读取和执行许可权,则Nginx将无法访问套接字,而无需允许全局的读取和执行许可权或确保将组所有权授予了Nginx参与其中的组 的。

有些故障排除,我并没都经历,只是简单翻译了原文,并且也不完整。如果碰到问题请参考下原文,或者留言,或者自己网上查找。

介绍结束。

猜你喜欢

转载自blog.csdn.net/leon_zeng0/article/details/113694861
今日推荐