Django 使用 Nginx 作为web服务器

引子

Nginx足够优秀【Web服务上】
Django也足够优秀【web框架上】

环境

  • centos7
  • docker 17
  • docker-compose 1.17
  • Django 1.11
  • nginx 1.11

目的

想搭建一个环境,让自己的web程序可以很方便的移植到服务器上
所以要用到Docker

艰难的配置

先给出架设最后的文件列表结构

~/django-web 根目录

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
├── docker-compose.yml
├── nginx
│   ├── conf.d
│   │   └── hProj.conf
│   ├── Dockerfile
│   ├── html
│   │   └── index.htm
│   └── log
│   ├── access.log
│   ├── error.log
│   ├── hProj.access.log
│   └── hProj.error.log
├── production.yml
├── README.md
└── web
├── db.sqlite3
├── Dockerfile
├── helloworld
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── migrations
│   ├── models.py
│   ├── __pycache__
│   ├── tests.py
│   └── views.py
├── hProj
│   ├── __init__.py
│   ├── __pycache__
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
└── requirements.txt

用的是Django

所以先创建一个 Django 的 Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 基于Python 版本
FROM python:3
# 设置Python环境
ENV PYTHONUNBUFFERED 1

# 创建一个Code文件夹作为工作目录,将python库的需求文件拷贝进来
RUN mkdir /code
ADD requirements.txt /code/

#以/code文件夹作为工作目录
WORKDIR /code

#执行安装库的脚本,这里考虑到默认库下载很慢,使用阿里云的镜像安装
RUN pip install --trusted-host mirrors.aliyun.com -i http://mirrors.aliyun.com/pypi/simple -r requirements.txt

requirements.txt【库信息】

1
2
3
4
Django>=1.8,<2.0
gunicorn>=19,<21
uwsgi
psycopg2

接下来需要使用Docker-compose

这是一个管理多Docker容器的工具,可以很方便管理多容器
docker-compose.yml

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
version: '3'

services:
nginx:
image: nginx:latest
ports:
- "8000:80"
- "3000:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/html:/usr/share/nginx/html
- ./nginx/log:/var/log/nginx
depends_on:
- web
links:
- web
container_name: ng01

db:
image: postgres
container_name: ps01


web:
build: ./web
container_name: dg01
command: uwsgi -s :3031 -w hProj.wsgi -p 3
volumes:
- ./web:/code
expose:
- "3031"
depends_on:
- db

这个里面有太多东西了

慢慢说:

  1. version: ‘3’
    表示Docker-compose 文件的解析版本

  2. services Docker服务节点

  3. 这里面创建了三个节点

nginx : web服务用

db: 数据库用

web:web app处理逻辑

  1. 对nginx

image 表示选用什么镜像版本,这里是最新的Nginx

ports 表示端口映射 暴露容器端口到主机的任意端口或指定端口,用法:

1
2
3
4
ports:
- "8000:80" # 绑定容器的80端口到主机的8000端口
- "3000:1001" # 绑定容器的1001端口到主机的3000端口
- "443" # 绑定容器的443端口到主机的任意端口,容器启动时随机分配绑定的主机端口号

这里需要 注意 ports 和 expose 区别

expose暴露容器给link到当前容器的容器,用法:

1
2
3
expose:
- "3000"
- "8000"

以上指令将当前容器的端口3000和8000暴露给link到本容器的容器。
和ports的区别是,expose不会将端口暴露给主机。

expose 一般可与 link 一起使用

link :
在消费和服务容器之间创建链接
这会创建一系列环境变量,并且在/etc/hosts 文件中添加入口项

volumes 是什么?

相当于U盘,可以挂载数据,让主机数据挂载到容器上

depends_on 标签表示依赖,决定容器创建启动的先后顺序,决的是启动顺序问题

例如下面容器会先启动 redis 和 db 两个服务,最后才启动 web 服务:

1
2
3
4
5
6
7
8
9
10
11
version: '2'
services:
web:
build: .
depends_on:
- db
- redis
redis:
image: redis
db:
image: postgres

注意的是,默认情况下使用 docker-compose up web 这样的方式启动 web 服务时,也会启动 redis 和 db 两个服务,因为在配置文件中定义了依赖关系。

links: 这个标签解决的是容器连接问题,会连接到其它服务中的容器。
格式如下:

1
2
3
4
links:
- db
- db:database
- redis

使用的别名将会自动在服务容器中的/etc/hosts里创建。例如:

1
2
3
172.12.2.186  db
172.12.2.186 database
172.12.2.187 redis

最后一项比较容易,是指命名容器

这个时候最好先测试下

docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
version: '3'

services:
nginx:
image: nginx:latest
ports:
- "8000:80"
- "3000:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/html:/usr/share/nginx/html
- ./nginx/log:/var/log/nginx
container_name: ng01

使用

1
docker-compose up -d

然后访问

1
2
[root@localhost django_web]# curl 127.0.0.1:8000
curl: (56) Recv failure: Connection reset by peer

Why?

百度了一下没找到原因

没办法直接进容器去看

1
2
docker exec -it ng01 bash
cat /etc/nginx/nginx.conf

应该是没有配置默认网站信息

退出容器,添加网站配置

1
2
3
echo "hello">nginx/html/index.htm

vi nginx/conf.d/hProj.conf

nginx/conf.d/hProj.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server {
listen 80;
server_name 172.20.1.7;
#server_name localhost;

access_log /var/log/nginx/hProj.access.log;
error_log /var/log/nginx/hProj.error.log;

location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
access_log off;
}

然后访问

1
2
[root@localhost django_web]# curl 127.0.0.1:8000
hello

测试通过

接下来说Django

build: 标签标识使用 Dockerfile 建立容器

接下来

测试django 项目创建是否有问题

1
2
3
4
5
6
7
8
9
10
11
version: '3'

services:
web:
build: ./web
container_name: dg01
command: python3 manage.py runserver 0.0.0.0:8000
volumes:
- ./web:/code
ports:
- "8000:8000"

创建项目

1
docker-compose run web django-admin.py startproject django_example .

如果不Ok,请检查Dockerfile

接下来启动web服务

1
2
3
docker-compose stop 
docker-compose rm
docker-compose up -d

然后访问

1
[root@localhost django_web]# curl 127.0.0.1:8000

如果被拒绝请对应找原因,这里我没有被卡住

最重要的怎么将Django项目和 Nginx结合起来

这里使用 gunicorn 或者 uwsgi

简单说,这两个东西相当于在 web 和 nginx中间加了一层,所以一定会有数据传输,为什么要用这里不讨论

数据传输最简单就是 Socket,就会有 IP:PORT

修改 docker-compose.yml

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
version: '3'
services:
nginx:
image: nginx:latest
ports:
- "8000:80"
- "3000:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/html:/usr/share/nginx/html
- ./nginx/log:/var/log/nginx
depends_on:
- web
links:
- web
container_name: ng01

web:
build: ./web
container_name: dg01
command: uwsgi -s :3031 -w hProj.wsgi -p 3
volumes:
- ./web:/code
expose:
- "3031"

这里主要做 两件事

  1. 将命令换成 uwsgi ,换成这个命令以后,无法使用Web访问,所以这里无法测试

  2. 将端口从暴露给主机到不暴露给主机

将ports 写法改成 expose

需要注意在nginx 容器里面需要加上 links 属性,这里要特别注意

接下来需要修改 nginx的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
upstream uwsgicluster {
server web:3031;
}
server {
listen 80;
server_name localhost;

access_log /var/log/nginx/hProj.access.log;
error_log /var/log/nginx/hProj.error.log;

location / {
uwsgi_pass uwsgicluster;
include /etc/nginx/uwsgi_params;
}
access_log off;
}

重新启动

1
2
3
docker-compose stop 
docker-compose rm
docker-compose up -d

测试访问

1
[root@localhost django_web]# curl 127.0.0.1:8000

这里Ok 就结束了,如果有问题,检查下是否那一步不对

关于使用Nginx处理Django静态文件的问题

上述一通操作以后,发现无法使用静态文件了,原因是没有配置~~~

方法太多,这里 我只会一种

由于Django 里面有很多App,可能都用到静态文件了,这样如果每个app下都有一个static,冗余就多了

这里Django 可以收集静态文件,统一放到一个文件下

先设置 settings.py

1
2
3
4
STATIC_URL = '/static/'

# 新加
STATIC_ROOT = os.path.join(BASE_DIR, 'static')

然后收集 静态文件

1
python manage.py collectstatic

收集前结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[web@localhost web]$ tree -L 2
.
├── db.sqlite3
├── Dockerfile
├── helloworld
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── migrations
│   ├── models.py
│   ├── __pycache__
│   ├── serializers.py
│   ├── templates
│   ├── tests.py
│   └── views.py
├── hProj
│   ├── __init__.py
│   ├── __pycache__
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
└── requirements.txt

收集后结构

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
[web@localhost web]$ tree -L 2
.
├── db.sqlite3
├── Dockerfile
├── helloworld
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── migrations
│   ├── models.py
│   ├── __pycache__
│   ├── serializers.py
│   ├── templates
│   ├── tests.py
│   └── views.py
├── hProj
│   ├── __init__.py
│   ├── __pycache__
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
├── requirements.txt
└── static
├── admin
└── rest_framework

设置修改 Docker-compose Nginx选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
nginx:
image: nginx:latest
ports:
- "80:80"
- "8001:8001"
- "443:443"
volumes:
- ./web:/usr/share/nginx/web
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/html:/usr/share/nginx/html
- ./nginx/log:/var/log/nginx
depends_on:
- web
links:
- web
container_name: ng01

修改 nginx 配置 hProj.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
upstream uwsgicluster {
#server unix:///tmp/uwsgi.sock;
server web:3031;
}
server {
listen 80;
server_name localhost;

access_log /var/log/nginx/hProj.access.log;
error_log /var/log/nginx/hProj.error.log;
location /static {
alias /usr/share/nginx/web/static;
}
location / {
#root /usr/share/nginx/html;
#index index.html index.htm;
uwsgi_pass uwsgicluster;
include /etc/nginx/uwsgi_params;
#uwsgi_pass 127.0.0.1:8077;
#uwsgi_param UWSGI_SCRIPT index;
#uwsgi_param UWSGI_PYHOME $document_root;
#uwsgi_param UWSGI_CHDIR $document_root;
}
access_log off;
}

测试下就OK