修复线上视频导出卡死(OOM)+ 导出健壮性加固

根因:后端容器内存 limit 仅 768Mi,ffmpeg 编码 1080P 竖屏时内存峰值超限被
K8s OOMKill,导出后台线程随 Pod 重启而死,任务永远停在 35%(本地无 limit 29s 出片)。

- k8s/core/api-deployment.yaml: 后端内存 limit 768Mi→2Gi、requests 256→512Mi,
  给 ffmpeg 足够编码空间(根治)
- export.py: ffmpeg subprocess 加 900s 超时(卡死不再永久 RUNNING)+ -threads 2 降内存峰值
- views.py: submit-export 清理超 5 分钟未结束的僵尸任务,前端不再无限转圈

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
zyc
2026-06-15 13:58:18 +08:00
co-authored by Claude Opus 4.8
parent 5e2bc0fc5a
commit a8852345db
3 changed files with 24 additions and 6 deletions
@@ -268,7 +268,9 @@ def _build_export_command(*, n: int, specs: list[dict], starts: list[float], tot
cmd += ["-filter_complex", ";".join(parts), "-map", f"[{vlabel}]"]
if audio_label:
cmd += ["-map", f"[{audio_label}]"]
cmd += ["-c:v", "libx264", "-pix_fmt", "yuv420p", "-r", fps_expr, "-preset", "veryfast"]
# -threads 2:限制 x264 线程数,降低小容器(K8s 内存 limit 小)里的内存峰值,缓解 OOM 被杀;
# 同时避免在受限 CPU 上 fork 过多线程争抢。veryfast 保持编码速度。
cmd += ["-c:v", "libx264", "-pix_fmt", "yuv420p", "-r", fps_expr, "-preset", "veryfast", "-threads", "2"]
if audio_label:
cmd += ["-c:a", "aac", "-b:a", "192k"]
cmd += ["-t", f"{total:.3f}", "-movflags", "+faststart", "output.mp4"]
@@ -479,7 +481,12 @@ def run_export_job(export_job_id: str) -> ExportJob:
sub_overlays=sub_overlays, bgm_name=bgm_name, bgm_volume=(bgm_track.volume / 100.0) if bgm_track else 1.0,
has_audio=has_audio, fps=output_fps, voice_overlays=voice_overlays,
)
proc = subprocess.run(command, cwd=str(tmp), capture_output=True)
# 加超时:ffmpeg 卡死(资源不足/被 OOM 杀)时不能让任务永久停在 RUNNING、前端无限转圈;
# 超时即按失败收尾,前端能看到「导出失败」而非一直卡。15 分钟足够正常 60s 成片。
try:
proc = subprocess.run(command, cwd=str(tmp), capture_output=True, timeout=900)
except subprocess.TimeoutExpired:
raise RuntimeError("ffmpeg 导出超时(15 分钟未完成),通常是服务器资源不足或视频过大,请重试或联系运维扩容")
if proc.returncode != 0:
raise RuntimeError(f"ffmpeg export failed: {proc.stderr.decode('utf-8', 'ignore')[-1200:]}")
output_path = tmp / "output.mp4"