FunASR OpenAI 兼容 API 的 Kubernetes 部署实战:SenseVoice CPU 与 MOSS GPU 双配方指南
【免费下载链接】FunASROpen-source speech recognition toolkit for training, inference, streaming ASR, VAD, punctuation, speaker diarization pipelines, and OpenAI-compatible/MCP serving.项目地址: https://gitcode.com/GitHub_Trending/fun/FunASR
导读:本文以 FunASR 仓库内 examples/openai_api/kubernetes/README_zh.md 为核心,完整讲解如何把 FunASR 的 OpenAI 兼容语音接口部署进 Kubernetes 集群,为集群内部的 Agent、Web 后端、工作流引擎或批处理 worker 提供语音能力。你将掌握镜像构建与推送、Kustomize 清单部署、内网 port-forward 冒烟验证、按集群规模调整资源配置,以及 MOSS-Transcribe-Diarize GPU 配方的完整落地路径,并理解探针预算、PVC 缓存、GPU 调度与安全边界等运维要点。
1. 部署概览与前置条件
这套 Kubernetes 模板服务于一个明确场景:让集群内部的服务(而非公网用户)调用语音接口。典型调用方包括 Agent、Web 后端、工作流引擎(如 Dify、n8n)或批处理 worker。模板默认先使用 SenseVoice CPU 配方,文档同时提供独立的 MOSS GPU 配方,用于转写 + 说话人分离(speaker diarization)。
所有命令都必须从 FunASR 仓库根目录执行,包括新开终端中的命令。开始之前,请确认以下前置条件齐备:
- Docker 可用,且有可推送的镜像仓库(示例使用
registry.example.com,构建前必须替换); kubectl已配置并指向目标集群;- 当前账号有在
speechnamespace 中创建资源的权限; - 集群存在可供缓存 PVC 使用的默认 StorageClass;
- 本地 smoke 客户端需要Python 3.10 或更高版本,且不需要安装任何第三方 Python 包(脚本仅使用标准库,见下文源码分析)。
1.1 安全边界:ClusterIP 不是鉴权
这是本指南反复强调的第一原则:ClusterIP 不是鉴权。两套清单(CPU 与 MOSS)都没有提供 NetworkPolicy、鉴权网关、TLS 或请求限制。在允许不可信客户端访问前,务必先阅读 服务安全指南。以下所有配方都通过本地port-forward访问服务,不是公网入口。
1.2 模板默认的保守设计
从清单源码可以逐条印证模板的保守取向(见 funasr-api.yaml):
- 默认
ClusterIP:Service 类型为ClusterIP,不直接暴露公网LoadBalancer; - 默认
FUNASR_DEVICE=cpu:与便携 CPU Dockerfile 匹配,避免镜像与运行时不一致; - 持久化模型缓存:在
/root/.cache挂载 20Gi 的 PVC(funasr-cache),避免 Pod 重启后重复下载模型; - 健康检查三件套:startup、readiness、liveness 探针全部使用
/health; - 内存型
/dev/shm:挂载emptyDir+medium: Memory(2Gi),便于 PyTorch 和音频预处理使用共享内存。
2. 仓库清单结构速览
Kubernetes 目录下共 4 个文件,分工清晰:
| 文件 | 作用 |
|---|---|
| kustomization.yaml | Kustomize 入口,声明资源并覆盖镜像名与 tag |
| funasr-api.yaml | CPU/SenseVoice 配方:PVC + ConfigMap + Deployment + Service |
| funasr-moss-api.yaml | MOSS GPU 配方:PVC + Deployment + Service |
| README_zh.md | 本指南对应文档(另有 README.md 英文版) |
2.1 CPU 配方对象详解
funasr-api.yaml包含 4 个 Kubernetes 对象:
- PVC
funasr-cache:ReadWriteOnce,20Gi,label 标记为component: cache; - ConfigMap
funasr-api-config:通过envFrom注入三个环境变量——FUNASR_PORT=8000、FUNASR_DEVICE=cpu、FUNASR_MODEL=sensevoice; - Deployment
funasr-api:replicas: 1,strategy: Recreate(单副本滚动,避免 PVC 被多副本同时挂载),容器请求cpu: 2/memory: 8Gi,限制memory: 16Gi;挂载cache(PVC)到/root/.cache、dshm(内存 emptyDir)到/dev/shm; - Service
funasr-api:ClusterIP,port: 8000→targetPort: http。
探针参数与源码一一对应:
| 探针 | path | periodSeconds | timeoutSeconds | failureThreshold |
|---|---|---|---|---|
| startup | /health | 10 | 5 | 60 |
| readiness | /health | 10 | 5 | 3 |
| liveness | /health | 30 | 5 | 3 |
其中 startup probe 的失败预算约 10 分钟(周期 10s × 失败阈值 60),用于覆盖模型下载与首次加载。
3. 第一步:构建并推送镜像
保持 shell 位于仓库根目录。CPU 镜像使用 API 目录作为构建上下文:
docker build -f examples/openai_api/Dockerfile -t registry.example.com/speech/funasr-api:cpu-latest examples/openai_api docker push registry.example.com/speech/funasr-api:cpu-latest3.1 CPU Dockerfile 的构建语义
查看 Dockerfile 可以确认几个关键事实:
- 基础镜像为
python:3.10-slim,安装ffmpeg、git、libsndfile1等运行时依赖; - 通过 pip 安装
funasr、fastapi、uvicorn[standard]、python-multipart——从 PyPI 安装 FunASR,而不是安装当前 checkout 的 FunASR 包;依赖环境也未锁定(无固定版本约束); ENV预设默认值FUNASR_MODEL=sensevoice、FUNASR_DEVICE=cpu、FUNASR_PORT=8000;- 仅复制
server.py到/app/server.py(构建上下文小); - Docker 层内置
HEALTHCHECK(interval 30s、start-period 60s),与 Kubernetes 探针互为补充; - 启动命令
python server.py --host 0.0.0.0 --port ${FUNASR_PORT:-8000} --device ${FUNASR_DEVICE:-cpu} --model ${FUNASR_MODEL:-sensevoice},四个参数均可被环境变量覆盖。
3.2 修改镜像引用与不可变部署
推送完成后,需要修改 kustomization.yaml 的images配置:
images: - name: funasr-api newName: registry.example.com/speech/funasr-api newTag: cpu-latest需要可复现部署时,把示例可变 tag(如cpu-latest)换成镜像仓库的不可变 digest。每次 rollout 前记录镜像 digest 与清单内容,便于故障时回滚。
4. 第二步:部署到集群
依次执行三条命令:
kubectl create namespace speech --dry-run=client -o yaml | kubectl apply -f - kubectl -n speech apply -k examples/openai_api/kubernetes kubectl -n speech rollout status deploy/funasr-api --timeout=15m说明:
- 第一条命令以幂等方式创建
speechnamespace(dry-run 生成 manifest 再 apply); - 第二条命令通过 Kustomize 应用整个目录(PVC、ConfigMap、Deployment、Service 一次到位);
- 第三条命令阻塞等待 Deployment 完成滚动,超时 15 分钟。
启动耗时预期:CPU 服务在启动 HTTP 前会预加载配置的模型(从 ModelScope/HuggingFace 下载权重并加载),下载与首次加载可能需要几分钟。startup probe 的失败预算约 10 分钟,不包含拉取镜像、调度或 PVC 绑定时间。/health成功只代表进程存活与 HTTP 就绪,不代表推理验收通过——接入流量前,还必须完成下面的真实音频请求。
5. 第三步:内网 Smoke Test
建议保持服务内网私有,通过port-forward验证,不要直接暴露服务:
kubectl -n speech port-forward --address 127.0.0.1 svc/funasr-api 8000:8000保持 port-forward 运行。在另一个终端的同一仓库根目录执行:
python3 examples/openai_api/smoke_test.py --base-url http://127.0.0.1:8000 --model sensevoice --response-format verbose_json5.1 smoke 客户端的行为细节
阅读 smoke_test.py 的源码可以明确它的实际行为:
- 纯标准库实现(
urllib.request+json+uuid),无需第三方依赖,这也是文档要求 Python 3.10+ 的原因; - 仅在当前目录不存在
sample.wav时下载公开中文样本(默认 URL 指向阿里云 OSS 的 BAC009 测试音频),已有文件会被复用、不重复下载; - 依次请求并打印三部分内容:
/health、/v1/models(模型 metadata)、/v1/audio/transcriptions(multipart/form-data 上传音频并携带model与response_format字段),转写结果以完整 JSON 输出; - 支持
--audio-path(位置参数,默认sample.wav)、--base-url、--model、--response-format(json/verbose_json)、--sample-url、--timeout等参数,均可通过环境变量BASE_URL、MODEL、RESPONSE_FORMAT等覆盖。
必须理解验收边界:退出码为零只代表 HTTP 链路通畅,并不验证识别准确率、说话人标签、内存容量或并发能力。请自行核对输出的文本与时间戳。同时注意:
- 避免保留敏感音频或未脱敏输出;
- 客户端不发送
Authorization;安全指南中的"仅转写"网关会主动拒绝 metadata 路由(如/v1/models),因此这套 smoke 应通过本地 port-forward 运行,不要直接指向该网关。
5.2 集群内客户端的 base URL
集群内其他 Pod 访问该服务时,应使用 Kubernetes service name:
- 直接 HTTP 调用:
http://funasr-api.speech.svc.cluster.local:8000 - OpenAI SDK 调用:
http://funasr-api.speech.svc.cluster.local:8000/v1
6. 第四步:根据集群调整配置
| 配置 | 默认值 | 什么时候调整 |
|---|---|---|
FUNASR_MODEL | sensevoice | 先检查目标模型的依赖与硬件要求;/v1/models列出别名,不证明每个模型都已就绪 |
FUNASR_DEVICE | cpu | 只有在镜像已适配 CUDA 且集群 GPU 调度已配置后才改成cuda |
| PVC 大小 | 20Gi | 缓存多个模型或较大模型版本时增大 |
| 内存 request | 8Gi | 根据启动过程和真实音频负载观测结果调整 |
| Startup probe | 约 10 分钟 | 按模型初始化情况调整;镜像拉取、调度和 PVC 绑定需分别排查 |
6.1 从 server.py 理解模型别名体系
为什么FUNASR_MODEL的默认值是sensevoice?查看 server.py 中的MODEL_CONFIGS字典可以找到答案。服务内置了 5 个可配置模型别名,每个别名对应一组加载参数(模型 ID、VAD 模型、标点模型、hub 来源等):
| 别名 | 底层模型 | 关键加载参数 |
|---|---|---|
sensevoice | iic/SenseVoiceSmall | vad_model=fsmn-vad,vad_kwargs.max_single_segment_time=30000 |
paraformer | paraformer-zh | 附带punc_model=ct-punc |
paraformer-en | paraformer-en | 附带vad_model=fsmn-vad |
fun-asr-nano | FunAudioLLM/Fun-ASR-Nano-2512 | hub=hf、trust_remote_code=True |
moss-transcribe-diarize | OpenMOSS-Team/MOSS-Transcribe-Diarize | hub=hf、backend=hf、固定model_revision |
load_model()(server.py)在首次调用时通过funasr.AutoModel懒加载模型并缓存进MODEL_REGISTRY,加载耗时会被记录到日志。这意味着:
- 修改
FUNASR_MODEL前,先确认目标模型的依赖与硬件要求(部分模型需要 HuggingFace hub、trust_remote_code或 GPU); - 服务启动时只预加载ConfigMap 指定的模型,其他别名只有在被请求时才会现场加载;
- 换用未在
MODEL_CONFIGS中的名字会得到 400 错误。
7. MOSS GPU 替代方案(转写 + 说话人分离)
MOSS-Transcribe-Diarize 是 OpenMOSS-Team 的模型,由 FunASR 集成。这套清单运行打包后的 FunASR HTTP 适配器,使用verbose_json响应格式,不是原生 vLLM 或其diarized_json接口。分离标签是说话人区分结果,不是经过验证的说话人身份。模型要求、输出格式、不依赖外部 VAD 的行为及其他服务后端,见 MOSS 部署指南。
7.1 与 CPU 配方的关键差异
kustomization.yaml不包含MOSS 清单,需要单独apply -f;- MOSS 模板请求一张 NVIDIA GPU、24Gi 内存、40Gi 缓存 PVC,并配置 8Gi 内存型
/dev/shm——这些是模板设置,不是实测容量保证; - 部署前先配置集群的 GPU device plugin 和调度;
- 与 CPU 镜像不同,
Dockerfile.moss复制并安装整个 checkout(COPY . /opt/funasr+pip install .),因此构建上下文必须是仓库根目录;请使用干净的 checkout,不要把凭据或私有数据放入构建上下文。
从 Dockerfile.moss 还可以确认:基础镜像是pytorch/pytorch:2.9.1-cuda12.8-cudnn9-runtime,额外安装transformers>=5.6,<6,启动命令为打包入口funasr-server --host 0.0.0.0 --port ${FUNASR_PORT:-8000} --device ${FUNASR_DEVICE:-cuda:0} --model ${FUNASR_MODEL:-moss-transcribe-diarize}。
7.2 构建、推送与应用
docker build -f examples/openai_api/Dockerfile.moss -t registry.example.com/speech/funasr-api:moss-local . docker push registry.example.com/speech/funasr-api:moss-local应用前,将 funasr-moss-api.yaml 中 Deployment 的image: funasr-moss-api:local替换为已推送镜像的不可变 digest。如果跳过了 CPU 部署,先按第 4 节创建speechnamespace。保存镜像 digest 和修改后的清单作为回滚记录。
kubectl -n speech apply -f examples/openai_api/kubernetes/funasr-moss-api.yaml kubectl -n speech rollout status deploy/funasr-moss-api --timeout=15m kubectl -n speech port-forward --address 127.0.0.1 svc/funasr-moss-api 8001:8000在另一个终端的仓库根目录中,使用本地端口 8001(与 CPU 示例的 8000 区分):
python3 examples/openai_api/smoke_test.py --base-url http://127.0.0.1:8001 --model moss-transcribe-diarize --response-format verbose_json7.3 MOSS 探针差异与验收边界
对比两份清单可以发现:MOSS 模板有 startup 和 readiness probe,没有 liveness probe;其/health只做存活检查,不测试转写能力。因此必须对照音频检查返回的文本与分离结果。这套配方不认证具体 GPU 型号、实时性能或生产负载——它提供的是可运行的起点,容量与性能需要你在真实负载下自行验证。
8. GPU 资源调度说明
普通 Dockerfile 默认面向 CPU,单独设置FUNASR_DEVICE=cuda不会使它成为受支持的 GPU 镜像。其他 GPU 模型需要额外适配依赖与调度配置。下面是字段位置的示意——resources属于 container,nodeSelector属于 Pod spec,不是可以整体粘贴的同层完整清单:
resources: limits: nvidia.com/gpu: "1" nodeSelector: nvidia.com/gpu.present: "true"不同 Kubernetes 发行版的 GPU label、runtime class 和 device plugin 配置并不相同,请以集群实际提供的资源扩展为准。服务对外开放前,先补齐鉴权、TLS、上传大小限制和限流。
9. 运维检查清单
- 排查顺序:修改探针预算前,先检查 PVC 绑定、镜像拉取、Pod events 和模型加载日志,再检查
/health、/v1/models和真实音频响应; - 记录关键信息:模型别名、设备、音频时长、响应格式、延迟和错误文本;
- 单副本起步:缓存 PVC 是
ReadWriteOnce,建议先从 1 个副本开始;横向扩容前先评估镜像、每 Pod 缓存或共享只读模型缓存方案; - 网络隔离:为预期客户端实施鉴权和 NetworkPolicy——namespace 本身不是网络隔离边界;
- 集群内调用规范:Dify、n8n 或 Web 后端在同一集群内访问时,应使用 Kubernetes service name(如
http://funasr-api.speech.svc.cluster.local:8000),不要使用localhost。
10. 安全与上线边界(补充自服务安全指南)
Kubernetes 模板使用ClusterIP,这本身不能阻止其他 Pod 或可达主机调用服务。在增加 Ingress 或 LoadBalancer 前,建议按 SECURITY_zh.md 的 Kubernetes 注意事项完成:
- 使用 Ingress controller 或 API 网关强制 TLS、鉴权、上传大小限制和限流;
- 模型缓存卷只暴露给拥有该服务的 namespace 或 node pool;
- 使用
NetworkPolicy限制可调用服务的 namespace; - 第一次验证先
kubectl port-forward+smoke_test.py,再开放路由; - 增加 GPU 后固定调度规则,并在部署说明中记录镜像 tag、CUDA runtime 和模型 alias。
上线前的验收检查至少覆盖:无鉴权本地 loopback 诊断、带凭据的认证上传、未认证请求必须 401 且不触发推理、非转写路由(/health、/v1/models、/openapi.json、/docs等)保持拒绝、大小与超时边界、以及从不可信网络确认后端不可直连。示例服务会把上传内容读入内存并写临时音频文件,压缩文件的上传字节数不等于解码后的时长或内存占用,容量评估必须以真实音频负载为准。
11. 总结
本指南完整覆盖了 FunASR OpenAI 兼容 API 在 Kubernetes 上的两条部署路径:SenseVoice CPU 配方(Kustomize 一键部署 + 20Gi 模型缓存 + 三探针健康检查)与MOSS GPU 配方(单卡调度 + 转写/说话人分离 + 40Gi 缓存)。从镜像构建、命名空间创建、滚动状态确认、内网 smoke 验证到资源配置调整,每一步都有对应清单源码与 server.py 实现可供查证。部署完成后,集群内的 Agent、Web 后端与工作流引擎即可通过 OpenAI 兼容的/v1/audio/transcriptions接口获得语音识别能力——但请始终牢记:ClusterIP 不是鉴权,对外开放前务必补齐网络策略与网关防护。
【免费下载链接】FunASROpen-source speech recognition toolkit for training, inference, streaming ASR, VAD, punctuation, speaker diarization pipelines, and OpenAI-compatible/MCP serving.项目地址: https://gitcode.com/GitHub_Trending/fun/FunASR
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考