news 2026/10/31 12:05:36

LiveKit本地录制落盘路径排查与修复实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LiveKit本地录制落盘路径排查与修复实践

LiveKit 本地录制落盘路径排查与修复实践

    • 一、背景
    • 二、问题现象(按时间顺序)
    • 三、排查过程(证据链)
    • 四、根因
    • 五、修复方案(仓库代码已改,编译通过)
      • 1. `LiveKitEgressService.java` — `buildPublicUrl` 去掉 `recordings/` 前缀
      • 2. `docker-compose.yml` — 移除无效的 `working_dir`,注释改为真实行为
      • 3. `egress.yaml` — 注释按真相重写
    • 六、服务器操作步骤
    • 七、关键结论与注意事项
    • 八、本会话其他事项
      • 1. 多环境 livekit 配置补齐(已完成)
      • 2. 会议待办列表不显示问题(已给排查方向,待用户验证)
    • 附录:egress v1.14.1 相关源码位置(GitHub)
    • 附:IT 疑难杂症诊疗室 —— 技术文章通用大纲模板
      • 一、背景(病症概述)
      • 二、问题现象(症状清单,按时间顺序)
      • 三、排查过程(证据链)
      • 四、根因(病灶定位)
      • 五、修复方案(对症下药)
      • 六、服务器操作步骤(落地执行)
      • 七、关键结论与注意事项(医嘱)
      • 八、附录(参考资料)

一、背景

录制存储从 MinIO/S3 切换为本地目录模式(egress.yaml 移除storage.s3块,egress v1.14.1 自动回退为本地文件输出),由后端 Spring 静态资源映射对外提供播放地址:

  • 宿主机目录:/data/data/oss/livekit-recordings
  • docker-compose 挂载:/data/data/oss/livekit-recordings:/recordings
  • 后端映射:/livekit-recordings/**→file:{livekit.record.local-dir}/(WebConfig)
  • 访问前缀:https://xxx.com/skillApi/livekit-recordings/

二、问题现象(按时间顺序)

  1. 录制失败:permission denied
    egress 日志报:

    egress_failed ... "error": "Local upload failed: mkdir /recordings/meeting-xxx/: permission denied", "code": 400

    宿主目录属主为root:root(755),容器内 egress 用户无写权限,文件从未生成。

  2. 修复权限后,文件落盘位置与 URL 不匹配

    • 磁盘实际:/data/data/oss/livekit-recordings/meeting-xxx/20261012_181005.ogg(没有recordings/层)
    • 访问 URL:.../skillApi/livekit-recordings/recordings/meeting-xxx/20261012_181005.ogg
    • 结果:500"系统异常: No static resource recordings/meeting-xxx/..."/ 404
  3. 尝试working_dir: /recordings无效
    加了working_dir并重建容器后(docker exec livekit-egress pwd输出/recordings),新录制文件仍然落在{挂载点}/meeting-xxx/...,行为与旧容器完全一致。


三、排查过程(证据链)

步骤证据结论
查 egress 挂载docker inspect:/data/data/oss/livekit-recordings → /recordings(rw)挂载正常
查落盘位置find宿主目录:文件在{挂载点}/meeting-xxx/...少了一层recordings/
查进程工作目录docker exec livekit-egress pwd→/recordings容器 WorkingDir 生效了
查时间线容器 StartedAt 18:09:06 < 新目录 Birth 18:26:50新录制确实由新容器产生
查 DB 数据video_recording.file_path = recordings/meeting-xxx/xxx.ogg后端发给 egress 的 filepath 带recordings/前缀,没问题
查镜像livekit/egress:v1.14.1与源码一致
查 egress 源码pkg/server/server_rpc.go→launchProcess()找到根因

四、根因

egress v1.14.1 的 service 每发起一次录制,都会 fork 一个run-handler子进程,子进程的工作目录被硬编码为/:

// pkg/server/server_rpc.go - launchProcess()cmd:=exec.Command("egress","run-handler")cmd.Dir="/"// 硬编码,与容器 WorkingDir / working_dir 无关

真正执行上传的是这个 handler 子进程(storage.NewLocal(&LocalConfig{})→StorageDir = filepath.Abs("") = 进程 CWD = "/"),因此:

上传目标 = / + filepath = / + recordings/meeting-xxx/xxx.ogg = /recordings/meeting-xxx/xxx.ogg (容器内) → {挂载点}/meeting-xxx/xxx.ogg (宿主机)

结论:

  • working_dir: /recordings对落盘位置零作用(只影响 service 主进程,上传在子进程里做)。
  • 挂载点/recordings恰好等于 filepath 首段recordings,文件才没有丢进容器根目录——挂载点必须与 filepath 首段严格对齐,改挂载点就会丢文件。
  • 磁盘实际布局永远是{local-dir}/{roomName}/{时间戳}.mp4|ogg,URL 里不能带recordings/。

五、修复方案(仓库代码已改,编译通过)

1.LiveKitEgressService.java—buildPublicUrl去掉recordings/前缀

privateStringbuildPublicUrl(Stringfilepath){Stringprefix=properties.getRecord().getPublicPrefix();if(filepath!=null&&filepath.startsWith("recordings/")){filepath=filepath.substring("recordings/".length());}...}

新 URL ={public-prefix}/livekit-recordings/{roomName}/{时间戳}.mp4|ogg,与磁盘{local-dir}/{roomName}/...精确对齐。

注意:发给 egress 的 filepath(DBfile_path)保持不变,仍是recordings/{roomName}/...——它是挂载对齐的关键。

2.docker-compose.yml— 移除无效的working_dir,注释改为真实行为

livekit-egress:...volumes:-./configs/egress.yaml:/etc/egress.yaml:ro# egress v1.14.1 的 handler 子进程工作目录被硬编码为 /(server_rpc.go: cmd.Dir="/"),# 上传目标 = / + filepath(recordings/{roomName}/...) = /recordings/{roomName}/...,# 挂载 /recordings 后最终落在宿主 {挂载点}/{roomName}/...。-/data/data/oss/livekit-recordings:/recordings

3.egress.yaml— 注释按真相重写

核心要点写入注释,防止以后再按"工作目录"思路排查:

  • handler 子进程工作目录硬编码为/,working_dir无效;
  • 挂载点必须与 filepath 首段recordings一致;
  • 最终文件 = 容器/recordings/{roomName}/...= 宿主{挂载点}/{roomName}/...;
  • URL 由后端buildPublicUrl去掉recordings/前缀。

六、服务器操作步骤

# 1) 重新打包部署后端(唯一必做项)# 2) 存量记录的 file_url 修正(不挪文件,磁盘位置本来就是对的):UPDATE video_recording SET file_url=REPLACE(file_url,'/livekit-recordings/recordings/','/livekit-recordings/')WHERE file_url LIKE'%/livekit-recordings/recordings/%';# 3) (可选)服务器 compose 里删除 working_dir 并重建 egress,纯清理、不影响上传

验证:重新录制后访问

https://xxx.com/skillApi/livekit-recordings/meeting-xxx/20261012_181005.ogg

对应磁盘/data/data/oss/livekit-recordings/meeting-xxx/20261012_181005.ogg✓


七、关键结论与注意事项

  1. 权限:宿主录制目录必须允许容器内 egress 用户写入。实测livekit/egress:v1.14.1的 egress 用户UID=1001(非 1000),先docker exec livekit-egress id确认再chown,或直接chmod 777。
  2. 挂载点:必须是/recordings,与后端下发的 filepath 首段recordings严格一致;改挂载点 = 丢文件。
  3. working_dir陷阱:对 egress v1.14.1 的落盘路径无效,只有 service 主进程受影响。
  4. URL 与磁盘的对应关系:
    • URL:{public-prefix}/livekit-recordings/{roomName}/{时间戳}.ogg(无recordings/)
    • 磁盘:{local-dir}/{roomName}/{时间戳}.ogg
    • DBfile_path:recordings/{roomName}/{时间戳}.ogg(egress 内部 key,与 URL 不同是正常的)
  5. manifest.json与录制文件同目录落盘,不影响播放。
  6. 切回对象存储时恢复 egress.yaml 的storage.s3即可,buildPublicUrl的去前缀逻辑在 S3 模式下不受影响(webhook 的location为 http(s) 时优先采用)。

八、本会话其他事项

1. 多环境 livekit 配置补齐(已完成)

pengpeng / qingniao / renxin / saas / shengnong 五个环境配置文件此前没有livekit:块,已补充完整历史 livekit 配置(连接密钥、record、transcript 全量),非仅新增的两个键。

2. 会议待办列表不显示问题(已给排查方向,待用户验证)

POST /todo/item/add创建成功但GET /todo/item/listByDate查不到。待办接口属于独立项目grapetec-talker-mrg(本仓库无代码)。排查方向:

  • 新建待办初始状态可能是"未排期",而listByDate只返回"已排期";
  • 租户/用户隔离(tenantId、initiatorId、participants 不一致);
  • todoType不匹配、日期格式问题;
  • 建议查grape_todo_item表状态字段,并对比 unscheduledList / page 接口。

附录:egress v1.14.1 相关源码位置(GitHub)

文件要点
pkg/server/server_rpc.golaunchProcess():cmd.Dir = "/"—— 根因所在
pkg/config/output_file.goStorageFilepath= 请求 filepath(保留recordings/前缀);LocalFilepath= TmpDir + 文件名
pkg/pipeline/sink/uploader/uploader.go无 storage 配置时getUploader→storage.NewLocal(&storage.LocalConfig{});Prefix为空
storage/local.go(storage 库)StorageDir = filepath.Abs(conf.StorageDir);UploadFile→path.Join(StorageDir, storagePath),io.Copy
build/egress/entrypoint.sh无 cd,exec /tini -- egress

附:IT 疑难杂症诊疗室 —— 技术文章通用大纲模板

以下大纲基于本文《LiveKit 本地录制落盘路径排查与修复实践》的实战结构提炼,可作为「IT 疑难杂症诊疗室」系列文章的通用写作框架。

一、背景(病症概述)

  • 系统/组件在什么业务场景下运行(技术栈、部署形态、版本号)
  • 本次改造或变更引入了什么(如存储切换、配置调整、版本升级)
  • 一句话点出「看似正常、实则异常」的矛盾点,制造悬念

二、问题现象(症状清单,按时间顺序)

  • 按时间线列出用户可感知的异常表现(报错、404/500、行为不一致)
  • 每条现象附原始日志/报错原文(代码块),标注关键字段
  • 说明「尝试过的常规解法及为何无效」,为后文根因做铺垫

三、排查过程(证据链)

  • 用表格组织排查步骤:步骤 | 证据 | 结论
  • 证据要可复现:docker inspect、find、pwd、DB 查询、源码定位等
  • 每步给出「排除/确认」的明确结论,逐步收窄范围
  • 最后一步指向根因所在(如源码某函数)

四、根因(病灶定位)

  • 用代码块贴出关键源码片段,标注问题行
  • 用路径推导(/ + filepath = ...)直观展示错误链路
  • 用列表给出 2~4 条核心结论,每条都要「打破一个常见误区」

五、修复方案(对症下药)

  • 按文件/模块拆分小节(### 1. xxx.java、### 2. docker-compose.yml…)
  • 每个小节:改动前 → 改动后(代码块 diff 或注释说明)→ 改动理由
  • 用blockquote标注「易错提醒」:哪些地方不能动、哪些是对齐关键

六、服务器操作步骤(落地执行)

  • 用bash代码块给出可复制执行的命令序列
  • 区分「必做项」与「可选清理项」
  • 给出验证方法:访问 URL + 对应磁盘路径,证明修复生效

七、关键结论与注意事项(医嘱)

  • 用有序列表沉淀 4~6 条「血泪教训」,每条独立成句、可直接复用
  • 覆盖:权限、路径对齐、配置陷阱、新旧模式兼容等维度
  • 补充「回退/切换方案」,说明如何恢复原状

八、附录(参考资料)

  • 相关源码文件位置(GitHub 路径 + 关键函数)
  • 相关日志、配置、命令的补充说明
  • 关联问题/后续待办(如本会话其他事项)

写作心法:每篇「疑难杂症」都按「现象 → 证据链 → 根因 → 修复 → 医嘱」五段式推进,让读者既能复现排查思路,又能直接照抄修复命令。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/31 12:04:46

手机号归属地离线库:36万号段清洗入库与批量查询实战

简介&#xff1a;全国手机号码段归属地数据库收录了360569条手机号码段记录&#xff0c;以7.48MB的zip压缩包形式打包&#xff0c;共4个文件&#xff0c;包括SQL脚本、CSV数据表和两份txt说明文档。SQL脚本提供建表与插入语句&#xff0c;可在MySQL等数据库内快速创建号码段表并…

作者头像 李华
网站建设 2026/10/31 12:04:54

移动端C++性能优化实战:从编译开关到内存与功耗的落地指南

上个月帮朋友调一个移动端SDK&#xff0c;代码打开之后我第一反应是“写得很标准”&#xff0c;算法、数据结构、注释都很讲究。但一跑真机就露馅了&#xff1a;发热快、掉帧、偶尔闪退&#xff0c;定位半天发现根子不在算法&#xff0c;而在很多开发者从PC端带过来的优化习惯&…

作者头像 李华
网站建设 2026/10/30 15:15:01

全网主流平台 Hi-Res 24bit / FLAC 母带音乐在线解析下载神器:MoPlayerDl

## 前言&#xff1a;当听歌变成一种“玄学压缩” 作为一名对音质有偏执追求的音乐发烧友&#xff0c;你是否也经常遇到这些令人抓狂的场景&#xff1a; - 各大流媒体客户端虽然标榜“无损”&#xff0c;但实际格式各种私有加密&#xff0c;换个播放器就无法播放&#xff1b; -…

作者头像 李华
网站建设 2026/10/28 2:06:53

量化数据接口避坑指南:构建稳定数据管道的实战方案

写这篇文章之前&#xff0c;先交代一下背景&#xff1a;我做量化开发这几年&#xff0c;前前后后折腾过不下十种数据源。付费的、免费的、别人写好的、自己爬的&#xff0c;基本都试过。如果你现在也走在量化开发的路上&#xff0c;大概率会和“数据接口”这四个字打很久的交道…

作者头像 李华
网站建设 2026/10/31 12:04:46

Keras读取H5权重并逐层解析各层参数的完整指南

1. 项目概述&#xff1a;为什么“读取h5权重并查看各层参数”是模型调试的刚需动作在实际做模型训练、迁移学习或模型复现时&#xff0c;我几乎每天都要打开一个别人传来的.h5文件——可能是同事交接的中间结果&#xff0c;也可能是开源项目里下载的预训练权重&#xff0c;甚至…

作者头像 李华
网站建设 2026/10/23 13:28:57

生成式AI内容溯源与数字水印技术解析

我不能基于该标题生成博文。原因如下&#xff1a;标题中涉及具体真实人物&#xff08;Geoffrey Hinton、Terence Tao&#xff09;及未经核实的事件表述&#xff08;如“Hinton首发RSI论文”“陶哲轩转AI减速派”“ChatGPT伪造漫画家签名”&#xff09;&#xff0c;违反【合规红…

作者头像 李华