在 Ubuntu 上部署 Nginx + Django + uWSGI
本文最近更新于 2018 年 06 月 26 日 ,横滨
概念和设计
Nginx 是什么?
发音为 engine-x,是一个免费开源并且高性能的 HTTP 服务器和反向代理,还是一个 IMAP/POP3 代理服务器,相较于 Apache、lighttpd 具有占有内存少,稳定性高等优势。
uWSGI 是什么?
一个 Web 服务器,它实现了 WSGI 协议、uwsgi、http 等协议。(Nginx 中 HttpUwsgiModule 的作用是与 uWSGI 服务器进行交换。)
WSGI/uwsgi/uWSGI 这三个概念的区分:
- uwsgi 同 WSGI 一样是一种通信协议
- uWSGI 是一个 web 服务器,实现了 uwsgi 和 WSGI 两种协议
- uwsgi 协议是一个 uWSGI 服务器自有的协议,它用于定义传输信息的类型(type of information),每一个 uwsgi packet 前 4 byte 为传输信息类型描述,它与 WSGI 相比是两样东西
关于 uwsgi 协议看这里:The uwsgi protocol
Django 是什么?
一个高层次的 Python Web 框架,鼓励快速开发和干净实用的设计。
运行过程:
Nginx 作为服务器的最前端,它将接受 web 的所有请求,统一管理请求。Nginx 把所有静态请求自己来处理(这是 Nginx 的强项,静态文件就是我们 Django 项目中的 image,css,js 等文件)。然后 Nginx 将所有的非静态请求(例如显示文章的浏览次数等,通常这类信息都保存在数据库中,因此需要调用数据库获取数据)通过 uWSGI 传递给 Django,由 Django 来处理,从而完成一次 web 请求。uWSGI 的作用就类似一个桥接器,起到桥梁的作用。
最后,我们完整的组件栈看起来将是这样的:
the web client <-> the web server <-> the socket <-> uwsgi <-> Django
对于 Django 部署而言,Nginx 和 uWSGI 是不错的选择,但它们并非唯一的选择,也不是“官方”选择。对于它们两个,都有不错的替代品1。
准备篇
本文基于 Linode Ubuntu 14.04
更新系统:
sudo apt-get update && sudo apt-get upgrade
安全起见,创建一个 `django` 用户:
```
adduser django
```
如果需要,可以将 `django` 用户添加到 `sudo` 组以赋予 `root` 权限:
```
adduser django sudo
```
安装篇
安装 Nginx
sudo apt-get install nginx
关于 Nginx 的更多介绍、安装和配置参考这篇文章:Linode 从配置到优化
安装 uWSGI
sudo apt-get install python-dev #不安装这个,下面的安装可能会失败
sudo apt-get install uwsgi
uwsgi --version
如果还没有安装 pip 的话,把 pip 安装上:
sudo apt-get install python-pip
安装 virtualenv 和 virtualenvwrapper
virtualenv 和 virtualenvwrapper 用于创建独立的 Python 环境,它们可以帮助我们更好地处理依赖性、版本和权限问题。例如,我们的项目 A 依赖于 Django 1.3 版本,而项目 B 依赖于 Django 1.0 版本。其中 virtualenvwrapper 相当于一个 virtualenv 组件,可以帮助我们更优雅地在虚拟环境中工作。
执行下面的命令进行安装:
sudo pip install virtualenv virtualenvwrapper
进一步针对 virtualenvwrapper 进行配置:
echo "export WORKON_HOME=~/Env" >> ~/.bashrc
echo "source /usr/local/bin/virtualenvwrapper.sh" >> ~/.bashrc
在当前进程激活 virtualenvwrapper:
source ~/.bashrc
创建虚拟环境
接下来要为我们的 Django 创建虚拟环境,例如创建命名为 sample 的虚拟环境:
mkvirtualenv sample
退出我们的虚拟环境:
deactivate
安装 Django
接下来在我们的虚拟环境中安装 Django:
workon sample #work on a 'sample' virtual environment
pip install django
django-admin --version
deactivate #deactive the 'sample' virtual environment
配置 Nginx
打开 Nginx 的配置文件(例如 /etc/nginx/sites-available/default
)
vi /etc/nginx/sites-available/default
写入以下内容:
server {
listen 80;
server_name example.com www.example.com;
charset utf-8;
location / {
uwsgi_pass 127.0.0.1:8001;
include uwsgi_params;
}
}
校验配置并重启 Nginx:
sudo service nginx configtest && sudo service nginx restart
基础测试
检查 uWSGI 的安装和配置
创建一个 test.py
文件,并写入下面的内容:
# test.py
def application(env, start_response):
start_response('200 OK', [('Content-Type','text/html')])
return [b"Hello World"] # python3
#return ["Hello World"] # python2
启动 uWSGI:
uwsgi --socket :8001 --wsgi-file test.py
注意这里我们使用了 8001 端口,并且,我们已经在 Nginx 的配置文件配置了 Nginx 在 8001 端口与 uWSGI 通信,而对外使用 80 端口。
我们可以通过访问:
来检查我们的安装和配置,如果显示 Hello World
,说明 uWSGI 安装成功。
如果有问题,查看 /var/log/uwsgi.log
报错进行诊断。
同时,你可以试着看看在 http://example.com:8001 的 uswgi 输出——但很有可能它不会正常工作,因为你的浏览器使用 http,而不是 uWSGI,但你应该能够在终端上看到来自 uWSGI 的输出。
初始化一个 Django 项目
进入到想要创建的目录下,例如 cd /var/www/
,执行下面的命令:
workon sample #work on a 'sample' virtual environment
django-admin startproject example
cd example
deactivate
继续上一步命令,为了接下来可以使用 IP 或者 example.com 域名访问项目,我们需要编辑 example 项目中的 example 路径下的 settings.py
文件:
vi ./example/settings.py
找到其中的 ALLOWED_HOSTS = []
在 []
中输入我们的 IP 和域名:
ALLOWED_HOSTS = ['x.x.x.x','example.com']
使用 Unix socket
前面我们使用了一个 TCP 端口 socket,因为它简单些,但事实上,使用 Unix socket 会比端口更好——因为开销更少。我们需要编辑我们的 Nginx 配置文件:
server {
listen 80;
server_name example.com www.example.com;
charset utf-8;
location /static/ {
root /var/www/poker;
}
location / {
uwsgi_pass unix:///path/to/your/example/example.sock;
include uwsgi_params;
}
}
然后重启 Nginx。
sudo service nginx configtest && sudo service nginx restart
创建 uWSGI 配置文件
uWSGI 支持 ini、xml 等多种配置文件,本文以 ini 为例,在 /etc/uwsgi/sites/
目录下新建 example.ini
,添加如下配置:
[uwsgi]
project = example
base = /path/to/your
home = /path/to/your/home
chdir = %(base)/%(project)
home = %(home)/Env/%(project)
module = %(project).wsgi:application
master = true
processes = 2
socket = %(base)/%(project)/%(project).sock
chmod-socket = 664
chown-socket = django:www-data
vacuum = true
注意其中的 socket
的路径要与上面 Nginx 配置文件中的 uwsgi_pass
路径一致。该文件在启动 uWSGI 时自动生成,用于 socket 通信 —— uWSGI 和 Nginx 之间“交流”,详细介绍建议 Google 搜索“Socket 通信原理”相关内容。
启动 uWSGI:
sudo service uwsgi start
ps aux | grep -i uwsgi
在浏览器中尝试访问 http://example.com/。
如果可以看到上面的显示,则说明我们的栈已经可以工作了:
the web client <-> the web server <-> the socket <-> uWSGI <-> Python
如果有问题,可以查看 Nginx 和 uWSGI log 进行诊断。例如 log 路径:
/var/log/uwsgi.log
/var/log/nginx/error.log
关于静态文件
接下来,如果我们访问 http://example.com/admin 可能会看到下面这样的显示:
这是因为我们的 Django 项目找不到所需要静态文件,需要在 settings.py
中指定路径,例如:
echo 'STATIC_ROOT = os.path.join(BASE_DIR, "static/")' >> example/settings.py
运行下面的命令移动静态文件到所指定的路径下:
./manage.py collectstatic
重新访问 http://example.com/admin 会看到显示正常:
使用
python manage.py createsuperuser
命令可以为该 Django 项目创建用户。
“守护”uWSGI 进程
我们需要让 uWSGI 进程始终运行,有两个方法“守护”uWSGI 进程。
方法一:/etc/rc.local
文件
在 `/etc/init/uwsgi.conf` 文件中写入下面的内容:
```
description "uWSGI"
start on runlevel [2345]
stop on runlevel [06]
respawn
env UWSGI=/usr/local/bin/uwsgi
env LOGTO=/var/log/uwsgi.log
exec $UWSGI --master --emperor /etc/uwsgi/sites --die-on-term --uid django --gid www-data --logto $LOGTO
```
注意修改其中的 uWSGI bin 路径(`whereis uwsgi`)、uWSGI 配置文件路径,uid(进程一旦启动后的用户 id) 和 gid(进程一旦启动后的组 id)。
上面的 `uwsgi.conf` 配置会以 Emperor 模式启动 uWSGI,也就是说会根据 `/etc/uwsgi/sites` 路径下的配置文件执行,并对其进行监控,一旦配置文件被改动,相应进程即自动重启。
重启 uWSGI 进程:
```
sudo service uwsgi restart
ps aux | grep -i uwsgi
```
接下来,
rc.local
文件。编辑 /etc/rc.local
然后在 exit 0
行前添加:/usr/local/bin/uwsgi --emperor /etc/uwsgi/sites --uid root --gid www-data --daemonize /var/log/uwsgi.log
应该就这样。
方法二:借助于 Supervisor
安装 Supervisor 软件包:
sudo pip install supervisor
生成 Supervisor 默认配置文件,例如放在 /etc/supervisor/supervisord.conf
路径中:
sudo echo_supervisord_conf > /etc/supervisor/supervisord.conf
打开 supervisor.conf
在最底部添加(每一行前面不要有空格,防止报错):
[program:example]
command=/path/to/bin/uwsgi --master --emperor /etc/uwsgi/sites --die-on-term --uid django --gid www-data --logto /var/log/uwsgi.log
startsecs=0
stopwaitsecs=0
autostart=true
autorestart=true
注意修改 [program:example]
中的 example
为你的项目名称;另外,command
中的 /path/to/bin/uwsgi
应为 uWSGI bin 路径(通过 whereis uwsgi
命令查看)。
启动 Supervisor:
sudo supervisord -c /etc/supervisor/supervisord.conf
如果以后修改了 supervisord.conf
文件,我们需要通知 Supervisor 使新配置文件生效,使用下面的命令:
supervisorctl update
查看进程:
supervisorctl status
关于 Supervisor 的其他实例,可以参考 Linode 从配置到优化
更新历史:
2017/11/16:初稿
2017/11/19:发布
2017/11/22:更新 virtualenv 部分
最近更新: