使用uWSGI和nginx来搭建Django应用

近期用python的Django写了一个小程序,想部署到服务器上面。网上搜了一下,发现所有使用nginx的解决方案基本上都是来源于这篇文章。所以就把这篇文章翻译了一下,加深自己的理解。

原文地址

这篇教程适用于想在生产服务器上搭建应用的Django使用者。只需要几部就可以让你用nginx和uWSGI搭建一个Django应用。它涵盖所有三个组件,提供一个完整的Web应用程序和服务器软件搭建方案。(It covers all three components, providing a complete stack of web application and server software.)

DJango是一个高质量的Web开发框架,鼓励快速、简介、务实的设计(pragmatic design)

nginx是一款免费、开源、高并发访问的HTTP服务器和反向代理以及IMAP/POP3 代理服务器

这篇教程的一些笔记

这只是一篇入门教程,它的目的不是提供一个全面的参考指南,不会为开发者提供一个详细的开发指南


nginx和uWSGI是DJango开发者的好选择,但不是唯一的或者说“官方”的选择。每一种方案都有优点,值得你去尝试。


这种方法也许是一种好的方法,但不是唯一的方法,从某些方面来说他甚至不是最好的方法


然而,这是一个可靠和简单的方法,这里涵盖的材料将向您介绍概念和程序,您将需要熟悉用于部署Django的任何软件。


在熟悉了这些配置和这些步骤之后,如果你能够探索其他的方法也能达到相同的目的


这篇教程假定你的系统已经完成了一些配置


假定你使用的是进行可以进行包管理的Unix系列的系统。然而如果你有类似“Mac OS系统该怎样配置”这样的问题,你会很容易找到这类问题的答案。


这篇教程使用的是Django1.4或者更新版本,它能够在你新建的项目中创建一个wsgi模块。如果你使用的是更早期的版本的话,你需要自己去创建一个wsgi模块

概念

一个Web服务面向世界。它能够直接从系统提供一些文件(比如说HTML、图像、CSS等等)。然而,他不能直接从Django中获取,这需要一些东西来运行这些应用,比如来自客户端的请求和响应。



一个Web服务网关接口==WSGI,就是干这件事情的。WSGI是一个python的标准。

uWSGI是WSGI的一个实现工具,在这篇教程中我们将设置uWSGI从而创建一个Unix socket服务。服务响应通过WSGi协议来对传给web应用。最后,我们完成的主要原理图如下:

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

在开始设置uWSGI之前需要做的准备

virtualenv

确保你在虚拟环境下安装软件(稍后我们将介绍在原生系统中安装uWSGI)

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

在虚拟环境下安装Django,创建一个项目,并且通过cd来进入这个项目

1
2
3
 pip install Django
django-admin.py startproject mysite
cd mysite
关于域名和端口

教程中默认你的域名为:example.com 你可以替换成你的域名或者Ip地址


跟Django默认使用的端口一样,在这里我们将始终使用8000端口作为服务的对外端口。当然你也可以使用其他的你想用的端口,但是我使用这个端口并没有和之前的配置发生任何冲突。


基本uWSGI安装和配置

在你的虚拟环境中安装uWSGI

pip install uwsgi

当然还有其他的方式安装,不过这种方法是最好的一种。注意你必须要安装好相关的依赖包。例如在Debain或者像Ubuntu这样与Debain类似的系统中,你就需要安装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:

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

选项的意思:

http :8000 使用http的8000端口
wsgi-file test.py 加载指定文件

当你使用浏览器访问8000端口的时候应该会看到“Hello,World”的字样

http://example.com:8000

检查如果是这样的话,说明一下这些部分没有问题了;

the web client <-> uWSGI <-> Python

测试你的Django应用

现在uWSGI做着同样的事情,只不过运行的是Django应用而不是test.py模块
如果你还没有做到这里,确保你的mysite项目能够工作:

python manage.py runserver 0.0.0.0:8000

如果他能够正常工作,就用uWSGI运行它:

uwsgi –http :8000 –module mysite.wsgi

  • module mysite.wsgi 加载指定的uWSGI模块

在你的浏览器中查看,如果服务能够正常运行,就意味着uWSGI能够从虚拟环境中运行Django应用,现在就到了这一步:

the web client <-> uWSGI <-> Django

我们一般不让客户端直接和uWSGI联系。这是Web服务器的工作,在其中扮演者中间人的角色。

Nginx基本配置

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

现在通过浏览器访问网站的80端口,你应该会看到nginx的一条信息:Welcome to nginx!。这就说明一下环节是打通的:

the web client <-> the web server

如果其他应用已经占据了80端口,但是你还是想使用nginx服务,你就要更改nginx监听另外的端口。在这篇教程中,我们将使用8000端口。

在你的网站中配置nginx

你需要uwsgi_params 文件,它能够然nginx又uWSGI支配,你可以从这里下载

将它复制到你的项目目录下,接下来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,另外一个服务处理Django请求,但在这里,这样刚刚好。

创建一个链接到/etc/nginx/sites-enabled让nginx能够识别它:

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

部署静态文件

在运行nginx之前,你要在static目录下收集Django用到的静态文件,首先你要在mysite/settings.py中加上:

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

然后运行

python manage.py collectstatic

基本nginx测试

重启nginx服务

sudo /etc/init.d/nginx restart
为了检查media文件是否正确,在/path/to/your/project/project/media directory添加一张图片media.png ,然后访问http://example.com:8000/media/media.png 。如果起作用,至少说明nginx能够提供服务。
你可以不重启nginx服务,而是先关掉他,然后再重新打开,这样你就会发现一些问题。

nginx、uWSGI和test.py

让我们将nginx与‘hello word’的test.py应用连接起来

uwsgi –socket :8001 –uwsgi-file test.py #刚才nginx对内的端口是8001

除了这个部分,这次跟前面一样:

socket :8001 #使用uwsgi协议,8001端口

nginx同时也配置成在那个端口与uWSGI相连接,外网连接的端口是8000端口

http://example.com:8000/

点击一下,说明我们现在所处的阶段是:

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

于此同时你也可以访问uWSGI的对外端口:8000,但是有非常大的可能他是不会工作的因为你的浏览器使用的是http而不是uWSGI,虽然你能够在你的终端上看到来自uWSGI的输出

使用Unix sockets来代替端口

现在为止我们已经使用了TCP端口(因为它很简单),但是事实上用Unix sockets比端口更好——他会减少很多麻烦(here’s less overhead)

编辑文件 mysite_nginx.conf ,把它改成这样:

1
2
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)

然后重启nginx服务

重新运行uWSGI:

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

这个时候socket告诉uWSGI他要用哪个文件

在浏览器里面再次打开连接http://example.com:8000/

如果不起作用

检查你的nginx日志文件(/var/log/nginx/error.log),如果你看到下面的错误:

connect() to unix:///path/to/your/mysite/mysite.sock failed (13: Permissiondenied)

也许你需要管理权限允许nginx使用socket

尝试:

uwsgi –socket mysite.sock –wsgi-file test.py –chmod-socket=666 # (very permissive)

或者:

uwsgi –socket mysite.sock –wsgi-file test.py –chmod-socket=664 # (more sensible)

你可能还需要添加你的用户到nginx的用户群中去(可能是类似www-data这样的),这样nginx就可以读写你的socket了

nginx的日志文件值得你在终端一直打开,这样你就可以很容易排除错误

使用uWSGI和nginx来运行Django应用

让我们来运行我们的Django 应用:

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

现在uWSGI和nginx应该不只是显示‘hello,world’了,而是你的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:

uwsgi –ini mysite_uwsgi.ini # the –ini option is used to specify a file
(注意这里同时也要给socket加权限才可以)

测试Django应用是否运行成功

在本地系统中运行uWSGI

到目前为止,uWSGI只能够在我们的虚拟环境下运行,为了开发我们要将他放在本地系统中

退出虚拟环境:

deactivate

在本地系统安装uWSGI:

1
2
3
4
sudo pip install uwsgi

# Or install LTS (long term support).
pip install http://projects.unbit.it/downloads/uwsgi-lts.tar.gz

uWSGI的Wiki中有一个安装说明,在安装之前,值得你去考虑使用哪个版本并且使用最合适的方式去安装它

再一次检查你能够跟之前一样运行uWSGI:

uwsgi –ini mysite_uwsgi.ini # the –ini option is used to specify a file

Emperor 模式

uWSGI能够运行emperor模式。再这个模式下,能够监视uWSGI的配置文件,并且为每种情况产生样例(will spawn instances (‘vassals’) for each one it finds.)

每次只要配置文件修改了,emperor就会自动重启服务:

1
2
3
4
5
6
7
# create a directory for the vassals
sudo mkdir /etc/uwsgi
sudo mkdir /etc/uwsgi/vassals
# symlink from the default config directory to your config file
sudo ln -s /path/to/your/mysite/mysite_uwsgi.ini /etc/uwsgi/vassals/
# run the emperor
uwsgi --emperor /etc/uwsgi/vassals --uid www-data --gid www-data

你可能要用到超级用户的权限来运行:

sudo uwsgi –emperor /etc/uwsgi/vassals –uid www-data –gid www-data

选项的含义:

  • emperor : 去哪里找到服务
  • uid: 启动的用户id
  • gid:启动的群id

检查一下站点服务,它应该能够成功运行

让uWSGI自启动

最后一步就是让以上这些在开机的时候能够自启动

你只需要编辑/etc/rc.local 并添加:

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

更加全面的ini配置文件:

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
35
[uwsgi]
uid = www # 设置启动的用户组
gid = www

chdir = /alidata/www/fdcode/devops # 指定项目目录

module = wsgi # 加载wsgi.py模块

master = true # 启动主进程,一般设置true或 1

processes = 2 # 设置启动的进程数

listen = 100 # 设置socket监听队列大小,超出队列的请求将会被拒绝

;socket = /var/run/uwsgi/uwsgi.sock # 设置socket监听

socket = :9090 # 设置端口监听

pidfile = /var/run/uwsgi/uwsgi.pid # 指定pid文件

vacuum = true # 当服务器退出的时候自动删除unixsocket文件和pid文件。

enable-threads = true # 允许用内嵌的语言启动线程。这将允许你在app程序中产生一个子线程

buffer-size = 32768 # 设置用于uwsgi包解析的内部缓存区大小为64k。默认是4k。

reload-mercy = 8 # 设置在平滑的重启(直到接收到的请求处理完才重启)一个工作子进程中,等待这个工作结束的最长秒数。这个配置会使在平滑地重启工作子进程中,如果工作进程结束时间超过了8秒就会被强行结束(忽略之前已经接收到的请求而直接结束)

max-requests = 5000 # 为每个工作进程设置请求数的上限。当一个工作进程处理的请求数达到这个值,那么该工作进程就会被回收重用(重启)。你可以使用这个选项来默默地对抗内存泄漏

limit-as = 512 # 通过使用POSIX/UNIX的setrlimit()函数来限制每个uWSGI进程的虚拟内存使用数。这个配置会限制uWSGI的进程占用虚拟内存不超过256M。如果虚拟内存已经达到256M,并继续申请虚拟内存则会使程序报内存错误,本次的http请求将返回500错误。

harakiri = 60 # 一个请求花费的时间超过了这个harakiri超时时间,那么这个请求都会被丢弃,并且当前处理这个请求的工作进程会被回收再利用(即重启)

daemonize = /alidata/log/uwsgi/uwsgi_server.log # 使进程在后台运行,并将日志打到指定的日志文件或者udp服务器

总结一波

在一开始的流程图中各段的实现:

  • the web client <-> the web server
    通过nginx服务器,监听的是8000端口。配置文件:mysite_nginx.conf

  • the web server <-> the socket
    nginx配置文件指定socket, 在配置文件中添加sock路径 我这里是htmltest/htmltest.sock 配置文件:mysite_nginx.conf

  • the socket <-> uwsgi
    通过指定socket(给出socket的路径) 配置文件:mysite_uwsgi.ini (注意要在配置中给uWSGI权限)

  • uwsgi <-> Django
    通过指定服务模块 配置文件:mysite_uwsgi.ini

第一次翻译文章,如果有什么恰当的地方望指正。