写在前面
实际的生产环境中,我们往往需要定义数量庞大的 docker 容器,并且容器之间具有错综复杂的依赖联系,一个一个去手动创建容器并记录和配置这些复杂的容器关系,不仅效率低下而且容易出错,所以迫切需要一种定义容器集群编排和部署的工具,这就是docker-compose
什么是docker-compose及docker-compose工具的安装
Docker-compose是一个用来定义和运行复杂应用的 Docker 工具。使用 docker-compose 后不再需要使用 shell 脚本来逐一创建和启动容器,还可以通过 docker-compose.yml 文件构建和管理复杂多容器组合。
pip安装docker-compose
pip3 install docker-compose
ln -s /usr/local/python3/bin/docker-compose /usr/bin/docker-compose
docker-compose -version
更换docker镜像源
vi /etc/docker/daemon.json
{
"registry-mirrors": ["http://hub-mirror.c.163.com"]
}
systemctl restart docker
多容器部署Django项目示意图
- 当客户端通过80端口访问服务,宿主机80端口映射到
djang_nginx
容器的80端口,找到django_nginx
容器,django_nginx
收到请求转发到uwsgi
服务的8000端口 - 8000端口映射到
django_uwsgi
容器,View
视图处理时如果用到了数据库内容,则将寻找settings.py
中配置的数据信息,寻找3306端口的mysql服务 - 3306端口映射到
MySQL
容器,从该容器的上获取数据,然后依次返回用户
Django+Uwsgi+Nginx+MySQL代码布局图
docker_project # 项目根目录
├── compose # 存放各项容器服务的Dockerfile和配置文件
│ ├── mysql
│ │ ├── conf
│ │ │ └── my.cnf # MySQL配置文件
│ │ └── init
│ │ └── init.sql # MySQL启动脚本
│ └── nginx
│ ├── Dockerfile # 构建Nginx镜像所的Dockerfile
│ ├── nginx.conf # Nginx配置文件
│ ├── nginx.log # 挂载保存nginx容器内nginx日志
│ │ ├── access.log
│ │ └── error.log
│ └── ssl
├── docker-compose.yml # 核心编排文件
└── docker_django # 常规Django项目目录
├── db.sqlite3
├── docker_django # Django项目配置文件
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── Dockerfile # 构建Django+Uwsgi镜像的Dockerfile
├── manage.py
├── media # 用户上传的媒体资源与静态文件
├── requirements.txt # Django项目依赖文件
├── start.sh # 启动Django+Uwsgi容器后要执行的脚本
├── static # 项目所使用到的静态文件
└── uwsgi.ini # uwsgi配置文件
部署开始
第一步:编写Web (Django+Uwsgi)镜像和容器所需文件
FROM python:3.7.2
MAINTAINER Fat Puffer <dcpuffer@outlook.com>
# 设置 python 环境变量
ENV PYTHONUNBUFFERED 1
# 在容器内/var/www/html/下创建docker_django文件夹
RUN mkdir -p /var/www/html/docker_django
# 设置容器内工作目录
WORKDIR /var/www/html/docker_django
# 将当前目录文件加入到容器工作目录中(. 表示当前宿主机目录)
ADD . /var/www/html/docker_django
# 更新pip版本
RUN /usr/local/bin/python3 -m pip3 install --upgrade pip
# 利用 pip3 安装依赖
RUN pip3 install -r requirements.txt -i https://pypi.douban.com/simple
# Windows环境下编写的start.sh每行命令结尾有多余的\r字符,需移除。
RUN sed -i 's/\r//' ./start.sh
# 设置start.sh文件可执行权限
RUN chmod +x ./start.sh
- start.sh脚本
#!/bin/bash
# 从第一行到最后一行分别表示:
# 1. 收集静态文件到根目录,
# 2. 生成数据库可执行文件,
# 3. 根据数据库可执行文件来修改数据库
# 4. 用 uwsgi启动 django 服务
python3 manage.py collectstatic --noinput &&
python3 manage.py makemigrations &&
python3 manage.py collectstatic &&
python3 manage.py migrate &&
uwsgi --ini /var/www/html/docker_project/docker_django/uwsgi.ini
- uwsgi.ini文件
[uwsgi]
; 使用nginx连接时使用
socket = 0.0.0.0:8000
; 直接做web服务器使用,指定要监听的ip和端口号,即我们运行项目时的ip和端口
; http = 0.0.0.0:8000
; 项目目录
chdir = /var/www/html/docker_project/docker_django
; 项目中的wsgi.py文件的目录
module = docker_django.wsgi:application
; 静态文件映射,测试uwsgi配置时为了能够访问到静态资源,所以加上这个配置。在使用nginx时,需要注销掉这个配置,改用nginx来代理静态资源访>问
; 可使用 python manage.py collectstatic
static-map=/static=/var/www/html/docker_project/docker_django/static
; 指定启动的工作进程数
processes = 4
; 指定每个进程中的线程数
threads = 2
; 指定在工作进程中存在一个主进程
master = True
; 服务停止时自动移除unix Socket和pid文件
vacuum = True
; 保存启动之后主进程的进程号
pidfile = uwsgi.pid
; 设置uwsgi后台运行,运行信息保存在uwsgi.log
; 切记在docker中使用uwsgi记录日志时,指定日志目录需要使用logto,而不是daemonize,否则会导致容器直接退出
; daemonize = uwsgi.log
logto = uwsgi.log
; 单个日志的大小
buffer-size=32768
; 设置每个工作进程处理请求的上限,达到上限时,将回收(重启)该进程。可以预防内存泄漏
max-requests=5000
第二步:编写Nginx镜像和容器所需文件
xxx/compose/nginx/Dockerfile
FROM nginx:latest
# 镜像维护者
MAINTAINER Fat Puffer <dcpuffer@outlook.com>
# 将项目nginx配置文件拷贝到nginx配置文件目录下
ADD nginx.conf /etc/nginx/conf.d/
# sed是一个Linux编辑器吧,此命令的作用是查找文件/etc/nginx/nginx.conf中包含user的行,并将此行的nginx替换成root
RUN sed -i '/user/{s/nginx/root/}' /etc/nginx/nginx.conf
# 删除原有配置文件,创建静态资源文件夹和ssl证书保存文件夹
RUN rm /etc/nginx/conf.d/default.conf \
&& mkdir -p /usr/share/nginx/html/static \
&& mkdir -p /usr/share/nginx/html/media \
&& mkdir -p /usr/share/nginx/ssl
CMD ["nginx", "-g", "daemon off;"]
xxx/compose/nginx/nginx.conf
# nginx配置文件。
upstream django {
ip_hash; # 负载策略
server web:8000; # Django+uwsgi容器所在IP地址及开放端口,非宿主机外网IP
}
server {
listen 80; # 监听80端口
server_name localhost; # 可以是nginx容器所在ip地址或127.0.0.1,不能写宿主机外网ip地址
location /static {
alias /usr/share/nginx/html/static; # 静态资源路径
}
location /media {
alias /usr/share/nginx/html/media; # 媒体资源,用户上传文件路径
}
location / {
include /etc/nginx/uwsgi_params;
uwsgi_pass django;
uwsgi_read_timeout 600;
uwsgi_connect_timeout 600;
uwsgi_send_timeout 600;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
# proxy_pass http://django;
}
}
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
第三步:编写MySQL镜像和容器所需文件
xxx/compose/mysql/my.cnf
[mysqld]
user=mysql
default-storage-engine=INNODB
character-set-server=utf8
# 端口与docker-compose里映射端口保持一致
port=3306
#一定要注释掉,mysql所在容器和django所在容器不同IP
# bind-address=localhost
basedir=/usr
datadir=/var/lib/mysql
tmpdir=/tmp
pid-file=/var/run/mysqld/mysqld.pid
socket=/var/run/mysqld/mysqld.sock
# 这个参数是禁止域名解析的,远程访问推荐开启skip_name_resolve
skip-name-resolve
[client]
port = 3306
default-character-set=utf8
[mysql]
no-auto-rehash
xxx/compose/mysql/init.sql
# 注意这里的数据库名django_docker和用户名admin和密码password必需和docker-compose.yml里与MySQL相关的环境变量保持一致
GRANT ALL PRIVILEGES ON docker_django.* TO admin@"%" IDENTIFIED BY "password@";
FLUSH PRIVILEGES;
第四步:编写docker-compose.yml文件
docker-compose.yml
的核心内容如下。我们定义了2个数据卷,用于挂载各个容器内动态生成的数据,比如MySQL的存储数据,和django容器中用户上传的媒体资源与文件。这样即使删除容器,容器内产生的数据也不会丢失。我们还编排了3项容器服务,别名分别为mysql, nginx和web,接下来我们将依次看看各个容器的Dockerfile和配置文件。
version: "3"
volumes: # 自定义数据卷,默认位于宿主机/var/lib/docker/volumes内
docker_django_mysql_vol: # 定义数据卷同步容器内mysql数据
docker_django_media_vol: # 定义数据卷同步media文件夹数据
services:
db-mysql:
image: mysql:5.7
environment:
- MYSQL_ROOT_PASSWORD=password@123 # root用户数据库密码
- MYSQL_DATABASE=docker_django # 数据库名称
- MYSQL_USER=admin # 数据库用户名
- MYSQL_PASSWORD=password@ # 创建的admin用户密码
volumes:
- docker_django_mysql_vol:/var/lib/mysql:rw # 挂载数据库数据, 可读可写
- ./compose/mysql/conf/my.cnf:/etc/mysql/my.cnf # 挂载配置文件
-v /etc/localtime:/etc/localtime:ro
- ./compose/mysql/init:/docker-entrypoint-initdb.d/ # 挂载数据初始化sql脚本
ports:
- "3306:3306" # 与配置文件保持一致
restart: always
web:
build: ./docker_django # 使用docker_django目录下的Dockerfile
expose:
- "8000"
volumes:
- ./docker_django:/var/www/html/docker_django # 挂载项目代码
- docker_django_media_vol:/var/www/html/docker_django/media # 以数据卷挂载容器内用户上传媒体文件
- ./compose/uwsgi:/tmp # 挂载uwsgi日志
links:
- db-mysql
depends_on: # 依赖关系
- db-mysql
environment:
- DEBUG=False
restart: always
tty: true
stdin_open: true
nginx:
build: ./compose/nginx
ports:
- "80:80"
- "443:443"
expose:
- "80"
volumes: # 可以在此处指定日志目录,将docker容器内的日志路径映射到本地
- ./docker_django/static:/usr/share/nginx/html/static # 挂载静态文件
- ./compose/nginx/ssl:/usr/share/nginx/ssl # 挂载ssl证书目录
- ./compose/nginx/nginx.log:/var/log/nginx # 挂载日志
- docker_django_media_vol:/usr/share/nginx/html/media # 挂载用户上传媒体文件
links:
- web
depends_on:
- web
restart: always
第五步:修改Django项目settings.py
# 生产环境设置 Debug = False
Debug = False
# 设置ALLOWED HOSTS
ALLOWED_HOSTS = ['localhost', '10.10.101.238']
# 设置STATIC ROOT 和 STATIC URL
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATIC_URL = "/static/"
# 设置MEDIA ROOT 和 MEDIA URL
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = "/media/"
# 设置数据库。这里用户名和密码必需和docker-compose.yml里mysql环境变量保持一致
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'docker_django', # 数据库名
'USER':'admin', # 你设置的用户名 - 非root用户
'PASSWORD':'password@', # 换成你自己密码
'HOST': 'db-mysql', # 注意:这里使用的是db别名,docker会自动解析成ip
'PORT':'3306', # 端口
}
}
第六步:修改项目配置文件下的__init__.py
- 由于要使用mysql作为是数据库后台,所以需要在该文件下增加以下内容
import pymysql
pymysql.install_as_MySQLdb()
- 安装
pymysql
:pip3 install pymysql
- 别忘记重新导出环境依赖包
pip3 freeze > requirements.txt
第七步:使用docker-compose 构建镜像并启动容器组服务
# 进入docker-compose.yml所在文件夹,输入以下命令构建镜像
sudo docker-compose build
# 查看已生成的镜像
sudo docker images
# 启动容器组服务 -d 后台启动
sudo docker-compose up -d
# 查看运行中的容器
sudo docker ps
# 停止容器组命令:
sudo docker-compose down
# 启动某个容器
sudo docker-compose start 容器名
# 停止某个容器
sudo docker-compose stop 容器名
第八步:进入web容器内执行Django命令并启动uwsgi服务器
sudo docker exec -it docker_project_web_1 /bin/bash start.sh
第九步:浏览器访问
http://10.10.102.238/admin
项目代码
GitHub传送门:https://github.com/FatPuffer/docker-compose-deploy-django