Files
lisglosips/docs/RECORDING_TRANSFER_PLAYBACK_RUNBOOK.md

7.0 KiB
Raw Permalink Blame History

Recording Transfer and Playback Runbook

任务:S24 - 录音搬运与播放
完成时间:2026-06-21 20:20 +08:00 当前模式:S56 - Server B 本机录音闭环(2026-08-27

0. 当前生产架构(S56

录音不再经过 B 到 A 的专用 SSH。Server B 上的 RTPEngine Recording Daemon 先写入 /dev/shm/voip_rec/incoming,本机 finalizer 将稳定文件原子改名为 /dev/shm/voip_rec/ready/**/*.readyRecording Worker 再复制、校验并持久化到 /data/recordings,最后写入数据库并删除本机 .ready 源文件。

B RTPEngine -> B incoming -> B finalizer -> B ready
             -> B Recording Worker -> B /data/recordings + MySQL

旧的 B -> A SSH 配置仅作为短期回滚材料保留,不参与当前服务。S24 以下章节保留为历史实施记录。

1. 目标

S24 完成录音闭环的最小能力:

  • Server B 通过 Tailscale 私网从 Server A 拉取 .ready 录音文件。
  • 拉取后校验文件大小和 SHA-256。
  • 本地 .part-* 临时文件校验成功后原子改名。
  • 校验和数据库标记成功后才允许删除 Server A 源 .ready 文件。
  • API 提供受 RBAC 保护的播放入口,通过 Nginx X-Accel-Redirect 进入内部 /_recordings/ location,支持 Range。

2. 代码产物

文件 说明
apps/worker-recording/src/transfer.ts 录音扫描、SSH 拉取、大小校验、SHA-256、原子落盘、源文件安全删除、recordings 入库
apps/worker-recording/src/main.ts Recording Worker 主循环
apps/worker-recording/src/transfer.spec.ts 搬运、哈希、删除保护和路径安全测试
apps/api/src/modules/recordings/* GET /api/v2/recordings/:id/play 播放入口
apps/api/src/modules/recordings/recordings.service.spec.ts Nginx 内部路径和路径穿越防护测试
infra/server-a/s19/scripts/lisglosips-recording-finalize A 端 finalizer 仓库副本,ready 目录改为 0770,便于专用拉取用户校验后删除源文件

3. Worker 环境变量

S56 当前生产配置:

DATABASE_URL
RECORDING_SOURCE_MODE=local
RECORDING_LOCAL_READY_DIR=/dev/shm/voip_rec/ready
RECORDING_LOCAL_ROOT=/data/recordings
RECORDING_SCAN_INTERVAL_MS=10000
RECORDING_MAX_FILES_PER_SCAN=50
RECORDING_DELETE_SOURCE_AFTER_COPY=true

RECORDING_SOURCE_MODE=ssh 仍受代码支持,以下变量只用于回滚到旧架构:

DATABASE_URL
RECORDING_SOURCE_MODE=ssh
RECORDING_REMOTE_HOST=lisglosips-a
RECORDING_SSH_CONFIG=/etc/lisglosips/recording/ssh_config
RECORDING_REMOTE_READY_DIR=/dev/shm/voip_rec/ready
RECORDING_LOCAL_ROOT=/data/recordings
RECORDING_SCAN_INTERVAL_MS=10000
RECORDING_MAX_FILES_PER_SCAN=50
RECORDING_DELETE_SOURCE_AFTER_COPY=true

SSH 模式本地开发可使用 .codex-private/ssh/config;生产回滚时才使用 /etc/lisglosips/recording/ 下的受控 key 和 known_hosts。

4. 服务器变更

Server B

  • S56 新增 lisglosips-recording-finalize.timer,每 5 秒检查本机稳定录音文件。
  • Recording Worker 以 lisglo-recorder 身份运行,通过 SupplementaryGroups=rtpengine 访问 ready 目录。
  • systemd 只开放 /dev/shm/voip_rec/ready/data/recordings 所需写权限。
  • Worker 使用 RECORDING_SOURCE_MODE=local,运行期不再启动 ssh 子进程。
  • 新增 /etc/lisglosips/recording/a_pull_ed25519,权限 0640 root:lisglosips
  • 新增 /etc/lisglosips/recording/known_hosts
  • 录音持久化目录仍为 /data/recordings,由 lisglo-recorder:lisglosips 管理。
  • 备份目录:/var/backups/lisglosips-s24/20260621T121100Z

Server A

  • 新增专用用户 lisglo-rec-pull,加入 rtpengine 组。
  • 安装 B 端录音拉取公钥到 /var/lib/lisglo-rec-pull/.ssh/authorized_keys
  • /dev/shm/voip_rec/ready 调整为 0770 rtpengine:rtpengine
  • 更新 /usr/local/sbin/lisglosips-recording-finalize,让 ready 根目录和 ready 子目录保持 0770
  • 备份目录:/var/backups/lisglosips-s24/20260621T121130Z

未调整 SSH 管理端口,未禁止 SSH key 登录,未重启 OpenSIPS、RTPEngine、Nginx 或 API。

5. 播放路径

API

GET /api/v2/recordings/:id/play
Permission: recordings.play

API 只查询状态为 READY 的录音,返回:

X-Accel-Redirect: /_recordings/<storage-key>
Content-Type: audio/wav 或 audio/mpeg
Cache-Control: private, no-store

Nginx 已在 S05 配置:

location /_recordings/ {
    internal;
    alias /data/recordings/;
    add_header Accept-Ranges bytes always;
}

外部直接访问 /_recordings/... 返回 404,只能经 API 鉴权后内部转发。

6. 验证结果

本地:

  • corepack pnpm@10.33.0 exec vitest run apps/worker-recording/src/transfer.spec.ts apps/api/src/modules/recordings/recordings.service.spec.ts 通过,5 条测试。
  • corepack pnpm@10.33.0 --filter @lisglosips/worker-recording build 通过。
  • corepack pnpm@10.33.0 typecheck 通过。
  • corepack pnpm@10.33.0 lint 通过。
  • corepack pnpm@10.33.0 build 通过。

服务器冒烟:

  • A 生成测试文件 /dev/shm/voip_rec/incoming/s24-smoke/s24-smoke.wav
  • A finalizer 生成 /dev/shm/voip_rec/ready/s24-smoke/s24-smoke.wav.ready
  • B 使用 lisglo-recorder 和专用 SSH key 从 A 私网拉取。
  • B 落盘 /data/recordings/s24-smoke/s24-smoke.wav,大小 31,属主 lisglo-recorder:lisglosips
  • A/B SHA-256 一致:c72dd3909a317ad55817435b7ed5a0db6947455ffc66e09a2dde787fe4a8c348
  • 校验成功后 A 源 .ready 文件已删除。
  • 外部直接访问 https://127.0.0.1/_recordings/s24-smoke/s24-smoke.wav 返回 404,内部录音 location 未暴露。

7. 当前限制

S24 记载的 placeholder 限制已经失效;B 已运行完整 monorepo API/Worker release。S56 不迁移 A 的历史录音,也不自动删除旧 SSH key;旧凭据待本地闭环稳定运行并完成真实录音验收后再单独清理。

8. 回滚

Server A

sudo cp -a /var/backups/lisglosips-s24/20260621T121130Z/lisglosips-recording-finalize /usr/local/sbin/lisglosips-recording-finalize
sudo chmod 0755 /usr/local/sbin/lisglosips-recording-finalize
sudo userdel -r lisglo-rec-pull
sudo chmod 0750 /dev/shm/voip_rec/ready

Server B

sudo systemctl disable --now lisglosips-recording-finalize.timer
sudo cp /var/backups/lisglosips-s56/<timestamp>/recording-worker.env /etc/lisglosips/recording-worker.env
sudo cp /var/backups/lisglosips-s56/<timestamp>/10-s28-recording.conf /etc/systemd/system/lisglosips@recording-worker.service.d/10-s28-recording.conf
sudo systemctl daemon-reload
sudo systemctl restart lisglosips@recording-worker

代码回滚:

  • 恢复 apps/worker-recording/src/main.ts 到骨架版本。
  • 删除或恢复 apps/worker-recording/src/transfer.tstransfer.spec.ts
  • 删除 apps/api/src/modules/recordings/ 并从 AppModule 移除 RecordingsModule
  • 恢复 apps/worker-recording/package.jsontsconfig.jsonpnpm-lock.yaml
  • 重新执行 corepack pnpm@10.33.0 build