Files
dpb/doc/桃育种系统生产上线与运营手册.md
T
34047007@qq.com b95053c52c init: 初始化 dpb 桃育种系统代码库
前后端 + 后端 FastAPI 全量源码、部署脚本与文档。
2026-08-06 00:17:49 +08:00

19 KiB
Raw Blame History

数字化桃育种系统 生产上线与运营手册

版本:3.0.1 适用:生产(Docker Compose)部署 前置阅读:docker/README.md(部署操作)、doc/桃育种系统模块扩展需求规格.md(功能规格,v2.72026-08-04 本文档基于 2026-08-04 安全加固后的代码与配置撰写,上线前请逐项核对。


1. 部署架构

1.1 组件清单

组件 技术栈 容器/进程 说明
前端 VueVite 构建) nginx 托管静态文件 /web 路径访问
反向代理 nginx 1.25-alpine 容器 nginx TLS 终结、HTTP→HTTPS、API 反代、限流
后端 FastAPI + SQLAlchemy async + Redis 容器 backend python main.py run --env=prod
数据库 PostgreSQL 16 容器 postgres 见 §3
缓存 Redis 7 容器 redis 会话、验证码、登录锁定计数、系统参数缓存

1.2 端口暴露(已收紧到本机回环)

服务 容器端口 宿主机绑定 说明
nginx 80 / 443 0.0.0.0(公网) 唯一对外入口
backend 8001 127.0.0.1:8001 仅本机/容器网络可访问
数据库 5432 127.0.0.1 仅本机可连,公网不可达
Redis 6379 127.0.0.1 仅本机可连

安全边界:公网只能到达 nginx。后端 8001 直连会被跳过 Host 头/CORS/HTTPS 约束,因此切勿把 backend 端口改成公网绑定

1.3 数据流

浏览器 ──443──> nginxTLS/限流/反代)──8001──> backend ──> PostgreSQL 16
                                                └──> Redis

2. 配置体系

项目有两层配置,容易混淆,务必分清:

2.1 编排层:docker/.env

Docker Compose 使用(docker compose --env-file .env up -d)。必填项缺失时 compose 直接报错。

变量 必填 说明
DATABASE_USER 数据库用户(默认 dpb
DATABASE_PASSWORD 数据库口令,强随机
DATABASE_NAME 数据库名(默认 dpb
DATABASE_PORT 数据库端口(默认 5432
REDIS_PASSWORD Redis 口令,强随机
SECRET_KEY JWT 签名密钥,openssl rand -hex 32,勿用示例值
BACKEND_PORT / HTTP_PORT / HTTPS_PORT 默认 8001 / 80 / 443
DEPLOY_ENV 固定 prod

2.2 应用层配置(容器部署:全部收敛到 docker/.env

容器镜像已排除 backend/env 目录(构建镜像不携带任何 .env 文件,防止凭据进镜像),生产全部配置由 docker-compose 以环境变量注入后端。因此 docker/.env 是生产唯一配置源backend/env/.env.prod 仅用于裸机/非容器部署。

镜像内 backend/env/.env.prod 不存在时,main.py 的 env_file 加载被跳过,配置完全来自 compose 注入的环境变量(compose 已注入下述全部字段)。

变量 生产要求 说明
ENVIRONMENT prod compose 已注入
DEBUG false 开启则 prod 校验拒绝启动
SECRET_KEY ≥32 位独立随机 compose 已注入,兜底校验
PROD_CORS_ORIGINS 具体域名,逗号分隔 空则拒绝启动
ALLOWED_HOSTS 真实域名列表 nginx 反代时后端据此校验 Host 头,未配置则请求 403
IP_TRUST_PROXY_HEADERS true 经 nginx 转发时必须开启;公网直连必须 false
LOGIN_MAX_FAILURES 保持默认 登录锁定策略,见 §5
DATABASE_TYPE postgres compose 已注入,固定不变
DATABASE_* compose 注入(容器部署时不读 .env.prod
REDIS_PASSWORD compose 注入
DEV_DEFAULT_PASSWORD 必须留空 非空 = 把密码明文下发浏览器
SMTP_* / OPENAI_* 真实密钥 按需在 docker/.env 配置

2.3 加载优先级

容器部署(镜像不含 env 文件):docker-compose 注入的环境变量 > setting.py 默认值。 裸机部署(本地起 prod):backend/env/.env.prod > setting.py 默认值。


3. 数据库(PostgreSQL 16

生产固定使用 PostgreSQL 16,与开发环境(localhost:5432,库/角色 dpb)一致,可用 pg_dump/pg_restore 无缝迁移。

docker/docker-compose.yaml 已编排 postgres:16 服务:

配置
镜像 postgres:16(容器名 postgres
库名 / 用户 默认 dpbdocker/.envDATABASE_NAME / DATABASE_USER
口令 docker/.envDATABASE_PASSWORD(compose 强制必填,缺失即报错)
宿主机端口 127.0.0.1:5432(仅本机可连,公网不可达)
数据卷 docker/postgres/databind,容器内 /var/lib/postgresql/data
健康检查 pg_isreadybackend 容器依赖其 healthy 才启动)

首次启动时 postgres 容器自动初始化库与角色(POSTGRES_DB/USER/PASSWORD),无需手动建库。后端按 DATABASE_TYPE=postgres + asyncpg 驱动连接(compose 已注入),首次启动自动建表并导入种子数据(见 §4.3)。

数据目录docker/postgres/data 需存在(deploy.sh 会自动创建)。postgres 镜像启动时会自动把该目录 chown 到 postgres 用户(uid 999);若手动建目录后遇到权限错误,执行 sudo chown -R 999:999 docker/postgres/data

3.1 开发数据迁移到生产

# 开发机导出(需本机装有 PostgreSQL 客户端 pg_dump
pg_dump -h localhost -p 5432 -U dpb -d dpb --no-owner -F c -f dpb.dump

# 生产导入(进 postgres 容器;生产服务器同样需要 pg_restore)
docker compose exec postgres pg_restore -U dpb -d dpb --no-owner /tmp/dpb.dump
# 或先将 dump 拷入容器:docker compose cp dpb.dump postgres:/tmp/

注意中文编码:Windows 下 psql 交互易乱码,用 pg_restore -F c(二进制格式)导入可规避。生产服务器如缺 pg_restore,先 apt-get install -y postgresql-client


4. 首次上线步骤

4.1 服务器准备

  • Docker ≥ 20.10 + Docker Compose v2;建议 4 核 / 8GB 起步。
  • 防火墙/安全组只开放 80、443,其余端口(8001、5432、6379 等)一律不放行。
  • 确认服务器时区、磁盘空间(数据库卷建议 ≥50GB,监控磁盘水位)。

4.2 配置落地

  1. 拷贝项目到服务器(当前仓库未接入 git,直接同步目录)。
  2. 准备编排层配置:
    cd docker
    cp .env.example .env
    chmod 600 .env
    # 必改:REDIS_PASSWORD、SECRET_KEYopenssl rand -hex 32)、DATABASE_PASSWORD
    
  3. 数据库已是 PostgreSQL 16compose 已编排 postgres 服务),无需改编排;DATABASE_* 已在步骤 2 配置。
  4. 生产配置全部在 docker/.env(镜像已排除 backend/env,无需改 .env.prod):
    # 必改:SECRET_KEYopenssl rand -hex 32)、DATABASE_PASSWORD、REDIS_PASSWORD、
    #      PROD_CORS_ORIGINS(真实域名)、ALLOWED_HOSTS(真实域名)、SMTP_*/OPENAI_*(按需)
    
  5. SSL 证书放入 docker/nginx/ssl/server.pem + server.key),生产用正规 CA 证书。
  6. 修改 docker/nginx/nginx.conf 两处 server_name 为真实域名。
  7. 构建前端(若单独维护):
    cd frontend/web && npm install && npm run build
    cp -r dist ../docker/nginx/web/dist
    

4.3 建表与初始数据(自动完成,无需手操)

后端首次启动时 InitializeData.init_db() 会:

  1. create_all 自动创建全部表(仅建新表,不会修改已存在的表);
  2. 导入种子数据:菜单、部门、字典、角色、系统参数、默认用户。

种子用户(backend/sql/data/sys_user.json):superadmin(均为超管)、user

生产首次初始化时,种子账号的固定弱密码会被自动随机化(消除公开的 123456)。一次性初始密码写入容器 logs/prod_initial_passwords.txt(同时打印到后端启动日志),运维需在首次登录后立即修改,并删除该文件

docker compose exec backend cat /home/logs/prod_initial_passwords.txt   # 查看随机初始密码
# 登录修改密码后删除:
docker compose exec backend rm /home/logs/prod_initial_passwords.txt

表结构变更:create_all 只建新表,不会更新已存在的表。模型字段变更走 backend/sql/weld_*.sql 幂等 ALTER(见 §6.4),不在本机执行;首次上线后新列的 SQL 由运维/开发在升级时统一应用。

4.4 构建与启动

cd docker
docker compose --env-file .env build backend
docker compose --env-file .env up -d
docker compose ps                 # 全部 healthy 为佳

4.5 上线验证清单

  • docker compose ps 四个服务均 Upbackend 至少 startinghealthy
  • 健康检查:curl -s https://域名/api/v1/common/health/check 返回 200(就绪含依赖状态:/api/v1/common/health/ready
  • 前端 https://域名/web 可打开登录页
  • super/admin 登录成功(初始密码见 §4.3,生产默认开启滑块验证码,登录前需先完成滑块)
  • 登录后立即修改初始密码,并删除 logs/prod_initial_passwords.txt
  • 生产文档已关闭:https://域名/docs 应返回 404(符合预期)
  • docker compose exec backend python -c "from app.config.setting import settings; print(settings.SECRET_KEY[:4]+'...')" 确认不是默认密钥
  • 改掉种子账号默认密码,删除/停用不需要的账号(如 user
  • 验证上传:控制台上传一张图片,确认 backend/static/upload 有文件且可访问

5. 安全加固核对表

5.1 代码层已加固(本次安全修复,无需重复操作)

加固内容 位置
生产配置 fail-safe SECRET_KEY 非默认/≥32 位、DEBUG=false、CORS 非空,否则拒绝启动 setting.py
日志脱敏 密码/token/验证码等字段掩码为 ***refresh/logout 裸 body 整体掩码 router_class.py
登录暴力破解 同一用户名+IP 连续失败 5 次锁定 15 分钟(Redis);Redis 故障时自动降级不锁死 auth/service.py
生产关闭文档 /docs/redoc 生产不注册 init_app.py
客户端 IP 防伪造 默认不信任 XFFIP_TRUST_PROXY_HEADERS=true 才读取 ip_local_util.py
端口收内网 数据库/Redis/后端均绑 127.0.0.1 docker-compose.yaml
镜像自包含 Dockerfile COPY ./backend/,生产不依赖宿主机代码挂载 Dockerfile
Redis 危险命令禁用 CONFIG/FLUSHALL/FLUSHDB 重命名禁用;protected-mode yes redis.conf
镜像排除凭据 .dockerignore 排除 backend/env.env.dev(含真实 SMTP 密码)等不进镜像;生产配置全走 docker/.env .dockerignore
容器非 root 运行 Dockerfile USER app + compose user: 1001:1001;配合 cap_drop: ALLno-new-privilegestmpfs /tmp Dockerfile / docker-compose.yaml
种子账号弱口令消除 生产首次初始化把公开的 123456 种子密码随机化,一次性初始密码写 logs/prod_initial_passwords.txt initialize.py
HSTS / 权限策略 nginx 补 Strict-Transport-SecurityPermissions-Policy,移除已废弃的 X-XSS-Protection nginx.conf
无用数据库依赖 移除 aiomysql/pymysql/aiosqlite(仅 PostgreSQL 驱动 asyncpg/psycopg requirements.txt / pyproject.toml

5.2 运维层待办(上线时逐项落实)

  • docker/.env 权限 chmod 600,禁止进版本库
  • 防火墙仅放行 80/443
  • 使用正规 CA 的 SSL 证书
  • logs/prod_initial_passwords.txt 获取随机初始密码登录,立即修改并删除该文件
  • 数据库账号使用独立强口令,不用 root 直连业务
  • 配置备份任务并做一次恢复演练(§6.2)

6. 日常运营

6.1 启停与状态

cd docker
docker compose --env-file .env ps              # 状态
docker compose --env-file .env up -d           # 启动
docker compose --env-file .env restart backend # 仅重启后端
docker compose --env-file .env stop            # 停止(保留数据卷)
docker compose --env-file .env down            # 停止并移除容器(卷保留)
docker compose logs -f backend                 # 实时日志

6.2 备份与恢复

备份优先级:数据库 > 上传文件 > 配置 > 前端产物(可重建)> Redis(会话可重建)。

数据 位置 策略
数据库(核心) 数据库卷 每日全量 + 保留 7~14 天,异地一份
上传文件 backend/static/upload(容器内 /home/static/upload 每日增量同步
配置 docker/.envbackend/env/.env.prod 每次变更后备份
Redis 会话/缓存 无需备份(丢失仅踢下线)

PostgreSQL 每日备份(宿主机 crontab 示例):

0 2 * * * cd /opt/dpb/docker && docker compose exec -T postgres pg_dump -U dpb -d dpb --no-owner -F c \
  > /backup/dpb_$(date +\%F).dump && \
  find /backup -name 'dpb_*.dump' -mtime +14 -delete

恢复演练(每季度一次,确保备份可用):

# 临时起一个空库恢复验证:
docker compose exec postgres pg_restore -U dpb -d dpb --no-owner /backup/dpb_最近.dump
# 上传文件:解压覆盖 backend/static/upload 即可

6.3 日志与监控

  • 容器日志:Docker 已配置轮转(每容器最多 3 个文件 × 10MB)。查看:docker compose logs --tail=200 backend
  • 后端应用日志backend/logs/(容器内 /home/logs)。
  • 监控项
    • 健康检查:/api/v1/common/health/check(存活)与 /api/v1/common/health/ready(就绪,含 DB/Redis/磁盘,依赖未就绪返回 503);compose 已内置存活探针(15s 间隔,探测 /common/health/check
    • 磁盘水位(数据库卷最易满)
    • 登录失败/锁定:日志关键字 登录失败次数过多已锁定
    • 资源:compose 已设内存上限(backend 1G / postgres 1G / redis 512M / nginx 256M

6.4 升级与表结构变更

常规升级(无模型变更):

cd docker
docker compose --env-file .env build backend   # 重新构建新镜像
docker compose --env-file .env up -d --no-deps backend

模型字段变更create_all 只建新表、不更新已存在的表。本项目实际变更约定为幂等 ALTER SQL 文件backend/sql/weld_*.sqlADD COLUMN IF NOT EXISTS),新增列均以 psql 手动应用(Alembic 脚手架存在但从未使用——backend/app/alembic/versions/ 为空,勿在本项目启用,与既有 weld 文件重复冲突):

# 开发机应用(对 dev 库):
PGPASSWORD=dpb psql -h localhost -p 5432 -U dpb -d dpb -w -f backend/sql/weld_xxx.sql
# 生产应用(先进容器,SQL 已随镜像/同步目录带入):
docker compose cp backend/sql/weld_xxx.sql postgres:/tmp/weld_xxx.sql
docker compose exec postgres psql -U dpb -d dpb -f /tmp/weld_xxx.sql
# 清理:
docker compose exec postgres rm /tmp/weld_xxx.sql

新增列示例:weld_prediction_rigor.sqlPA/溯源)、weld_rootstock.sql(矮化类/砧穗亲和性)、weld_trait_direction.sql(方向标注)、weld_combining_design.sql(交配设计)——文件幂等,重跑无害,可批量串行应用。升级前先备份数据库(§6.2;涉及大表加列建议维护窗口执行(Postgres 11+ ADD COLUMN 默认仅加元数据,不需重写表,风险低)。

6.5 密钥轮换

SECRET_KEY 轮换:改动会令所有已签发 JWT 失效(全员需重新登录),在维护窗口操作:

# 1. 备份当前 .env → 2. 生成新密钥写入 docker/.env → 3. 重启 backend → 4. 验证登录
openssl rand -hex 32
docker compose --env-file .env up -d --no-deps backend

REDIS_PASSWORD 轮换:先改 docker/.env 重启 redis,再改 backend 注入(compose 已自动传递),最后重启 backend。


7. 故障排查

症状 原因 处理
backend 启动即退出,日志提示 SECRET_KEY 配置不安全 .env 未设置/长度不足/命中开发默认值 生成 ≥32 位随机密钥,写 docker/.env 后重建启动
日志提示 PROD_CORS_ORIGINS 必须配置 .env.prod 未填域名白名单 填具体域名(逗号分隔),勿用 *
backend 连不上数据库 连接参数与 postgres 容器不一致(DATABASE_USER/PASSWORD/NAME),或容器未 healthy docker compose logs postgres 看初始化是否成功;核对 docker/.envDATABASE_*
能登录但请求 403 / 跨域失败 CORS 白名单没包含当前域名;或 Host 头不在 ALLOWED_HOSTS 核对 PROD_CORS_ORIGINSALLOWED_HOSTS
登录提示"账号已临时锁定" 连续失败 5 次触发锁定(15 分钟) 正常防护;等 TTL 或 redis-cli DEL login:lock:*(生产禁止清库)
不知道初始密码 生产首次初始化已随机化种子密码 docker compose exec backend cat /home/logs/prod_initial_passwords.txt(仅首次初始化生成,若已删除需走数据库重置密码)
/docs 404 生产已关闭文档 符合预期;如需排障临时 DEBUG=true 后恢复
日志出现 unknown command HELLO 连到了不支持 RESP3 的旧 Redis 生产用 redis:7 无此问题;连接已固定 protocol=2 兜底
中文内容乱码 数据库/迁移窗口编码问题 用二进制 dump-F c)导入;后端连接串为 UTF-8,无需额外设置
上传失败/图片 404 上传目录权限 docker compose exec backend ls /home/static/upload,检查卷或 chown
公网访问 https 打不开 防火墙/安全组未放行 80/443,或证书未配置 核对安全组、docker/nginx/ssl/ 证书、nginx server_name

8. 应急预案与回滚

8.1 回滚镜像

镜像带标签 backend:${BACKEND_IMAGE_TAG:-3.0.0}。上一版本镜像仍在本地时:

# 用旧标签启动(先备份当前 .env 与数据库)
BACKEND_IMAGE_TAG=<上个版本号> docker compose --env-file .env up -d --no-deps backend

8.2 数据恢复

按 §6.2 备份恢复。注意:恢复会覆盖当前数据,恢复前先导出线上现状存档。

8.3 紧急下线

cd docker && docker compose --env-file .env stop   # 服务停、数据卷保留
# 或直接封防火墙 80/443(保留进程,便于排查)

9. 常用命令速查

操作 命令
查看状态 cd docker && docker compose --env-file .env ps
看后端日志 docker compose --env-file .env logs -f backend
进后端容器 docker compose --env-file .env exec backend sh
数据库 psql docker compose --env-file .env exec postgres psql -U dpb -d dpb
Redis 客户端 docker compose --env-file .env exec redis redis-cli -a "$REDIS_PASSWORD" ping
生产健康检查 curl -s https://域名/api/v1/common/health/check
生成新密钥 openssl rand -hex 32

维护:本手册随配置变更更新;涉及安全项的修改请同步核对 §5 清单。