# 部署指南 · DEPLOY 目标:从一台全新的 Ubuntu 24 香港 VPS,到能访问的私人新闻系统。 ## 0. 准备 - 香港 VPS(最低 2C2G 30G) - 域名(可选,没域名走 IP + 自签证书) - 腾讯云账号 + 已开通「文本翻译 TMT」 ## 1. 服务器初始化 ```bash # SSH 登录 ssh root@YOUR_SERVER_IP # 创建非 root 用户 adduser news usermod -aG sudo news # 基础包 apt update && apt -y upgrade apt -y install curl git ufw fail2ban # 防火墙 ufw allow OpenSSH ufw allow 80/tcp ufw allow 443/tcp ufw enable # Docker curl -fsSL https://get.docker.com | sh usermod -aG docker news # 退出 root,切换到 news exit ssh news@YOUR_SERVER_IP ``` ## 2. 拉代码 ```bash sudo mkdir -p /srv/news sudo chown news:news /srv/news cd /srv/news git clone <你的仓库地址> . # 或者 scp 上传 ``` ## 3. 配置环境变量 ```bash cp .env.example .env nano .env ``` **必填字段:** | 字段 | 怎么填 | | --- | --- | | `POSTGRES_PASSWORD` | `openssl rand -hex 24` | | `REDIS_PASSWORD` | `openssl rand -hex 24` | | `JWT_SECRET` | `openssl rand -hex 64` | | `TENCENTCLOUD_SECRET_ID` | 腾讯云控制台 → 访问管理 → API 密钥 | | `TENCENTCLOUD_SECRET_KEY` | 同上 | | `TENCENTCLOUD_REGION` | `ap-hongkong` | | `DOMAIN` | 域名(可选,留空走 IP) | ## 4. 启动 ```bash docker compose up -d --build # 等 30 秒 docker compose ps # 全部 healthy 即可 ``` ## 5. 初始化 ```bash # 5.1 数据库迁移 docker compose exec api alembic upgrade head # 5.2 创建 owner 账号 docker compose exec api python -m app.scripts.create_user \ --username owner --password YOUR_STRONG_PASS # 5.3 导入 5 个种子源 docker compose exec api python -m app.scripts.seed_sources # 5.4 手动触发一次抓取(看效果) docker compose exec worker python -c "import asyncio; from app.workers.pipeline import run_once; asyncio.run(run_once())" # 等 1~3 分钟,刷一下 docker compose exec postgres psql -U $POSTGRES_USER -d $POSTGRES_DB -c "SELECT count(*) FROM articles;" ``` ## 6. 验证清单 - [ ] 浏览器打开 `http://YOUR_IP/` 看到登录页 - [ ] 用 owner 登录成功 - [ ] Feed 列表显示 24h 内新闻(标题中英对照) - [ ] 详情页原文+译文并列 - [ ] `/admin/sources` 能看到 5 个源 - [ ] 翻译配额仪表盘显示已用字符 - [ ] 等到凌晨,worker 自动跑批,文章数持续增长 ## 7. 域名 + HTTPS(可选) 1. 域名 A 记录指向服务器 IP 2. 编辑 `.env` 填 `DOMAIN=news.example.com` + `ACME_EMAIL=you@example.com` 3. 编辑 `Caddyfile`,把 `http://{$DOMAIN}` 改成 `{$DOMAIN}`(取消注释下面块) 4. `docker compose restart caddy` 5. Caddy 自动申请 Let's Encrypt 证书 ## 8. 备份 ```bash # 每天凌晨 4 点备份到本地 cat > /srv/news/scripts/backup.sh <<'EOF' #!/bin/bash set -e BACKUP_DIR=/srv/news/backups/$(date +%Y%m%d) mkdir -p "$BACKUP_DIR" docker compose exec -T postgres pg_dump -U $POSTGRES_USER $POSTGRES_DB | gzip > "$BACKUP_DIR/db.sql.gz" # 保留 7 天 find /srv/news/backups -type d -mtime +7 -exec rm -rf {} + EOF chmod +x /srv/news/scripts/backup.sh # 加 cron crontab -e # 添加一行: # 0 4 * * * /srv/news/scripts/backup.sh ``` **强烈建议**:把 `/srv/news/backups/` 同步到腾讯云 COS / 阿里云 OSS,做异地灾备。 ## 9. 升级 ```bash cd /srv/news git pull docker compose pull docker compose up -d --build docker compose exec api alembic upgrade head ``` ## 10. 常见问题 **Q: 某个源一直 fail?** A: 看 `docker compose logs worker | grep `,90% 是 RSS URL 失效或者被反爬。在 `sources` 表里 `enabled=false` 暂停。 **Q: 翻译字符超 500 万?** A: 配 `.env` 的 `TENCENT_TMT_QUOTA_BUFFER=0.05`,系统在 475 万字符后自动切本地 NLLB(需启用 `LOCAL_TRANSLATE_ENABLED=true`)。 未启用本地翻译时,系统会在原文末尾标 `[本条未翻译]`。 **Q: 30G 硬盘快满了?** A: 执行冷热分层 cron: ```sql DELETE FROM articles WHERE published_at < now() - interval '90 day' AND duplicate_of IS NULL; ``` **Q: 怎么加新源?** A: 网页登录 owner → `/admin/sources` → 新增。填 name / kind=rss / url=RSS 链接 / 优先级 / 抓取频率。保存后 worker 下个轮询周期自动拉。