上一章我们实现了在 Docker 中添加了 MySQL 数据库,但采用的开发服务器虽然使用便捷,但性能差、可靠性低,无法应用在生产环境中。
因此本章将实现 Docker + Django + MySQL + Nginx + Gunicorn 容器项目,完成最终的服务器部署。
直接进入本章的 Docker 入门读者,建议回到教程第一章开始阅读,否则某些内容不好理解。对 Django 项目部署都没有概念的读者,还可以先阅读我的博文: 将 Django 项目部署到服务器。
Docker-compose
在部署到服务器之前,先来尝试本地部署。
在上一章的基础上,继续修改 docker-compose.yml
配置:
version: "3"
services:
app:
restart: always
build: .
command: bash -c "python3 manage.py collectstatic --no-input && python3 manage.py migrate && gunicorn --timeout=30 --workers=4 --bind :8000 django_app.wsgi:application"
volumes:
- .:/code
- static-volume:/code/collected_static
expose:
- "8000"
depends_on:
- db
networks:
- web_network
- db_network
db:
image: mysql:5.7
volumes:
- "./mysql:/var/lib/mysql"
ports:
- "3306:3306"
restart: always
environment:
- MYSQL_ROOT_PASSWORD=mypassword
- MYSQL_DATABASE=django_app
networks:
- db_network
nginx:
restart: always
image: nginx:latest
ports:
- "8000:8000"
volumes:
- static-volume:/code/collected_static
- ./config/nginx:/etc/nginx/conf.d
depends_on:
- app
networks:
- web_network
networks:
web_network:
driver: bridge
db_network:
driver: bridge
volumes:
static-volume:
有点复杂。来看看大体思路:
- 定义了 3 个容器,分别是
app
、db
和nginx
。容器之间通过定义的端口进行通讯。 - 定义了 2 个网络,分别是
web_network
和db_network
。只有处在相同网络的容器才能互相通讯。不同网络之间是隔离的,即便采用同样的端口,也无法通讯。 - 定义了 1 个数据卷,
static-volume
。数据卷非常适合多个容器共享使用同一数据,你可以看到app
和nginx
都用到了它。 -
expose
和ports
都可以暴露容器的端口,区别是 expose 仅暴露给其他容器,而 ports 会暴露给其他容器和宿主机。
这么讲可能还是很难理解,让我们继续分解。
网络 network
Docker 允许用户给每个容器定义其工作的网络,只有在相同的网络之中才能进行通讯。你可以看到 nginx
容器处于 web_network
网络,而 db
容器处于 db_network
网络,因此它两是无法通讯的,实际上确实也不需要通讯。而 app
容器同时处于 web_network
和 db_network
网络,相当于是桥梁,连通了3个容器。
定义网络可以隔离容器的网络环境,也方便运维人员一眼看出网络的逻辑关系。
数据卷
之前我们见识过的用于映射宿主机和容器目录的卷了,实际上称为挂载;现在新出现的 static-volume
才叫卷。它的使用方式像这样:static-volume:/code/collected_static
,冒号后面还是容器内的目录,但冒号前的却不是宿主机目录、仅仅是卷的名称而已。从本质上讲,数据卷也是实现了宿主机和容器的目录映射,但是数据卷是由 Docker 进行管理的,你甚至都不需要知道数据卷保存在宿主机的具体位置。
相比挂载,数据卷的优点是由于是 Docker 统一管理的,不存在由于权限不够引发的挂载问题,也不需要在不同服务器指定不同的路径;缺点是它不太适合单配置文件的映射。
和挂载一样,数据卷的生命周期脱离了容器,删除容器之后卷还是存在的。下次构建镜像时,指定卷的名称就可以继续使用了。
既然 Docker 能够管理卷,所以要想删除卷也是非常容易的。指令嘛,我不告诉你,生产环境千万不要手贱。定期备份数据是个好习惯。
数据卷有个很重要的特性:启动时如果卷是空的,则会将容器映射目录的所有内容复制到卷里去。换句话说就是,只要卷初始化完成后,容器原始的 collected_static
目录就不会再使用了,新增的文件也只存在于卷中,容器中是没有的。
实际上 static 静态文件(以及 media 媒体文件)的持久存储,通过挂载或者数据卷都可以实现;具体用哪种,这个就见仁见智了,你自己选择。
篇幅有限,教程没有讲到 media 媒体文件,但它的设置和 static 是完全相同的。
其他配置
首先修改 Nginx 的配置文件,即映射到 nginx
容器的 config/nginx/django_app.conf
:
upstream app {
ip_hash;
server app:8000;
}
server {
listen 8000;
server_name localhost;
location /static/ {
autoindex on;
alias /code/collected_static/;
}
location / {
proxy_pass http://app/;
}
}
此配置下 Nginx 会监听容器的 8000 端口,并将受到的请求发送到 app
容器(静态文件请求除外)。
在 requirements.txt
文件中增加 gunicorn
库:
django==2.2
mysqlclient==1.3.14
gunicorn==19.9.0
最后修改 django_app/settings.py
的域和静态文件存放目录的配置:
...
ALLOWED_HOSTS = ['*']
...
STATIC_ROOT = os.path.join(BASE_DIR, 'collected_static')
STATIC_URL = '/static/'
所有配置就完成了。
教程使用空的 Django 项目,为演示效果,就没有修改 DEBUG=False 了。若你用的自己的项目测试,记得把它为 False。
测试
测试指令就一条:
$ docker-compose up
浏览器访问 127.0.0.1:8000
又看到熟悉的 Django 小火箭了。
和上一章类似,第一次启动容器时可能会出现无法连接 MySQL 的错误,这是由于虽然 db 容器已经启动,但初始化并未完成;重新启动容器之后就可以正常工作了。若多次启动都无法正常工作,那就是别的原因了,好好检查吧。
本地部署成功,下一步服务器部署。
服务器部署
有了本地部署的经验,服务器部署就非常非常简单了。
还是类似的,部署前将 Docker 、 Docker-compose 、 Python3 等工具在服务器上安装好;将项目用 Git 克隆到服务器本地。
接下来把 settings.py
、config/nginx/django_app.conf
、requirements.txt
相关位置都按教程流程改好;将 docker-compose.yml
和 Dockerfile
复制到服务器。
由于 http 请求默认为 80 端口,所以为了接收公网请求,还需要做一点点修改 docker-compose.yml
的工作:
version: "3"
services:
app:
...
command: bash -c "... your_project_name.wsgi:application" # 改为你的项目名称
...
db:
...
nginx:
...
ports:
- "80:8000" # 监听 80 端口
...
networks:
...
volumes:
...
修改 Gunicorn 绑定的项目名称,以及让宿主机监听公网 http 默认的 80 端口。
此外还要修改 config/nginx/django_app.conf
:
upstream your_domain_name {
ip_hash;
server app:8000;
}
server {
...
location / {
proxy_pass http://your_domain_name/;
}
}
这个改动主要是为了照顾各种第三方登录的回调地址(不改这里, GitHub、Weibo 三方登录都会失败)。如果你没有类似的需求,不改也是可以的。比如博主的个人网站是 www.dusaiphoto.com
,所以这里的 your_domain_name
就修改为 www.dusaiphoto.com
。
最后,记得将 settings.py
中的 DEBUG 配置修改好:
# DEBUG=True 注释掉
DEBUG=False
这样就可以了!构建镜像并启动容器:
docker-compose up
在浏览器中就可以正常访问你的网站了。
总结
现在你已经可以部署一个线上的容器化 Django 项目了,恭喜!
若本教程对你有帮助,请到GitHub给个 Star 哟,也欢迎阅读我的Django 搭建博客教程。
老朋友们,下个教程见!
- 有疑问请在杜赛的个人网站留言,我会尽快回复。
- 教程示例代码:django-docker-tutorial
- 或Email私信我:[email protected]