Skip to content

58|Docker 构建与部署发布

开发环境前后端分开跑,生产环境需要打包成可部署的形态:前端构建为静态文件,后端打包成 Docker 镜像,Nginx 统一对外提供入口,静态资源和 API 请求都走 80/443 端口。这一篇把整个构建和部署链路串起来。

一、前端构建

bash
cd frontend
npm run build

构建产物在 frontend/dist/ 目录:

text
dist/
├── index.html
├── assets/
│   ├── index-xxxx.js
│   ├── index-xxxx.css
│   └── logo.png

index.html 引用了带哈希的 JS 和 CSS 文件,浏览器会长期缓存。Vite 自动处理文件名哈希,代码改动后客户端会加载新版本。

二、后端多阶段构建

dockerfile
# backend/Dockerfile
FROM python:3.12-slim as builder

WORKDIR /app
COPY pyproject.toml .
RUN pip install uv && uv venv && uv pip install -r pyproject.toml

# 生产镜像
FROM python:3.12-slim

WORKDIR /app
COPY --from=builder /app/.venv /app/.venv
COPY app/ ./app/

ENV PATH="/app/.venv/bin:$PATH"
ENV PYTHONPATH=/app

EXPOSE 8000

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
阶段作用
builder安装依赖,构建虚拟环境
生产镜像只复制虚拟环境和源码,体积小

构建:

bash
cd backend
docker build -t ops-console-backend .

三、Nginx 配置

Nginx 做两件事:

  1. 对外暴露 80 端口
  2. /api 请求转发给后端,/ 请求返回前端静态文件
nginx
# nginx.conf
server {
    listen 80;
    server_name localhost;

    # 前端静态资源
    location / {
        root /usr/share/nginx/html;
        try_files $uri $uri/ /index.html;
    }

    # API 请求转发给后端
    location /api/ {
        proxy_pass http://backend:8000/api/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

try_files $uri $uri/ /index.html 是单页应用的关键——Vue Router 的 history 模式下,刷新页面时 Nginx 需要返回 index.html,让前端路由接管。

四、Docker Compose

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

services:
  backend:
    build: ./backend
    environment:
      - DATABASE_URL=sqlite:///data/ops.db
      - SECRET_KEY=${SECRET_KEY}
    volumes:
      - ./data:/app/data
    restart: unless-stopped

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./frontend/dist:/usr/share/nginx/html:ro
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - backend
    restart: unless-stopped
服务作用
backendFastAPI 应用
nginx反向代理 + 静态文件服务

启动:

bash
docker-compose up -d

五、部署检查清单

部署前

  • [ ] 前端 npm run build 成功,无报错
  • [ ] 后端 docker build 成功
  • [ ] 环境变量 SECRET_KEY 已设置(生产环境必须随机生成)
  • [ ] docker-compose.ymlrestart: unless-stopped 已配置

部署后

  • [ ] 首页能打开,无 404
  • [ ] 登录接口正常,Token 能获取
  • [ ] 刷新页面不报错(Nginx try_files 配置正确)
  • [ ] 数据库文件已挂载到宿主机(volumes: ./data:/app/data

六、生产环境调整

HTTPS

生产环境必须用 HTTPS。可以用 Nginx 配置 SSL:

nginx
server {
    listen 443 ssl;
    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;
    ...
}

server {
    listen 80;
    return 301 https://$host$request_uri;   # HTTP 强制跳转 HTTPS
}

或用反向代理(云负载均衡、Traefik)处理 TLS 终止,Nginx 只跑 80。

多 Worker

dockerfile
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]

Worker 数通常设为 CPU 核心数 × 2 + 1

数据库换 PostgreSQL

SQLite 不适合生产并发:

yaml
services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: ops
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: ops_console
    volumes:
      - pg_data:/var/lib/postgresql/data

  backend:
    environment:
      - DATABASE_URL=postgresql://ops:${DB_PASSWORD}@db/ops_console

七、常见错误

前端刷新 404

nginx
# 错误:没有 try_files,刷新时 Nginx 找 /assets/123 这个文件,不存在就 404
location / {
    root /usr/share/nginx/html;
}

# 正确
location / {
    root /usr/share/nginx/html;
    try_files $uri $uri/ /index.html;   # 找不到文件时回退到 index.html
}

后端数据库文件没持久化

yaml
# 错误:容器删除后数据丢失
services:
  backend:
    build: ./backend

# 正确:挂载宿主机目录
services:
  backend:
    build: ./backend
    volumes:
      - ./data:/app/data

SECRET_KEY 用默认值

python
# 错误:生产环境用硬编码的弱密钥
SECRET_KEY = "dev-key"

# 正确:从环境变量读取
SECRET_KEY = os.environ["SECRET_KEY"]