Files
backend/docs/12-部署实际操作记录.md
T
34047007@qq.com a6cd99a4ca
CI / backend (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
feat: initial commit - oncology literature search platform
OncoLit: a multi-tenant oncology literature search, feed, and
collaboration platform. Built with FastAPI + Vue 3 + PostgreSQL.
Includes PubMed pipeline, drug approvals, AI summaries, and
systematic review tools.
2026-07-27 07:59:18 +08:00

10 KiB
Raw Blame History

SciLit 生产部署实际操作记录

部署日期: 2026-07-10 ~ 2026-07-11 服务器: 123.207.9.209(腾讯云 · TencentOS 4 部署方式: Docker ComposeNginx 前端反代 + Uvicorn 后端 API


1. 服务器环境

1.1 基础信息

  • OS TencentOS 4RHEL 系,基于 RHEL 9,非 Ubuntu
  • 磁盘: 20GB/dev/vda1),已用 ~9GB(部署含镜像后 ~12GB)
  • CPU/内存: 未确认,当前 Elasticsearch 分配 1GB,可跑完整 Docker 栈

1.2 Docker 安装(非标准流程)

TencentOS 4 不被 Docker 官方安装脚本支持(get.docker.com 会报不支持的发行版)。

# 不能用标准方法:
# curl -fsSL https://get.docker.com | bash  # ❌ 失败

# 改用 Tencent 源 + --releasever=9(兼容 CentOS 9
dnf install -y dnf-plugins-core
dnf config-manager --add-repo https://mirrors.cloud.tencent.com/docker-ce/linux/rhel/docker-ce.repo
dnf --releasever=9 install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
systemctl enable --now docker

1.3 Docker 镜像加速配置

国内拉取 docker.io 镜像极慢或超时,必须配置镜像加速器:

# /etc/docker/daemon.json
{
  "registry-mirrors": [
    "https://mirror.ccs.tencentyun.com",
    "https://docker.mirrors.ustc.edu.cn"
  ]
}

配置后重启 Docker

systemctl daemon-reexec && systemctl restart docker

1.4 验证安装

docker info                            # Registry Mirrors 应显示加速地址
docker run hello-world                 # 拉取测试
docker compose version                  # 应显示 v2.x

2. SSH 连接

2.1 服务器 SSH 配置

服务器 SSH 默认禁用密码登录 + 禁用 root 登录PasswordAuthentication no, PermitRootLogin no)。

需要在腾讯云控制台 VNC 或 web shell 中修改:

# /etc/ssh/sshd_config
PermitRootLogin yes
PasswordAuthentication yes
systemctl restart sshd

2.2 本地 SSH 工具

  • Git Bash 的 ssh:密码登录交互模式在自动化场景不可靠
  • sshpassWindows Git Bash 不支持(exit code 127
  • pexpectWindows 下 import 失败
  • plinkPuTTY:不支持 OpenSSH 新版私钥格式(BEGIN OPENSSH PRIVATE KEY
  • 解决方案:在服务器添加本地生成的公钥到 ~/.ssh/authorized_keys,用密钥登录
# 本地生成密钥
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N "" -C "deploy"

# 将公钥内容添加到服务器 ~/.ssh/authorized_keys
echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... claude-code-deploy' >> ~/.ssh/authorized_keys

# 登录
ssh -i ~/.ssh/id_ed25519 root@123.207.9.209

3. 代码传输与部署目录

3.1 传输方式

用 SCP(无 rsync):

scp -i ~/.ssh/id_ed25519 -r d:/ClaudeCode/* root@123.207.9.209:/root/scilit/

注意:

  • SCP 不支持 --exclude(要用 rsync),大目录(node_modules、.git)需要手动排除
  • 传输前清理无关文件可大幅减少时间
  • SCP 不会覆盖已存在的同名文件(除非加 -r 强制)

3.2 部署目录

/root/scilit/
├── docker-compose.prod.yml     # 生产 compose 配置
├── .env                        # 环境变量(含敏感信息)
├── backend/                    # 后端代码 + Dockerfile
└── frontend/                   # 前端代码 + Dockerfile.prod

4. Dockerfile 构建(关键问题和修复)

4.1 elasticsearch-py 包名错误

问题: pyproject.toml 中写的是 elasticsearch-py,这是旧版/别名。PyPI 上的正确包名是 elasticsearch

# 报错信息
ERROR: Could not find a version that satisfies the requirement elasticsearch-py>=8.14.0

修复: pyproject.toml

- "elasticsearch-py[async]>=8.14.0",
+ "elasticsearch[async]>=8.14.0",

同时添加 email-validator 依赖(Pydantic 验证 email 字段需要):

+ "email-validator>=2.0",

4.2 PyPI 镜像导致包缺失

问题: 腾讯 PyPI 镜像(mirrors.cloud.tencent.com/pypi/simple/)缺少 elasticsearch>=8.14.0

解决: 分两步预装

COPY pyproject.toml .
# 第一步:腾讯镜像有 elasticsearch 就用,没有会报错
RUN pip install --no-cache-dir -i https://mirrors.cloud.tencent.com/pypi/simple/ "elasticsearch[async]>=8.14.0" "email-validator>=2.0"
# 第二步:腾讯镜像有其余包(fastapi, sqlalchemy, etc.
RUN pip install --no-cache-dir -i https://mirrors.cloud.tencent.com/pypi/simple/ .

实测结果:

  • 4/4/2026 时腾讯镜像 elasticsearch>=8.14.0(已验证通过)
  • 如果未来腾讯镜像再次缺失,改为两步:官方 PyPI 先装 elasticsearch,腾讯镜像装其余包

4.3 Debian apt 源

Docker 基础镜像 python:3.12-slim 基于 Debian。腾讯 apt 源地址:

http://mirrors.tencent.com/debian          → trixie main/updates
http://mirrors.tencent.com/debian-security → trixie-security main

Dockerfile 中:

RUN echo "deb http://mirrors.tencent.com/debian trixie main" > /etc/apt/sources.list \
 && echo "deb http://mirrors.tencent.com/debian trixie-updates main" >> /etc/apt/sources.list \
 && echo "deb http://mirrors.tencent.com/debian-security trixie-security main" >> /etc/apt/sources.list \
 && apt-get update \
 && apt-get install -y --no-install-recommends \
    build-essential libpq-dev \
 && rm -rf /var/lib/apt/lists/*

注意 deb.debian.org 在中国大陆极慢(~100 KB/s),覆盖 sources.list 后需要确认 apt-get update 的输出只从 mirrors.tencent.com 获取。如果尚有 deb.debian.org 的记录,是 docker build cache 导致,加 --no-cache 或使用 COPY --chmod 在早期层清掉默认配置。

4.4 alembic/env.py 找不到 app 模块

问题: alembic env.pyfrom app.config import settings 在工作目录(/app/alembic/)下找不到同级 app/ 目录。

修复: 在 env.py 顶部添加 sys.path

import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent))

同时 docker-compose.prod.ymlmigrate 服务需要添加 JWT_SECRETSPECIALTY 等环境变量(因为 app.config.Settings() 验证配置时会检查这些值):

migrate:
  environment:
    DATABASE_URL: ...
    JWT_SECRET: ${JWT_SECRET}
    SPECIALTY: ${SPECIALTY:-oncology}
    DEBUG: "false"

4.5 Docker 构建速度优化

优化前 优化后
单层 pip install .,全部从官方 PyPI 下载,~100 KB/s 分两步:腾讯镜像加速下载(~50 MB/s),全量约 15s
每次修改代码需全部重建 COPY pyproject.toml . + 依赖安装缓存 → 仅改代码时跳过 pip install

关键思路:将依赖安装与应用代码分离,只在 pyproject.toml 变更时重跑 pip

# 先拷贝仅 pyproject.toml → 安装依赖(缓存层)
COPY pyproject.toml .
RUN pip install --no-cache-dir ...
# 最后拷贝全部代码(变更频繁,但不会触发依赖重装)
COPY . .

5. Docker Compose 启动流程

5.1 命令

cd /root/scilit
docker compose -f docker-compose.prod.yml --env-file .env up -d

5.2 容器启动顺序(depends_on 控制)

postgres ─┬→ migrate ─→ backend ─→ frontend
redis ────┤              │
es ───────┤              └→ worker
minio ────┘
  • postgres 必须 health check 通过 → migrate 才启动(运行 alembic upgrade
  • migrate exit 0 → backend + worker 才启动
  • backend 健康检查通过 → frontend 才启动(虽然 frontend 只是 Nginx 静态文件,无实质依赖)

5.3 迁移日志确认

启动后检查:

docker logs scilit-migrate-1    # 确认所有 migration 版本 applied
docker ps --format 'table {{.Names}}\t{{.Status}}'  # 确认全部 healthy

5.4 健康检查

# 后端 API
curl http://localhost:8000/health
# → {"status":"ok","specialty":"oncology","db":"ok","version":"0.1.0"}

# 前端
curl http://localhost/
# → <!DOCTYPE html><html lang="zh-CN">...

# 外网
curl http://123.207.9.209/health

6. 本次部署的变更

6.1 代码变更

文件 变更内容
docker-compose.prod.yml migrate 服务添加 JWT_SECRET/SPECIALTY/DEBUG 环境变量;移除已废弃的version属性;添加 COS env vars;新增 statusline-setup services 等
backend/Dockerfile 腾讯镜像源覆盖默认 deb.debian.org;两步 pip 策略预装 elasticsearch
backend/pyproject.toml elasticsearch-pyelasticsearch;新增 email-validator
backend/alembic/env.py 添加 sys.path.insert(0, ...) 解决模块导入
backend/app/models/literature.py 新增 full_text_path 字段
新增 backend/app/services/cos_client.py 腾讯云 COS 客户端
新增 alembic/versions/d959d4b8a9f1_*.py 添加 full_text_path 列迁移

6.2 基础设施变更

项目 之前 现在
对象存储 MinIO(自建) 腾讯云 COS(生产,可降级到 MinIO)
Dockerfile apt 源 deb.debian.org(极慢) mirrors.tencent.com~50 MB/s
Dockerfile pip 源 无/官方 PyPI mirrors.cloud.tencent.com(腾讯镜像加速)
SSH 认证 密码 SSH Key + 密码均可

7. 回滚要点

  • Docker image tag 未设置版本号,scilit-backend:latest 覆盖上次构建。如需回滚需重建旧代码
  • 数据库迁移是递增的,回滚代码后需确认兼容性。如须回退迁移:alembic downgrade -1
  • COS 迁移不需回滚:full_text_sections 字段仍保留,COS 不可用时自动降级到 PG
  • .env 文件包含敏感信息,如需回滚注意不要覆盖

8. 注意事项

  1. 不要用 docker compose down -v-v 会删除所有数据卷(包括 PG 数据库、ES 索引、Redis 数据、MinIO 文件)
  2. env 文件必须安全存放.env 包含 SMTP 密码、JWT 密钥、DeepSeek API Key、PubMed API Key
  3. Elasticsearch 吃内存:生产已配置 -Xms1g -Xmx1g。如果内存紧张可以暂不启动 ES(代码中 ES_URL 为空时自动降级到 tsvector
  4. Docker 日志:每个容器配置 logging.driver: json-file + max-size: 10m, max-file: 3,防止日志占满磁盘
  5. Nginx 端口 80 映射到前端容器,通过 iptables/防火墙控制。SSL/443 暂未配置,需上游反代(Caddy/Nginx/CLB