Files
content-harvester/DEPLOY.md
T
huyueandClaude Opus 4.7 3dc5f02aaf
Deploy / Build and deploy (dev) (push) Successful in 1m40s
ci: 加 dev 自动部署工作流(push dev → 重建 content-harvester 容器)
.gitea/workflows/deploy.yml:push 到 dev 分支(或 workflow_dispatch)→ dev 开发机 runner
(标签 srv1563674)→ checkout → docker build → docker rm -f → docker run(env 走 --env-file .env,
内容来自 gitea repo secret CH_ENV_FILE)→ healthz 检查 → image prune。只触发 dev,master/prod 不碰。
部署参数(端口 8019 / 网络 ai-admin-cuiyue_default / 卷 content-harvester-profile / mem 2g)在 yml 的 env 块里。
DEPLOY.md / README.md 同步更新(dev 自动、prod 仍手动;前置:配 CH_ENV_FILE secret)。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 22:18:49 +08:00

6.9 KiB

部署 / 运维 — content-harvester

敏感值(token、MinIO 凭据、服务器登录信息)不写进本文件 —— 放本地工作目录里被 .gitignore 排除的文件:.env.dev(env 变量,docker run --env-file 直接用)、SERVERS.local.md(各环境服务器登录方式、目录、容器、注意事项)。下面命令里凡是 --env-file .env.dev 就会带上 token/凭据。

部署形态

  • 跑一个容器 content-harvester,--restart unless-stopped,挂在跟主站 backend 同一个 docker 网络上(主站用 http://content-harvester:8000 访问),host 端口任选(仅供本机直接调试),挂卷 content-harvester-profile:/data/browser_profile,--memory=2g --cpus=2,MinIO 配置走容器 env。
  • 代码:git clone -b dev https://git.aidfxn.com/admin/content-harvester.git,以后 git pull origin dev。
  • 对接的主站 Skill:「内容网站采集(content-harvester)」(executor_type=lobster,lobster_api_url=http://content-harvester:8000,lobster_api_key = .env.dev 里的 HARVESTER_TOKEN)。

重新部署 / 更新

dev:自动部署(push dev 分支即触发)

.gitea/workflows/deploy.yml —— push 到 dev 分支(或在 gitea 上手动 workflow_dispatch)→ dev 开发机的 gitea runner(标签 srv1563674,跟主站 AIautomation 的 deploy.yml 用同一个)自动:actions/checkout → docker build -t content-harvester . → docker rm -f content-harvester → docker run -d ...(env 走 --env-file .env)→ healthz 检查 → docker image prune。只管 dev;master/prod 不在 on.push.branches 里,不会被这个工作流碰(prod 仍走下面的手动流程)。

前置条件(一次性):在该仓库的 Gitea Actions secrets 里加一个 CH_ENV_FILE,内容就是要喂给容器的 env 文件全文(等价于 .env.dev),至少:

HARVESTER_TOKEN=<和主站采集 Skill 的 lobster_api_key 一致>
MINIO_ENDPOINT=https://minio.aidfxn.com
MINIO_BUCKET=clawlerdata
MINIO_ACCESS_KEY=<...>
MINIO_SECRET_KEY=<...>
# 可选:HEADLESS=0 / BROWSER_CHANNEL=chrome / MAX_PAGES=... / DEFAULT_MAX_IMAGES=... 等

没配 CH_ENV_FILE 的话工作流会在「Prepare env file」那步报错退出(不会拿一份残缺的 env 去跑)。工作流里硬编码的部署参数(非密钥):host 端口 8019、docker 网络 ai-admin-cuiyue_default、卷 content-harvester-profile:/data/browser_profile、--memory=2g --cpus=2、--restart unless-stopped —— 要改这些就改 deploy.yml 顶部的 env: 块。

工作流在 runner 的 workspace 里 build(actions/checkout 取的那份代码),不动 /opt/content-harvester —— 那个目录会逐渐过时,「现在 dev 跑的是哪份代码」要看运行中的容器(docker exec content-harvester ...),别看 /opt/content-harvester 的 git。

手动部署(prod;或 dev 想临时绕过工作流)

cd /opt/content-harvester
git pull -q origin <dev|master>          # prod 用 master
docker build -t content-harvester .
docker rm -f content-harvester
docker run -d --name content-harvester \
  --restart unless-stopped \
  --network ai-admin-cuiyue_default \    # 跟主站 backend 同一个 docker 网络
  -p 8019:8000 \
  --env-file /opt/content-harvester/.env.dev \   # 这个文件要先放到服务器上(本地 scp 过去,或在服务器上照样写一份;内容同 CH_ENV_FILE)
  -v content-harvester-profile:/data/browser_profile \
  --memory=2g --cpus=2 \
  content-harvester
sleep 6 && curl -s localhost:8019/healthz   # 期望 {"ok":true,"headless":false,...}

注:.env.dev 被 .gitignore 排除,git pull 不会带它过去。第一次在服务器上手动部署时把它放到 /opt/content-harvester/.env.dev(从本地 scp,或照本地内容写一份)。或者也可以不用 env-file、把那几个 -e KEY=VALUE 直接展开写在 docker run 里(但 token/凭据就会出现在 shell history / docker inspect)。

如果一时没法在服务器上 build,也可以本地 docker build 后 docker save content-harvester | gzip | ssh ... 'gunzip | docker load' —— 但注意 CPU 架构:Mac 一般是 arm64,服务器多是 x86_64,本地构建的 arm64 镜像在服务器跑不了。所以一般还是在服务器上 build。

在一台新服务器上首次部署

  1. 装 Docker。
  2. git clone -b dev https://git.aidfxn.com/admin/content-harvester.git(私有仓库,需要 git.aidfxn.com 的凭据)。
  3. 在工程目录放一份 .env.dev(从本地拷,或照 .env.dev 注释里的字段写;至少要 HARVESTER_TOKEN + 4 个 MINIO_* + HEADLESS=0)。
  4. docker build -t content-harvester .(base 镜像 ~1GB+,首次几分钟)。
  5. docker run -d ...(同上面那串;--network 改成跟主站 backend 同一个 docker 网络的名字,这样主站能用 http://content-harvester:8000 访问)。
  6. 主站侧:确认采集 Skill 的 lobster_api_url 指向 http://content-harvester:8000、lobster_api_key 跟 HARVESTER_TOKEN 一致;agent-system-backend 已含配套的 5 处改动(见 README 第 6 节)。
  7. x86_64 服务器想用真实 Chrome 过 Cloudflare 更稳:Dockerfile 里把 playwright install chromium 改成 playwright install chromium chrome,.env.dev 里加 BROWSER_CHANNEL=chrome,重 build。

本地跑(开发调试)

python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt && python -m playwright install chromium
set -a; . ./.env.dev; set +a        # 加载 HARVESTER_TOKEN / MINIO_* 等
HEADLESS=1 uvicorn server.app:app --reload   # 本机没 Xvfb 用 HEADLESS=1,但那样过不了 xchina 详情页的 Cloudflare,只能验证接口/列表页
# 真要本地完整测(过 Cloudflare),用 Docker 跑(镜像里有 xvfb):docker build -t content-harvester . && docker run --rm -p 8019:8000 --env-file .env.dev content-harvester

排查

  • curl -s localhost:8019/healthz —— 服务活着吗。
  • 从主站网络内看:docker run --rm --network <主站网络> curlimages/curl -s http://content-harvester:8000/healthz —— 主站 backend 能不能访问到。
  • 提一个作业看日志:curl -X POST localhost:8019/agents/xchina/invoke -H "Authorization: Bearer $HARVESTER_TOKEN" -H 'Content-Type: application/json' -d '{"source_kind":"website","content_url":"https://xchina.co/video/id-XXXX.html"}' → 拿 task_id → curl localhost:8019/tasks/<id>(返回里 log_tail 是最近作业日志)。docker logs content-harvester 看服务日志。
  • 主站那头任务一直 RUNNING:看主站 backend 容器日志,call_lobster_agent 在轮询;crawl 类超时是 CRAWL_LOBSTER_TIMEOUT(默认 7200s)。任务 SUCCEEDED 但资源库没东西:多半是 content 为空被 ingest 拒收(reason:"正文为空")——本服务已兜底用 title,理论上不会再发生,真发生了查 scraper 的 _extract_description。
  • 容器抽风/吃内存:--memory=2g --cpus=2 已经限了,真有问题重启容器即可(状态都在内存,重启即丢,主站会把那次任务标失败,重试就行)。