准备好以下的内容
- 项目文件
- 使用
pip freeze > requirements.txt
命令打包好项目的依赖包列表 - 安装好Docker和Docker-compose,可以分别用
docker -v
和docker-vompose -v
命令查看是否安装成功。
编写Dockerfile文件
Docker 允许通过文本格式的配置文件来构建镜像,默认名称为 Dockerfile
代码语言:javascript复制# 从Docker仓库中拉去带有Python3.7的Linux环境
FROM python:3.7
# 设置 python 环境变量
ENV PYTHONUNBUFFERED 1
# 这两行是在系统钟安装了MySQL的连接器
RUN apt-get update
RUN apt-get install python3-dev default-libmysqlclient-dev -y
# 创建 code 文件夹并将其设置为工作目录
RUN mkdir /code
WORKDIR /code
# 更新 pip
RUN pip install pip -U
# 将 requirements.txt 复制到容器的 code 目录
ADD requirements.txt /code/
# 安装库
RUN pip install -r requirements.txt
# 将当前目录复制到容器的 code 目录
ADD . /code/
理解这些Docker指令的关键,在于牢记容器内的环境和宿主机是隔离的,核心问题是搞清楚那些操作是针对宿主机,哪些操作是针对容器。
FROM python:3.7
指令从仓库拉取一个包含 python 3.7 的 Linux 操作系统环境(Linux 版本为 Debian)。
RUN
和 WORKDIR
指令都是针对容器的,功能是在容器里创建目录、并将其设置为工作目录。注意宿主机是没有这个目录的。
ADD
指令出现了两次。ADD requirements.txt /code/
意思是将宿主机当前目录(即 Dockerfile 所在目录)的 requirements.txt
文件复制到容器的 /code
目录中。ADD . /code/
意思是把当前目录所有内容复制到容器 /code/
目录,注意中间那个点。
编写docker-compose.yml文件
代码语言:javascript复制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:
- "3307:3306"
restart: always
environment:
- MYSQL_ROOT_PASSWORD=mypassword
- MYSQL_DATABASE=django_app
networks:
- db_network
nginx:
restart: always
image: nginx:latest
ports:
- "8001: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:
version
代表 docker-compose.yml 的版本,目前最新版为 3,不需要改动它。
从整体上看,我们定义了三个容器,分别是app
、db
、和nginx
,容器之间通过定义的端口进行通讯。定义了两个网络,分别是web_network
和db_network
,只有处在同一网络下的容器才能够互相通讯。不同网络之间是隔离的,即便采用同样的端口,也无法通讯。定义了一个数据卷static-volume
。数据卷非常适合多个容器共享使用同一数据,可以看到app
和nginx
都使用到了它。expose
和ports
都可以暴露容器的端口,区别是expose
仅暴露给其他容器,而ports
会暴露给其他容器和宿主机。
下面具体分析一下:
定义了一个名叫 app
的容器。后面的内容都是 app
容器的相关配置:
restart
:除正常工作外,容器会在任何时候重启,比如遭遇 bug、进程崩溃、docker 重启等情况。build
:指定一个包含 Dockerfile 的路径,并通过此 Dockerfile 来构建容器镜像。注意那个 "." ,代表当前目录。command
:容器运行时需要执行的命令。这里就是我们很熟悉的运行开发服务器了。volumes
:卷,这是个很重要的概念。前面说过容器是和宿主机完全隔离的,但是有些时候又需要将其连通;比如我们开发的 Django 项目代码常常会更新,并且更新时还依赖如 Git 之类的程序,在容器里操作就显得不太方便。所以就有卷,它定义了宿主机和容器之间的映射:"." 表示宿主机的当前目录,":" 为分隔符,"/code" 表示容器中的目录。即宿主机当前目录和容器的 /code 目录是连通的,宿主机当前目录的 Django 代码更新时,容器中的 /code 目录中的代码也相应的更新了。这有点儿像是在容器上打了一个洞,某种程度上也是实用性和隔离性的一种妥协。
严格意义上讲,这里用到的 .:/code
并不是卷,而是叫挂载,它两是有区别的,只不过 docker-compose 允许将挂载写到卷的配置中。
expose
:暴露容器的8000端口供其他容器访问,宿主机和外界无法访问networks
:能够访问web_network
和db_network
depends_on
,意思是此容器需要等待db
容器启动完毕才能够启动。
分析一下 db
容器:
image
:从仓库拉取 MySQL 5.7 。volumes
:这里出现的static-volume
叫卷。它的使用方式像这样:static-volume:/code/collected_static
,冒号后面还是容器内的目录,但冒号前的却不是宿主机目录、仅仅是卷的名称而已。从本质上讲,数据卷也是实现了宿主机和容器的目录映射,但是数据卷是由 Docker 进行管理的,你甚至都不需要知道数据卷保存在宿主机的具体位置。
相比挂载,数据卷的优点是由于是 Docker 统一管理的,不存在由于权限不够引发的挂载问题,也不需要在不同服务器指定不同的路径;缺点是它不太适合单配置文件的映射。和挂载一样,数据卷的生命周期脱离了容器,删除容器之后卷还是存在的。下次构建镜像时,指定卷的名称就可以继续使用了。
ports
:MySQL 默认通信端口为 3306 。由于我的机子上已经跑了一个MySQL服务,所以我将容器内的3306端口映射为本机的3307端口。environment
:定义容器的环境变量,设置了 MySQL 的 root 用户的密码、数据库的名称。network
:只能够访问db_network
。
添加db
容器后记得的修改Django
里的数据库设置。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'django_app',
'USER': 'root',
'PASSWORD': 'mypassword',
'HOST': 'db',
'PORT': '3306',
'OPTIONS': {'charset': 'utf8mb4'},
}
}
最后分析一下nginx
容器,其他配置与上述两个大致一样,值得说一说的是ports
设置,由于我的服务器上部署了其他服务,所以我将nginx端口映射为8001。
Docker 允许用户给每个容器定义其工作的网络,只有在相同的网络之中才能进行通讯。可以看到 nginx
容器处于 web_network
网络,而 db
容器处于 db_network
网络,因此它两是无法通讯的,实际上确实也不需要通讯。而 app
容器同时处于 web_network
和 db_network
网络,相当于是桥梁,连通了3个容器。
Nginx配置
修改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
文件的最后两行增加mysqlclient
库和gunicorn
库。
mysqlclient==2.0.1
gunicorn==19.9.0
再修改Django项目的配置文件
代码语言:javascript复制ALLOWED_HOSTS = ['*']
...
STATIC_ROOT = os.path.join(BASE_DIR, 'collected_static')
STATIC_URL = '/static/'
部署
运行命令docker-compose build
构造镜像,再使用docker-compose up
即可启用服务。
下面附上一下经常用到的命令:
- 停止容器,
docker-compose down
- 后台运行docker容器:
docker-compose up -d
。 - 只想启动其中的一个容器:
docker-compose up -d db
或者docker-compose up -d app
即可启动db
容器或app
容器。 - 进入容器:
docker exec -it container_id /bin/bash
- 上面的
container_id
如果不知道如何获取可以通过docker ps
命令查看。