在 Ubuntu 上部署 Nginx + Django + uWSGI

 2017-11-16    东京    晴 /django/deploy.html django python, django, ubuntu, nginx, uwsgi

本文最近更新于 2018 年 06 月 26 日 ,横滨

概念和设计

Nginx 是什么?

发音为 engine-x,是一个免费开源并且高性能的 HTTP 服务器和反向代理,还是一个 IMAP/POP3 代理服务器,相较于 Apache、lighttpd 具有占有内存少,稳定性高等优势。

uWSGI 是什么?

一个 Web 服务器,它实现了 WSGI 协议、uwsgi、http 等协议。(Nginx 中 HttpUwsgiModule 的作用是与 uWSGI 服务器进行交换。)

WSGI/uwsgi/uWSGI 这三个概念的区分:

关于 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 端口。

我们可以通过访问:

http://example.com/

来检查我们的安装和配置,如果显示 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 ```

接下来,

我们要在系统启动时即运行 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 部分


  1. Django 官网的部署文档,点击这里 [return]
关于作者
Jason,80 后,现从事通信行业。安卓玩家一个人的书房朗读者麦子
 英语入门到放弃
 jsntn
 jasonwtien
 jasonwtien
更多…… /about.html

最近更新: