How to deploy django with wsgi


本文介绍如何部署nginx+uWSGI+Django的生产环境。

通过uWSGI和nginx部署你的Django项目

This tutorial is aimed at the Django user who wants to set up a production web server. It takes you through the steps required to set up Django so that it works nicely with uWSGI and nginx. 原文链接

本教程的目标人群是那些希望部署自己的web server的Django用户。本教程会指引你按照步骤使Django与uWSGI和nginx很好的工作在一起。本教程对三个软件都有讲解,并提供了一整套工作流程。

Django使一种Python的high-level web框架,提倡快速开发与简洁的设计。

nginx使一款免费、开源、高性能的HTTP服务器,同时也可以作为反向代理或IMAP/POP3服务器

关于本教程

这只是一篇教程,并非是为了作为引用说明来编写的。请不要过分追求部署过程中过于详尽的细节。

对于Django部署来说,nginx和uWSGI都是很好的选择,但这并不是必须的,或者说官方的。这两款软件都非常棒,花费时间去研究他们是很值得的。

这里我们提供的Django部署方式是一个很好的方法,但并不是唯一的选择。在某些特定情况,这也许并不是最好的选择。

然而我们提供的方法将会是最可靠、最简单的方法。本教程所涵盖的内容将想你介绍一些你在使用任何软件部署Django时都需要了解的方法和策略。当像你展现了设置过程,并描述了进行这些设置的前提之后,你将掌握采用其他方法部署Django所需要的基本知识。

这篇文章针对你正在使用的系统做了部分假设。

我们假设你使用的是Unix-like系统,并且包含包管理系统。但是如果你想问像“Mac OSX中有什么包管理系统的替代品?”这样的问题,你可能很容易找到相关的答案。

并且,本教程针对1.4或更新版本的Django,这些版本的Django会在工程目录下自动创建wsgi模块。The instructions会针对之前的版本作介绍,你可能需要自己生成wsgi模块,并且Django工程的目录也会有些许不同。

为什么要这样做

Web服务器要面对外网的流量,它可以通过文件系统提供HTML、images、CSS等文件。但是Web服务器不能和Django应用直接通信,它需要某些程序运行这些Django应用,为其提供客户端传来的请求,并返回Django应用的回应。

Web Server Gateway Interface,也就是WSGI就是为了这个而存在的。WSGI is a Python standard。

uWSGI是WSGI的一种实现。在本教程中我们将通过设置uWSGI来创建一个Unix socket,以WSGI协议来向web服务器返回回应。最后我们将按照如下结构完成我们的部署。

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

开始前要做的事

virtualenv

确保你在virtualenv中按照我们所需的软件(我们之后将介绍如何在系统范围内安装uwsgi)。

1
2
3
virtualenv uwsgi-tutorial
cd uwsgi-tutorial
source bin/activate

Django

在virtualenv环境中安装Django,创建新工程,并切换到工程目录中。

1
2
3
pip install Django==1.10
django-admin.py startproject mysite
cd mysite

关于域名和端口

在本教程中我们可以将域名设置为example.com。请按照你的实际环境配置。

在本教程全文中我们都将使用8000来作为我们发布的web server的端口,就像Django默认做的那样。你当然可以按照自己的意愿进行指定,但是我选这个端口的原因是一般的web server并不会选择8000这个端口,这样可以避免很多冲突。

uWSGI的基本安装与配置

在virtualenv环境中安装uWSGI

1
pip install uwsgi

当然还有其他方式可以安装uWSGI,但是这种方法可以达到最好效果。记住你需要安装必须的Python开发组件。我们使用的环境是Debian,或基于Debian的Ubuntu,那么你需要安装pythonX.Y-dev,这里的X.Y是你所使用的的Python的版本。

基础测试

创建一个名为test.py的文件:

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

需要注意的是Python3需要传入bytes()对象。

运行uWSGI:

1
uwsgi --http :8000 --wsgi-file test.py

各个选项的含义:

上面一系列操作后,在浏览器访问8000端口,将得到hello world的回应:

1
http://example.com:8000

这说明如下的连接是联通的:

1
the web client <-> uWSGI <-> Django

测试你的Django工程

现在我们希望使用uWSGI再做一次上面的事情,但这次我们要使用Django站点来替换test.py的角色。

如果你还没有这么做过,那么首先要确保你的mysite工程能够正常工作:

1
python manage.py runserver 0.0.0.0:8000

之后我们通过如下命令运行uWSGI

1
uwsgi --http :8000 --module mysite.wsgi  # 需要在mysite工程目录下执行该命令

通过浏览器访问服务器,如果站点正常出现,则证明uWSGI与Django合作的设置已经成功了,连接过程如下:

1
the web client <-> uWSGI <-> Django

但正常情况下,我们不会让浏览器直接和uWSGI通信。和浏览器通信这个工作应该由web server来完成,就像中间人一样。

nginx基础

安装nginx

1
2
sudo apt-get install nginx
sudo /etc/init.d/nginx start

现在可以检查nginx是否已经成功在80端口提供了服务。当你看到了从nginx返回的“Welcome to nginx!”之后,则代表配置完成了。

1
the web client <-> the web server

如果80端口已经被其他服务占用,那你就需要重新配置nginx令其使用其他端口了。在本教程中,我们使用8000端口。

为你的站点配置nginx

为了完成配置工作,你需要获取nginx目录下的uwsgi_params文件。你可以在nginx的conf目录中找到它,当然你可以在github上下载该文件:https://github.com/nginx/nginx/blob/master/conf/uwsgi_params

将该文件复制到你的Django工程根目录下,等一会我们会在nginx配置文件中引用它的所在路径。

现在创建一个名为mysite_nginx.conf的文件,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# mysite_nginx.conf

# the upstream component nginx needs to connect to
upstream django {
    # server unix:///path/to/your/mysite/mysite.sock; # for a file socket
    server 127.0.0.1:8001; # for a web port socket (we'll use this first)
}

# configuration of the server
server {
    # the port your site will be served on
    listen      8000;
    # the domain name it will serve for
    server_name .example.com; # substitute your machine's IP address or FQDN
    charset     utf-8;

    # max upload size
    client_max_body_size 75M;   # adjust to taste

    # Django media
    location /media  {
        alias /path/to/your/mysite/media;  # your Django project's media files - amend as required
    }

    location /static {
        alias /path/to/your/mysite/static; # your Django project's static files - amend as required
    }

    # Finally, send all non-media requests to the Django server.
    location / {
        uwsgi_pass  django;
        include     /path/to/your/mysite/uwsgi_params; # the uwsgi_params file you installed
    }
}

这个配置文件将告知nginx在处理Django有关的请求时,到指定目录中查找media和static文件。对于较大规模的工程,我们建议使用一个单独的server去处理static和media文件,由另一个来处理Django应用,但现在这些都不是问题。

在/etc/nginx/sites-enabled中创建软链接。

1
sudo ln -s ~/path/to/your/mysite/mysite_nginx.conf /etc/nginx/sites-enabled/

部署static文件

在运行nginx之前,你必须将Django的static文件都收集到static文件夹中。首先编辑mysite/settings.py,添加内容:

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

然后运行

1
python manage.py collectstatic

nginx基础测试

重启nginx

1
sudo /etc/init.d/nginx restart

现在验证服务器是否正常工作,并且在/path/to/your/project/project/media directory目录下添加一个文件media.png,然后通过http://example.com:8000/media/media.png查看是否能正常访问该文件。

如果不是直接重启nginx,而是先停止然后重新启动nginx的话,你可能会在发生错误时得到提示。

nginx、uWSGI和test.py

我们让nginx和返回“Hello World”的test.py程序联动起来。

1
uwsgi --socket :8001 --wsgi-file test.py

这和之前的命令相同,只是这次我们换了一个新的端口。

nginx刚才已经通过配置使其通过8001端口来和uWSGI通信了,并且向外部开放8000端口以提供服务。此时可以访问http://example.com:8000。 工作流程如下:

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

此外,你还可以通过http://example.com:8001来访问uwsgi提供的服务,但此时你并不会得到所期望的内容,因为浏览器以http协议通信,而不是WSGI,但是你可以在运行uWSGI的终端看到部分输出。

使用Unix sockets代替端口

目前为止,我们一直使用TCP socket,因为它非常简单,但事实上使用Unix sockets是个更好的选择,因为它的额外开销更小。

编辑mysite_nginx.conf,按照如下内容进行修改:

1
2
server unix:///path/to/your/mysite/mysite.sock;
# server 127.0.0.1:8001;

重启nginx,并重新运行uWSGI:

1
uwsgi --socket mysite.sock --wsgi-file test.py

这一次socket选项说明了uWSGI通过哪个文件来使用Unix sockets。现在请再次访问http://example.com:8000以查看结果。

如果出现了问题

检查nginx的错误日志/var/log/nginx/error.log。如果你看到了如下内容:

1
connect() to unix:///path/to/your/mysite/mysite.sock failed (13: Permission denied)

那就意味着你需要在使用uWSGI时指定权限以便nginx能够访问.sock文件。

1
2
uwsgi --socket mysite.sock --wsgi-file test.py --chmod-sock=666 # (very permission)
# uwsgi --socket mysite.sock --wsgi-file test.py --chmod-sock=664 # (more sensible)

你也可以将你的用户加入到nginx的用户组中(可能时www-data),或者vice-versa,以使nginx能够访问你创建的.sock文件。

让nginx在运行的终端持续输出日志是一个比较好的选择,因为这样你可以在出错时很快速的定位到问题所在。

在uwsgi和nginx的协助下运行Django应用

让我们通过下面的命令运行Django应用。

1
uwsgi --socket mysite.sock --module mysite.wsgi --chmod-socket=664

现在uWSGI和nginx应该已经可以提供Django应用的内容了。

在.ini文件中配置uWSGI

我们可以把uWSGI所使用的参数放到一个配置文件中,然后在uWSGI启动时指定该文件,这样可以简化很多操作。

创建名为mysite_uwsgi.ini的文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# mysite_uwsgi.ini file
[uwsgi]

# Django-related settings
# the base directory (full path)
chdir           = /path/to/your/project
# Django's wsgi file
module          = project.wsgi
# the virtualenv (full path)
home            = /path/to/virtualenv

# process-related settings
# master
master          = true
# maximum number of worker processes
processes       = 10
# the socket (use the full path to be safe
socket          = /path/to/your/project/mysite.sock
# ... with appropriate permissions - may be needed
# chmod-socket    = 664
# clear environment on exit
vacuum          = true

然后如下运行uwsgi

1
uwsgi --ini mysite_uwsgi.ini

我们Django站点又一次如期望的那样运行了。

在系统环境中安装uWSGI

目前为止,我们的uWSGI只安装在了virtualenv环境中,我们需要在系统环境中安装uWSGI来达到实际部署的目的。

退出你的virtualenv环境:

1
deactivate

然后在系统环境中安装uWSGI

1
2
sudo pip install uwsgi
# pip install http:ss/projects.unbit.it/downloads/uwsgi-lts.tar.gz  # 安装长期支持的版本

uWSGI wiki站点描述了多种安装步骤。在安装前请选择好版本,并以对应的方式进行安装。

检查你的uWSGI是否能正常工作:

1
uwsgi --ini mysite_uwsgi.ini

Emperor模式

uWSGI可以运行在Emperor Mode中,在这种模式下,它将见识uWSGI配置文件所在目录,然后为每个以发现的配置文件生成一个实例(vassals/奴隶)。

无论何时修改配置文件,emperor都会自动的重新启动vassal。

1
2
3
4
sudo mkdir /etc/uwsgi
sudo mkdir /etc/uwsgi/vassals  # 创建vassal目录
sudo ln -s /path/to/your/mysite/mysite_uwsgi.ini /etc/uwsgi/vassals/
uwsgi --emperor /etc/uwsgi/vassals --uid www-data --gid www-data

你可能会需要以root权限运行uWSGI,各个选项含义:

检查站点是否仍然正常运行。

设置uWSGI开机启动

最后一步,我们要让uWSGI开机时自启动。

对于很多系统,最简单的设置方式是使用rc.local

编辑rc.local并在exit 0之前添加如下内容:

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

更多设置

一定要记得,这只是一篇让你入门的教程。如果想在生产环境中使用nginx和uWSGI,你需要阅读相关的详细文档。

nginx和uWSGI都有很有爱的社区,你可以在这些社区中找到关于配置和使用的建议。

nginx

如果你希望是nginx在生产环境中使用80端口,而不是8000端口,那么你在这篇教程中将不会找到答案。

你应该同时配置为非Django文件配置单独的服务。例如通过uWSGI访问static文件是件效率很低的事情。相反,你应该让nginx绕过uWSGI直接访问它们。

uWSGI

uWSGI支持多种配置的方式。具体可以查看uWSGI’s documentation examples

这篇教程中只提到了一部分uWSGI的选项,其他你应该了解的在生产环境中部署Django的参数如下:

1
2
3
4
5
6
env = DJANGO_SETTINGS_MODULE=mysite.settings # set an environment variable
safe-pidfile = /tmp/project-master.pid # create a pidfile
harakiri = 20 # respawn processes taking more than 20 seconds
limit-as = 128 # limit the project to 128 MB
max-requests = 5000 # respawn processes after serving 5000 requests
daemonize = /var/log/uwsgi/yourproject.log # background the process & log