Appearance
部署发布
27 篇用 Docker 打包了前后端,本地 docker-compose up 就能跑。部署到真实服务器还差几步:域名、HTTPS、生产配置、CI/CD 自动化发布。
一、生产环境跟开发的区别
| 开发 | 生产 | |
|---|---|---|
| 数据库 | SQLite 内存文件 | PostgreSQL 独立实例或云数据库 |
| 前后端 | 分别跑在 5173 和 8000 | Nginx 统一入口(同域,无 CORS) |
| CORS | allow_origins=["http://localhost:5173"] | 不需要(同域)或收紧到正式域名 |
| 调试模式 | DEBUG=true,--reload | DEBUG=false,多 worker |
| 密钥 | .env 随便填 | openssl rand -hex 32 生成 |
| HTTPS | 不需要 | 需要(Nginx + Let's Encrypt) |
生产环境的核心原则:关闭调试、收紧来源、加密通信、密钥安全。
二、Nginx 反向代理
生产环境通常在 Docker 前面再放一个 Nginx——它管域名、HTTPS 证书、把请求分发给前端容器和后端容器。
nginx
# /etc/nginx/conf.d/ops.example.com.conf
server {
listen 80;
server_name ops.example.com;
# HTTP 跳 HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name ops.example.com;
# TLS 证书
ssl_certificate /etc/letsencrypt/live/ops.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ops.example.com/privkey.pem;
# 前端:静态文件 + SPA 路由
location / {
proxy_pass http://127.0.0.1:80; # 前端容器的 Nginx
proxy_set_header Host $host;
}
# 后端:API 请求
location /api/ {
proxy_pass http://127.0.0.1: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;
}
}外层 Nginx 管域名和 HTTPS。location / 把非 API 请求转发给前端容器(前端容器里的 Nginx 提供静态文件),location /api/ 把 API 请求转发给后端容器。
X-Forwarded-For 和 X-Forwarded-Proto 必须传——后端需要知道客户端真实 IP(做审计日志)和原始协议(生成正确的回调 URL)。FastAPI 侧要信任这些头(配 ProxyHeadersMiddleware 或在 CORS 里处理)。
HTTPS 证书
用 Let's Encrypt 免费证书:
bash
# 安装 certbot
apt install certbot python3-certbot-nginx
# 申请证书(自动改 Nginx 配置)
certbot --nginx -d ops.example.com
# 自动续期(Let's Encrypt 证书 90 天过期)
certbot renew --dry-run三、生产环境变量
生产环境不依赖 .env 文件,直接在 docker-compose 里注入:
yaml
# docker-compose.prod.yml
services:
backend:
environment:
- DATABASE_URL=postgresql://appuser:${DB_PASSWORD}@db:5432/opsconsole
- SECRET_KEY=${SECRET_KEY} # 从系统环境变量读
- DEBUG=false
- CORS_ORIGINS=https://ops.example.com # 生产用正式域名
deploy:
replicas: 2 # 后端跑两个实例(Nginx 负载均衡)SECRET_KEY 和 DB_PASSWORD 不写在文件里,部署时从密钥管理系统或 .env(不进 Git 的本地文件)注入:
bash
# 部署机器上的 .env(不进 Git)
SECRET_KEY=a3f5e8b2c1d4f7a9e6b3c8d1f4a7b9e2c5d8f1a4b7e9c2d5f8a1b4e7c9d2f5a8
DB_PASSWORD=s3cret_database_password四、数据库迁移
每次部署新版本,先跑迁移再启动应用:
bash
# 在后端容器里执行迁移
docker-compose exec backend alembic upgrade head
# 然后重启后端(如果代码有变化)
docker-compose restart backend迁移必须在应用启动前完成——新代码可能依赖新字段。如果应用先起来了但表结构没更新,接口会报错。
五、CI/CD——自动发布
手动 docker-compose up 适合单台机器。团队大了要用 CI/CD 自动化——代码推到 Git,流水线自动构建镜像、推到镜像仓库、部署到服务器。
基本流程:
GitHub Actions 示例(.github/workflows/deploy.yml):
yaml
name: Deploy
on:
push:
branches: [main] # 推到 main 分支触发部署
jobs:
deploy:
runs-on: ubuntu-latest
steps:
# 1. 拉代码
- uses: actions/checkout@v4
# 2. 构建镜像
- name: Build images
run: |
docker-compose build
# 3. 推到镜像仓库(以 Docker Hub 为例)
- name: Push images
run: |
docker tag ops-console-backend ${{ secrets.DOCKER_USER }}/ops-backend:latest
docker tag ops-console-frontend ${{ secrets.DOCKER_USER }}/ops-frontend:latest
docker push ${{ secrets.DOCKER_USER }}/ops-backend:latest
docker push ${{ secrets.DOCKER_USER }}/ops-frontend:latest
# 4. SSH 到服务器执行部署
- name: Deploy to server
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_KEY }}
script: |
cd /opt/ops-console
docker-compose pull
docker-compose exec backend alembic upgrade head
docker-compose up -dsecrets.SERVER_HOST、secrets.SSH_KEY 等敏感信息存在 GitHub 仓库的 Secrets 里——不写在代码里、不进 Git 历史。
六、发布检查清单
每次发布后验证:
- [ ] 前端页面能打开(
https://ops.example.com) - [ ] 登录功能正常
- [ ] 资产列表能加载
- [ ] 创建/删除资产正常
- [ ] 任务执行正常(提交任务 → 状态变化 → 有结果)
- [ ] 事件日志有记录
- [ ] 数据库迁移执行了(
alembic current) - [ ] HTTPS 证书有效(浏览器地址栏有锁)
- [ ] 错误日志没有异常(
docker-compose logs backend)
全通过,发布成功。有问题就回滚——docker-compose 拉旧版本镜像重新 up,数据库用 alembic downgrade 回退迁移。
28 篇到此结束——从 Python 变量到全栈项目部署,一条线走完。前端基础篇(01-09)用文章管理的中性例子打基础,后端基础篇(10-14)用文章 CRUD API 接上前端的 fetch,进阶篇(15-20)补充生产级要用的东西,项目实战篇(21-28)把所有知识合到运维平台 ops-console 上。