Nginx + uWSGI 部署Django站点

Django有Apache和Nginx两种常见部署方式

主要工具:

  • 一台远程服务器
  • Nginx
  • uWSGI
  • Django

知识点:

  • 站点结构
  • Nginx结构
  • debug方式

主机使用的是DigitalOcean家的基础版ubuntu,版本16.01。

从裸机开始的环境准备

基础环境

zsh,oh-my-zsh,vimrc从github上pull过来,一切都是熟悉的感觉。
apt-get 要更新。

Python3/pip 和 虚拟环境

主要是为了环境简洁和适应版本

Nginx 安装

远程服务器这个是第一步的,没有服务器,开发调试都是头疼的事情。Nginx是一款反向代理服务器,可以让网站的内容呈现给客户。

1
2
3
4
5
6
7
8
9
10
11
12
# install
sudo apt-get install nginx
# basic operation
sudo /etc/init.d/nginx start
sudo /etc/init.d/nginx stop
sudo /etc/init.d/nginx restart
or:
sudo service nginx start
sudo service nginx stop
sudo service nginx restart

启动后访问服务器ip,会看到Welcome to nginx!信息。

uWSGI 安装

uWSGI是实现了WSGI的服务器。WSGI一个Web接口。提供规范化协议,提高服务器的工作效率。uWSGI本身已经可以胜任服务器的工作,为什么要再前面挡一个Nginx,这是出于更好的安全性,更有效率的工作和Nginx作为专业的服务器的优势。可以详见这个讨论

1
2
3
#install
apt-get install python-dev
sudo pip install uwsgi --upgrade

用wWSGI挂起一个最简单的程序,创建一个文件命名test.py

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

用wWSGI挂起:

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

这条命令是说,使用http协议,8000端口,加载指定文件。文件很简单,收到GET请求后,返回Hello World,在浏览器输入ip:8000就可以看到结果了。

安装Django

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#install
pip install django
#create project
django-admin.py startproject project-name
#create app
python manage.py startapp app-name
#同步数据库
python manage.py makemigrations
python manage.py migrate
#开发服务器
python manage.py runserver #localhost
python manage.py runserver 0.0.0.0:8000 #ip:8000端口

理解Nginx服务器的结构

Nginx结构

最重要的文件:一开始就要加载

默认下,Nginx会安装在/usr/local/nginx,所有的配置文件在/etc/nginx下,最主要的配置文件是 /etc/nginx/nginx.conf,这个文件建议放在git上,或者进行周期性备份

1
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.$(date "+%b_%d_%Y_%H.%M.%S")

nginx.conf 的结构如下:

1
2
3
4
5
6
7
8
9
10
11
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 768;
}
http {
......
}

nginx.conf 配置了主要的工作功能,include允许从后面的地址中找到配置文件,这样可以让主配置文件看起来更加精简,但是多层套用include也不推荐,让代码不易读。从默认的配置文件可以看出,/etc/nginx/conf.d/里所有的配置文件和/etc/nginx/sites-enalbed/里的配置文件都会在一开始被加载。log文件在/var/log/nginx/下。

HTTP模块:全局配置

HTTP 模块会处理所有来自于网络的HTTP请求,这是Nginx的大门。
重点说下/etc/nginx/sites-enalbed/这个目录,它可以让不同的虚拟域名分开来。一般都是通过软连接将配置文件连接进这个目录,很容易修改,ngnix提供了一个配置模版:

1
cp /etc/nginx/sites-available/default /etc/nginx/sites-available/example.com

Server模块:虚拟域名的配置

/etc/nginx/sites-enalbed/下配置的格式:

1
2
3
4
5
6
7
8
9
10
server {
listen 80
root /usr/share/nginx/html;
server_name localhost;
location / {
}
}

server 模块是用户修改量最大的地方。对于每一个虚拟域名都可以有不同的配置文件。/usr/share/nginx/html这个路径现在已经是Nginx默认存放静态文件的路径了,不需要再配置。

listen: 用来监听端口
server_name: 自己网站的域名

是否可以使用不有效的域名呢?
完全可以,在局域网中,是一个很好的办法。

log: log文件,HTTP模块已经设置过,在server模块里可以更细致。

1
2
access_log logs/example.access.log;
access_log /srv/www/example.com/logs/access.log;

建议使用完全路径,相对路径是与现在的路径下部分匹配的,不好控制。

location:文件系统配置
location设置可以让服务器去应答一些要服务器资源的请求。例如:

1
2
3
# http://example.com/blog/
location /blog/ { }

注意字符串的匹配是最佳匹配,Nginx会选择匹配的最好的location。更复杂的匹配情况请参照官方文件。

部署一个Nginx的最小Web结构

实践是检验真理的唯一标准,这里部署一个Nginx的最小结构,看看Nginx能不能正常工作。

linux 新建用户名用于部署Nginx并赋予权限.

1
2
3
visudo
# 添加权限:
username ALL=(ALL) ALL

nginx.conf用户名修改为当前用户。
在当前用户的根目录下创建如下结构:

1
2
3
4
5
6
mysite:
- mysite.conf
- www
- media
-test.png
- log

最简单的目标就是通过ip访问网站里的静态内容。简单的配置下,注意使用绝对路径:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server {
listen 8000;
server_name mysite.com
charset utf_8;
access_log /home/username/mysite/log/access.log;
error_log /home/username/mysite/log/error_log;
location /media/ {
alias /home/username/mysite/media/;
}
}
# 命令行软连接到Nginx
sudo ln -s /home/username/mysite/mysite.conf /etc/nginx/sites-enabled/

重启Nginx后,在 http://mysite.com/media/test.png能够看到正常显示内容就说名Nginx正常工作了。log文件显示正常。

常见的错误

403 forbidden

这里就要注意下用户名了,很多时候登录一台主机用户是root,而Nginx用户不是root,所以忽略了用户名的部署多会遇到这个问题。

  1. 可以将Nginx的用户名改为root,这样风险会很大。
  2. 网站静态内容部署在当前用户目录下,如列子所示。
  3. /usr/share/nginx/html下部署静态文件或软连接到网站目录。

404 notfound

一般出现这个问题是目录的配置出现问题,使用绝对路径可解决

调试方法

最好的办法就是log文件了,每次的请求,错误记录都会非常详细。

理解Django部署结构

djangoe结构

每个环节都要理解

到现在为止,已经可以分别用Nginx和uWSGI挂起服务器了。说明这两个部分都是通的:

web-server

web-uWSGI-Python

用uWSGI挂起Django:

1
uwsgi --http :8000 --module mysite.wsgi

这里sWSGI会找到 mysite下的wsgi的配置文件,即mysite/wsgi.py如果看到Django的“it worked!”信息,则说明Django被成功挂起。

web-uWSGI-Django

通过socket连接Nginx和uWSGI

socket中文翻译为套接字,它的作用就像一个连接器,比方说把两段水管连接起来,每段管子都有自己不同的编号,下面就来试验下套接字的作用。
带socket的Nginx配置多了这个部分:

1
2
3
4
upstream django {
# server unit:///path/to/yourmysite/mysite.sock;
server 127.0.0.1:8022; #for a webport socket
}

这是要说明监听来自8022端口的请求,8022口做什么了呢?

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

8022挂起了最先的python测试文件,如果有GET请求,则返回hello world。Nginx则设置为监听8023端口。通过socket,有检测到8023口的外部请求,则交给8022端口去处理,这就是socket的作用,非常灵活。也说明了这一套结构都通了:

Web-Nginx-Socket-uWSGI-Python

Django sockect

1
2
3
4
upstream django {
server unit:///path/to/yourmysite/mysite.sock;
# server 127.0.0.1:8022; #for a webport socket
}

重启Nginx,挂起uWSGI:

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

在当前项目下可以看到生成了空的文件mysite.sock,2023口测试通过。

最后拉起整个Django项目:

1
uwsgi --socket mysite.sock --module mysite.wsgi

看到Django工作界面,完成,Done!
Web-Nginx-Socket-uWSGI-Python

常见错误

connect() to unix:mysite.sock failed (2: No such file or directory)

基本是路径设置问题,upstrem和socket都要使用绝对路径。

connect() to uunix:mysite.sock failed (13: Permission denied)

添加socket权限:

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

参考资料:
Django Document django官方最权威的参考文档
Setting up Django and your web server with uWSGI and nginx uWSGI提供的参考文档,有些小问题
How To Serve Django Applications with uWSGI and Nginx on Ubuntu 16.04 DigitalOcean提供的部署指导
How to Configure nginx 如何配置Nginx