入门指南:Docker与Django的正确打开方式
很多新手在尝试Docker部署Django项目时,会陷入一个常见误区:把整个项目(包括requirements.txt、源代码、配置文件等)一股脑打包进镜像,期待Docker能像包管理器一样自动完成所有安装步骤。
实际上,Docker部署Django项目的核心理念不是"打包成品",而是"构建环境"。Django是Python的Web框架,其核心逻辑在于动态生成的wsgi.py和结构化的settings.py——这些文件本身不是静态资源,而是需要解释器运行的代码,不适合硬编码进镜像层。
更关键的是,Docker部署Django项目项目往往依赖大量第三方库(如django-celery、django-filter、django-rest-framework),每个库又有自己的依赖链。若直接用ADD或COPY整个项目,会导致镜像体积膨胀至5GB甚至10GB,启动缓慢、维护困难,违背了Docker轻量、快速、可复用的初衷。
在Docker部署Django项目实践中,Docker更像一个"超级虚拟Python解释器",它提供隔离、一致、可移植的运行环境,而非成品打包工具。其优势在于:
- 环境一致性:开发、测试、生产环境完全一致,避免"在我机器上能跑"的尴尬
- 资源隔离:多个Django应用互不影响,资源可配额
- 版本控制:镜像可打标签、回滚,便于版本管理
因此,Docker部署Django项目的正确姿势是:用Dockerfile构建一个干净、精简的Python运行环境,将项目代码作为"挂载卷"或"最终阶段复制"的资源,而非全程打包进镜像。
python manage.py runserver即可" → 生产环境必须用Gunicorn/uWSGI
以下是一个典型错误的Dockerfile示例(请勿使用):
1FROM python:3.10
2COPY . /app
3RUN pip install -r requirements.txt
4CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
问题在于:
- 未使用
--no-cache-dir,缓存导致镜像膨胀 - 未分离依赖安装与代码复制,修改代码会重装所有依赖
- 未设置非root用户,存在安全风险
- 未优化层顺序,无法利用Docker缓存
我们将在下一节提供经过生产验证的优化方案。
前置准备:构建前的必备条件
在开始Docker部署Django项目前,请确保以下环境已就绪:
- 已安装Docker(推荐版本 ≥ 20.10)和Docker Compose(≥ 2.0)
- 已安装Python 3.8+(开发环境)
- 已创建Django项目(建议使用
django-admin startproject myproject) - 项目中已包含
requirements.txt(可用pip freeze > requirements.txt生成) - 已配置数据库连接(推荐PostgreSQL,需提前启动服务)
docker run --name postgres -e POSTGRES_PASSWORD=mysecretpassword -p 5432:5432 -d postgres:14-alpine快速启动。
推荐以下标准化项目结构,便于后续维护与部署:
1myproject/
2├── Dockerfile
3├── docker-compose.yml
4├── requirements.txt
5├── .env.example
6├── .dockerignore
7└── myproject/
8 ├── manage.py
9 ├── settings.py
10 ├── urls.py
11 └── wsgi.py
其中:
.dockerignore:排除__pycache__、.git、.env等无关文件,减小镜像体积.env.example:环境变量模板,供团队协作使用docker-compose.yml:定义服务依赖(Django + PostgreSQL + Redis等)
.dockerignore示例(防止无关文件进入构建上下文):
1__pycache__
2.pyc
3.pyo
4.log
5.env
6.git
7.idea
8.vscode
9node_modules
10venv/
11db.sqlite3
.env.example示例(环境变量模板):
1DEBUG=False
2SECRET_KEY=your-secret-key-here
3DATABASE_URL=postgresql://user:password@db:5432/dbname
4REDIS_URL=redis://redis:6379/0
5ALLOWED_HOSTS=
这些文件将作为后续Docker部署Django项目配置的基础。
Dockerfile详解:生产级构建方案
以下是一个经过生产验证的、兼顾体积、安全、可维护性的Dockerfile:
1# 基于精简版Python镜像,体积仅200MB左右
2FROM python:3.10-slim
3
4# 设置工作目录
5WORKDIR /app
6
7# 安装构建依赖(用于编译某些需要C扩展的包)
8RUN apt-get update && apt-get install -y --no-install-recommends
9 build-essential
10 libpq-dev
11 && rm -rf /var/lib/apt/lists/
12
13# 创建非root用户,提升安全性
14RUN useradd --create-home --shell /bin/bash appuser
15USER appuser
16
17# 先复制依赖文件(利用Docker层缓存)
18COPY --chown=appuser:appuser requirements.txt .
19
20# 安装依赖(关键:禁用缓存,减少体积)
21RUN pip install --no-cache-dir -r requirements.txt
22
23# 复制项目代码(在依赖安装后,避免每次修改代码重装依赖)
24COPY --chown=appuser:appuser . .
25
26# 暴露端口(默认8000)
27EXPOSE 8000
28
29# 默认启动命令(开发环境可用,生产环境请用Gunicorn)
30CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
- 先复制
requirements.txt再安装依赖 → 修改代码不会重装包- 使用
python:3.10-slim → 镜像体积控制在250MB内- 禁用
pip缓存 → 避免冗余文件- 非root用户运行 → 符合安全最佳实践
- 清理
apt-get缓存 → 减小镜像体积
适用于需要编译C扩展包(如psycopg2-binary)的项目,可进一步减小最终镜像体积:
1# 第一阶段:构建阶段
2FROM python:3.10-slim AS builder
3
4WORKDIR /build
5RUN apt-get update && apt-get install -y --no-install-recommends build-essential libpq-dev
6 && rm -rf /var/lib/apt/lists/
7
8COPY requirements.txt .
9RUN python -m venv /opt/venv
10ENV PATH="/opt/venv/bin:$PATH"
11RUN pip install --no-cache-dir -r requirements.txt
12
13# 第二阶段:运行阶段
14FROM python:3.10-slim
15
16WORKDIR /app
17RUN useradd --create-home --shell /bin/bash appuser && chown -R appuser:appuser /app
18USER appuser
19
20# 从构建阶段复制虚拟环境(仅保留必要文件)
21COPY --from=builder /opt/venv /opt/venv
22ENV PATH="/opt/venv/bin:$PATH"
23
24# 复制项目代码
25COPY --chown=appuser:appuser . .
26
27EXPOSE 8000
28CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
gcc)不会污染运行环境。
支持热重载,适合本地开发阶段使用:
1FROM python:3.10-slim
2
3WORKDIR /app
4RUN apt-get update && apt-get install -y --no-install-recommends vim curl
5 && rm -rf /var/lib/apt/lists/
6
7COPY requirements.txt .
8RUN pip install --no-cache-dir -r requirements.txt
9
10# 挂载本地代码目录(实现热重载)
11VOLUME ["/app"]
12
13CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
配合docker-compose.yml挂载本地目录:
1services:
2 web:
3 build: .
4 volumes:
5 - .:/app
6 ports:
7 - "8000:8000"
环境配置:Django与Docker的协同设置
在settings.py中,应使用环境变量动态配置数据库:
1# settings.py
2import os
3import dj_database_url
4
5# 从环境变量读取DATABASE_URL
6DATABASES = {
7 'default': dj_database_url.config(default='postgres://user:password@localhost:5432/dbname')
8}
若未安装dj-database-url,请添加到requirements.txt:
1dj-database-url==1.3.0
.env文件(配合python-dotenv),开发环境可直接在docker-compose.yml中设置。
以下是在Docker环境中必须正确设置的Django配置:
- ALLOWED_HOSTS:必须包含容器内访问的域名或IP(如
[""]或["localhost", "127.0.0.1"]) - DEBUG:生产环境务必设为
False,避免泄露敏感信息 - STATIC_ROOT / MEDIA_ROOT:需指向容器内可写目录(如
/app/static/) - CSRF_TRUSTED_ORIGINS:若使用HTTPS或反向代理,需添加对应域名
示例配置:
1# settings.py
2ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "localhost").split(",")
3DEBUG = os.getenv("DEBUG", "False") == "True"
4CSRF_TRUSTED_ORIGINS = ["https://yourdomain.com"]
5STATIC_ROOT = "/app/static/"
6MEDIA_ROOT = "/app/media/"
在Docker中,静态文件需在构建时或运行时收集:
1# Dockerfile末尾添加
2RUN python manage.py collectstatic --noinput
但更推荐的做法是使用卷挂载,避免每次重建镜像:
1# docker-compose.yml
2services:
3 web:
4 volumes:
5 - static_volume:/app/static
6 - media_volume:/app/media
7
8volumes:
9 static_volume:
10 media_volume:
启动容器后,首次运行docker-compose run web python manage.py collectstatic收集静态文件。
生产部署:从开发到生产的跃迁
Django内置的runserver是单线程、不支持并发的开发服务器,仅适合本地调试。生产环境必须使用:
- Gunicorn:轻量、易配置,适合中小型项目
- uWSGI:功能更强大,适合大型高并发场景
- Hypercorn:支持ASGI,适合Django 3.1+的异步特性
以Gunicorn为例,修改Dockerfile的启动命令:
1# 安装Gunicorn
2RUN pip install --no-cache-dir gunicorn==20.1.0
3
4# 启动命令(替换runserver)
5CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "3", "--threads", "2"]
-
--workers:工作进程数(通常为CPU核心数×2+1)-
--threads:每进程线程数(适合I/O密集型任务)-
--bind:监听地址和端口
生产环境通常使用Nginx作为反向代理和静态文件服务器:
1# docker-compose.yml
2services:
3 web:
4 image: nginx:alpine
5 ports:
6 - "80:80"
7 volumes:
8 - ./nginx.conf:/etc/nginx/nginx.conf:ro
9 - static_volume:/app/static
10 - media_volume:/app/media
11 depends_on:
12 - app
13
14 app:
15 build: .
16 expose:
17 - "8000"
18 volumes:
19 - static_volume:/app/static
20 - media_volume:/app/media
nginx.conf示例:
1events { worker_connections 1024; }
2
3http {
4 upstream django {
5 server app:8000;
6 }
7
8 server {
9 listen 80;
10 server_name example.com;
11
12 location / {
13 proxy_pass http://django;
14 proxy_set_header Host $host;
15 proxy_set_header X-Real-IP $remote_addr;
16 }
17
18 location /static/ {
19 alias /app/static/;
20 }
21
22 location /media/ {
23 alias /app/media/;
24 }
25 }
26}
创建gunicorn_config.py管理复杂配置:
1# gunicorn_config.py
2bind = "0.0.0.0:8000"
3workers = 3
4threads = 2
5timeout = 30
6accesslog = "-" # 输出到stdout
7errorlog = "-"
8capture_output = True
9worker_class = "sync"
10preload_app = True
修改Dockerfile启动命令:
1CMD ["gunicorn", "myproject.wsgi:application", "-c", "gunicorn_config.py"]
故障排查:常见问题与解决方案
原因:未正确设置COPY路径或.dockerignore排除了文件
修复:检查Dockerfile中COPY requirements.txt .的路径是否与构建上下文一致
原因:依赖未正确安装或pip install命令被跳过
修复:确保pip install --no-cache-dir -r requirements.txt在COPY . .之前执行
原因:未等待数据库服务启动完成,或DATABASE_URL配置错误
修复:使用wait-for-it.sh脚本或docker-compose的depends_on配合健康检查
原因:未运行collectstatic或Nginx配置错误
修复:在Dockerfile末尾添加RUN python manage.py collectstatic --noinput,或检查nginx.conf的location /static/路径
使用以下命令实时查看容器日志:
1# 查看所有服务日志
2docker-compose logs -f
3
3# 查看特定服务日志
4docker-compose logs -f web
5
6# 进入容器调试
7docker-compose exec web bash
在容器内可执行:
1# 检查依赖是否安装
2pip list | grep django
3
4# 测试数据库连接
5python manage.py dbshell
6
7# 手动运行collectstatic
8python manage.py collectstatic --noinput
- 确认
DEBUG=False(生产环境) - 检查
SECRET_KEY是否从环境变量读取,未硬编码 - 确认
ALLOWED_HOSTS已正确设置 - 检查
SECURE_SSL_REDIRECT、SESSION_COOKIE_SECURE等安全设置 - 确认使用非root用户运行应用
- 定期更新基础镜像(
python:3.10-slim)
进阶技巧:性能优化与最佳实践
通过以下技巧,可将Django镜像控制在200MB以内:
- 使用
python:slim或python:alpine基础镜像 - 在
Dockerfile末尾清理apt-get缓存 - 使用
pip install --no-cache-dir - 移除开发依赖(如
ipdb、django-debug-toolbar)
构建完成后,用docker history 镜像名分析各层大小,针对性优化。
使用Redis缓存Django会话和查询结果:
1# settings.py
2CACHES = {
3 'default': {
4 'BACKEND': 'django_redis.cache.RedisCache',
5 'LOCATION': os.getenv('REDIS_URL', 'redis://redis:6379/1'),
6 'OPTIONS': {
7 'CLIENT_CLASS': 'django_redis.client.DefaultClient',
8 }
9 }
10}
django-redis已安装:pip install django-redis
在docker-compose.yml中添加健康检查:
1services:
2 web:
3 healthcheck:
4 test: ["CMD", "curl", "-f", "http://localhost:8000/health/"]
5 interval: 30s
6 timeout: 10s
7 retries: 3
8 start_period: 40s
9 restart: unless-stopped
在Django中添加健康检查端点:
1# urls.py
2from django.urls import path
3from django.http import JsonResponse
4
5def health_check(request):
6 return JsonResponse({'status': 'ok'})
7
8urlpatterns = [
9 path('health/', health_check),
10 # ...其他URL
11]
推荐使用python-dotenv统一管理环境变量:
1# .env(开发环境)
2DEBUG=True
3SECRET_KEY=dev-secret-key
4DATABASE_URL=sqlite:///db.sqlite3
1# docker-compose.yml
2services:
3 web:
4 env_file:
5 - .env.production
6 environment:
7 - DEBUG=False
重要:生产环境的.env.production应通过CI/CD注入,而非提交到Git仓库。
常见问题(FAQ)
A:常见原因包括:
- 未指定CMD或ENTRYPOINT
- 应用启动失败(检查日志:
docker-compose logs) - 端口被占用(确保
EXPOSE与启动命令端口一致)
解决方案:在Dockerfile末尾添加CMD ["tail", "-f", "/dev/null"]临时保持容器运行,再逐步排查。
A:推荐使用docker-compose.yml定义服务依赖:
1services:
2 db:
3 image: postgres:14-alpine
4 environment:
5 POSTGRES_DB: mydb
6 POSTGRES_USER: user
7 POSTGRES_PASSWORD: pass
8 volumes:
9 - postgres_data:/var/lib/postgresql/data/
10
11 web:
12 depends_on:
13 - db
14 environment:
15 DATABASE_URL: postgres://user:pass@db:5432/mydb
16
17volumes:
18 postgres_data:
A:使用卷挂载代码目录,并在启动命令中添加--noreload参数(避免Django子进程无法同步文件):
1# docker-compose.yml
2services:
3 web:
4 volumes:
5 - .:/app
6 command: python manage.py runserver --noreload 0.0.0.0:8000
A:在Nginx层配置SSL证书:
1# nginx.conf
2server {
3 listen 443 ssl;
4 ssl_certificate /etc/nginx/ssl/cert.pem;
5 ssl_certificate_key /etc/nginx/ssl/key.pem;
6 # ...其他配置
7}
将证书文件挂载到Nginx容器,并在settings.py中设置:
1SECURE_SSL_REDIRECT = True
2SESSION_COOKIE_SECURE = True
3CSRF_COOKIE_SECURE = True
A:推荐使用独立的迁移服务:
1# docker-compose.yml
2services:
3 web:
4 command: gunicorn myproject.wsgi:application -c gunicorn_config.py
5
6 migrate:
7 build: .
8 command: python manage.py migrate
9 depends_on:
10 - db
11
12 create-superuser:
13 build: .
14 command: python manage.py createsuperuser --noinput
15 depends_on:
16 - db
首次部署时运行:docker-compose run migrate,后续修改模型后重新执行。
根据社区反馈,以下问题高频出现:
- Docker Compose vs Kubernetes:小型项目建议用Docker Compose;超大规模集群可考虑K8s
- 是否需要Redis?:若使用缓存、会话或Celery任务队列,必须有Redis;否则可选
- 如何实现CI/CD?:推荐GitHub Actions + Docker Hub,每次Push自动构建镜像
- 如何备份数据库?:使用
pg_dump定期导出数据卷,或用pgbackups工具
附录:资源推荐
- django-environ:简化环境变量管理
- gunicorn:生产环境必备WSGI服务器
- whitenoise:Django静态文件服务优化
- django-debug-toolbar:开发调试神器(生产环境禁用)