update: 使用docker 可能是个更优雅的方式,本文是比较原始的方式,仅供学习和参考
Django的部署可以有很多方式,采用nginx+uwsgi的方式是其中比较常见的一种方式。
在这种方式中,我们的通常做法是,将nginx作为服务器最前端,它将接收WEB的所有请求,统一管理请求。 nginx把所有静态请求自己来处理(这是NGINX的强项)。 然后,NGINX将所有非静态请求通过uwsgi传递给Django,由Django来进行处理,从而完成一次WEB请求。 可见,uwsgi的作用就类似一个桥接器。起到桥梁的作用。
NOTE:不使用nginx,只使用uwsgi+django也是可以实现WEB服务的。uwsgi也可以直接处理WEB请求
部署过程回顾
在继续深入之前,我们先来回顾一下当我们在本地开发环境下更新了代码后,在服务器上的整个部署过程。
远程连接服务器。
进入项目根目录,从远程仓库拉取最新的代码。
如果项目引入了新的依赖,需要执行 pip install -r requirement.txt 安装最新依赖。
如果修改或新增了项目静态文件,需要执行 python manage.py collectstatic 收集静态文件。
如果数据库发生了变化,需要执行 python manage.py migrate 迁移数据库。
重启 Nginx 和 uwsgi 或者自动化脚本
一些概念
什么是web服务器?
web server直接接触外面的东西,它可以从文件系统直接提供 (HTML, images, CSS, etc)给外部访问.但是它不能直接地与我们的django应用程序进行通信 。它还需要一些东西来运行我们的应用以及处理来自web客户端的请求(比如浏览器)已经返回响应,常见的web服务器有Nginx,IIS等
什么是WSGI?
WSGI是 web server gateway interface的简称,也就是说WSGI负责联通web服务器,WSGI是python的一种协议标准,而uWSGI 是WSGI的一种实现,在本教程中我们会使用uWSIG区创建一个Unix socket (套接字),将response(程序响应)通过socket使用WSGI协议进行通信,最终我们的组件结构如下:
the web client <-> the web server <-> the socket <-> uwsgi <-> Django
eg:
Browser <-> Nginx <-> the unix socket <-> uwsgi <-> Django
在设置uWSGI前
虚拟环境virtualenv
安装所需软件前确保你正在使用虚拟环境(稍后我们会谈系统级的uwsgi)先使用apt-get
安装virtualenv
virtualenv uwsgi-tutorial
cd uwsgi-tutorial
source bin/activate
如果已经有建立了项目,直接去到虚拟环境bin目录下 执行source activate即可激活虚拟环境
Django项目
在虚拟环境下安装Django,创建一个django项目并进入到项目文件夹中
鉴于本文是说部署项目所以这里假设你已经设定好项目,如果实在没创建请参考Django博客创建教程
我的项目创建在
/root/ENV/bin/my_blog/
我的配置文件夹是在
/root/ENV/bin/my_blog/ my_blog
现在进入到项目文件夹
cd /root/ENV/bin/my_blog
关于域名和端口
在本教程,假设你的域名是 example.com. 当然你可以换成你自己的域名或者IP。由始至终我们都将使用端口8000来发布web服务。 没错,就是Django默认使用的那个端口。 实际上你部署的时候你喜欢用什么端口就完全由你决定。 这里之所以选择8000是为了防止和其他的已经存在的web server冲突,比如说我之前已经部署了一个80端口的,这里就不能再用80了
基本的uWSGI安装以及配置
安装uswgi到你的虚拟环境
pip install uwsgi
也有其他的方式安装uWSGI,但是这种方式是最好的,你可能同时还要确保本机已经安装了Python开发环境。 在Debian以及其衍生发行版中例如Ubuntu,可能需要安装pythonX.Y-dev, 这里的 X.Y 是Python版本号.
基础测试
用你最爱的编辑器创建一个文件叫 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
注:请留意python3和python2的区别
运行uWSGI:
uwsgi --http :8000 --wsgi-file test.py
参数含义:
- http :8000 u使用http协议并指定8000端口
- wsgi-file test.py: 加载python web源文件的方式运行,文件为test.py
然后在浏览器使用8000端口进行访问的时候应该会返回 ‘hello world’ 响应
访问: http://example.com:8000 如果确认输出没问题即说明以下环节是正常的
the web client <-> uWSGI <-> Python
(如果你在生产环境,只要命令行怎么测试8000端口已经开启了web服务呢,其实你可以安装一个命令行浏览器w3m,然后运行 w3m http://localhost:8000, 或者curl http://localhost:8000)
测试你的 Django 项目
刚才我们只是试用了一个简单源码的web模块,现在我们换成我们的Django项目再次测试uWSGI
首先使用内置的 manage.py
跑起来你的项目(其实manager.py也相当一个小型的web服务器,但是能力有限不足以撑起外部访问,就像我们用visual studio进行调试而实际发布我们采用专用的IIS服务器)。
这一步确保你的项目是可以运行的
python manage.py runserver 0.0.0.0:8000
如果你在生产环境,只要命令行怎么测试8000端口已经开启了web服务呢,其实你可以安装一个命令行浏览器w3m,然后运行
w3m http://localhost:8000
确认上面操作没问题后就使用uWSGI启动项目:
uwsgi --http :8000 --module wsgi
module mysite.wsgi: 加载指定的wsgi模块(wsgi 模块是一个项目的入口,我这里是指wsgi.py 文件,当作为uwsgi的参数的时候只需要指名文件名,不需要后缀“.py” )
wsgi.py:
#!coding:utf-8
import os
from os.path import join,dirname,abspath
import sys
PROJECT_DIR = dirname(dirname(abspath(__file__)))#3
import sys # 4
sys.path.insert(0,PROJECT_DIR) # 5
os.environ["DJANGO_SETTINGS_MODULE"] = "my_blog.settings" # 7
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
用你的浏览器打开上面的网址如果能看到你的网站输出,就是说明uWSGI可以在当前虚拟环境下充当的你的web服务器并运行了你的网站,说明以下结构是没问题的:
the web client <-> uWSGI <-> Django
好了现在我们看到客户端是直接与uWSG连接通讯的,就是说uWSGI充当了web server的角色
基本的nginx设置 安装nginx
sudo apt-get install nginx
sudo /etc/init.d/nginx start # start nginx
现在nginx已经运行了你可以使用80端口访问你的服务器,然后将会看到
> “Welcome to nginx!”.
恭喜你,证明了下面的结构是没问题的:
the web client <-> the web server
如果其他人已经使用了80端口,然后你要继续使用nginx,那就需要重新配置下nginx使用其他端口,前面说了,本篇将始终使用8000端口。
现在我先创建一个文件叫mysite_nginx.conf,然后写入以下内容(实际上你直接修改/etc/nginx/nginx.conf
的文件也是可以的):
# 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
}
}
eg: 上面只是个通用示例下面是我自己的配置
upstream django {
#server unix:/root/ENV35/bin/my_blog/my_blog/my_blog.sock; #unix:/root/ENV35/bin/my_blog/my_blog/my_blog.sock; # for a file socket
server 127.0.0.1:8077; # 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 128.199.134.134; # substitute your machine's IP address or FQDN
charset utf-8;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# max upload size
client_max_body_size 75M; # adjust to taste
# Django media
location /media {
alias /root/ENV35/bin/my_blog/media; # your media files
}
location /static {
alias /root/ENV35/bin/my_blog/collected_static; # you static files
}
# Finally, send all non-media requests to the Django server.
location / {
uwsgi_pass django;
include /etc/nginx/uwsgi_params; # the uwsgi_params file you installed
}
先忽略其他的配置,仅仅
# Django media
location /media {
alias /root/ENV35/bin/my_blog/media; # your media files
}
location /static {
alias /root/ENV35/bin/my_blog/collected_static; # you static files
}
这里指明了nginx从文件系统里对静态文件提供web服务,对一些大型的部署,通常他们的静态文件需要单独一个服务器来部署静态文件
和媒体文件。但是现在还是简单的放在一起部署吧
部署静态文件
首先安装上面设置好nginx的配置,现在还需要收集Django的静态文件放置在静态文件夹。请编辑Django的settings.py
文件,添加
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
(如果有其他的静态文件,请另行配置)
然后在终端运行:
python manage.py collectstatic
基本的nginx 测试
重启 nginx:
sudo /etc/init.d/nginx restart
为了测试媒体文件是否正确地被nginx服务着。添加一个图片media.png到你项目的媒体目录
/path/to/your/project/project/media directory, 然后访问 http://example.com:8000/media/media.png 如果能看到图片,至少证明目前nginx已经正确地服务着你的文件。
nginx 和 uWSGI 以及 test.py
现在让我们用nginx跑起你的简单test.py程序并输出响应“hello world” 。
uwsgi --socket :8077 --wsgi-file test.py
与前面所做的非常相似,只是这次的参数略有不同:
socket :8077: uwsgi使用socket通讯, 且端口为 8077
nginx 同时也已经设置了同一个端口与uwsgi进行通讯,nginx的设置是包含了:
upstream django {
#server unix:/root/ENV35/bin/my_blog/my_blog/my_blog.sock; #unix:/root/ENV35/bin/my_blog/my_blog/my_blog.sock; # for a file socket
server 127.0.0.1:8077; # for a web port socket (we'll use this first)
}
现在访问http://example.com:8000/
检查下如果ok了,那就说明了下面的结构是通的:
the web client <-> the web server(nginx,port:8000) <-> the socket(port:8077) <-> uWSGI <-> Python(django)
这时,你也可以看下在浏览器直接打开8007端口看uwsgi的输出 http://example.com:8077 理论上这个行不通的,因为你的浏览器是使用了http协议进行通讯的,而不是uWSGI协议,你也可以看到uwsgi在终端输出的错误。 而访问8000端口的时候浏览器的请求是先到达了nginx,nginx再通过与uwsgi通讯获取到响应流所以浏览是正常的。
使用Unix socket通信取代端口
目前为止,我们都是使用TCP端口的socket,原因无他-简单。实际上,推荐使用Unix的socket 比端口要好,因为Unix的socket具有更低的开销。
修改nginx配置文件 mysite_nginx.conf, 修改内容:
server unix:/root/ENV35/bin/my_blog/my_blog/my_blog.sock; #unix:/root/ENV35/bin/my_blog/my_blog/my_blog.sock; # for a file socket, auto created when running uwsgi and nginx
#server 127.0.0.1:8077; # for a web port socket (we'll use this first)
然后重启 nginx.
再次运行 uWSGI :
uwsgi --socket my_blog.sock --wsgi-file test.py
这一次,我们让uwsgi使用了指定的socket文件了(同nginx里配置的socket,socket文件无须手动创建,uwsgi会自动创建并在结束运行后自动删除)
再在浏览器里打开http://example.com:8000/.
如果不行的哈不妨查看下nginx的错误日志(/var/log/nginx/error.log) 如果你看到类似这样的:
connect() to unix:///path/to/your/mysite/mysite.sock failed (13: Permission
denied)
那么你可能需要修改socket的权限。使nginx具有权限使用它:
可以尝试:
uwsgi --socket my_blog.sock --wsgi-file test.py --chmod-socket=666 # (very permissive)
或者:
uwsgi --socket my_blog.sock --wsgi-file test.py --chmod-socket=664 # (more sensible)
另外你可能还需要添加用户到nginx用户组(比如www-data) ,这样nginx才有权限读写你的socket
同时也强烈建议在终端运行的nginx同时输出它的日志,这样可以轻松地诊断定位故障。
使用uwsgi和nginx运行Django应用程序
现在我们用下面的方式跑我们的Django程序:
uwsgi --socket my_blog.sock --module wsgi --chmod-socket=664
现在uWSGI和nginx不仅仅是运行"Hello World"的简单应用,而是通过模块wsgi入口加载了你的整个Django项目
配置uWSGI使用.ini文件
其实我们大可将一些参数写入到文件中,这样外终端调用的时候只需要指定这个文件即可,uwsgi的配置文件支持多种格式,比如xml或者ini,现在我们创建一个配置文件,然后在运行uwsgi的时候指明这个文件。配置文件的创建和配置都是很简单
现在就在项目的配置文件夹创建一个文件mysite_uwsgi.ini:
#mysite_uwsgi.ini file
[uwsgi]
# Django-related settings
# the base directory (full path)
chdir = /root/ENV35/bin/my_blog/my_blog/
# Django's wsgi file, here is wsgi.py ,just the name , no extension
module = wsgi
# the virtualenv (full path)
home = /root/ENV35/
# process-related settings
# master
master = true
# maximum number of worker processes
processes = 10
# the socket (use the full path to be safe
#socket = :8077 unix socket file,crate a empty file eg:/root/ENV35/bin/my_blog/my_blog/my_blog.sock
socket = /root/ENV35/bin/my_blog/my_blog/my_blog.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
再一次,我们的Django网站又能运行起来了,前提是你的配置是没问题的
安装系统范围可访问的uWSGI.
细心的你可能发现了,目前为止,我们的uWSGI只是安装在虚拟环境中virtualenv,(当然如果你可以设置每次开机后分别运行nginx,虚拟环境以及uwsgi的话外部也是可以正常访问你的网站的)但是更进一步我们现在想要安装整个系统可访问部署环境中
先关闭你的虚拟环境virtualenv:
deactivate
然后安装系统环境下的uWSGI:
sudo pip install uwsgi
#或者你也可以安装LTS版本 (long term support,长期支持).
pip install https://projects.unbit.it/downloads/uwsgi-lts.tar.gz
uWSGI维基提供了详细的安装细节可参考installation procedures. 在安装系统级的uWSGI之前,很有必要考虑具体安装哪一个版本已经最适合你的安装方式
现在和前面一样,再运行uwsgi检查一次你的网站(非虚幻环境中):
uwsgi --ini mysite_uwsgi.ini # the --ini option is used to specify a file
当全部都没问题后即可将nginx设置为监听90端口,并编写脚本在系统启动的时候启动nginx和uwsgi
附调试过程你可能需要的一些命令:
sudo systemctl stop nginx
sudo /etc/init.d/nginx restart
#80 port is in used:
netstat -ap | grep 80
sudo fuser -k 80/tcp
nginx -s reload
vim /etc/nginx/nginx.conf
cat /etc/nginx/nginx.conf
cat /var/log/nginx/error.log
#permission
#user www-data--> user root
uwsgi --socket my_blog.sock --module wsgi --chmod-socket=664
鸣谢:
五步教你实现使用Nginx+uWSGI+Django方法部署Django程序(上) uwsgi-docs 五步教你实现使用Nginx+uWSGI+Django方法部署Django程序