db621b4fcb
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m53s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m17s
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m19s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
AI Code Review / AI Code Review (pull_request) Successful in 7m47s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 8m44s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 10m37s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 29m54s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 28m18s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 27m55s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 24s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 7m43s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 40m20s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 1h36m46s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Failing after 4s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
新增 deploy/gpu_worker/ 下 3 个 systemd 单元 + 3 个运维脚本: - musetalk-worker.service: MuseTalk 推理 API systemd 服务 - gpu-poll.service + gpu-poll.timer: 每30秒轮询Gitea最新commit - scripts/update-gpu-worker.sh: 备份→拉代码→重启→健康检查→失败回滚 - scripts/poll_and_update.sh: SHA比对触发update - scripts/setup-gpu-node.sh: 新节点一键初始化(apt依赖+目录+systemd+sudo免密+首次启动) - README.md 追加第七章「自动部署」说明服务架构/部署步骤/更新机制/日志/注意事项 不改动现有 gpu_worker.py/musetalk_server.py/requirements.txt/xiaoxia-gpu-worker.service/.env.example 不改动CI/Docker/镜像构建。脚本路径写死/home/ying,后续多节点再参数化。
MuseTalk GPU Worker 部署指南
本目录包含两个组件:
- gpu_worker.py:反向轮询客户端,部署在 RTX2060 本地,轮询 SaaS API 拉取口型任务,调用本地 MuseTalk 服务推理,上传结果回 SaaS。
- musetalk_server.py:MuseTalk Flask HTTP 服务端,接收 gpu_worker.py 的推理请求,调用 MuseTalk 模型生成口型同步视频。
一、环境准备
1.1 硬件要求
- GPU: NVIDIA RTX 2060 或更高(显存 ≥ 6GB)
- CUDA: 11.8+
- Python: 3.10+
- ffmpeg: 需安装并加入 PATH
1.2 安装依赖
cd deploy/gpu_worker
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
二、MuseTalk 服务端部署(musetalk_server.py)
2.1 配置环境变量
复制 .env.example 为 .env,修改配置:
cp .env.example .env
vim .env
关键配置:
| 变量 | 说明 | 默认值 |
|---|---|---|
MUSE_PORT |
监听端口 | 7861 |
MUSE_INFERENCE_TIMEOUT |
推理超时秒数 | 600 |
MUSE_VIDEO_MAX_MB |
视频上传大小限制 MB | 100 |
MUSE_AUDIO_MAX_MB |
音频上传大小限制 MB | 20 |
MUSE_DEFAULT_FPS |
视频 fps 兜底值 | 25.0 |
MUSE_TEMP_DIR |
临时文件目录 | /tmp/musetalk_$$ |
MUSE_VIDEO_ENCODER |
兜底循环视频时的编码器:auto(优先 h264_nvenc,失败回退 libx264)/h264_nvenc/libx264 |
auto |
2.2 更新部署(v2 性能修复,必做)
⚠️ 2026-09-20 v2 架构:修复 16 倍性能回归。旧版在推理前 loop 视频导致 MuseTalk 处理帧数翻倍、RTX2060 推理 >200s、nginx 504。必须重新拉取并重启:
# 在 RTX2060 上备份旧文件并拉取新版本
cp ~/projects/MuseTalk/musetalk_server.py ~/projects/MuseTalk/musetalk_server.py.bak
wget -O ~/projects/MuseTalk/musetalk_server.py \
"https://git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas/raw/branch/develop/deploy/gpu_worker/musetalk_server.py"
# 重启服务
sudo systemctl restart musetalk-server
sudo systemctl status musetalk-server
curl http://127.0.0.1:7861/health
v2 架构核心变化:
- MuseTalk 直传全量音频:不再在推理前用 ffmpeg 循环视频。MuseTalk 原生支持长音频输入,内部自动循环视频帧。推理时间不变(~14s/5s 视频)
- ffmpeg 只做快速封装:
-c:v copy -c:a aac -shortest,秒级完成,不重编码 - 循环仅兜底:仅当 MuseTalk 输出画面短于音频时(极端情况),才
-stream_loop+ NVENC 兜底 - 删除
MUSE_ENABLE_VIDEO_LOOP:不再需要此开关,MuseTalk 原生处理
2.3 启动服务
# 前台运行(调试用)
python musetalk_server.py
# 后台运行(生产用 systemd)
sudo systemctl start musetalk-server
sudo systemctl enable musetalk-server
2.4 验证健康检查
curl http://127.0.0.1:7861/health
应返回:
{
"status": "healthy",
"gpu": {
"gpu_name": "NVIDIA GeForce RTX 2060",
"memory_total_mb": 6144,
"memory_used_mb": 1024,
"memory_free_mb": 5120
},
"current_task": {
"task_id": null,
"running": false,
"elapsed_seconds": 0.0
},
"timestamp": 1700000000.0
}
三、GPU Worker 客户端部署(gpu_worker.py)
3.1 配置环境变量
复制 .env.example 为 .env,修改配置:
cp .env.example .env
vim .env
关键配置:
| 变量 | 说明 | 默认值 |
|---|---|---|
API_BASE_URL |
SaaS API 基础 URL | https://staging-api.xiaoxiajianji.com |
GPU_WORKER_TOKEN |
长期 API Token(与服务端一致) | - |
MUSE_TALK_URL |
本地 MuseTalk 服务地址 | http://127.0.0.1:7861 |
POLL_INTERVAL |
轮询间隔秒 | 5 |
HEARTBEAT_INTERVAL |
空闲心跳间隔秒 | 15 |
REQUEST_TIMEOUT |
HTTP 请求超时秒 | 900 |
TASK_MAX_RETRY |
本地最大重试次数 | 1 |
TASK_HEARTBEAT_INTERVAL |
推理期间任务心跳间隔秒 | 30 |
MIN_VIDEO_DURATION_SECONDS |
最短输入视频时长秒 | 3 |
3.2 启动 Worker
# 前台运行(调试用)
python gpu_worker.py
# 后台运行(生产用 systemd)
sudo systemctl start xiaoxia-gpu-worker
sudo systemctl enable xiaoxia-gpu-worker
3.3 验证启动日志
应看到:
============================================================
MuseTalk GPU Worker 启动
worker_id = rtx2060-xxxx
api_base = https://staging-api.xiaoxiajianji.com
muse_talk = http://127.0.0.1:7861
poll = 5.0s / heartbeat = 15.0s
============================================================
MuseTalk 健康检查通过: {...}
注册/心跳成功
四、常见问题排查
| 现象 | 可能原因 / 排查 |
|---|---|
日志 401 Invalid GPU worker token |
.env 的 GPU_WORKER_TOKEN 与服务端不一致 |
日志 MuseTalk 健康检查未通过 |
本地 MuseTalk 没启动,或端口不是 7861;curl http://127.0.0.1:7861/health 验证 |
| 任务长时间不被拉取 | Worker 和服务端连不上;检查 API_BASE_URL 是否可达、Token 是否正确 |
| 推理后上传 OSS 失败 | 本地出口网络被防火墙拦截 OSS 域名(oss-cn-hangzhou.aliyuncs.com) |
| 服务端看到任务回退到 pending 重试 | 任务心跳真正超时(默认 900s):Worker 进程崩溃/断网,或推理彻底卡死;正常长推理期间心跳线程每 30s 续期,不会回退 |
日志 MuseTalk 推理超时或连接失败 |
视频太长或显存不足;可临时调大 REQUEST_TIMEOUT(服务端 GPU_TASK_TIMEOUT_SECONDS 需同步调大),或限制输入视频时长 |
日志 视频过短(x.xxs < 3s) |
输入视频不足 3s,MuseTalk 对短视频会 division by zero,已在本地直接上报失败;可用 MIN_VIDEO_DURATION_SECONDS 调整阈值 |
MuseTalk 服务端 503 GPU 正在处理其他任务 |
并发请求被锁拒绝,等当前推理完成即可 |
MuseTalk 服务端 504 推理超时 |
推理超过 MUSE_INFERENCE_TIMEOUT,客户端会调 /cancel 终止服务端任务 |
五、安全注意事项
.env包含长期 Token,文件权限设为 600(chmod 600 .env)- Token 泄露要立即在服务端更换
GPU_WORKER_TOKEN并重启 Worker - Worker 只需要出站访问 SaaS API 和 OSS,不需要开放任何入站端口
- MuseTalk 服务端只监听本地 127.0.0.1(或 0.0.0.0 但通过防火墙限制),不暴露到公网
- 临时文件自动清理(推理完成/失败后),无需手动维护
六、工程改进记录(musetalk_server.py)
相比原 worker.py,修复了以下 8 个 bug:
- Flask 单线程阻塞:
app.run(threaded=True),推理时/health仍可响应 - fps=0 除零崩溃:
_get_video_fps()兜底MUSE_DEFAULT_FPS - ffmpeg 不检查返回码:
subprocess.run(check=True)+ 超时检查,失败立即报错 - 无并发锁:
threading.Lock控制并发,第二请求立即 503 - 无推理超时:线程 join timeout,超时返回 504 并调
/cancel - 结果文件不清理:推理完成/失败后自动删除临时目录
- 无人脸检测兜底:MuseTalk 推理内部处理(TODO: 可在
_run_inference前置检查) - 上传无大小限制:
_check_file_size()校验,超限返回 413
新增:
/cancel端点:终止当前推理任务,清理临时文件/health端点:返回 GPU 显存信息和当前任务状态
2026-09-20 追加修复(音轨正确性,上线阻断级):
- 音轨未替换(严重):旧最终封装让 ffmpeg 默认选流,结果保留了源视频自带音轨(与画面相关系数 0.9998,与 TTS 无关)。改为
_mux_video_with_audio()统一封装,强制-map 0:v:0 -map 1:a:0,画面取 MuseTalk 无声产物、音轨只取驱动音频 - 音视频时长不对齐:TTS 长于原视频时
-shortest会截短语音。改为探测双方时长,音频更长时-stream_loop -1循环画面 +h264_nvenc硬件重编码(MUSE_VIDEO_ENCODER=auto,失败回退 libx264)+-t <音频时长>;不循环时-c:v copy秒封装- 开关
MUSE_ENABLE_VIDEO_LOOP=0可关闭循环;请求也支持 form 参数enable_video_loop单任务覆盖
- 开关
2026-09-20 v2 架构重构(性能回归修复,上线阻断级):
- 16 倍性能回归:#9/#10 的实现虽然音轨正确,但在某些集成场景下(推理前 loop 视频再喂 MuseTalk)导致推理帧数 ×2.2 + 叠加 ffmpeg 软编码预处理,5s 视频 +11s 音频推理 >200s,nginx 60s 超时 504
- 正确架构:MuseTalk 原生支持长音频输入,内部自动循环视频帧。把【原视频】+【全量音频】直传 MuseTalk,输出时长=音频时长
- ffmpeg 后置快速封装:
-c:v copy -c:a aac -shortest秒级完成,不重编码 - 循环仅兜底:仅当 MuseTalk 输出画面短于音频时(极端情况),才
-stream_loop+ NVENC 兜底补齐 - 业务侧异步化:POST /lipsync/jobs 创建 GPU 任务后立即返回
job.status="processing",Celery 异步等待结果回写。前端 GET /jobs/{id} 轮询。避免同步阻塞 HTTP 请求 >200s - 删除
MUSE_ENABLE_VIDEO_LOOP:不再需要此开关
七、自动部署
从 2026-09-20 起,GPU 节点配置文件和脚本全部入库到 deploy/gpu_worker/,支持一键初始化新节点 + develop 分支 push 后 30 秒内自动拉取更新。
7.1 服务架构
每个 GPU 渲染节点运行三个 systemd 单元:
| 单元 | 类型 | 作用 |
|---|---|---|
musetalk-worker.service |
simple(常驻) | MuseTalk Flask 推理 API(监听 127.0.0.1:7861) |
xiaoxia-gpu-worker.service |
simple(常驻) | 反向轮询 SaaS API 拉口型任务的 Worker 客户端 |
gpu-poll.timer + gpu-poll.service |
timer(每 30s 触发 oneshot) | 轮询 Gitea deploy/gpu_worker/ 最新 commit,有变更自动执行 update 脚本 |
脚本目录(节点本地):
| 路径 | 来源 | 作用 |
|---|---|---|
~/projects/update-gpu-worker.sh |
scripts/update-gpu-worker.sh |
备份 → 拉代码 → 重启两个服务 → 健康检查 → 失败回滚 |
~/projects/gpu-webhook/poll_and_update.sh |
scripts/poll_and_update.sh |
轮询 Gitea API 比对 SHA,有新 commit 时触发 update |
7.2 新节点部署步骤
前置准备(手动,首次部署必做):
- 安装 NVIDIA 驱动 + CUDA 11.8+,
nvidia-smi能看到 GPU - 克隆 MuseTalk 代码到
~/projects/MuseTalk/,下载模型权重到~/projects/MuseTalk/models/musetalk/(权重约几 GB,不适合自动下载) - 创建 Python 虚拟环境
~/projects/MuseTalk/venv/并安装 MuseTalk 依赖(PyTorch CUDA 版等) - 创建 Worker 虚拟环境
/opt/xiaoxia-gpu-worker/venv/并pip install -r requirements.txt - 准备
.env文件(Worker 端):/opt/xiaoxia-gpu-worker/.env,填好API_BASE_URL、GPU_WORKER_TOKEN、MUSE_TALK_URL等(参考.env.example)
⚠️ 模型权重和 Python 虚拟环境(含 CUDA 版 PyTorch)体积大、安装慢,首次部署必须手动准备;后续脚本只更新
.py文件和配置,不碰权重和 venv。
一键初始化:
# 从仓库拉取 setup 脚本并执行(在全新 GPU 机器上以 ying 用户执行)
wget -q -O /tmp/setup-gpu-node.sh \
"https://git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas/raw/branch/develop/deploy/gpu_worker/scripts/setup-gpu-node.sh"
bash /tmp/setup-gpu-node.sh
脚本自动完成:
- apt 安装系统依赖(python3、ffmpeg、wget、curl、git)
- 创建必要目录(
~/projects/MuseTalk、~/projects/gpu-webhook、/opt/xiaoxia-gpu-worker) - 从仓库拉取三个 systemd 单元文件 + update/poll 脚本到本地
- 安装 systemd 服务到
/etc/systemd/system/ - 配置 sudo 免密(仅允许
ying用户免密 restart 两个服务、status、journalctl、cp、chmod、tee) - 首次执行 update 脚本拉取最新
musetalk_server.py和gpu_worker.py systemctl daemon-reload+ enable + start 三个单元
初始化后检查:
sudo systemctl status musetalk-worker # 应 active (running)
sudo systemctl status xiaoxia-gpu-worker # 应 active (running)
sudo systemctl status gpu-poll.timer # 应 active (waiting)
curl http://127.0.0.1:7861/health # 应返回 healthy + GPU 显存信息
7.3 自动更新机制
push 到 develop 分支且修改了 deploy/gpu_worker/ 下任何文件后:
gpu-poll.timer每 30 秒触发gpu-poll.servicepoll_and_update.sh调用 Gitea API 取deploy/gpu_worker/路径最新 commit SHA- 与本地
~/projects/gpu-webhook/.last_commit比对,无变更直接退出 - 有变更:写入新 SHA → 执行
update-gpu-worker.sh update-gpu-worker.sh执行流程:- 备份当前
musetalk_server.py/gpu_worker.py(带时间戳后缀) - wget 拉取最新
musetalk_server.py、gpu_worker.py - 比对
requirements.txt,有变化则 pip install sudo systemctl restart musetalk-worker,等 5 秒sudo systemctl restart xiaoxia-gpu-worker,等 8 秒curl http://127.0.0.1:7861/health健康检查- 健康 → 写日志退出 0
- 不健康 → 回滚到最新备份 → 重启 → 退出 1(日志记录 rolled back)
- 备份当前
端到端延迟:从 push 到节点拉到新代码并重启,约 30~60 秒。
7.4 手动更新命令
# 立即手动触发一次更新(不依赖 timer)
bash ~/projects/update-gpu-worker.sh
# 查看更新日志
tail -f /tmp/gpu-worker-update.log
# 查看轮询日志
tail -f /tmp/gpu-poll.log
# 查看服务运行日志
journalctl -u musetalk-worker -f # MuseTalk 推理服务日志
journalctl -u xiaoxia-gpu-worker -f # GPU Worker 客户端日志
journalctl -u gpu-poll.service -f # 轮询/更新触发日志
7.5 仓库文件清单(自动部署相关)
deploy/gpu_worker/
├── musetalk-worker.service # MuseTalk 推理 API 的 systemd 服务
├── gpu-poll.service # 自动更新轮询 oneshot service
├── gpu-poll.timer # 每 30 秒触发轮询的 timer
├── xiaoxia-gpu-worker.service # GPU Worker 客户端 systemd 服务(已有)
├── gpu_worker.py # GPU Worker 客户端脚本(已有,自动更新)
├── musetalk_server.py # MuseTalk Flask 服务端(已有,自动更新)
├── requirements.txt # Worker Python 依赖(已有)
├── .env.example # Worker 环境变量模板(已有)
├── README.md # 本文档
└── scripts/
├── update-gpu-worker.sh # 更新脚本:备份→拉取→重启→健康检查→回滚
├── poll_and_update.sh # 轮询脚本:SHA 比对→触发更新
└── setup-gpu-node.sh # 新节点一键初始化脚本
7.6 注意事项
- 首次部署必须手动准备:MuseTalk 代码仓库、模型权重(
models/musetalk/,几 GB)、MuseTalk 的 Python 虚拟环境(venv/,含 CUDA 版 PyTorch)。这些体积大、安装耗时长,不在自动更新范围内。 - 脚本路径写死:当前脚本路径固定为
/home/ying/projects/和/opt/xiaoxia-gpu-worker/,用户名固定ying。后续如有多节点/多用户需求再做参数化。 - sudo 免密范围最小化:setup 脚本写入
/etc/sudoers.d/ying-gpu-update,仅放行 restart/status 两个 GPU 相关服务、daemon-reload、journalctl、cp、chmod、tee,不开放全量 root。 - 回滚只回滚 .py 文件:健康检查失败只回滚
musetalk_server.py和gpu_worker.py,不回滚 pip 依赖(requirements.txt 变化概率低,且 pip 操作本身可能失败)。如需完全回滚,手动pip install -r requirements.txt指定旧版本。 - poll 脚本容错:Gitea API 请求失败直接跳过,不触发更新,不会因为网络抖动误重启服务。