守望的麦子

在 Ubuntu 上部署 Nginx + Django + uWSGI

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

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

Python 是一门优雅而健壮的编程语言,它继承了传统编译语言的强大性和通用性,同时也借鉴了简单脚本和解释语言的易用性。

Python 编程简明教程 https://wheat.at/python/

概念和设计

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 的作用就类似一个桥接器,起到桥梁的作用。

最后,我们完整的组件栈看起来将是这样的:

1 the web client <-> the web server <-> the socket <-> uwsgi <-> Django

对于 Django 部署而言,Nginx 和 uWSGI 是不错的选择,但它们并非唯一的选择,也不是“官方”选择。对于它们两个,都有不错的替代品1

准备篇

本文基于 Linode Ubuntu 14.04

更新系统:

1 sudo apt-get update && sudo apt-get upgrade

安全起见,创建一个 `django` 用户:

1 adduser django

如果需要,可以将 `django` 用户添加到 `sudo` 组以赋予 `root` 权限:

1 adduser django sudo

安装篇

安装 Nginx

1 sudo apt-get install nginx

关于 Nginx 的更多介绍、安装和配置参考这篇文章:Linode 从配置到优化

安装 uWSGI

1 sudo apt-get install python-dev #不安装这个,下面的安装可能会失败
2 sudo apt-get install uwsgi
3 uwsgi --version

如果还没有安装 pip 的话,把 pip 安装上:

1 sudo apt-get install python-pip

安装 virtualenv 和 virtualenvwrapper

virtualenv 和 virtualenvwrapper 用于创建独立的 Python 环境,它们可以帮助我们更好地处理依赖性、版本和权限问题。例如,我们的项目 A 依赖于 Django 1.3 版本,而项目 B 依赖于 Django 1.0 版本。其中 virtualenvwrapper 相当于一个 virtualenv 组件,可以帮助我们更优雅地在虚拟环境中工作。

执行下面的命令进行安装:

1 sudo pip install virtualenv virtualenvwrapper

进一步针对 virtualenvwrapper 进行配置:

1 echo "export WORKON_HOME=~/Env" >> ~/.bashrc
2 echo "source /usr/local/bin/virtualenvwrapper.sh" >> ~/.bashrc

在当前进程激活 virtualenvwrapper:

1 source ~/.bashrc

创建虚拟环境

接下来要为我们的 Django 创建虚拟环境,例如创建命名为 sample 的虚拟环境:

1 mkvirtualenv sample

退出我们的虚拟环境:

1 deactivate

安装 Django

接下来在我们的虚拟环境中安装 Django:

1 workon sample #work on a 'sample' virtual environment
2 pip install django
3 django-admin --version
4 deactivate #deactive the 'sample' virtual environment

配置 Nginx

打开 Nginx 的配置文件(例如 /etc/nginx/sites-available/default

1 vi /etc/nginx/sites-available/default

写入以下内容:

 1 server {
 2     listen 80;
 3     server_name example.com www.example.com;
 4     charset utf-8;
 5 
 6     location / {
 7         uwsgi_pass 127.0.0.1:8001;
 8         include uwsgi_params;
 9     }
10 }

校验配置并重启 Nginx:

1 sudo service nginx configtest && sudo service nginx restart

基础测试

检查 uWSGI 的安装和配置

创建一个 test.py 文件,并写入下面的内容:

1 # test.py
2 def application(env, start_response):
3     start_response('200 OK', [('Content-Type','text/html')])
4     return [b"Hello World"] # python3
5     #return ["Hello World"] # python2

启动 uWSGI:

1 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/,执行下面的命令:

1 workon sample #work on a 'sample' virtual environment
2 django-admin startproject example
3 cd example
4 deactivate

继续上一步命令,为了接下来可以使用 IP 或者 example.com 域名访问项目,我们需要编辑 example 项目中的 example 路径下的 settings.py 文件:

1 vi ./example/settings.py

找到其中的 ALLOWED_HOSTS = [][] 中输入我们的 IP 和域名:

1 ALLOWED_HOSTS = ['x.x.x.x','example.com']

使用 Unix socket

前面我们使用了一个 TCP 端口 socket,因为它简单些,但事实上,使用 Unix socket 会比端口更好——因为开销更少。我们需要编辑我们的 Nginx 配置文件:

 1 server {
 2         listen      80;
 3         server_name example.com www.example.com;
 4         charset     utf-8;
 5 
 6         location /static/ {
 7                 root /var/www/poker;
 8         }
 9 
10         location / {
11                 uwsgi_pass unix:///path/to/your/example/example.sock;
12                 include uwsgi_params;
13         }
14 }

然后重启 Nginx。

1 sudo service nginx configtest && sudo service nginx restart

创建 uWSGI 配置文件

uWSGI 支持 ini、xml 等多种配置文件,本文以 ini 为例,在 /etc/uwsgi/sites/ 目录下新建 example.ini,添加如下配置:

 1 [uwsgi]
 2 project = example
 3 base = /path/to/your
 4 home = /path/to/your/home
 5 
 6 chdir = %(base)/%(project)
 7 home = %(home)/Env/%(project)
 8 module = %(project).wsgi:application
 9 
10 master = true
11 processes = 2
12 
13 socket = %(base)/%(project)/%(project).sock
14 chmod-socket = 664
15 chown-socket = django:www-data
16 vacuum = true

注意其中的 socket 的路径要与上面 Nginx 配置文件中的 uwsgi_pass 路径一致。该文件在启动 uWSGI 时自动生成,用于 socket 通信 —— uWSGI 和 Nginx 之间“交流”,详细介绍建议 Google 搜索“Socket 通信原理”相关内容。

启动 uWSGI:

1 sudo service uwsgi start
2 ps aux | grep -i uwsgi

在浏览器中尝试访问 http://example.com/。

如果可以看到上面的显示,则说明我们的栈已经可以工作了:

1 the web client <-> the web server <-> the socket <-> uWSGI <-> Python

如果有问题,可以查看 Nginx 和 uWSGI log 进行诊断。例如 log 路径:

1 /var/log/uwsgi.log
2 /var/log/nginx/error.log

关于静态文件

接下来,如果我们访问 http://example.com/admin 可能会看到下面这样的显示:

这是因为我们的 Django 项目找不到所需要静态文件,需要在 settings.py 中指定路径,例如:

1 echo 'STATIC_ROOT = os.path.join(BASE_DIR, "static/")' >> example/settings.py

运行下面的命令移动静态文件到所指定的路径下:

1 ./manage.py collectstatic

重新访问 http://example.com/admin 会看到显示正常:

使用 python manage.py createsuperuser 命令可以为该 Django 项目创建用户。

“守护”uWSGI 进程

我们需要让 uWSGI 进程始终运行,有两个方法“守护”uWSGI 进程。

方法一:/etc/rc.local 文件

在 `/etc/init/uwsgi.conf` 文件中写入下面的内容:

1 description "uWSGI"
2 start on runlevel [2345]
3 stop on runlevel [06]
4 respawn
5 
6 env UWSGI=/usr/local/bin/uwsgi
7 env LOGTO=/var/log/uwsgi.log
8 
9 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 进程:

1 sudo service uwsgi restart
2 ps aux | grep -i uwsgi

接下来,

我们要在系统启动时即运行 uWSGI,对于许多系统来说,最简单的方式是使用 rc.local 文件。编辑 /etc/rc.local 然后在 exit 0 行前添加:

1 /usr/local/bin/uwsgi --emperor /etc/uwsgi/sites --uid root --gid www-data --daemonize /var/log/uwsgi.log

应该就这样。

方法二:借助于 Supervisor

安装 Supervisor 软件包:

1 sudo pip install supervisor

生成 Supervisor 默认配置文件,例如放在 /etc/supervisor/supervisord.conf 路径中:

1 sudo echo_supervisord_conf > /etc/supervisor/supervisord.conf

打开 supervisor.conf 在最底部添加(每一行前面不要有空格,防止报错):

1 [program:example]
2 command=/path/to/bin/uwsgi --master --emperor /etc/uwsgi/sites --die-on-term --uid django --gid www-data --logto /var/log/uwsgi.log
3 startsecs=0
4 stopwaitsecs=0
5 autostart=true
6 autorestart=true

注意修改 [program:example] 中的 example 为你的项目名称;另外,command 中的 /path/to/bin/uwsgi 应为 uWSGI bin 路径(通过 whereis uwsgi 命令查看)。

启动 Supervisor:

1 sudo supervisord -c /etc/supervisor/supervisord.conf

如果以后修改了 supervisord.conf 文件,我们需要通知 Supervisor 使新配置文件生效,使用下面的命令:

1 supervisorctl update

查看进程:

1 supervisorctl status

关于 Supervisor 的其他实例,可以参考 Linode 从配置到优化

更新历史:

2017/11/16:初稿
2017/11/19:发布
2017/11/22:更新 virtualenv 部分

  1. Django 官网的部署文档,点击这里 

关于作者
麦子,80 后,现从事通信行业。安卓玩家一个人的书房朗读者。
MRJENGLISH
jsntn
jasonwtien
jasonwtien
更多…… /about.html

最近更新: