一、docker-compose安装
1、在安装好docker基础上,linux系统运行以下命令以下载 Docker Compose 的当前稳定版本
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose 要安装其他版本的 Compose,请替换 1.29.2。
2、将可执行权限应用于二进制文件、创建软连接、测试是否安装成功。
sudo chmod +x /usr/local/bin/docker-compose sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
3、docker-compose version测试是否安装成功
二、部署
Django + Uwsgi + Nginx + Postgres + Redis组合容器
本例中我们将使用docker-compose编排并启动4个容器,这个更接近于实际生成环境下的部署。
- Django + Uwsgi容器:核心应用程序,处理后端请求
- PostgreSQL 容器:数据库服务
- Redis 容器:缓存服务
- Nginx容器:反向代理服务并处理静态资源请求
这四个容器的依赖关系是:Django+Uwsgi 容器依赖 Redis 容器和 PostgreSQL 容器,Nginx 容器依赖Django+Uwsgi容器。为了方便容器间的相互访问和通信,我们使用docker-compose时可以给每个容器取个别名,这样访问容器时就可以直接使用别名访问,而不使用Docker临时给容器分配的IP了。
Docker-compose部署Django-vue项目路径树形图
pwd显示的目录:/home/edwin/docker_project/quotationBackend
该目录下的大致项目结构:
└── quotationBackend # 常规Django项目目录
├── Dockerfile # 构建Django+Uwsgi镜像的Dockerfile
├── docker-compose.yml # 核心编排文件
├── manage.py
├── quotationBackend # Django项目配置文件
│ ├── asgi.py
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── pip.conf # 非必需。pypi源设置成国内,加速pip安装
├── requirements.txt # Django项目依赖文件
├── start.sh # 启动Django+Uwsgi容器后要执行的脚本
├── dist # 前端vue打包后的文件
└── uwsgi.ini # uwsgi配置文件
├── nginx
│ ├── Dockerfile # 构建Nginx镜像所的Dockerfile
│ ├── log # 挂载保存nginx容器内日志log目录
│ ├── nginx.conf # Nginx配置文件
│ ├── ssl # 如果需要配置https需要用到
下面正式开始部署:
第一步:编写Nginx镜像和容器所需文件
1、构建Nginx镜像所使用的Dockerfile如下所示:
FROM nginx
RUN rm -rf /etc/nginx/conf.d/default.conf &&
mkdir -p /root/docker_project/frontend
# 第一句删除nginx容器内路径下的default.conf(可不删除)和创建存放前端项目的路径。
# -p 确保目录名称存在,不存在的就建一个。
COPY ./nginx.conf /etc/nginx # 拷贝当前路径下的配置文件到nginx容器里。
2、Nginx的配置文件如下所示:
user root root;
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log warn;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream django_backend {
server django_web:9000;
}
server {
listen 9001;
server_name localhost; # 不能填宿主机IP
charset utf-8;
client_max_body_size 10M; # 限制用户上传文件大小
location / {
root /root/docker_project/frontend;
index index.html index.htm;
}
location /api/{
rewrite ^/api/(.*)$ /$1 break;
include /etc/nginx/uwsgi_params;
uwsgi_pass django_backend;
}
}
}
第二步:编写Web (Django+Uwsgi)镜像和容器所需文件
1、构建Web镜像(Django+Uwsgi)的所使用的Dockerfile如下所示:
FROM python:3.7
# 镜像作者
MAINTAINER zt_9773
RUN mkdir -p /root/docker_project/quotationBackend
WORKDIR /root/docker_project/quotationBackend # 指定工作目录,比较重要,一般挂载路径是基于此路径
# 可选:设置镜像源为国内,设置后会快很多
COPY pip.conf /root/.pip/pip.conf
# 将当前目录copy到工作目录中(. 表示当前目录)
COPY . /root/docker_project/quotationBackend
# 安装项目依赖、给start.sh可执行权限(用于后续启动uwsgi)
RUN pip install -r requirements.txt \
&& chmod +x ./start.sh
2、start.sh
启动脚本文件内容如下所示
#!/bin/bash
python manage.py makemigrations &&
python manage.py migrate &&
uwsgi --ini uwsgi.ini && tail -f /dev/null
# tail空命令防止web容器执行脚本后退出
3、uwsgi.ini
配置文件如下所示:文件没有配置python虚拟环境,用的是容器默认的环境,所以可不用配置。
[uwsgi]
socket=0.0.0.0:9000
chdir=/root/docker_project/quotationBackend/
module=quotationBackend.wsgi:application
master=true
processes=4
threads=2
vacuum=true
thunder-lock=true
enable-threads=true
pidfile=uwsgi.pid
daemonize = uwsgi.log
第三步:编写docker-compose.yml文件
定义了2个数据卷,用于挂载容器内动态生成的数据,比如postSQL的存储数据,redis生成的快照这样即使删除容器,容器内产生的数据也不会丢失。
我们还定义了3个网络,分别为nginx_network
(用于nginx和web容器间的通信),db_network
(用于db和web容器间的通信)和redis_network
(用于redis和web容器间的通信)。
整个编排里包含4项容器服务,别名分别为redis
, database
, nginx
和django_web
,接下来我们将依次看看各个容器的Dockerfile和配置文件。
我这里redis和数据库直接使用的官方镜像,没有配置文件,因为可以直接使用,所以就没有单独给redis和数据库编写Dockerfile文件。
version: "3"
# 自定义数据卷
volumes:
postgres_vol:
redis_vol:
# 自定义网络(默认桥接), 不使用links通信
networks:
nginx_network:
driver: bridge
db_network:
driver: bridge
redis_network:
driver: bridge
services:
redis:
image: redis
networks:
- redis_network
volumes:
- redis_vol:/data # 以数据卷挂载给reids数据备份
ports:
- "6379:6379"
restart: always
database:
image: postgres:12
environment:
POSTGRES_DB: quotationBackend # 数据库名字,不存在会自动创建
POSTGRES_USER: postgres
POSTGRES_PASSWORD: 123456
networks:
- db_network
volumes:
- postgres_vol:/var/lib/postgresql/data
ports:
- "5432:5432"
restart: always
django_web:
build: ./
expose:
- "9000"
volumes:
- .:/root/docker_project/quotationBackend # 挂载项目代码
networks:
- db_network
- redis_network
- nginx_network
depends_on:
- database
- redis
command: ./start.sh # 启动uwsgi
restart: always
nginx:
build: ./nginx
ports:
- "9001:9001"
volumes:
- ./dist:/root/docker_project/frontend # 挂载前端vue打包后文件
- ./nginx/nginx.conf:/etc/nginx/nginx.conf # 挂载配置文件
- ./logs:/var/log/nginx # 挂载日志
networks:
- nginx_network
depends_on:
- django_web
restart: always
第四步:修改Django项目settings.py
主要几项配置需要修改如下:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'quotationBackend',
'USER': 'postgres',
'PASSWORD': '123456',
'HOST': 'database', # docker-compose.yml中
'PORT': '5432'
}
}
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' # 用处后面问题总结中介绍
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://redis:6379", # docker-compose.yml中
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
# 连接池数量,如果decode_responses不设置为True的话,
# 使用get_redis_connection读取的数据是bytes,
# 需要decode为utf-8
"CONNECTION_POOL_KWARGS": {"max_connections": 100, "decode_responses": True},
# "PASSWORD": "123456",
}
}
}
第五步:使用docker-compose 构建镜像并启动容器组服务
现在我们可以使用docker-compose命名构建镜像并启动容器组了。
# 进入docker-compose.yml所在文件夹,输入以下命令构建镜像
sudo docker-compose build
# 查看已生成的镜像和已经启动的容器
sudo docker images
sudo docker ps
# 启动容器组服务
sudo docker-compose up
如果一切顺利,此时你应该可以看到四个容器服务都已经成功运行了。此时打开你的浏览器,输入你服务器的ip地址或域名指向地址,你就应该可以访问你的网站拉。
第六步:遇到的问题排错总结
1、Nginx容器排错
Nginx报错nginx: [emerg] "user" directive is not allowed here in /etc/nginx/conf.d/nginx.conf:1。。路径有问题,我开始把nginx的配置文件copy到这里的路径下了。应该放在/etc/nginx路径下面。
nginx排错最重要的是查看Nginx的错误日志,还有重点检查端口,和uwsgi配套检查,基本就不会有问题。
2、Web容器排错
(1)、上面问题为start.sh格式配置不对,在最前面加上 #!/bin/bash 即可。。另外一个常发生的错误是 docker-compose生成的web容器执行脚本命令后立刻退出(exited with code 0),在start.sh中加入
tail -f /dev/null后
容器服务可持续运行。
(2)、在运行start.sh迁移数据库时有报警导致容器退出。
django_web_1 | user.User: (models.W042) Auto-created primary
key used when not defining a primary key type,
by default 'django.db.models.AutoField'.
django_web_1 | HINT: Configure the DEFAULT_AUTO_FIELD
setting or the UserConfig.default_auto_field attribute
to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.
在玩新发布的 Django 3.2 时,我注意到一个警告,我以前在从 Django 3.1 迁移的网站上没有看到警告(警告)。models.W042
该警告在 Django 3.2 中引入了一个新功能,该功能允许更改 Django 自动添加的原数默认字段,如果您没有明确定义主字段。官方这样说的。
我的django版本是3.2.8,所以需要在settings.py配置文件中加上这句DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'。
(3)、构建数据库时有个端口占用错误Error starting userland proxy: listen tcp4 0.0.0.0:5432: bind: address already in use。直接在root用户运行netstat -tanlp ,然后找到端口5432的PID。 kill PID即可。
(4)、在requirements.txt中删掉psycopg2包,只需要安装psycopg2-binary且可以不用指定版本直接安装最新版本。
The psycopg2 wheel package will be renamed from release 2.8;
in order to keep installing from binary please use
"pip install psycopg2-binary" instead.
三、最后小结
1、说一说ports和expose的区别。
- ports是将端口暴露给宿主机,而expose是将端口暴露给和本容器通信的容器,这里是通过 networks通信。
- 用法区别,语法格式如下。
ports:
- "80:80" # 绑定容器的80端口到主机的80端口
- "9000:8080" # 绑定容器的8080端口到主机的9000端口
- "443" # 绑定容器的443端口到主机的任意端口,容器启动时随机分配绑定的主机端口号
expose:
- "3000"
- "8000" # 将当前容器的端口3000和8000暴露给link到本容器的容器。
2、容器数据卷volumes。
- 作用就是实现容器和容器之间数据共享,容器和宿主机之间数据备份防止数据丢失
- 匿名挂载和具名挂载:就是在指定数据卷的时候,不指定容器路径对应的主机路径,这样对应映射的主机路径就是默认的路径/var/lib/docker/volumes/中自动生成一个随机命名的文件夹(默认以id命名)。例如本例中docker-compose.yml中定义的redis_vol和postgres_vol,启动成功后可在宿主机看到挂载的路径/var/lib/docker/volumes 下面。这里命名为redis_vol叫具名挂载。不命名docker会默认以id命名叫匿名挂载。
- 指定路径挂载:主容器文件所在路径:容器内路径。nginx的几个挂载都是指定路径挂载。
- 注意:不能通信的两个容器之间不能共享数据
待续