Skip to content

章节3:部署与运维


学习目标

完成本章学习后,你将能够:

  1. 编写 Dockerfile 和 docker-compose.yml,实现全栈项目的容器化打包
  2. 配置 Nginx 反向代理与静态资源托管
  3. 使用 Gunicorn 部署 Django 生产级服务
  4. 配置域名解析与 HTTPS 证书(Let's Encrypt SSL)
  5. 搭建日志监控体系,使用 Locust 进行性能压测

3.1 Docker 容器化打包

3.1.1 Docker 核心概念

定义:Docker 是一种容器化技术,将应用及其依赖打包在一个轻量级、可移植的容器中,保证开发、测试、生产环境的一致性。

概念说明类比
Image(镜像)只读模板,包含运行环境与代码安装盘
Container(容器)镜像的运行实例,可读写虚拟机
Dockerfile构建镜像的指令清单配方
Volume(数据卷)持久化容器数据外接硬盘
docker-compose编排多容器服务乐队指挥

3.1.2 后端 Dockerfile

dockerfile
# backend/Dockerfile

# ===== 构建阶段 =====
FROM python:3.11-slim AS builder

WORKDIR /app

# 安装 Python 依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt \
    && pip install gunicorn gevent

# ===== 运行阶段 =====
FROM python:3.11-slim

WORKDIR /app

# 从构建阶段复制依赖
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=builder /usr/local/bin/gunicorn /usr/local/bin/gunicorn

# 复制项目代码
COPY . .

# 创建非 root 用户运行(安全最佳实践)
RUN useradd -m -r appuser && chown -R appuser:appuser /app
USER appuser

EXPOSE 8000

# 启动命令(使用 Gunicorn)
CMD ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000", \
     "--workers", "4", "--worker-class", "gevent", "--timeout", "120"]

3.1.3 前端 Dockerfile

dockerfile
# frontend/Dockerfile

# ===== 构建阶段 =====
FROM node:20-alpine AS build

WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build

# ===== Nginx 运行阶段 =====
FROM nginx:alpine

# 复制构建产物
COPY --from=build /app/dist /usr/share/nginx/html

# 复制 Nginx 配置
COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

3.1.4 docker-compose.yml 编排

yaml
# docker-compose.yml
version: '3.8'

services:
  # ===== MySQL 数据库 =====
  mysql:
    image: mysql:8.0
    container_name: ecommerce-mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
      - ./backend/init.sql:/docker-entrypoint-initdb.d/init.sql
    networks:
      - ecommerce-net
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

  # ===== Redis 缓存 =====
  redis:
    image: redis:7-alpine
    container_name: ecommerce-redis
    restart: always
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    networks:
      - ecommerce-net
    command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}

  # ===== Django 后端 =====
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: ecommerce-backend
    restart: always
    environment:
      DJANGO_SETTINGS_MODULE: config.settings.prod
      MYSQL_HOST: mysql
      REDIS_HOST: redis
      REDIS_PASSWORD: ${REDIS_PASSWORD}
    env_file:
      - .env
    ports:
      - "8000:8000"
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_started
    volumes:
      - static_volume:/app/static
      - media_volume:/app/media
    networks:
      - ecommerce-net

  # ===== Celery 异步任务 =====
  celery:
    build:
      context: ./backend
    container_name: ecommerce-celery
    restart: always
    command: celery -A config worker -l info --concurrency=4
    environment:
      DJANGO_SETTINGS_MODULE: config.settings.prod
    env_file:
      - .env
    depends_on:
      - backend
      - redis
    networks:
      - ecommerce-net

  # ===== Nginx 反向代理 =====
  nginx:
    image: nginx:alpine
    container_name: ecommerce-nginx
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
      - static_volume:/static
      - media_volume:/media
      - ./ssl:/etc/nginx/ssl
    depends_on:
      - backend
    networks:
      - ecommerce-net

volumes:
  mysql_data:
  redis_data:
  static_volume:
  media_volume:

networks:
  ecommerce-net:
    driver: bridge

3.1.5 构建与运行命令

bash
# 构建镜像
docker-compose build

# 启动所有服务
docker-compose up -d

# 查看运行状态
docker-compose ps

# 查看日志
docker-compose logs -f backend

# 执行 Django 管理命令
docker-compose exec backend python manage.py migrate
docker-compose exec backend python manage.py collectstatic --noinput

# 停止所有服务
docker-compose down

# 完全重建(含卷清理)
docker-compose down -v && docker-compose up -d --build

3.2 Nginx 反向代理配置

3.2.1 Nginx 核心概念

定义:Nginx 是一款高性能的 Web 服务器和反向代理服务器,承担请求分发、负载均衡、静态资源托管、SSL 终结等职责。

架构角色

客户端请求


Nginx(反向代理)

    ├──→ /api/*  →  Django Backend (8000)
    ├──→ /static/* → 静态文件目录
    ├──→ /media/*  → 上传媒体文件
    └──→ /*        →  Vue 前端 SPA

3.2.2 Nginx 配置示例

nginx
# nginx/nginx.conf
upstream django_backend {
    server backend:8000;       # docker-compose 服务名
    # server 127.0.0.1:8001;   # 可添加多个实现负载均衡
    # server 127.0.0.1:8002;
}

server {
    listen 80;
    server_name your-domain.com;
    return 301 https://$server_name$request_uri;  # HTTP → HTTPS 重定向
}

server {
    listen 443 ssl http2;
    server_name your-domain.com;

    # SSL 证书配置
    ssl_certificate     /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;

    # 请求大小限制(上传文件)
    client_max_body_size 20M;

    # ===== 静态文件 =====
    location /static/ {
        alias /static/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # ===== 媒体文件 =====
    location /media/ {
        alias /media/;
        expires 7d;
    }

    # ===== API 代理 =====
    location /api/ {
        proxy_pass http://django_backend;
        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;

        # WebSocket 支持(如需)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    # ===== Admin 后台 =====
    location /admin/ {
        proxy_pass http://django_backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # ===== Vue SPA(前端路由)=====
    location / {
        root   /usr/share/nginx/html;
        index  index.html;
        try_files $uri $uri/ /index.html;  # SPA 路由 fallback
    }
}

3.2.3 Nginx 常用运维命令

bash
# 测试配置文件语法
nginx -t

# 重载配置(不中断服务)
nginx -s reload

# 查看访问日志
tail -f /var/log/nginx/access.log

# 查看错误日志
tail -f /var/log/nginx/error.log

3.3 Gunicorn 生产级部署

3.3.1 Gunicorn 核心参数

定义:Gunicorn(Green Unicorn)是 Python 的 WSGI HTTP 服务器,用于在生产环境中运行 Django 应用,比 Django 自带的开发服务器更稳定、高性能。

bash
# 基础启动命令
gunicorn config.wsgi:application \
    --bind 0.0.0.0:8000 \
    --workers 4 \
    --worker-class gevent \
    --timeout 120 \
    --access-logfile /var/log/gunicorn/access.log \
    --error-logfile /var/log/gunicorn/error.log \
    --max-requests 10000 \
    --max-requests-jitter 2000

参数详解

参数推荐值说明
--workers(2 × CPU核数) + 1工作进程数,4 核推荐 9
--worker-classgevent异步 Worker,处理 I/O 密集型请求
--timeout120请求超时时间(秒)
--max-requests10000进程处理多少请求后重启(防内存泄漏)
--keep-alive5保持连接秒数

3.3.2 Gunicorn 配置文件

python
# backend/gunicorn.conf.py
import multiprocessing

bind = '0.0.0.0:8000'
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = 'gevent'
worker_connections = 1000
timeout = 120
graceful_timeout = 30
keepalive = 5

# 日志配置
accesslog = '/var/log/gunicorn/access.log'
errorlog = '/var/log/gunicorn/error.log'
loglevel = 'info'

# 进程名
proc_name = 'ecommerce-backend'

# 守护进程化
daemon = False  # Docker 中保持前台运行

3.3.3 生产环境 Django 配置

python
# backend/config/settings/prod.py
from .base import *
import environ

env = environ.Env()
environ.Env.read_env(os.path.join(BASE_DIR, '.env'))

# ===== 安全配置 =====
DEBUG = False
SECRET_KEY = env('DJANGO_SECRET_KEY')
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=['your-domain.com'])

# ===== CORS 配置 =====
CORS_ALLOWED_ORIGINS = env.list('CORS_ALLOWED_ORIGINS')
CORS_ALLOW_CREDENTIALS = True

# ===== 数据库 =====
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': env('MYSQL_DATABASE'),
        'USER': env('MYSQL_USER'),
        'PASSWORD': env('MYSQL_PASSWORD'),
        'HOST': env('MYSQL_HOST'),
        'PORT': '3306',
        'OPTIONS': {
            'charset': 'utf8mb4',
            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
        },
        'CONN_MAX_AGE': 60,  # 连接复用
    }
}

# ===== Redis 缓存 =====
CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': f"redis://:{env('REDIS_PASSWORD')}@{env('REDIS_HOST')}:6379/0",
        'OPTIONS': {'CLIENT_CLASS': 'django_redis.client.DefaultClient'},
    }
}

# ===== 安全头部 =====
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True

3.4 域名与 HTTPS 配置

3.4.1 域名解析

记录类型主机记录记录值说明
A@服务器公网IP根域名解析
Awww服务器公网IPwww 子域名
CNAMEapiyour-domain.comAPI 子域名别名

3.4.2 使用 Certbot 获取 SSL 证书

bash
# 安装 Certbot
apt install certbot python3-certbot-nginx -y

# 获取 SSL 证书(自动配置 Nginx)
certbot --nginx -d your-domain.com -d www.your-domain.com

# 证书续期(Let's Encrypt 90天有效期,自动续期)
certbot renew --dry-run

# 查看证书状态
certbot certificates

3.4.3 手动配置 SSL 证书

bash
# 将证书文件放到指定目录
mkdir -p /etc/nginx/ssl
# fullchain.pem  — 完整证书链
# privkey.pem    — 私钥文件

# 验证证书
openssl x509 -in /etc/nginx/ssl/fullchain.pem -text -noout

3.5 日志监控与性能压测

3.5.1 日志集中管理

定义:日志是运维的眼睛,记录系统运行状态、错误堆栈、请求信息,帮助快速定位问题。

python
# backend/config/settings/base.py  —— 日志配置
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
            'style': '{',
        },
        'json': {  # JSON 格式,方便 ELK 收集
            '()': 'pythonjsonlogger.jsonlogger.JsonFormatter',
            'format': '%(asctime)s %(levelname)s %(name)s %(message)s',
        },
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'verbose',
        },
        'file': {
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': '/var/log/django/app.log',
            'maxBytes': 10485760,  # 10MB
            'backupCount': 10,
            'formatter': 'json',
        },
        'error_file': {
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': '/var/log/django/error.log',
            'maxBytes': 10485760,
            'backupCount': 10,
            'level': 'ERROR',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['console', 'file'],
            'level': 'INFO',
            'propagate': False,
        },
        'django.request': {
            'handlers': ['error_file'],
            'level': 'ERROR',
            'propagate': False,
        },
    },
}

3.5.2 性能压测(Locust)

定义:性能压测通过模拟大量用户并发访问,检测系统的吞吐量(TPS)、响应时间、错误率,找到性能瓶颈。

python
# locustfile.py
from locust import HttpUser, task, between, tag

class ECommerceUser(HttpUser):
    """模拟电商平台的普通用户行为"""
    wait_time = between(1, 5)  # 用户操作间隔 1~5 秒
    token = None

    def on_start(self):
        """用户启动时登录"""
        res = self.client.post("/api/v1/auth/login/", json={
            "username": "test_user",
            "password": "test_pass"
        })
        if res.status_code == 200:
            self.token = res.json()["data"]["access_token"]
            self.client.headers = {"Authorization": f"Bearer {self.token}"}

    @tag("home")
    @task(3)
    def view_homepage(self):
        """浏览首页(权重 3)"""
        self.client.get("/api/v1/banners/")
        self.client.get("/api/v1/products/hot/")

    @tag("product")
    @task(5)
    def browse_products(self):
        """浏览商品(权重 5)"""
        self.client.get("/api/v1/products/", params={
            "page": 1, "page_size": 20, "ordering": "-sales_count"
        })

    @tag("cart")
    @task(2)
    def add_to_cart(self):
        """加入购物车(权重 2)"""
        self.client.post("/api/v1/cart/", json={
            "product_id": 1, "quantity": 1
        })

    @tag("order")
    @task(1)
    def create_order(self):
        """下单(权重 1)"""
        self.client.post("/api/v1/orders/", json={
            "cart_ids": [1, 2, 3], "address_id": 1
        })


# 运行命令
# locust -f locustfile.py --host=https://your-domain.com --web-host=0.0.0.0
# 浏览器访问 http://localhost:8089 设置并发数和生成速率

压测指标解读

指标含义健康值
RPS / TPS每秒请求数 / 每秒事务数≥ 500
P50 响应时间50% 的请求在此时间内完成≤ 200ms
P95 响应时间95% 的请求在此时间内完成≤ 800ms
P99 响应时间99% 的请求在此时间内完成≤ 2000ms
错误率非 2xx/3xx 响应占比≤ 1%

3.5.3 监控工具推荐

工具用途类别
Prometheus + Grafana系统指标采集与可视化面板基础设施监控
Sentry错误追踪与性能监控应用层监控
ELK Stack日志收集、搜索、分析日志管理
Supervisor进程守护与管理进程管理
Docker Healthcheck容器健康检查容器层
yaml
# docker-compose.yml 中集成 Healthcheck 示例
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:8000/api/v1/health/"]
  interval: 30s
  timeout: 10s
  retries: 3
  start_period: 30s

小结

知识回顾

  1. Docker 容器化:通过 Dockerfile 构建镜像,docker-compose 编排多服务,实现环境一致性
  2. Nginx 反向代理:动静分离、API 代理、SSL 终结、SPA 路由支持
  3. Gunicorn 生产部署:多进程 + Gevent 异步 Worker,参数调优,守护进程管理
  4. HTTPS 配置:Let's Encrypt 免费证书 + Certbot 自动化续期
  5. 监控与压测:日志分级管理 + Locust 性能压测 + 指标解读

练习与思考

  1. Docker 练习:为本节课项目编写一个完整的 Dockerfile(多阶段构建),并解释每一行的作用
  2. Nginx 配置:给 Nginx 添加 IP 白名单限制,只允许公司出口 IP 访问 /admin/
  3. Gunicorn 调优:在一台 4 核 8G 的服务器上,你应该设置多少 workers?为什么?
  4. 压测实践:使用 Locust 对本地项目进行压测,找出瓶颈(数据库/缓存/网络/代码),写出分析报告
  5. 思考题:前后端分离部署时,跨域问题可以在 Nginx 层解决,也可以在 Django CORS 层解决,谈谈两种方案的优缺点

Python 学习资料