Django 从 0 到 1 打造完整电商平台:使用 Docker 容器化电商项目
IT策士 10余年一线大厂经验,专注 IT 思维、架构、职场进阶。我会在公众号、今日头条持续发布最新文章,助你少走弯路。
大家好,我是IT策士。上一篇我们用 Nginx + uWSGI 手动部署了电商项目,但每换一台服务器就要重新安装 Python、配置环境、改一堆路径,整个过程繁琐又容易出错。今天,我们直接迈入现代部署的标准答案——Docker 容器化。
Docker 能把你整个应用和环境打包成一个“集装箱”,做到一次构建,处处运行。不管是在你的开发机、测试服务器,还是云平台,只要装上 Docker,一行命令就能让整个电商系统跑起来。更妙的是,我们还不用再手动管理多个进程——Docker Compose 能编排 Nginx、Django、Celery、Redis、PostgreSQL 等所有服务,一键启动,一键停止。今天就带大家把我们的电商平台完整容器化。
一、为什么要容器化?
回顾上一节的部署流程:装系统依赖、创建虚拟环境、装包、配 uWSGI、配 Nginx、配 systemd……服务器越多,工作量翻倍。Docker 解决了三大痛点:
-
环境一致性:“我这能跑,你那不行”成为历史。Docker 镜像保证了开发、测试、生产环境完全相同。
-
快速部署:新服务器只需安装 Docker,拉取镜像即可运行,不用再踩环境坑。
-
服务编排:Docker Compose 把 Nginx、Django、Celery、Redis、数据库定义在一个 YAML 文件里,一条命令全部启动。
容器化架构图:
浏览器
↓
Nginx 容器 (80端口) ← 处理静态文件,反向代理
↓
Django + uWSGI 容器 (内网,8000端口)
↓
PostgreSQL 容器 (5432) Redis 容器 (6379)
↑
Celery Worker 容器 (连接 Redis)
每个服务一个容器,相互独立又协同工作。
二、准备项目:生产级数据库与配置
容器化后,SQLite 不适合多容器并发访问,我们将数据库升级为 PostgreSQL。同时,配置从环境变量读取,便于在不同环境切换。
2.1 修改 settings_production.py
在项目配置目录 django_ecommerce/ 下已有 settings_production.py(第 27 篇创建),我们需要改成通过环境变量配置数据库和 Redis:
import os
from .settings import *
DEBUG = os.getenv('DJANGO_DEBUG', 'False') == 'True'
SECRET_KEY = os.getenv('DJANGO_SECRET_KEY', 'change-me-in-production')
ALLOWED_HOSTS = os.getenv('DJANGO_ALLOWED_HOSTS', '*').split(',')
# PostgreSQL 数据库(容器内通过服务名连接)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.getenv('POSTGRES_DB', 'django_ecommerce'),
'USER': os.getenv('POSTGRES_USER', 'django_user'),
'PASSWORD': os.getenv('POSTGRES_PASSWORD', 'django_pass'),
'HOST': os.getenv('DB_HOST', 'db'), # docker-compose 中的服务名
'PORT': os.getenv('DB_PORT', '5432'),
}
}
# Redis 配置
CELERY_BROKER_URL = f"redis://{os.getenv('REDIS_HOST', 'redis')}:6379/0"
CELERY_RESULT_BACKEND = f"redis://{os.getenv('REDIS_HOST', 'redis')}:6379/1"
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': f"redis://{os.getenv('REDIS_HOST', 'redis')}:6379/2",
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
},
}
}
# 静态文件
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
# 媒体文件
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
注意:数据库引擎需要 psycopg2,稍后在 Dockerfile 中安装。
2.2 创建环境变量文件
在项目根目录创建 .env(不要提交到 Git):
DJANGO_DEBUG=False
DJANGO_SECRET_KEY=your-ultra-secret-key-here
DJANGO_ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com,localhost,127.0.0.1
POSTGRES_DB=django_ecommerce
POSTGRES_USER=django_user
POSTGRES_PASSWORD=strong_password_123
DB_HOST=db
REDIS_HOST=redis
三、编写 Dockerfile
在项目根目录创建 Dockerfile,用于构建 Django 应用镜像:
# 基础镜像
FROM python:3.11-slim
# 设置环境变量
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
DJANGO_SETTINGS_MODULE=django_ecommerce.settings_production
# 安装系统依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
# 创建应用目录
WORKDIR /app
# 安装 Python 依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt uwsgi psycopg2-binary
# 复制项目代码
COPY . .
# 收集静态文件
RUN python manage.py collectstatic --noinput
# 创建非 root 用户
RUN useradd -m django && chown -R django:django /app
USER django
# 暴露端口(uWSGI 将在 8000 端口监听)
EXPOSE 8000
# 默认命令:启动 uWSGI
CMD ["uwsgi", "--ini", "uwsgi_docker.ini"]
这里使用了 uwsgi_docker.ini,不同于之前的 Unix socket,容器中我们直接监听 TCP 端口,便于 Nginx 容器连接。
在项目根目录创建 uwsgi_docker.ini:
[uwsgi]
chdir = /app
module = django_ecommerce.wsgi:application
master = true
processes = 4
threads = 2
socket = :8000
buffer-size = 32768
vacuum = true
uid = django
gid = django
四、编写 Nginx 配置(容器版)
在项目根目录创建 nginx/nginx.conf:
upstream django {
server web:8000; # web 是 Django 容器在 docker-compose 中的服务名
}
server {
listen 80;
server_name _;
location /static/ {
alias /app/staticfiles/;
expires 30d;
}
location /media/ {
alias /app/media/;
expires 7d;
}
location / {
uwsgi_pass web:8000; # 直接通过 HTTP 代理,因为 uWSGI 监听 8000
include uwsgi_params;
# 如果使用 uwsgi 协议:uwsgi_pass web:8000; include uwsgi_params;
}
client_max_body_size 10M;
}
但注意:上面我们用 uwsgi_pass 会尝试 uwsgi 协议。由于 uWSGI 监听的是 HTTP 端口(socket = :8000),这里应该用 proxy_pass http://web:8000; 或者改用 uWSGI 协议监听。我们统一用 HTTP 代理模式,简单通用。修改配置:
server {
listen 80;
server_name _;
location /static/ {
alias /app/staticfiles/;
expires 30d;
}
location /media/ {
alias /app/media/;
expires 7d;
}
location / {
proxy_pass http://web:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
client_max_body_size 10M;
}
并且 uWSGI 的 socket = :8000 需要改为 http-socket = :8000,或者直接用 HTTP 协议:protocol = http。
更新 uwsgi_docker.ini:
[uwsgi]
chdir = /app
module = django_ecommerce.wsgi:application
master = true
processes = 4
threads = 2
http = :8000 # 直接使用 HTTP 协议
buffer-size = 32768
vacuum = true
uid = django
gid = django
五、编写 docker-compose.yml
这是编排所有服务的核心文件。在项目根目录创建 docker-compose.yml:
version: '3.8'
services:
# PostgreSQL 数据库
db:
image: postgres:15
container_name: ecom_db
volumes:
- postgres_data:/var/lib/postgresql/data/
environment:
POSTGRES_DB: ${POSTGRES_DB:-django_ecommerce}
POSTGRES_USER: ${POSTGRES_USER:-django_user}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-django_pass}
ports:
- "5432:5432" # 可选,方便本地调试
restart: unless-stopped
# Redis
redis:
image: redis:7-alpine
container_name: ecom_redis
volumes:
- redis_data:/data
restart: unless-stopped
# Django + uWSGI 应用
web:
build: .
container_name: ecom_web
command: uwsgi --ini /app/uwsgi_docker.ini
volumes:
- .:/app
- static_volume:/app/staticfiles
- media_volume:/app/media
environment:
- DJANGO_DEBUG=${DJANGO_DEBUG:-False}
- DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY}
- DJANGO_ALLOWED_HOSTS=${DJANGO_ALLOWED_HOSTS}
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- DB_HOST=db
- REDIS_HOST=redis
depends_on:
- db
- redis
restart: unless-stopped
# Celery Worker
celery:
build: .
container_name: ecom_celery
command: celery -A django_ecommerce worker -l info
volumes:
- .:/app
environment:
- DJANGO_DEBUG=${DJANGO_DEBUG:-False}
- DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY}
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- DB_HOST=db
- REDIS_HOST=redis
depends_on:
- db
- redis
restart: unless-stopped
# Nginx
nginx:
image: nginx:1.25
container_name: ecom_nginx
ports:
- "80:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
- static_volume:/app/staticfiles
- media_volume:/app/media
depends_on:
- web
restart: unless-stopped
# 命名卷
volumes:
postgres_data:
redis_data:
static_volume:
media_volume:
关键点:
-
数据库和 Redis 数据通过命名卷持久化,容器删除数据不丢失。
-
Django 应用通过
build: .从 Dockerfile 构建。 -
depends_on确保启动顺序,但不保证数据库完全就绪,我们可以在 web 启动前执行迁移命令,或使用wait-for-it.sh。后面会用一个初始化脚本处理。 -
Nginx 静态文件通过共享卷
static_volume访问,这是 Django 收集后的静态文件。
六、处理数据库迁移和初始化
我们需要在 Django 容器首次启动时自动执行迁移和创建超级用户。创建一个初始化脚本 entrypoint.sh:
#!/bin/sh
set -e
# 等待数据库就绪
echo "Waiting for PostgreSQL..."
while ! nc -z db 5432; do
sleep 1
done
echo "PostgreSQL started"
# 迁移
python manage.py migrate --noinput
# 初始化商品数据(仅首次,加判断)
python manage.py shell -c "from products.models import Category; print('ok')" 2>/dev/null || python manage.py init_product_data
# 创建超级用户(若不存在)
python manage.py shell -c "
from django.contrib.auth import get_user_model;
User = get_user_model();
if not User.objects.filter(username='admin').exists():
User.objects.create_superuser('admin', 'admin@example.com', 'admin123')
"
# 启动 uWSGI
exec uwsgi --ini /app/uwsgi_docker.ini
在 Dockerfile 中复制并启用这个脚本:
...
COPY . .
COPY entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
...
CMD ["/app/entrypoint.sh"]
同时,entrypoint.sh 需要 netcat 工具来检测数据库端口。在 Dockerfile 中安装:
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc libpq-dev netcat-openbsd && rm -rf /var/lib/apt/lists/*
七、构建和启动容器
确保 .env 和 nginx/nginx.conf、entrypoint.sh 已就绪。在项目根目录执行:
docker-compose up -d --build
-d 表示后台运行,--build 强制重新构建镜像。
控制台输出示例:
[+] Building 15.2s (15/15) FINISHED
=> [internal] load build definition from Dockerfile
=> => transferring dockerfile: 567B
=> [internal] load .dockerignore
...
=> [web] exporting to image
=> => exporting layers
=> => writing image sha256:abc123...
=> [web] naming to docker.io/library/django_ecommerce_web
[+] Running 6/6
⠿ Network django_ecommerce_default Created
⠿ Container ecom_redis Started
⠿ Container ecom_db Started
⠿ Container ecom_web Started
⠿ Container ecom_celery Started
⠿ Container ecom_nginx Started
查看运行状态:
控制台输出:
NAME COMMAND SERVICE STATUS
ecom_db "docker-entrypoint.s…" db Up 2 minutes
ecom_redis "docker-entrypoint.s…" redis Up 2 minutes
ecom_web "/app/entrypoint.sh" web Up 2 minutes
ecom_celery "celery -A django_ec…" celery Up 2 minutes
ecom_nginx "/docker-entrypoint.…" nginx Up 2 minutes (healthy)
八、查看日志与调试
查看所有服务的日志(类似 tail -f):
查看特定服务日志:
docker-compose logs -f web # Django 日志
docker-compose logs -f celery # Celery 日志
docker-compose logs -f nginx # Nginx 日志
控制台输出示例(web 服务初始化日志):
ecom_web | Waiting for PostgreSQL...
ecom_web | PostgreSQL started
ecom_web | Operations to perform:
ecom_web | Apply all migrations: admin, auth, cart...
ecom_web | Running migrations:
ecom_web | Applying contenttypes.0001_initial... OK
...
ecom_web | Superuser created successfully.
ecom_web | [uWSGI] getting INI configuration from /app/uwsgi_docker.ini
ecom_web | *** Starting uWSGI 2.0.23 (64bit) ***
ecom_web | spawned uWSGI worker 1 (pid: 11)
九、验证部署
浏览器访问 http://服务器IP 或 http://localhost(如果是本地 Docker),可以看到电商首页。
验证静态文件:图片、CSS、JS 正常加载。
验证动态功能:
-
注册新用户(终端可查看 Celery 异步发送邮件日志);
-
浏览商品;
-
加入购物车,下单。
验证支付:需要支付宝沙箱回调地址指向服务器公网 IP 或域名(可能需要配置 ALIPAY_RETURN_URL 和 ALIPAY_NOTIFY_URL)。可以在 .env 中增加相应变量,并在 settings_production.py 中读取环境变量:
ALIPAY_RETURN_URL = os.getenv('ALIPAY_RETURN_URL', 'http://localhost/payment/return/')
ALIPAY_NOTIFY_URL = os.getenv('ALIPAY_NOTIFY_URL', 'http://localhost/payment/notify/')
常用管理命令:
-
进入容器 Shell:
docker-compose exec web bash -
执行 Django 管理命令:
docker-compose exec web python manage.py createsuperuser -
停止所有服务:
docker-compose down -
停止并删除数据卷(⚠️ 会清空数据库):
docker-compose down -v
十、生产环境注意事项
-
密钥管理:生产环境需使用真实的复杂密钥,避免将
.env提交到仓库。可以使用 Docker secrets 或外部配置工具。 -
静态/媒体文件存储:容器化环境中,媒体文件最好使用对象存储(如 S3),避免文件丢失。对于小规模项目,挂载本地卷即可。
-
数据库备份:定期备份 PostgreSQL 数据卷,或导出 SQL 文件。
-
日志收集:可将日志统一输出到 stdout/stderr,配合 Docker 的日志驱动进行收集。也可以挂载日志卷。
-
资源限制:在
docker-compose.yml中为服务设置mem_limit和cpus,防止单个容器占用过多主机资源。
十一、总结与下集预告
今天我们完成了电商项目的 Docker 容器化,彻底告别“环境不一致”的痛苦:
-
使用 Dockerfile 构建了 Django + uWSGI 镜像,支持多进程并发;
-
用 docker-compose 编排了 Nginx、Django、Celery、Redis、PostgreSQL 五大服务;
-
配置了生产级 PostgreSQL 数据库,通过环境变量灵活配置;
-
编写了 entrypoint 脚本自动完成数据库迁移和初始化;
-
实现了服务间网络通信、数据持久化卷、日志查看等生产必备功能。
现在,整个电商平台可以在任何安装了 Docker 的服务器上,通过两条命令(git clone + docker-compose up)迅速上线。这是现代部署的标准范式。但我们的部署还差最后一环——HTTPS 安全证书。第 29 篇,我将带大家配置 HTTPS 与域名绑定,让用户通过 https://www.yoursite.com 安全访问,真正达到上线标准。
想了解更多还可以去公众号、今日头条搜索「IT策士」,一起升级 IT 思维 !
*本文为《Django 从 0 到 1 打造完整电商平台》系列第 28 篇。
*

告别环境不一致!Dockerfile 构建 Django+uWSGI 镜像,Compose 编排 Nginx、Celery、Redis、PostgreSQL 五大服务,环境变量灵活配置,entrypoint 自动初始化,一键部署电商全家桶。
浙公网安备 33010602011771号