SERVER_DEPLOY.md 13 KB

中华药典 AI Agent — 全新服务器部署手册

适用:Ubuntu 24.04 LTS 全新安装
版本:v2.0
最后更新:2026-07-14


一、服务器配置要求

项目 最低 推荐
CPU 4 核 8 核
内存 8 GB 16 GB
系统盘 50 GB SSD 100 GB SSD
操作系统 Ubuntu 22.04+ Ubuntu 24.04 LTS
网络 公网 IP 公网 IP + 域名

不需要 GPU(LLM 走阿里云 Qwen API)。


二、部署总览(10 步)

① 基础环境     → apt update, 装 git/curl 等
② Docker       → 容器运行时
③ 拉取代码     → git clone 到 /opt/pharmacopoeia-ai
④ 环境变量     → .env 配置
⑤ PostgreSQL   → Docker 启动 pgvector
⑥ 数据库建表   → 导入 schema.sql
⑦ Python 后端  → venv + FastAPI + Supervisor 守护
⑧ Nginx       → 反向代理 + SSE 免缓冲
⑨ 防火墙      → ufw 只开放 80/443
⑩ 备份 + 日志  → cron 每日备份

三、详细步骤

① 基础环境

# 更新系统
sudo apt update && sudo apt upgrade -y

# 安装基础工具
sudo apt install -y curl wget git vim htop ca-certificates gnupg lsb-release

# 设置时区
sudo timedatectl set-timezone Asia/Shanghai

② 安装 Docker

# 官方安装脚本
curl -fsSL https://get.docker.com | sudo bash

# 把当前用户加入 docker 组(不用每次 sudo)
sudo usermod -aG docker $USER
newgrp docker

# 验证
docker --version
docker compose version

Docker 镜像拉取慢的话,配置国内镜像加速:

> sudo tee /etc/docker/daemon.json << 'EOF'
> {
>   "registry-mirrors": ["https://docker.m.daocloud.io"]
> }
> EOF
> sudo systemctl restart docker
> ```

---

### ③ 拉取项目代码

bash

创建应用目录

sudo mkdir -p /opt/pharmacopoeia-ai sudo chown $USER:$USER /opt/pharmacopoeia-ai

拉取代码(替换为你的仓库地址)

cd /opt/pharmacopoeia-ai git clone https://github.com/your-org/pharmacopoeia-ai.git .

确认目录结构

ls -la

应看到: backend-python/ deploy/ database/ docs/ .env.example README.md


---

### ④ 配置环境变量

bash cd /opt/pharmacopoeia-ai

从模板创建

cp .env.example .env

生成随机密钥

echo "SECRET_KEY=$(openssl rand -hex 32)" echo "DB_PASSWORD=$(openssl rand -base64 16)"


编辑 `.env`,修改以下值:

bash vim .env


ini

===== 必改 =====

APP_ENV=production APP_DEBUG=false SECRET_KEY=<上面生成的随机密钥>

POSTGRES_PASSWORD=<上面生成的数据库密码>

QWEN_API_KEY=sk-xxxxxxxxxxxxxxxx # 阿里云百炼 API Key

===== 有微信小程序才填 =====

WECHAT_APPID=wx1234567890 WECHAT_SECRET=xxxxxxxx

===== 生产建议 =====

QWEN_MAX_TOKENS=2048 LOG_LEVEL=WARNING


> **Qwen API Key 获取**:注册 [阿里云百炼](https://bailian.console.aliyun.com/) → 模型广场 → 选择 qwen-max → API-KEY 管理 → 创建 API Key。
>
> 新用户有免费额度,足够开发测试。

---

### ⑤ 启动 PostgreSQL + Redis

bash cd /opt/pharmacopoeia-ai/deploy

先确认 .env 中的密码已写入 docker-compose 能读取

docker-compose.yml 会读取 POSTGRES_PASSWORD 环境变量

source /opt/pharmacopoeia-ai/.env

启动容器

docker compose up -d

等待健康检查通过

sleep 10 docker ps

应该看到 pharmacopoeia-pg (healthy) 和 pharmacopoeia-redis (healthy)

验证 PG

docker exec pharmacopoeia-pg psql -U postgres -d pharmacopoeia -c "SELECT 1"

返回 1 行 = 正常

验证 Redis

docker exec pharmacopoeia-redis redis-cli ping

返回 PONG = 正常

设置开机自启

docker update --restart=always pharmacopoeia-pg pharmacopoeia-redis


---

### ⑥ 初始化数据库表

bash

导入表结构

docker exec -i pharmacopoeia-pg psql -U postgres -d pharmacopoeia \ < /opt/pharmacopoeia-ai/database/schema.sql

确认表已创建(应看到 9 张表)

docker exec pharmacopoeia-pg psql -U postgres -d pharmacopoeia -c "\dt"


9 张表说明:

| 表 | 用途 |
|---|------|
| `drugs` | 药品主表 |
| `drug_chunks` | 药品切片 + 1024维向量(RAG 核心) |
| `knowledge_points` | 执业药师知识点 |
| `questions` | 题库 |
| `conversations` | 对话会话 |
| `messages` | 对话消息 |
| `users` | 用户 |
| `user_progress` | 学习进度 |
| `answer_records` | 答题记录 |

---

### ⑦ 编译并启动 Java 后端

#### 7.1 编译

bash cd /opt/pharmacopoeia-ai/backend-java

编译(跳过测试)

mvn clean package -DskipTests


#### 7.2 手动启动测试

bash

设置数据库密码环境变量

export POSTGRES_PASSWORD=postgres

启动

java -jar target/pharmacopoeia-ai-1.0.0.jar

看到 Started PharmacopoeiaApplication 后

新开终端验证:

curl http://localhost:9000/health

返回 {"status":"ok"} 则正常

Ctrl+C 停掉


#### 7.3 使用启动脚本管理

bash

赋予执行权限

chmod +x /opt/pharmacopoeia-ai/tools/start-java.sh

启动

bash /opt/pharmacopoeia-ai/tools/start-java.sh start

查看状态

bash /opt/pharmacopoeia-ai/tools/start-java.sh status

查看日志

bash /opt/pharmacopoeia-ai/tools/start-java.sh logs

重启

bash /opt/pharmacopoeia-ai/tools/start-java.sh restart

停止

bash /opt/pharmacopoeia-ai/tools/start-java.sh stop


#### 7.4 配置 Supervisor 守护进程(推荐)

bash sudo apt install -y supervisor

创建日志目录

sudo mkdir -p /var/log/pharmacopoeia

使用项目自带的 Supervisor 配置

sudo cp /opt/pharmacopoeia-ai/deploy/supervisord-java.conf /etc/supervisor/conf.d/pharmacopoeia.conf

⚠️ 编辑配置,填入真实的 QWEN_API_KEY

sudo vim /etc/supervisor/conf.d/pharmacopoeia.conf

启动

sudo supervisorctl reread sudo supervisorctl update sudo supervisorctl start pharmacopoeia-backend

查看状态

sudo supervisorctl status


---

### ⑧ 配置 Nginx

bash

安装 Nginx

sudo apt install -y nginx

创建站点配置

sudo tee /etc/nginx/sites-available/pharmacopoeia << 'NGINX'

---- 后端上游 ----

upstream backend {

server 127.0.0.1:8000;
keepalive 64;

}

---- 限流 ----

limit_req_zone $binary_remote_addr zone=api_limit:10m rate=60r/m;

---- HTTP(有域名后可加 HTTPS) ----

server {

listen 80;
server_name _;

client_max_body_size 10m;

# Gzip
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;

# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;

# API 反向代理(SSE 流式关键:禁用缓冲)
location /api/ {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    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;
    proxy_set_header Connection "";

    # SSE 支持
    proxy_buffering off;
    proxy_cache off;
    proxy_read_timeout 300s;

    # 限流
    limit_req zone=api_limit burst=20 nodelay;
}

# 健康检查(不记录日志)
location /health {
    proxy_pass http://backend;
    access_log off;
}

# API 文档
location /docs {
    proxy_pass http://backend;
}

# 静态文件
location /static/ {
    alias /opt/pharmacopoeia-ai/static/;
    expires 7d;
    add_header Cache-Control "public, immutable";
}

# 根路径 → 前端页面
location / {
    alias /opt/pharmacopoeia-ai/static/;
    try_files $uri /index.html;
}

} NGINX

启用站点

sudo ln -sf /etc/nginx/sites-available/pharmacopoeia /etc/nginx/sites-enabled/ sudo rm -f /etc/nginx/sites-enabled/default

测试配置

sudo nginx -t

重启 Nginx

sudo systemctl reload nginx sudo systemctl enable nginx


**验证**:浏览器访问 `http://服务器IP/` 应该看到前端页面。

---

### ⑨ 配置防火墙

bash

安装 ufw

sudo apt install -y ufw

只开放必要端口

sudo ufw allow 22/tcp # SSH sudo ufw allow 80/tcp # HTTP sudo ufw allow 443/tcp # HTTPS

开启防火墙

sudo ufw --force enable sudo ufw status verbose


>   PostgreSQL (5432)、Redis (6379)、FastAPI (8000) **不对外开放**,只通过本地回环访问。

---

### ⑩ 数据库备份

bash

创建备份脚本

sudo tee /opt/pharmacopoeia-ai/tools/backup.sh << 'EOF' #!/bin/bash BACKUP_DIR="/opt/backups/pharmacopoeia" mkdir -p "$BACKUPDIR" DATE=$(date +%Y%m%d%H%M%S) docker exec pharmacopoeia-pg pg_dump -U postgres pharmacopoeia

"$BACKUPDIR/pg${DATE}.sql"

保留最近 7 天

find "$BACKUPDIR" -name "pg*.sql" -mtime +7 -delete echo "$(date '+%F %T') backup done: pg_${DATE}.sql" >> /var/log/pharmacopoeia/backup.log EOF sudo chmod +x /opt/pharmacopoeia-ai/tools/backup.sh

添加 cron(每天凌晨 2:00)

(crontab -l 2>/dev/null; echo "0 2 * * * /opt/pharmacopoeia-ai/tools/backup.sh") | crontab -


---

## 四、配置域名和 SSL(有域名后执行)

bash

安装 certbot

sudo apt install -y certbot python3-certbot-nginx

先修改 Nginx 配置中的 server_name

sudo vim /etc/nginx/sites-available/pharmacopoeia

把 server_name _; 改成 server_name yaodian.example.com;

sudo nginx -t && sudo systemctl reload nginx

自动签发 SSL 证书

sudo certbot --nginx -d yaodian.example.com

证书会自动续签,可手动测试续签流程:

sudo certbot renew --dry-run


---

## 五、导入数据(有药品数据后执行)

bash cd /opt/pharmacopoeia-ai/data-pipeline

安装数据管道依赖

python3 -m venv venv source venv/bin/activate pip install -r requirements.txt

准备数据文件放到 data-pipeline/data/ 下

格式参考 docs/SCHEME.md

执行向量化入库

python ingest.py


---

## 六、最终验证清单

部署完成后逐项确认:

bash

1. Docker 容器运行

docker ps | grep pharmacopoeia

✅ 两个容器都是 healthy

2. PostgreSQL 可访问

docker exec pharmacopoeia-pg psql -U postgres -d pharmacopoeia -c "SELECT count(*) FROM information_schema.tables WHERE table_schema='public'"

✅ 返回 9

3. Python 后端运行中

sudo supervisorctl status pharmacopoeia-backend

✅ RUNNING

4. API 响应正常

curl http://localhost:8000/health

✅ {"status":"ok","app":"PharmacopoeiaAI","env":"production"}

5. Nginx 代理正常

curl http://localhost/health

✅ 同上

6. 外网访问(用你电脑浏览器)

http://服务器IP/

✅ 能看到前端页面

7. 防火墙生效

sudo ufw status | grep -E "80|443"

✅ ALLOW


---

## 七、日常运维命令

| 操作 | 命令 |
|------|------|
| 查看后端状态 | `sudo supervisorctl status` |
| 重启后端 | `sudo supervisorctl restart pharmacopoeia-backend` |
| 查看后端实时日志 | `sudo supervisorctl tail -f pharmacopoeia-backend` |
| 查看错误日志 | `tail -100 /var/log/pharmacopoeia/backend-error.log` |
| 重启 Nginx | `sudo systemctl reload nginx` |
| 重启 PG | `docker restart pharmacopoeia-pg` |
| 重启 Redis | `docker restart pharmacopoeia-redis` |
| 进入 PG 控制台 | `docker exec -it pharmacopoeia-pg psql -U postgres -d pharmacopoeia` |
| 手动备份 | `bash /opt/pharmacopoeia-ai/tools/backup.sh` |
| 更新代码并重启 | `cd /opt/pharmacopoeia-ai && git pull && sudo supervisorctl restart pharmacopoeia-backend` |
| 查看磁盘 | `df -h` |
| 查看内存 | `free -h` |

---

## 八、部署架构图

公网用户

│
▼

┌──────────────────┐ │ UFW 防火墙 │ 开放: 22, 80, 443 └────────┬─────────┘

     │
     ▼

┌──────────────────┐ │ Nginx :80/:443 │ SSL / 反向代理 / 限流 / 静态文件 └────────┬─────────┘

     │ :8000 (本地)
     ▼

┌──────────────────┐ │ Supervisor │ 进程守护, 挂了自动重启 │ ┌──────────────┐ │ │ │ Uvicorn x4 │ │ │ │ FastAPI :8000│ │ │ └──────┬───────┘ │ └─────────┼─────────┘

┌──────┼──────┐ ▼ ▼ ▼ ┌──────┐ ┌────┐ ┌──────────┐ │ PG │ │Redis│ │ Qwen API │ │ :5432│ │:6379│ │ (阿里云) │ │Docker│ │Dockr│ │ 云端 │ └──────┘ └────┘ └──────────┘

快速管理: bash tools/start-java.sh {start|stop|restart|status|logs}


---

## 九、常见问题

### Q: pip install 报错,sentence-transformers 编译失败?

bash sudo apt install -y build-essential python3-dev

重试

pip install sentence-transformers


### Q: docker compose 提示 .env 中的变量未生效?

bash

在 docker compose 目录下创建 .env 软链接

cd /opt/pharmacopoeia-ai/deploy ln -sf ../.env .env docker compose up -d


### Q: Qwen API 连不上?

bash

测试网络可达性

curl -I https://dashscope.aliyuncs.com

如果超时,检查服务器能否访问外网,是否需要代理


### Q: 如何查看 AI 对话是否正常工作?

bash

测试非流式接口

curl -X POST http://localhost:8000/api/v1/chat/ask \ -H "Content-Type: application/json" \ -d '{"query": "阿莫西林是什么药"}' ```