build(docker): static ffprobe, --allow-run=ffprobe,sha256sum, three supervised processes
entrypoint runs api + worker (io) + worker:agent; compose/runbooks document WORKER_CONCURRENCY, WORKER_AGENT_CONCURRENCY, ORGANIZE_WALK_MAX_FILES. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
f6457a29db
commit
3792868816
8 files changed
+78
-21
No files matched your search
+28
-6
@@ -1,7 +1,7 @@
|
||||
# 部署运行手册(W4-B)
|
||||
|
||||
形态:**除 PostgreSQL 外全部在一个容器里**。镜像 `uhd-media-tool` 内一个 `deno compile` 出的二进制 `umt-server`,
|
||||
容器里同时跑 api 与 worker 两个进程,api 同端口托管 web 静态产物(`WEB_ROOT=/app/public`),对外只有 **4380** 一个端口。
|
||||
容器里同时跑 api、worker(io 组)、worker:agent(Agent 会话)三个进程,api 同端口托管 web 静态产物(`WEB_ROOT=/app/public`),对外只有 **4380** 一个端口。
|
||||
compose 只有 `app` + `postgres` 两个服务。
|
||||
|
||||
## 1. 构建镜像
|
||||
@@ -12,10 +12,14 @@ APP_VERSION=v0.1.0 docker compose build # 版本号写
|
||||
```
|
||||
|
||||
- 多阶段:`denoland/deno:2.9.7`(装依赖 → web 构建 → server 编译)→ `debian:trixie-slim` 运行镜像。
|
||||
- 运行镜像只额外装 `gosu`(降权)与 `postgresql-client`(备份/恢复用 pg_dump/pg_restore/psql,17 版客户端兼容 16 版服务端)。
|
||||
- 运行镜像只额外装 `gosu`(降权)与 `postgresql-client`(备份/恢复用 pg_dump/pg_restore/psql,17 版客户端兼容 16 版服务端),
|
||||
外加 `/usr/local/bin/ffprobe`:从 `mwader/static-ffmpeg:7.1.1` 拷单个静态二进制(约 135 MB;Debian `ffmpeg` 包要 200+ 个依赖约 450 MB)。
|
||||
换来源用 `--build-arg FFPROBE_IMAGE=<含 /ffprobe 的镜像>`。
|
||||
- `/app` 下的二进制、静态产物、脚本全部 root 属主只读;运行用户改不了它们。
|
||||
- 二进制编译时固定权限:`--allow-net --allow-env --allow-sys --allow-read --allow-write`,不给 `--allow-run`
|
||||
- 二进制编译时固定权限:`--allow-net --allow-env --allow-sys --allow-read --allow-write --allow-run=ffprobe,sha256sum`
|
||||
(库根/下载目录由用户挂载,位置不固定,所以读写不按路径限制;写保护靠只读 `/app` + 非 root 运行)。
|
||||
子进程只放两个:ffprobe(入库后探测视频流)、sha256sum(coreutils 自带;目标已存在/跨卷恢复时的全量哈希在子进程里算,
|
||||
不占 worker 事件循环;不可用时回退进程内流式哈希,整理结果里 `hashVia` 记录实际方式)。
|
||||
|
||||
## 2. 启动
|
||||
|
||||
@@ -40,7 +44,9 @@ curl -s localhost:4380/api/v1/health
|
||||
3. `--role preflight`:密钥不是占位符、非 root、数据库可达、`WEB_ROOT/index.html` 存在、**DATA_DIR 各子目录与 Agent 会话目录可写**
|
||||
(失败即退出);再逐个检查已启用的**库根**与**下载器路径映射**(`view_kind=downloader` 的 local 前缀)可读可写——
|
||||
这些是用户挂载的目录,不可用只打 `warn` 日志并写审计 `preflight.path_unavailable`,不阻止启动。
|
||||
4. 同时起 `--role api` 与 `--role worker`;任一退出就停掉另一个并以非 0 退出,由 `restart: unless-stopped` 拉起。
|
||||
4. 同时起 `--role api`、`--role worker`(io 组:下载提交/入库复制/缓存清理/资产对账/抓取等 + outbox 中继 + 后台编排循环)、
|
||||
`--role worker:agent`(只跑 runAgent/resolveDecision);任一退出就停掉其余并以非 0 退出,由 `restart: unless-stopped` 拉起。
|
||||
两组 worker 只认领自己的任务,Agent 长会话不占文件复制的并发槽。
|
||||
|
||||
健康检查:镜像 `HEALTHCHECK` 用二进制自带 fetch 打 `127.0.0.1:4380/api/v1/health`(`--role probe`),不装 curl。
|
||||
|
||||
@@ -81,7 +87,9 @@ docker compose logs app | grep 'setup token'
|
||||
- 登记库根时服务端会在目录里写哨兵文件 `.uhd-root`,写不进去直接 400(提示检查挂载与属主)。
|
||||
- **PUID/PGID**:设成宿主媒体目录的属主(`stat -c '%u:%g' /srv/media`)。改 PUID/PGID 后重启容器,entrypoint 会把 `/data` 重新 chown。
|
||||
也可以 `docker run --user` 直接指定(此时跳过 chown,`/data` 需事先可写)。
|
||||
- Agent 会话目录缺省 `DATA_DIR/agent-sessions`,可用 `AGENT_SESSION_DIR` 覆盖(api 与 worker 在同一容器里,天然共享)。
|
||||
- Agent 会话目录缺省 `DATA_DIR/agent-sessions`,可用 `AGENT_SESSION_DIR` 覆盖,或在控制台 设置 → Pi Agent → Agent 运行 → 会话目录
|
||||
(`settings.agent.sessionDir`,优先于环境变量)改成别的绝对路径(三个进程在同一容器里,天然共享)。改到 `/data` 之外时:目录要对
|
||||
`PUID:PGID` 可写(entrypoint 不会 chown 它),`umt backup` 只打包 `DATA_DIR/agent-sessions`,需自行备份;有运行中的会话时不要改。
|
||||
|
||||
## 5. 常用环境变量
|
||||
|
||||
@@ -93,6 +101,9 @@ docker compose logs app | grep 'setup token'
|
||||
| `PUID` / `PGID` | `10001` | 运行用户 |
|
||||
| `DATA_DIR` | `/data` | 应用数据目录(开发机缺省 `./data`) |
|
||||
| `AGENT_SESSION_DIR` | `DATA_DIR/agent-sessions` | Agent 会话目录 |
|
||||
| `WORKER_CONCURRENCY` | `4` | io 组 worker 并发槽 |
|
||||
| `WORKER_AGENT_CONCURRENCY` | `2` | agent 组 worker 并发槽(每个 Agent 会话占一槽直到结束) |
|
||||
| `ORGANIZE_WALK_MAX_FILES` | `5000` | 手动按路径整理时递归遍历的文件数上限;dryRun 超出返回 `truncated: true`,正式执行拒绝 |
|
||||
| `BACKUP_KEEP` | `14` | 备份保留份数,0 不轮转 |
|
||||
| `LOG_LEVEL` | `info` | pino 级别 |
|
||||
|
||||
@@ -109,7 +120,8 @@ docker compose ps && docker compose logs --tail=50 app
|
||||
|
||||
## 7. 日志与排障
|
||||
|
||||
- `docker compose logs -f app`:pino JSON,一行一条;`role` 字段区分 api / worker。json-file 驱动,单文件 10 MB × 5 份。
|
||||
- `docker compose logs -f app`:pino JSON,一行一条;worker 日志带 `group`(io / agent)。
|
||||
- `worker 事件循环延迟超过阈值`(warn,`lagMs`):worker 主线程被同步重活占住超过 5s,租约续期可能受影响,带上前后日志排查。json-file 驱动,单文件 10 MB × 5 份。
|
||||
- entrypoint 自身的输出(`entrypoint: ...`)说明哪个进程先退出、退出码多少。
|
||||
- 自检失败(`check=data_dir`):`/data` 卷不可写,检查是否挂了只读卷或 `--user` 与卷属主不一致。
|
||||
- 挂载路径告警(`挂载路径不可用`):看审计页 `preflight.path_unavailable` 事件,核对 bind mount 与 PUID/PGID。
|
||||
@@ -146,3 +158,13 @@ docker compose ps && docker compose logs --tail=50 app
|
||||
|
||||
首次演练发现:镜像里的 pg_restore 17 直连 16 版服务端会因 `SET transaction_timeout` 失败(单事务回滚,库未被破坏),
|
||||
已改为 pg_restore 输出 SQL、过滤该行后由 psql 单事务回放,复测通过。
|
||||
|
||||
2026-09-27,W4-D,临时项目名 `umtw4d`、app 127.0.0.1:4391、pg 127.0.0.1:55441(验证后已 `down -v` 并删除镜像)。
|
||||
|
||||
| 步骤 | 结果 |
|
||||
|---|---|
|
||||
| `docker build --build-arg APT_MIRROR=mirrors.ustc.edu.cn` | 成功;镜像内容 160.5 MB 压缩(W4-B 107 MB),新增层:静态 ffprobe 135 MB(未压缩),二进制 214 MB |
|
||||
| `compose up -d` | 约 8 秒 healthy;`/api/v1/health` → `status=ok role=api db=ok` |
|
||||
| 进程 | `entrypoint.sh serve` 下三个 `umt-server`:`--role api`、`--role worker`(日志 `group=io`,9 个任务,并发 4,模块循环 4 个)、`--role worker:agent`(`runAgent@1`/`resolveDecision@1`,并发 2,无模块循环),均为 uid 10001 |
|
||||
| ffprobe | 容器内 `gosu 10001 ffprobe` 探测一个 2 KB WAV → `pcm_s16le`;`ffprobe version 7.1.1` |
|
||||
| 编译权限 | 同一组编译参数的探针二进制在容器内以 10001 运行:node `execFile("ffprobe")` 成功、`Deno.Command("sha256sum")` 成功、`Deno.Command("ls")` → `NotCapable` |
|
||||
Reference in new issue
Block a user