配合之前的 whisper.cpp Vulkan 部署指南使用,解决长录音(会议、课程)导致的内存占用问题,避免跟 Qwen3.6-35B 抢内存触发 OOM。
第一步:docker-compose.yml 加内存限额
在原来的基础上加 deploy.resources.limits,给 whisper-server 设个硬顶,防止它把内存占爆:
services:
whisper-server:
image: ghcr.io/ggerganov/whisper.cpp:main-vulkan
container_name: whisper-server
restart: unless-stopped
ports:
- "8090:8080"
volumes:
- ./models:/models
devices:
- /dev/dri:/dev/dri
deploy:
resources:
limits:
memory: 6G # 按需调整,48G总内存里给whisper留个上限
reservations:
memory: 2G
command: >
--model /models/ggml-large-v3-turbo.bin
--host 0.0.0.0
--port 8080
--language zh
--print-progress
注意:如果你是用 docker compose up 而不是 swarm 模式,deploy.resources 在部分 Compose 版本下不生效,稳妥起见换成经典写法:
mem_limit: 6g
memswap_limit: 6g
两种写法效果类似,用后者兼容性更好。
第二步:长音频自动分段脚本
超过设定时长的音频,先用 ffmpeg 切片,分段调用 API,最后把文字结果拼接起来。避免整个超长文件一次性塞进模型。
#!/bin/bash
# whisper-long-audio.sh
# 用法: ./whisper-long-audio.sh <音频文件> [分段时长秒数,默认600]
set -e
INPUT_FILE="$1"
SEGMENT_SECONDS="${2:-600}" # 默认每段10分钟
WHISPER_API="http://192.168.0.188:8090/v1/audio/transcriptions"
WORK_DIR=$(mktemp -d)
OUTPUT_FILE="${INPUT_FILE%.*}_transcript.txt"
if [ -z "$INPUT_FILE" ]; then
echo "用法: $0 <音频文件> [分段时长秒数]"
exit 1
fi
echo "临时目录: $WORK_DIR"
echo "开始切分音频,每段 ${SEGMENT_SECONDS} 秒..."
# 用 ffmpeg 按时长切片,输出统一转成 16kHz 单声道 wav
ffmpeg -i "$INPUT_FILE" -f segment -segment_time "$SEGMENT_SECONDS" \
-ar 16000 -ac 1 -c:a pcm_s16le \
"$WORK_DIR/segment_%03d.wav" -y -loglevel error
echo "切分完成,开始逐段识别..."
> "$OUTPUT_FILE"
for segment in "$WORK_DIR"/segment_*.wav; do
echo "处理: $(basename "$segment")"
result=$(curl -s "$WHISPER_API" \
-F file="@$segment" \
-F language="zh")
# 提取文本字段(假设返回JSON里有text字段,具体按whisper-server实际返回格式调整)
text=$(echo "$result" | grep -o '"text":"[^"]*"' | sed 's/"text":"//;s/"$//')
echo "$text" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
# 每段之间留点间隔,给内存回收和GPU降温的时间,
# 也顺带避开和LLM推理请求撞车的高峰
sleep 2
done
echo "全部完成,结果保存在: $OUTPUT_FILE"
rm -rf "$WORK_DIR"
保存后:
chmod +x whisper-long-audio.sh
./whisper-long-audio.sh /path/to/meeting.mp3 600
第三步:反代 body size 限制
如果你前面套了 Nginx(fnOS 常见做法),记得加大上传限制,否则大文件还没到 whisper-server 就被拦下了:
location /whisper/ {
proxy_pass http://192.168.0.188:8090/;
client_max_body_size 500M;
proxy_read_timeout 600s; # 长音频转写时间长,超时也要放宽
}
小结:为什么这样能避免 OOM
切片:把内存占用从"整段音频总时长线性增长"变成"单段固定上限",每次只处理10分钟的量,处理完释放,不会累积。
mem_limit 硬顶:即使某次异常导致占用飙升,也不会波及到跟它同机运行的 Qwen3.6-35B 或 Frigate/CompreFace,容器会先被 OOM killer 干掉,而不是拖垮整个系统。
请求间隔:段与段之间加了 sleep,给 GPU/内存喘口气的时间,也降低了和 LLM 并发请求抢资源的概率。
如果你想要更进一步,比如自动检测音频总时长决定要不要走分段逻辑(短音频直接一次性处理,省得切片浪费时间),跟我说一声,我再加个判断逻辑进去。
评论区