Setting up Django with uWSGI and Nginx

A tutorial is aimed at the Django user who wants to set up a production web server.

Posted by Dusign on 2020-03-05
Words 2k and Reading Time 9 Minutes
Viewed Times

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.

Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design.

nginx (pronounced engine-x) is a free, open-source, high-performance HTTP server and reverse proxy, as well as an IMAP/POP3 proxy server.

1、准备好 linux 服务器,笔者使用的是 centos8,准备好开发好的 django 项目。

2、安装 pyenv 以及 pyenv-virtualenv,配置 pyehon 开发环境,具体教程见笔者前面的博客 Usage of Virtualenv

3、安装 nginx 并且关闭 selinux,打开防火墙的 80 端口、8000端口、8080端口等常用端口,linux 的防火墙操作方法见我前面的博客 Some Setting of Linux,之后通过命令

1
service nginx start

启动 nginx 服务,然后在 linux 本地和其他电脑访问 linux 的 IP ,如果出现 nginx 的启动界面,说明 nginx 工作正常。(如果没有安装桌面的话,本地可以用 lynx、elinks、w3m等文本浏览器访问测试)。

4、安装mysql ,方法见我前面的博客 MySQL Tutorial

5、安装 uwsgi ,通过 pip 安装,如果安装失败,请安装 python-devel
如果在虚拟环境中安装失败的话,选择带有 -dev 后缀的 python 版本(下载特别慢,最好晚上用网的人少了再下载)。

6、测试uwsgi
让我们从一个简单的 “Hello World” 开始,创建文件 test.py,代码如下:

1
2
3
def application(env, start_response):
start_response('200 OK', [('Content-Type','text/html')])
return [b"Hello World"]

uWSGI Python 加载器将会搜索的默认函数 application 。
接下来我们启动 uWSGI 来运行一个 HTTP 服务器,将程序部署在 HTTP 端口 9090 上:
1
uwsgi --http :9090 --wsgi-file test.py

访问 ip:9090 之后看到 “Hello World” ,则说明安装成功

7、django 中如果使用到 mysql 数据库,请安装 mysqlclient,如果出现如下错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Collecting mysqlclient
Using cached mysqlclient-1.4.6.tar.gz (85 kB)
ERROR: Command errored out with exit status 1:
command: /home/user/.pyenv/versions/3.8-dev/envs/myow/bin/python3 -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-5fgysz1z/mysqlclient/setup.py'"'"'; __file__='"'"'/tmp/pip-install-5fgysz1z/mysqlclient/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /tmp/pip-install-5fgysz1z/mysqlclient/pip-egg-info
cwd: /tmp/pip-install-5fgysz1z/mysqlclient/
Complete output (12 lines):
/bin/sh: mysql_config: command not found
/bin/sh: mariadb_config: command not found
/bin/sh: mysql_config: command not found
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/tmp/pip-install-5fgysz1z/mysqlclient/setup.py", line 16, in <module>
metadata, options = get_config()
File "/tmp/pip-install-5fgysz1z/mysqlclient/setup_posix.py", line 61, in get_config
libs = mysql_config("libs")
File "/tmp/pip-install-5fgysz1z/mysqlclient/setup_posix.py", line 29, in mysql_config
raise EnvironmentError("%s not found" % (_mysql_config_path,))
OSError: mysql_config not found
----------------------------------------
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.

原因是用mysql数据库的话需要安装 mysql-devel(centos) 或 mysql-dev(ubuntu),若是 mariadb 数据库,则需要安装 mariadb-devel。有时候也需要 libmysqlclient-dev。

8、在虚拟环境中导入自己的开发环境
导出开发环境 pip freeze > VIRTUALENV_NAME.txt
导入开发环境 pip install -r VIRTUALENV_NAME.txt

9、准备好 django 项目,在对应的虚拟环境下通过 python manage.py runserver 运行,如果访问正常,说明 django 项目没问题
然后通过

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

运行测试,其中 8000 可以修改为你自己想用的端口
如果访问报错 404,可能是你的 uwsgi 没有在项目对应的虚拟环境下运行, 在你的运行记录中找下面的这两行,看看路径与你的项目路径以及虚拟环境路径是否一样
1
2
current working directory: /home/user/Project/myow
detected binary path: /home/user/.pyenv/versions/3.8-dev/envs/myow/bin/uwsgi

如果不一样,请切换到相应的路径下运行,如果访问正常,说明django与uwsgi运行没问题。

10、在项目目录中新建 nginx 的配置文件,并且在 /etc/nginx/conf.d/ 中建立软连接,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
35
36
37
# the upstream component nginx needs to connect to
upstream django {
# server unix:///home/user/Project/myow/myow.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 _; # 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 /home/user/Project/myow/media; # your Django project's media files - amend as required

}

location /static {
alias /home/user/Project/myow/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 /etc/nginx/uwsgi_params; # the uwsgi_params file you installed

}

}

用上面建立的 test.py 文件测试 nginx 和 uwsgi 是否工作正常
1
uwsgi --socket :8001 --wsgi-file test.py

端口对应 nginx 配置文件中监听的端口
然后在浏览器中测试,输入 localhost:8000 ,如果看到 ”Hello World“ 则说明 uwsgi 和 nginx 工作正常。

11、上面的测试都正常之后,我们使用 unix sockets 代替端口,修改 nginx 配置文件中的 server 如下

1
2
server unix:///home/user/Project/myow/myow.sock; # for a file socket
# server 127.0.0.1:8001; # for a web port socket (we'll use this first)

然后运行uwsgi
1
uwsgi --socket test.sock --wsgi-file test.py

使用浏览器输入 localhost:8000 ,看是否工作正常,如果报错的话请检查 uwsgi 和 nginx 对当前工作目录有没有权限
如果 uwsgi 对当前目录没有权限的话请使用如下启动
1
uwsgi --socket test.sock --wsgi-file test.py --chmod-socket=666 # (very permissive)

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

如果 nginx 对 sock 没有权限,请提高 nginx 的权限,修改 nginx 的配置文件中的 user 为更高权限的用户
然后再运行,会在浏览器中看到 “Hello World”

12、使用 uwsgi 和 nginx 运行 django

1
uwsgi --socket /your/project.sock --module project.wsgi --chmod-socket=664

通过浏览器访问,如果报错 DisallowedHost at / Invalid HTTP_HOST header: ,说明你的 django 只有本地 ip 访问的权限,需要修改 setting.py 中的 ALLOWED_HOSTS
1
ALLOWED_HOSTS = ['*']     # 所有的ip都可以访问

13、使用 ini 文件启动 uwsgi
新建 uwsgi 的启动文件,uwsgi.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
36
37
38
39
40
41
42
43
44
45
[uwsgi]

# Django-related settings
# the base directory (full path)
chdir = /home/user/Project/myow
# Django's wsgi file
module = myow.wsgi
# the virtualenv (full path)
home = /home/user/.pyenv/versions/myow

# uwsgi 启动用户名和用户组
# uid = www
# gid = www

# process-related settings
# master
master = true

# maximum number of worker processes
processes = 10
safe-pidfile = /tmp/myow-uwsgi.pid

# the socket (use the full path to be safe
socket = /home/user/Project/myow/myow.sock

# ... with appropriate permissions - may be needed
chmod-socket = 664

# clear environment on exit
vacuum = true

# 启用线程
# enable-threads = true

# 设置自动中断时间
# harakiri =30

# 设置缓冲
# post-buffering = 4096

# 设置静态文件
# static-map = /static=//www/wwwroot/mysite/static

# 设置日志目录
# daemonize = /var/log/uwsgi/myow.log

uwsgi 的启动操作是通过包含主进程号的文件进行设置的,前提是要在 uwsgi 的启动文件中设置保存主进程的 pid
启动 uwsgi
1
uwsgi  --ini  uwsgi.ini

停止 uwsgi
1
uwsgi --stop /tmp/myow-uwsgi.pid

重启 uwsgi
1
uwsgi --reload /tmp/myow-uwsgi.pid

14、Vue 部署
如果想前后端分离部署 Vue 的话,只需要再新建一个 nginx 节点,用其他的端口访问前端页面,然后通过不同的端口请求后台数据。
上面的方法涉及到跨域访问,具体的解决办法如下
首先安装 django-cors-headers

1
pip install django-cors-headers

然后在 django 的项目的 setting 中设置如下
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
36
37
38
39
40
41
42
43
44
45
46
47
INSTALLED_APPS = [
...
'corsheaders',
...
]

CORS_ALLOW_CREDENTIALS = True

CORS_ORIGIN_ALLOW_ALL = True

# 白名单
CORS_ORIGIN_WHITELIST = (
'*',
)

CORS_ALLOW_METHODS = (
'DELETE',
'GET',
'OPTIONS',
'PATCH',
'POST',
'PUT',
'VIEW',
)

CORS_ALLOW_HEADERS = (
'XMLHttpRequest',
'X_FILENAME',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'x-csrftoken',
'x-requested-with',
)

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware', # 新增加的,注意顺序
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

之后就可以跨域访问了,有时候运行会出现如下错误
1
2
(corsheaders.E013) Origin '*' in CORS_ORIGIN_WHITELIST is missing  scheme or netloc
HINT: Add a scheme (e.g. https://) or netloc (e.g. example.com).

这是由于 django-cors-headers 的新版本的白名单设置在 ip 之前需要加 ‘http’,如果没有白名单,则删掉相应的设置,如果有的话请在 ip 之前加上 ‘http’,之后一切就正常了。


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them. Thank you !

...

...

00:00
00:00