# SciLit 生产部署实际操作记录 **部署日期:** 2026-07-10 ~ 2026-07-11 **服务器:** 123.207.9.209(腾讯云 · TencentOS 4) **部署方式:** Docker Compose(Nginx 前端反代 + Uvicorn 后端 API) --- ## 1. 服务器环境 ### 1.1 基础信息 - **OS:** TencentOS 4(RHEL 系,基于 RHEL 9,非 Ubuntu) - **磁盘:** 20GB(/dev/vda1),已用 ~9GB(部署含镜像后 ~12GB) - **CPU/内存:** 未确认,当前 Elasticsearch 分配 1GB,可跑完整 Docker 栈 ### 1.2 Docker 安装(非标准流程) TencentOS 4 不被 Docker 官方安装脚本支持(`get.docker.com` 会报不支持的发行版)。 ```bash # 不能用标准方法: # 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` 镜像极慢或超时,必须配置镜像加速器: ```json # /etc/docker/daemon.json { "registry-mirrors": [ "https://mirror.ccs.tencentyun.com", "https://docker.mirrors.ustc.edu.cn" ] } ``` 配置后重启 Docker: ```bash systemctl daemon-reexec && systemctl restart docker ``` ### 1.4 验证安装 ```bash 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 中修改: ```bash # /etc/ssh/sshd_config PermitRootLogin yes PasswordAuthentication yes systemctl restart sshd ``` ### 2.2 本地 SSH 工具 - **Git Bash 的 `ssh`**:密码登录交互模式在自动化场景不可靠 - **`sshpass`**:Windows Git Bash 不支持(exit code 127) - **`pexpect`**:Windows 下 import 失败 - **`plink`(PuTTY)**:不支持 OpenSSH 新版私钥格式(`BEGIN OPENSSH PRIVATE KEY`) - **解决方案**:在服务器添加本地生成的公钥到 `~/.ssh/authorized_keys`,用密钥登录 ```bash # 本地生成密钥 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): ```bash 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`。 ```bash # 报错信息 ERROR: Could not find a version that satisfies the requirement elasticsearch-py>=8.14.0 ``` **修复:** `pyproject.toml` 中 ```diff - "elasticsearch-py[async]>=8.14.0", + "elasticsearch[async]>=8.14.0", ``` 同时添加 `email-validator` 依赖(Pydantic 验证 email 字段需要): ```diff + "email-validator>=2.0", ``` ### 4.2 PyPI 镜像导致包缺失 **问题:** 腾讯 PyPI 镜像(`mirrors.cloud.tencent.com/pypi/simple/`)缺少 `elasticsearch>=8.14.0`。 **解决:** 分两步预装 ```dockerfile 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 中: ```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.py` 中 `from app.config import settings` 在工作目录(`/app/alembic/`)下找不到同级 `app/` 目录。 **修复:** 在 env.py 顶部添加 sys.path: ```python import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent)) ``` 同时 `docker-compose.prod.yml` 中 `migrate` 服务需要添加 `JWT_SECRET`、`SPECIALTY` 等环境变量(因为 `app.config.Settings()` 验证配置时会检查这些值): ```yaml 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: ```dockerfile # 先拷贝仅 pyproject.toml → 安装依赖(缓存层) COPY pyproject.toml . RUN pip install --no-cache-dir ... # 最后拷贝全部代码(变更频繁,但不会触发依赖重装) COPY . . ``` --- ## 5. Docker Compose 启动流程 ### 5.1 命令 ```bash 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 迁移日志确认 启动后检查: ```bash docker logs scilit-migrate-1 # 确认所有 migration 版本 applied docker ps --format 'table {{.Names}}\t{{.Status}}' # 确认全部 healthy ``` ### 5.4 健康检查 ```bash # 后端 API curl http://localhost:8000/health # → {"status":"ok","specialty":"oncology","db":"ok","version":"0.1.0"} # 前端 curl http://localhost/ # → ... # 外网 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-py` → `elasticsearch`;新增 `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)