生产环境配置 Nginx+uwsgi+Django 实录

Tech  2020年7月15日  21:28

起源

本网站的后端基于 Django,运行在腾讯云上。之前在开发环境中运行 Django 的方式是很简单的:

python manage.py runserver 0.0.0.0:8000

上云以后没多想,直接开了个 screen session 在里面运行这句命令把 Django 给跑起来了(当然 DEBUG 模式要关掉)。然而发现每隔大概几天的时间 Django 就会失去响应,进入 screen session 查看也没有崩掉,log 也很正常,但就是访问不了网站了。之前的解决办法就是一直重新启动 Django,但是仔细一想感觉很不对劲,于是上网查询了一番发现这好像是个可复现的问题,之前也有人遇到过并解决了。相关博客

根据博客描述,不建议把开发环境的启动方式应用到生产环境中,而他的解决方法是使用 uwsgi 启动(Django 对 uwsgi 有非常好的支持)。又查询了更多生产环境部署 Django 的博客后,跌跌撞撞配了一大堆东西总算是成功跑起来了。特别是我的网站有 SSL,而很多博客没有提到 SSL 如何部署,我是自己尝试了一番后成功的。为了以后查阅方便,所以打算把整个配置过程记录在此。

以下所用的系统均为 Ubuntu。

简介

Nginx 是一个常用的高效反向代理服务器,在 Nginx+uwsgi+Django 的模式中作为最前端,直接接受所有的网络请求并做第一步处理。用 apt 就可以很方便地安装 Nginx。

uWSGI 是一个 Web 服务器,它实现了 WSGI 协议、uwsgi、http 等协议。它速度快、内存占用小,优势很多(但我没有做什么深入研究,只是简单做了启动配置)。可以用以下方式安装:

pip install uwsgi

Django 不必多说,就是我们网站的后端本体。创建 Django 项目的时候在 urls.pysettings.py 同级文件夹下还可以看到一个 wgsi.py,就是给 uwsgi 使用的。

配置 Django

首先使用开发环境的方式启动一下项目确保能够运行:

python manage.py runserver 0.0.0.0:8000

确保能够运行以后其实就不用再管了。另外因为在 Nginx 配置的时候会配置一下 static 文件夹,对于静态资源文件请求 Nginx 不会再请求 Django,所以最好把 static 文件夹独立出来。具体来说,如果在 Django 的 settings.py 里通过:

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'css'),
    os.path.join(BASE_DIR, 'js'),
    os.path.join(BASE_DIR, 'img'),
    ...
]

设置了多个静态文件夹路径的话,那么建议加上一个:

STATIC_ROOT = os.path.join(BASE_DIR, 'static/')

指定一个专门的 static 文件夹,然后通过:

python manage.py collectstatic

把所有静态文件拷贝到统一的 static 文件夹下。

这一步不确定是不是必要的(因为没有试过不这样做能不能正常工作),但是根据 Nginx 的描述我猜测这一步是必要的,而且这一步也没什么代价,不妨做一下。

配置 Nginx

基本配置

Nginx 的配置文件是位于路径 /etc/nginx 下的 nginx.conf。一般来说,更改这个文件需要 sudo 权限。(通过我的观察)Nginx 的配置文件语法是以大括号作为配置项的作用域标志,配置项以空格和分号作为标志。也就是说一个常见的配置语法是这样的:

server {
    listen 80;
    location /static {
        alias /The/path/to/directory;
    }
}

强制 HTTPS:重写请求

我们可以通过重写请求的方式来完成强制 HTTPS。如果你的网站没有 SSL 证书,只能走 HTTP 的话,这部分就不用管了,可以直接看下一部分。

一个简单的方式是这样的:

server {
    listen 80;
    server_name your-domain.com; # 你的域名
    rewrite ^(.*)$ https://${server_name}$1 permanent;
}

首先 Nginx 在 80 端口( HTTP 请求端口)监听,然后通过 rewrite,把所有请求永久重发到 https 路径上(那个正则表达式相当于匹配任意字符串),这样就实现了强制 HTTPS。

SSL 配置

添加如下配置:

server {
    listen 443 ssl; # 1.1版本后这样写
    server_name your-domain.com; # 绑定证书的域名
    charset utf-8; # 指定编码,似乎可以不写
    ssl_certificate /The/path/to/your/xxxx.crt; # 证书路径,绝对路径
    ssl_certificate_key /The/path/to/your/xxxx.key; # 密钥路径,绝对路径
    ssl_session_timeout 5m; # 猜测是设置超时时间,其实比较无所谓
    location / {
        include uwsgi_params; # 在 /etc/nginx 文件夹下应该会有个 uwsgi_params 文件
        uwsgi_pass 127.0.0.1:9090; # 端口可以自定义
    }
    location /static {
        alias /The/path/to/static/directory; # 静态文件所在的文件夹,绝对路径,就是前面配置 Django 时的 STATIC_ROOT
    }
}

如果是 HTTP,那么 listen 项填写 80,ssl 相关的配置项不用写,其他基本是一样的。另外如果有媒体文件,可能需要另外指定 location /media,具体方法和 location /static 类似。

启动、停止、重载 Nginx

如果没有启动 Nginx,用以下方式启动 Nginx:

service nginx start

如果启动成功,不会有任何输出。检查 Nginx 是否有启动:

ps -ef | grep nginx

观察是否有 Nginx 的 master process 和 worker process。

如果要停止 Nginx,通过上一条命令查看 master process 的 pid,然后

kill -QUIT ${pid}

或者直接这样:

nginx -s quit

更改配置文件后,用以下方式重载 Nginx:

nginx -s reload
service nginx restart

如果成功,不会有任何输出。有些操作可能需要 sudo 权限(比如 service ),那就加上 sudo 再输入一遍就好了。

配置 uwsgi

编写配置文件

在 Django 项目根目录(与 manage.py 同级)下创建一个 uwsgi.ini 文件(理论上名字任意),填入以下内容:

[uwsgi]

chdir=/The/path/to/your/project/
# 你的项目根目录,绝对路径

socket=127.0.0.1:9090
# 使用 socket 和 Nginx 通信,使用之前 Nginx 配置时写的端口

wsgi-file=/The/path/to/wsgi.py
# wsgi.py 的路径,在 settings.py 的同级目录下

processes=4
# 进程个数
threads=2
# 线程个数(意义不明)
master=true
# 启用主线程,填 true
vacum=true
# (意义不明)

pidfile=/The/path/you/want/uwsgi.pid
# pid 文件路径,里面会储存进程 pid

daemonize=/The/path/you/want/uwsgi.log
# log 文件路径,里面会打印日志

module=server.wsgi
# 猜测是 wsgi 所在文件夹的名字加上 .wsgi

意义不明就是真的不知道是干啥的,写上就是了。猜测的就是指我这样写 work 了,但不保证就是这样的。

11 月 14 日更新:帮别人配置的时候发现,有时候 uwsgi 启动会报告无法加载 plugins,这时候可能是因为安装 uwsgi 的时候没有成功安装 python 插件。在配置文件中加入一行 plugin=python,同时使用 sudo apt-get install uwsgi-plugin-python 安装对应插件即可解决(对于 python3,分别改为 plugin=python3sudo apt-get install uwsgi-plugin-python3)。

启动、停止、检查 uwsgi

配置文件保存后,可以这样启动:

uwsgi -i uwsgi.ini

或者:

uwsgi --ini uwsgi.ini

文件名改成你自己设置的就可以。如果正常启动,只会输出一句 uWSGI 从对应的 ini 文件里加载了配置。想要检查它有没有在运行,可以:

ps -ef | grep uwsgi

想要停止的话可以找到 pid 然后用 kill 的方式发送停止信号,也可以:

uwsgi --stop ${pid}

重新加载:

uwsgi --reload ${pid}

填写 pid 的地方也可以换成配置文件中指定的储存 pid 的那个文件的路径。

完成

正常来说,完成以上配置,成功跑起 Nginx 和 uwsgi,并且 Django 项目是可以运行的,那么就完成了。此时访问你的网站就可以看到正常效果。不过这样似乎就看不了以前通过命令行打印的 Django 的日志,只能到 uwsgi 配置文件指定的日志文件路径里去看(那样看起来还是比较不舒适的)。

暂时就写这么多。如果以后遇到更多问题或者搞清楚了更多东西,此文还会更新的。