workflows/schedules/commands/notification-routing 与 source-manifests 四模块 清单同步登录统一化与新工作流(product.hk_anpei_public_inventory.daily 等); docs 运维手册、控制台与耗时分布文档、CI 关键脚本清单、AGENTS 指南更新; 测试与模块注册对齐。
230 lines
12 KiB
Markdown
230 lines
12 KiB
Markdown
# GYXX Flow 部署
|
||
|
||
## 推荐部署方式
|
||
|
||
生产环境推荐使用 Linux 主机:
|
||
|
||
- PostgreSQL 使用云端服务,完整 DSN 由服务器密钥环境注入。
|
||
- Python 调度器、本机 Hermes 和需要登录状态的浏览器运行在宿主机。
|
||
- systemd 只守护一个 Python 调度进程,所有业务时间规则仍来自 `config/schedules.json`。
|
||
- 运行数据使用 `/var/lib/gyxx-flow`,不得把生产 `GYXX_DATA_ROOT` 指向源码目录中的 `var/`。
|
||
|
||
这种方式能够直接访问两个本机 Hermes 角色和浏览器 CDP,也不会把业务定时规则复制到 systemd timer、cron 或 Windows Task Scheduler。
|
||
|
||
## 环境要求
|
||
|
||
- Python 3.12 和 `uv`
|
||
- 可访问的 PostgreSQL 13+ 云端实例及运行时注入的 `GYXX_POSTGRES_DSN`
|
||
- 本机 Hermes `data-analyzer` 与 `data-collector`
|
||
- Chrome 与 Scrapling 浏览器运行时,以及个别业务入口仍需要的 PowerShell 运行条件
|
||
- 可访问现有飞书身份的专用系统用户
|
||
|
||
创建生产目录和服务账户:
|
||
|
||
```bash
|
||
sudo useradd --system --create-home --shell /usr/sbin/nologin gyxx-flow
|
||
sudo install -d -o gyxx-flow -g gyxx-flow /opt/gyxx-flow
|
||
sudo install -d -o gyxx-flow -g gyxx-flow /var/lib/gyxx-flow
|
||
sudo install -d -o root -g gyxx-flow -m 0750 /etc/gyxx-flow
|
||
```
|
||
|
||
将代码发布到 `/opt/gyxx-flow` 后安装锁定依赖:
|
||
|
||
```bash
|
||
cd /opt/gyxx-flow
|
||
sudo -u gyxx-flow uv sync --python 3.12 --no-group dev --frozen
|
||
sudo -u gyxx-flow uv run scrapling install
|
||
```
|
||
|
||
业务采集代码只使用 Scrapling;其动态与隐身抓取器所需的底层浏览器由
|
||
Scrapling 安装和管理。具体边界与验证命令见
|
||
[`architecture.md`](architecture.md#scrapling-采集边界)。
|
||
|
||
凭据放入 `/etc/gyxx-flow/gyxx-flow.env`,权限设为 `0640`。该文件不提交到 Git,至少按实际环境注入数据库密码、飞书身份和可选 Hermes 密钥。
|
||
|
||
控制台和调度器都支持可重复的 `--env-file`,只加载显式列出的文件。systemd 的
|
||
`EnvironmentFile=` 或当前进程环境优先于文件中的同名值,避免本地文件意外覆盖密钥系统
|
||
注入值。
|
||
|
||
## 香港安沛 SKU 公有库存同步
|
||
|
||
`product.hk_anpei_public_inventory.daily` 每天按 `Asia/Shanghai` 09:00 查询计划书中的固定 SKU 清单,
|
||
调用聚水潭 `/open/inventory/query`,并按商品编码(光影)更新飞书电子表格「現有庫存」工作表的
|
||
「仓库现有库存」列。它要求调度进程环境提供
|
||
`JUSHUITAN_APP_KEY` 和 `JUSHUITAN_APP_SECRET`;`JUSHUITAN_BASE_URL` 可选,默认使用聚水潭官方 OpenAPI
|
||
地址。没有密钥时工作流会在查询前失败,不会把库存留成 0 或改写多维表格。
|
||
|
||
Windows NSSM 调度服务从受保护的运行时环境文件读取凭据。安装器默认读取通用运行环境文件
|
||
`D:\product-collector-analyze-flow\.env`,以及独立的聚水潭密钥文件
|
||
`D:\product-collector-analyze-flow\private-secrets\jushuitan.env`。两个文件都按服务账户实际身份限制 ACL;
|
||
可分别通过 `-RuntimeEnvFile` 和 `-JushuitanEnvFile` 指定其他路径:
|
||
|
||
```powershell
|
||
.\deploy\windows-service\install.ps1 -ProjectRoot D:\gyxx-flow -DataRoot D:\gyxx-flow-data -RuntimeEnvFile D:\product-collector-analyze-flow\.env -JushuitanEnvFile D:\product-collector-analyze-flow\private-secrets\jushuitan.env
|
||
```
|
||
|
||
仅将这两个变量的正式聚水潭应用值写入服务账户可读的环境文件,并限制文件 ACL;不要使用开发配置中的默认值,
|
||
也不要把真实值写进 Git、PowerShell 命令参数、服务日志或文档。已安装的服务需要让调度进程重新读取环境文件后,
|
||
新计划才会使用这些值。Linux 部署则将相同变量放入权限为 `0640` 的 `EnvironmentFile`。
|
||
|
||
## 三平台电商费用日报部署边界
|
||
|
||
`product.ecommerce_costs.daily` 是包含天猫万相台、京东京准通和抖音千川分支的电商费用工作流;三个
|
||
平台下载节点并行,导入节点分别等待本平台下载完成。当前项目对其中天猫万相台这条
|
||
浏览器链路按 Windows Server + NSSM 验收;Linux 上的通用调度器说明不等于万相台浏览器流程
|
||
已经完成 Linux/无头浏览器验收。部署到 Windows Server 时使用项目内的 NSSM 服务包装器,业务
|
||
时间仍只由 `config/schedules.json` 管理,不创建 Windows Task Scheduler 条目。
|
||
|
||
该工作流需要由服务账户注入以下环境变量,真实值不要写入 Git、命令参数或文档:
|
||
|
||
```text
|
||
GYXX_DATA_ROOT=D:\gyxx-flow-data
|
||
GYXX_POSTGRES_DSN=<云端 PostgreSQL DSN>
|
||
WANXIANG_ACCOUNT=<万相台账号>
|
||
WANXIANG_PASSWORD=<yourpassword>
|
||
```
|
||
|
||
未显式设置 `WANXIANG_USER_DATA_DIR` 时,登录态保存在
|
||
`<GYXX_DATA_ROOT>\state\browser-profiles\wanxiang-ads`。这个 Profile 是该脚本的独立登录态,
|
||
不要与其他淘宝/万相台脚本共用。使用 NSSM 的 `AppEnvironmentExtra` 或服务器密码管理器注入
|
||
凭据;不要把真实密码写进 PowerShell 脚本或 `nssm` 命令历史。
|
||
|
||
安装 Windows 常驻服务:
|
||
|
||
```powershell
|
||
cd D:\gyxx-flow
|
||
uv sync --python 3.12 --group dev
|
||
.\deploy\windows-service\install.ps1 -ProjectRoot D:\gyxx-flow -DataRoot D:\gyxx-flow-data
|
||
# 按服务器密码管理器的方式为 gyxx-flow-scheduler 注入上述环境变量
|
||
nssm start gyxx-flow-scheduler
|
||
```
|
||
|
||
首次上线先在有头浏览器中建立登录态并导入一日数据;验证码或滑块必须在同一 Profile 中人工
|
||
完成:
|
||
|
||
```powershell
|
||
$env:GYXX_DATA_ROOT = 'D:\gyxx-flow-data'
|
||
$env:WANXIANG_ACCOUNT = '<从密码管理器读取>'
|
||
$env:WANXIANG_PASSWORD = '<yourpassword>'
|
||
uv run gyxx doctor --json
|
||
uv run gyxx scripts run product.tmall_wanxiang_ads.collect --date 2026-08-17 --execute
|
||
uv run gyxx scripts run product.import.tmall_ads --date 2026-08-17 --execute
|
||
```
|
||
|
||
确认手工链路成功后,再让唯一的 `gyxx schedule run` 常驻服务接管;不要为 19:30 另建系统定时
|
||
任务。完整的服务重启和 dry-run 流程见本文件的“systemd 调度服务”章节以及
|
||
[`runbook.md`](runbook.md) 的万相台小节。
|
||
|
||
## PostgreSQL
|
||
|
||
从受限环境文件加载云端数据库连接:
|
||
|
||
```bash
|
||
cd /opt/gyxx-flow
|
||
set -a
|
||
source /etc/gyxx-flow/gyxx-flow.env
|
||
set +a
|
||
uv run gyxx doctor --json
|
||
```
|
||
|
||
生产环境必须设置 `GYXX_POSTGRES_DSN`,且 DSN 主机必须是非回环地址。项目不会把云端地址、用户名或密码写入源码;`deploy/postgres.compose.yml` 仅保留为开发和恢复场景的可选本地工具,不是当前生产数据库入口。
|
||
|
||
## 本机 Hermes
|
||
|
||
启动并验证两个独立角色:
|
||
|
||
```text
|
||
data-analyzer: API base http://127.0.0.1:8642/v1
|
||
data-collector: API base http://127.0.0.1:8643/v1
|
||
```
|
||
|
||
`28790/28791` 不作为工作流业务端点。
|
||
|
||
运行时配置必须保持回环地址。Hermes 不可用时,纯采集、文件处理、数据库同步和不依赖大模型的确定性通知仍可运行;仍依赖 Hermes 的工作流应保持停用或手工执行,不得静默改用远程 AI。飞书消息投递本身统一依赖运行服务账户的 `hermes-analyzer` lark-cli user 授权。
|
||
|
||
例外:`content.summary.weekly` / `content.summary.monthly` 的内容报告、`product.style_analysis.interval` 的款式周期分析,以及 `product.video_upload` / `product.jd_video_upload` 的视频标题与视觉颜色识别,均配置为显式直连 MiniMax。内容报告使用 `CONTENT_ANALYSIS_LLM_BASE_URL`、`CONTENT_ANALYSIS_LLM_MODEL` 和 `CONTENT_ANALYSIS_LLM_API_KEY`;商品/视频链路使用 `STYLE_ANALYSIS_LLM_*`(视频链路也支持 `GYXX_DIRECT_LLM_*` 覆盖)。这些链路不读取 Hermes 分析端口,但仍保留本地结果校验、断点、EffectLedger、飞书和 PostgreSQL 写入链路。
|
||
|
||
## 上线前验证
|
||
|
||
使用生产服务账户运行:
|
||
|
||
```bash
|
||
cd /opt/gyxx-flow
|
||
sudo -u gyxx-flow env GYXX_DATA_ROOT=/var/lib/gyxx-flow uv run gyxx doctor --json
|
||
sudo -u gyxx-flow env GYXX_DATA_ROOT=/var/lib/gyxx-flow uv run gyxx schedule run --dry-run --once
|
||
sudo -u gyxx-flow env GYXX_DATA_ROOT=/var/lib/gyxx-flow uv run gyxx list --json
|
||
```
|
||
|
||
开发或发布流水线另外执行:
|
||
|
||
```bash
|
||
uv run ruff check src tests
|
||
uv run pytest
|
||
uv build
|
||
uv run gyxx acceptance status --json
|
||
```
|
||
|
||
## systemd 调度服务
|
||
|
||
项目提供 `deploy/gyxx-flow.service`。安装并启动:
|
||
|
||
```bash
|
||
sudo cp /opt/gyxx-flow/deploy/gyxx-flow.service /etc/systemd/system/gyxx-flow.service
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl enable --now gyxx-flow.service
|
||
sudo systemctl status gyxx-flow.service
|
||
```
|
||
|
||
unit 的唯一业务入口是:
|
||
|
||
```text
|
||
/opt/gyxx-flow/.venv/bin/python -m gyxx_flow schedule run
|
||
```
|
||
|
||
若 unit 不使用 systemd `EnvironmentFile=`,入口必须显式追加:
|
||
|
||
```text
|
||
--env-file /etc/gyxx-flow/gyxx-flow.env
|
||
```
|
||
|
||
调度状态写入 `/var/lib/gyxx-flow/state/scheduler`,日志和子进程产物写入同一外置数据根。修改 `config/schedules.json` 后先运行一次 dry-run,再重启服务:
|
||
|
||
```bash
|
||
sudo -u gyxx-flow env GYXX_DATA_ROOT=/var/lib/gyxx-flow \
|
||
/opt/gyxx-flow/.venv/bin/python -m gyxx_flow schedule run --dry-run --once
|
||
sudo systemctl restart gyxx-flow.service
|
||
```
|
||
|
||
不要为单个工作流创建 systemd timer 或 cron 条目,也不得同时运行两个调度器实例。
|
||
|
||
## 发布更新
|
||
|
||
```bash
|
||
sudo systemctl stop gyxx-flow.service
|
||
cd /opt/gyxx-flow
|
||
# 切换到已验收版本后:
|
||
sudo -u gyxx-flow uv sync --python 3.12 --no-group dev --frozen
|
||
sudo -u gyxx-flow env GYXX_DATA_ROOT=/var/lib/gyxx-flow uv run gyxx doctor --json
|
||
sudo -u gyxx-flow env GYXX_DATA_ROOT=/var/lib/gyxx-flow uv run gyxx schedule run --dry-run --once
|
||
sudo systemctl start gyxx-flow.service
|
||
```
|
||
|
||
代码发布和回滚都不得覆盖 `/var/lib/gyxx-flow`。
|
||
|
||
## Docker 边界
|
||
|
||
当前 Compose 只负责 PostgreSQL。完整应用若进入容器,容器内 `127.0.0.1` 不再指向宿主机的两个 Hermes 和浏览器 CDP;同时部分工作流仍可能依赖可见桌面登录或 PowerShell。因此在完成网络、安全、浏览器 Profile 持久化和目标工作流验收前,不将完整生产应用声明为纯容器部署。
|
||
|
||
## Windows 兼容入口
|
||
|
||
`deploy/windows-service/` 暂时保留为 legacy NSSM 兼容入口。NSSM 只守护同一个 `gyxx schedule run` 进程,不注册 Windows Task Scheduler,也不保存业务时间规则。新服务器部署以 Linux systemd 为准。
|
||
|
||
## Windows 工作站保活(控制台 + 调度器自检自启动)
|
||
|
||
交互式 Windows 工作站(无法用 systemd/NSSM 常驻服务时)使用 `deploy/watchdog/gyxx_keepalive.py` 做进程外保活:
|
||
|
||
- **控制台自检**:控制台进程内置看门狗线程(`console.py` `_console_watchdog`),周期探测自身 `GET /api/health`;连续失败(默认 3 次,`GYXX_CONSOLE_WATCHDOG_FAILURES` / `GYXX_CONSOLE_WATCHDOG_INTERVAL_SECONDS` 可调,`GYXX_CONSOLE_WATCHDOG=0` 关闭)即以退出码 70 自杀,交给保活拉起。`/api/health` 不鉴权、只报进程 PID、uptime 与调度锁存活,供探针使用;API 500 现在会把完整堆栈落到 stderr(随启动重定向进 `var/logs/console-restarts/`)。
|
||
- **保活脚本**:登录自启动(Startup 目录 `gyxx-keepalive.vbs` → `pythonw gyxx_keepalive.py`)后常驻,每 30 秒一轮:控制台探测连续失败或端口空置则结束僵死进程树并按 `--env-file` 同款参数拉起;读 `var/state/scheduler/service.lock` 判定调度器存活,锁主死亡即拉起 `gyxx schedule run`(调度器单实例锁保证不双跑)。单目标冷却 120 秒;30 分钟内 5 次重启触发熔断,暂停拉起 1 小时并记日志待人工检查。日志在 `var/logs/keepalive/keepalive.log`。
|
||
- **验收**:`python deploy/watchdog/gyxx_keepalive.py --once` 单轮探测+处置;健康态应只输出 `keepalive-start`/`keepalive-once-done`。
|
||
- 该脚本只做进程可用性守护,不承载任何业务时间规则(唯一调度路径仍是 `gyxx schedule run`);未注册 Windows Task Scheduler。注意:Startup 方式要求用户已登录——机器重启后需人工登录一次,之后控制台与调度器会在约 2 分钟内自动就位。
|