Docker 简介
Docker 是一个广泛使用的容器化平台,它允许开发人员将应用程序及其依赖项打包到一个轻量级、可移植的容器中,从而实现应用程序在任何环境中快速而一致地运行。下面是对 Docker 的详细介绍:
1. Docker 基本概念
容器是 Docker 的核心概念。它类似于一个轻量级的虚拟机,但不需要完整的操作系统,在共享宿主机操作系统内核的情况下,提供独立的运行环境。每个容器都包含应用程序以及运行该应用程序所需的全部依赖,使得应用能够在不同环境下一致运行。
镜像是容器运行所依赖的静态模板,类似于虚拟机的快照。它包含操作系统、运行时环境、应用程序代码和依赖包。可以将镜像看作是构建容器实例的蓝图。Docker Hub 是一个主要的公共镜像仓库,也支持私有镜像仓库。
Dockerfile 是构建 Docker 镜像的脚本文件,它包含一系列指令,用于从基础镜像开始,安装依赖、拷贝代码、配置环境变量、暴露端口等步骤。使用 Dockerfile,可以自动化构建镜像,使得镜像版本管理变得简单和一致。
虚拟机(VM)在硬件级别上模拟完整的计算机系统,每个虚拟机需要单独加载操作系统;而 Docker 容器则共享宿主机的操作系统内核,但保持应用之间的隔离,因此容器更轻量、启动速度更快、资源占用更少。
2. Docker 的主要特点和优势
由于容器共享宿主机内核,不需要完整的操作系统,各容器的大小通常比虚拟机小得多,启动速度也非常快,适合快速部署和扩展服务。
Docker 镜像包含了所有的依赖和配置,保证在不同环境(开发、测试、生产)中运行一致。只要有 Docker 引擎,镜像就可以在不同平台间轻松迁移和部署。
每个容器都运行在独立的环境中,可以避免依赖冲突,并且提高安全性。不同的容器之间彼此隔离,同时可以共享宿主机资源,从而实现资源的高效利用。
通过将应用程序和依赖打包到容器中,部署过程变得简单、标准化,适合自动化部署和持续集成/持续交付(CI/CD)流程。
Docker 拥有丰富的生态系统,公共镜像仓库 Docker Hub、镜像管理工具、编排工具(如 Docker Compose、Kubernetes)等,使得容器管理变得更加简单和高效。
Docker 相关教程:
- 前言 · Docker – 从入门到实践
- Docker 入门教程 - 阮一峰的网络日志
- Docker 教程 | 菜鸟教程
使用 Docker 初步部署 Django 项目开发环境
标准流程
使用 Docker 部署 Django 项目的标准流程一般如下:
项目准备(生成依赖文件、配置环境变量等) —> 编写 Dockerfile
—> 编写 Docker Compose
组合各项服务 —> 调整 Django 配置 —> 创建并启动容器 —> 验证部署。
项目准备
-
生成依赖文件:确保项目根目录有 requirements.txt
,包含所有Python依赖。
-
配置环境变量:将敏感信息(如 SECRET_KEY
、数据库密码)存储在 .env
文件或Docker Compose的环境变量中。
-
创建 .dockerignore
:忽略虚拟环境、IDE文件等,例如:
1 2 3 4 5 6 7 8 9
| venv/
.git
__pycache__
*.sqlite3
|
编写 Dockerfile
在项目根目录创建 Dockerfile,内容可参考:
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
|
FROM python:3.9
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
COPY requirements.txt /app/
RUN pip install --no-cache-dir -r requirements.txt
RUN pip install debugpy
COPY . /app/
RUN mkdir -p /app/media \
/app/upload_files \
/app/function_test_files \
/app/function_test_other_files \
/app/eyes_test_files \
/app/xray_test_files \
/app/report_files \
/app/dev_report_files \
/app/dev_report_pdf_files \
/app/fun_report_files \
/app/fun_report_pdf_files \
/app/dev_fun_report_pdf_files \
/app/temp_order_files \
/app/feedback_files \
/app/temp_feedback_files \
/app/question_files \
/app/report_pdf_files \
/app/bug_report_pdf_files \
/app/batch_report_pdf_files \
/app/temporary_files \
&& chmod -R 755 /app
EXPOSE 8000 5678
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
|
这里有几个地方需要注意:
WORKDIR
- 容器内部的路径
设置的工作目录是 Docker 容器内部的绝对路径,不是宿主机(你的本地电脑)的路径;
这个目录会在构建镜像时自动创建(如果不存在),类似在容器内运行 mkdir /app && cd /app
。
- 后续操作的工作目录
所有后续命令(如 COPY . .
)都会将文件复制到容器的 /app
目录下。例如:
* COPY requirements.txt .
会将宿主机当前目录的 requirements.txt
复制到容器的 /app/requirements.txt
。
* RUN pip install ...
会在容器的 /app
目录下执行。
- 类比理解
如果将容器看作一个独立的 Linux 系统:
* /app
是这个系统中的一个普通文件夹(类似本地的 ~/myproject
)。
* WORKDIR
只是定义了这个系统的“当前工作目录”,宿主机完全看不到这个目录(除非你通过 volumes
挂载)。
为什么看不到容器内的 /app
目录?因为容器内的文件系统是隔离的,默认情况下宿主机无法直接访问。若需要查看:
1 2 3 4 5 6 7 8 9
|
docker-compose run web bash
ls
|
COPY
COPY
是 Dockerfile 中用于将文件或目录从宿主机(构建上下文)复制到容器镜像中的核心指令。具体用法可以参考上边给出的一些教程。
这里先创建了一些文件夹,然后再 COPY 文件到容器中,如果项目中有创建文件夹的代码,其实也可以不用创建文件夹;如果宿主机这些文件夹里有文件,通过 COPY 命令也可以直接复制过去,这里是为了保险期间做了冗余操作。
RUN
这里的 RUN 命令主要是用来安装依赖,确保项目可以正常运行。
EXPOSE
EXPOSE
是 Dockerfile 中用于 声明容器运行时监听的端口 的指令。它不会自动将端口映射到宿主机,而是作为元数据帮助开发者和工具理解容器需要哪些端口。
CMD
这是 Dockerfile 中用于启动 Django 开发服务器的典型命令,其作用是为容器定义默认执行行为。
为什么要绑定到 0.0.0.0
?
与 Docker 端口映射的关系
1 2 3
| docker run -d -p 8080:8000 your-image
|
此时可通过 http://宿主机IP:8080
访问服务。
这里适用于开发环境,如果需要部署生产环境,可能需要再配合生产级服务器如 Nginx。
编写 Docker Compose
组合各项服务
样例代码:
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
| services:
web:
build:
context: .
dockerfile: Dockerfile.dev
container_name: cnooc-backend-dev
ports:
- "8000:8000"
- "5678:5678"
volumes:
- .:/app:ro
- ./media:/app/media
- ./upload_files:/app/upload_files
- ./report_files:/app/report_files
- ./dev_report_files:/app/dev_report_files
- ./function_test_files:/app/function_test_files
- ./fun_report_files:/app/fun_report_files
- ./fun_report_pdf_files:/app/fun_report_pdf_files
- ./dev_fun_report_pdf_files:/app/dev_fun_report_pdf_files
environment:
- DJANGO_SECRET_KEY=django-insecure-t-70t&48!*#^x^9w^i)f(6_g%9094aet4#4_vkqf_rz!8wqe&^
- DJANGO_DEBUG=True
- DJANGO_SETTINGS_MODULE=config.settings.development
restart: always
command: python manage.py runserver 0.0.0.0:8000
networks:
- backend-dev-network
depends_on:
- redis
redis:
image: redis:latest
container_name: cnooc-redis-dev
ports:
- "6379:6379"
volumes:
- redis-data:/data
- ./redis.conf:/usr/local/etc/redis/redis.conf
command: redis-server /usr/local/etc/redis/redis.conf
networks:
- backend-dev-network
networks:
backend-dev-network:
driver: bridge
volumes:
redis-data:
|
上述配置文件主要配置了以下几个服务:
Web 服务(Django 应用)
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| web:
build:
context: .
dockerfile: Dockerfile.dev
container_name: cnooc-backend-dev
ports:
- "8000:8000"
- "5678:5678"
volumes:
- .:/app:ro
- ./media:/app/media
- ./upload_files:/app/upload_files
- ./report_files:/app/report_files
- ./dev_report_files:/app/dev_report_files
- ./function_test_files:/app/function_test_files
- ./fun_report_files:/app/fun_report_files
- ./fun_report_pdf_files:/app/fun_report_pdf_files
- ./dev_fun_report_pdf_files:/app/dev_fun_report_pdf_files
environment:
- DJANGO_SECRET_KEY=django-insecure-t-70t&48!*#^x^9w^i)f(6_g%9094aet4#4_vkqf_rz!8wqe&^
- DJANGO_DEBUG=True
- DJANGO_SETTINGS_MODULE=config.settings.development
restart: always
command: python manage.py runserver 0.0.0.0:8000
networks:
- backend-dev-network
depends_on:
- redis
|
Redis 服务
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
| redis:
image: redis:latest
container_name: cnooc-redis-dev
ports:
- "6379:6379"
volumes:
- redis-data:/data
- ./redis.conf:/usr/local/etc/redis/redis.conf
command: redis-server /usr/local/etc/redis/redis.conf
networks:
- backend-dev-network
|
关键配置
volumes: .:/app:ro
将宿主机代码以只读模式挂载到容器,实现修改后即时生效(适合开发,但生产环境应避免,因为生产环境应该构建独立镜像)。
通过挂载本地文件,实现容器内文件与宿主机文件同步,确保文件的持久化。
安全风险
这种开发环境配置是为了更加方便快速使用,但是也存在一些安全隐患,生产环境中应该避免如下情况:
DJANGO_SECRET_KEY
明文写入 Compose 文件不安全,应改用环境变量文件(.env
):
ports: "6379:6379"
允许外部直接访问 Redis,建议移除端口映射,仅限 Docker 内部网络访问。
使用 Docker 初步部署 Django 项目生产环境
对于生产环境来讲,最好使用 gunicorn 作为 WSGI 服务器,性能更好。其次可能需要使用 Nginx 进行反向代理。这里的 Nginx 仅仅是示例,我并没有使用 Nginx 部署过,可以根据需要进行调整。
这里创建了一个 appuser 用户,避免容器内进程以 root 身份运行,增加安全性。
依赖安装也进行了优化,先打包成 wheel,再安装,省去了源码编译时间;最终镜像仅含运行所需的 wheel,体积小且启动快。
生产级服务器使用的是Gunicorn,Gunicorn 多 worker、超时设置可以根据需求进行调整。替换掉 Django 自带的开发服务器,满足生产环境性能与稳定性。
其余解析多参考开发环境部署,基本大差不差。
不需要 Nginx 的话,删除掉所有 Nginx 相关配置即可。
生产环境 Dockerfile.prod
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
|
FROM python:3.9-slim AS builder
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc libpq-dev && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt
FROM python:3.9-slim
RUN useradd -ms /bin/bash appuser
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV DJANGO_SETTINGS_MODULE=config.settings.production
ENV DJANGO_DEBUG=False
RUN apt-get update && \
apt-get install -y --no-install-recommends libpq-dev && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/wheels /wheels
RUN pip install --no-cache /wheels/*
COPY . .
RUN mkdir -p /app/media \
/app/upload_files \
/app/function_test_files \
/app/function_test_other_files \
/app/eyes_test_files \
/app/xray_test_files \
/app/report_files \
/app/dev_report_files \
/app/dev_report_pdf_files \
/app/fun_report_files \
/app/fun_report_pdf_files \
/app/dev_fun_report_pdf_files \
/app/temp_order_files \
/app/feedback_files \
/app/temp_feedback_files \
/app/question_files \
/app/report_pdf_files \
/app/bug_report_pdf_files \
/app/batch_report_pdf_files \
/app/temporary_files \
&& chown -R appuser:appuser /app
USER appuser
RUN python manage.py collectstatic --noinput
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "--timeout", "120", "config.wsgi:application"]
|
Docker-compose.prod.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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
| services:
web:
build:
context: .
dockerfile: Dockerfile.prod
container_name: cnooc-backend-prod
restart: always
ports:
- "8000:8000"
volumes:
- static_volume:/app/staticfiles
- media_volume:/app/media
- ./upload_files:/app/upload_files
- ./report_files:/app/report_files
- ./dev_report_files:/app/dev_report_files
- ./function_test_files:/app/function_test_files
- ./fun_report_files:/app/fun_report_files
- ./fun_report_pdf_files:/app/fun_report_pdf_files
- ./dev_fun_report_pdf_files:/app/dev_fun_report_pdf_files
environment:
- DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY}
- DJANGO_ALLOWED_HOSTS=${DJANGO_ALLOWED_HOSTS}
networks:
- backend-prod-network
depends_on:
- redis
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
nginx:
image: nginx:1.23-alpine
container_name: cnooc-nginx
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- static_volume:/usr/share/nginx/html/static
- media_volume:/usr/share/nginx/html/media
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/ssl:/etc/nginx/ssl
networks:
- backend-prod-network
depends_on:
- web
redis:
image: redis:latest
container_name: cnooc-redis-prod
command: redis-server /usr/local/etc/redis/redis.conf
volumes:
- redis-data:/data
- ./redis/redis.conf:/usr/local/etc/redis/redis.conf
networks:
- backend-prod-network
restart: always
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 10s
retries: 3
networks:
backend-prod-network:
driver: bridge
volumes:
static_volume:
media_volume:
redis-data:
|