生产环境用 Docker 部署 Vue3 + Flask + PostgreSQL 18,最稳妥的方式是使用 Docker Compose 编排三个服务:Nginx(托管 Vue 静态文件并反向代理 API)、Gunicorn(运行 Flask)、PostgreSQL 18。核心要点是:前端构建产物交给 Nginx,API 请求通过 Docker 内部 DNS 转发到 Flask 容器,数据库使用命名卷持久化。
一、项目结构建议
project/
├── docker-compose.prod.yml
├── backend/
│ ├── Dockerfile
│ ├── requirements.txt
│ └── app.py (或你的 Flask 入口)
├── frontend/
│ ├── Dockerfile
│ ├── nginx.conf
│ └── (Vue 项目源码)
└── ssl/ (如需 HTTPS)
├── nginx.crt
└── nginx.key
二、后端 Flask Dockerfile
生产环境不使用 flask run,而应使用 Gunicorn。
# backend/Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt gunicorn
COPY . .
EXPOSE 8000
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:8000", "app:app"]
三、前端 Vue3 Dockerfile(多阶段构建)
在构建阶段用 Node 生成 dist,运行阶段用 Nginx 托管。
# frontend/Dockerfile
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
四、Nginx 配置(关键:SPA 路由与 API 代理)
这是最容易出错的地方。必须用 location ^~ /api/ 优先匹配 API 请求,其余请求全部 fallback 到 index.html,否则 Vue Router 的页面刷新会 404。
# frontend/nginx.conf
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# API 请求转发给 Flask 容器(backend 是 compose 服务名)
location ^~ /api/ {
proxy_pass http://backend: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;
}
# Vue SPA 路由 fallback
location / {
try_files $uri $uri/ /index.html;
}
# 静态资源缓存(文件名带 hash 时可长缓存)
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
}
五、docker-compose.prod.yml
services:
db:
image: postgres:18
container_name: prod_db
restart: always
environment:
POSTGRES_USER: prod_user
POSTGRES_PASSWORD: ${DB_PASSWORD} # 建议用 .env 文件管理
POSTGRES_DB: prod_dbname
volumes:
- pgdata_prod:/var/lib/postgresql/data
# 生产环境不对外暴露 5432 端口,仅容器内部访问
backend:
build: ./backend
container_name: prod_backend
restart: always
environment:
DATABASE_URL: postgresql://prod_user:${DB_PASSWORD}@db:5432/prod_dbname
depends_on:
- db
expose:
- "8000"
# 注意:depends_on 只保证启动顺序,不保证数据库已就绪
# Flask 应用需有数据库连接重试逻辑
frontend:
build: ./frontend
container_name: prod_frontend
restart: always
ports:
- "80:80"
- "443:443"
volumes:
# 如需 HTTPS,挂载证书
- ./ssl/nginx.crt:/etc/nginx/ssl/nginx.crt:ro
- ./ssl/nginx.key:/etc/nginx/ssl/nginx.key:ro
depends_on:
- backend
volumes:
pgdata_prod:
六、关键注意事项
数据库初始化:PostgreSQL 官方镜像仅在数据目录首次为空时执行 /docker-entrypoint-initdb.d/ 下的脚本。如果已有数据卷,再修改 POSTGRES_PASSWORD 环境变量不会生效,需要进入容器手动改密码或删除卷重建。
服务间通信:Flask 连接数据库时,DATABASE_URL 中的主机名必须是 Compose 服务名 db,而不是 localhost 或 127.0.0.1。同理,Nginx 的 proxy_pass 写 http://backend:8000。
Gunicorn 监听地址:必须绑定 0.0.0.0,不能是 127.0.0.1,否则 Nginx 无法从容器外部访问到 Flask。
Vue 的 API 基地址:前端代码中请求 API 的 base URL 应设为 /api(相对路径),这样生产环境走 Nginx 代理,开发环境可走 Vite 代理。不要在代码里写死 http://backend:8000,那是容器内部地址,浏览器无法解析。
HTTPS 配置:上述 Nginx 配置只监听了 80。如需启用 443,在 frontend/nginx.conf 中增加 listen 443 ssl; 并配置 ssl_certificate 路径指向挂载的证书。
七、部署命令
# 启动
docker compose -f docker-compose.prod.yml up -d --build
# 查看日志
docker compose -f docker-compose.prod.yml logs -f backend
# 停止(保留数据)
docker compose -f docker-compose.prod.yml down
如果部署后出现 502,优先检查 Flask 容器是否正常运行、Gunicorn 是否绑定 0.0.0.0;出现 404,检查 Nginx 的 try_files 是否指向了正确的 index.html 路径。