我到现在还记得第一次手工部署FunASR的场景conda环境建好、依赖装完、模型下好结果换一台机器全得重来。后来痛定思痛换成Docker部署整个流程压缩到半小时以内服务器之间迁移也就两条命令的事。这篇记录我用Docker安装部署FunASR的完整过程包含镜像选择、容器启动、模型挂载、接口验证和一路踩过的坑希望能让准备上语音识别服务的同学少走点弯路。FunASR是阿里达摩院开源的一套语音识别工具包基于Paraformer模型支持流式/非流式识别、VAD语音活动检测和标点恢复中文效果在开源方案里属于第一梯队。Docker部署最大的优势是把复杂的环境依赖全部打包进镜像你不需要在自己机器上装一堆CUDA、torch、ffmpeg之类的组件只要有个能跑Docker的环境就行。这篇文章适合谁看想在公司内网快速搭一套语音识别服务、本地实验想复现效果、或者准备把ASR服务容器化上生产的同学都可以直接照做。1. 部署前的准备硬件、系统与Docker环境1.1 硬件要求先搞清楚你的场景需要多大算力FunASR官方支持CPU和GPU两种部署方式。CPU部署用ONNX Runtime后端跑中英文通用的Paraformer-large模型4核8G的机器上非流式单条wav处理速度大约是实时率的3到5倍也就是说一段10秒的音频识别大概3秒左右。这个水平对内部工具、离线批量转写完全够用。如果要做实时通话转录或者高并发API建议上GPU显存8G以上的N卡基本够推荐T4、V100、A10这些。注意容器里的GPU不是默认就能用的宿主机需要安装NVIDIA驱动和nvidia-container-toolkit这个我后面第5章专门展开。系统选择上优先LinuxUbuntu 20.04/22.04或者CentOS 7/8都可以我两个系统上都部署过Ubuntu更省心。Windows机器可以用Docker Desktop但底层是WSL2涉及WSL内核的问题排查起来比较麻烦生产环境不建议。macOS的M系列芯片也能跑但要区分arm64的镜像FunASR官方镜像主要是x86架构的Apple Silicon上需要开着Rosetta模拟性能会打折扣所以能用Linux就用Linux。1.2 Docker环境安装的几个关键点Ubuntu装Docker比较简单sudo apt-get update sudo apt-get install -y docker.io sudo systemctl enable --now dockerCentOS需要先配置Docker官方仓库再安装sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install -y docker-ce docker-ce-cli containerd.io sudo systemctl enable --now docker安装完先看docker version确认Server和Client都有正常输出。新手最容易遗漏的是当前用户不在docker组里运行docker ps会直接报permission denied while trying to connect to the docker api。解决方法很简单sudo usermod -aG docker $USER改完退出重新登录或者执行newgrp docker再验证。如果公司环境不许改用户组就老实加sudo。Docker装好后建议做镜像加速特别是国内网络环境直接拉官方镜像经常超时。在/etc/docker/daemon.json里配置registry-mirrors把阿里云容器镜像加速、中科大这些地址按优先级排好然后sudo systemctl restart docker。这个配置影响很大我见过太多人卡在拉镜像这一步以为是网络断了其实只是少配置了加速。1.3 镜像选型官方镜像还是自己构建部署FunASR有两条路官方预构建镜像和自己写Dockerfile。两者的取舍很清晰方案优点缺点适合场景官方镜像环境封装完整Python依赖、ffmpeg、推理后端都配好了镜像体积大自定义额外工具不灵活快速验证、生产标准部署自构建镜像可以基于python:3.10-slim精简体积集成内部依赖需要维护DockerfileFunASR依赖版本变化时要自己调试离线环境、有私有镜像仓库的团队我的原则是初学先用官方镜像跑通确认业务逻辑没问题之后再去考虑自构建。一上来就自己写Dockerfile出了bug分不清是FunASR的问题还是镜像的问题排查成本很高。等流程都熟了再精简镜像也不是难事。2. 容器启动全流程从拉取镜像到服务跑通2.1 拉取镜像仓库地址与版本选择FunASR官方Docker镜像的具体仓库地址和tag以官方文档的最新说明为准。常见的有Docker Hub上的funasr/funasr以及ModelScope社区同步的镜像。国内拉取我一般优先走阿里云容器镜像仓库地址是registry.cn-hangzhou.aliyuncs.com下一个funasr相关的repo因为我遇到过多次Docker Hub直接拉取超时的情况。docker pull funasr/funasr:latest拉完之后用docker images看镜像ID和体积。FunASR镜像包含了不少模型推理依赖体积通常比较大第一次拉取慢是正常的只要网络基础和加速配置没问题耐心等就行。2.2 docker run参数逐个拆解镜像拉下来之后启动容器的命令大概是这样的docker run -d \ --name funasr-asr \ -p 10096:10096 \ -v /data/funasr/models:/workspace/models \ -v /data/funasr/audio:/workspace/audio \ funasr/funasr:latest每个参数都说一下-d后台运行容器不会因为退出终端就停掉。--name funasr-asr容器名之后执行docker exec、docker logs、docker stop都靠它。-p 10096:10096把宿主机的10096端口映射到容器的10096端口外部请求通过宿主机IP:10096就能访问到服务。-v /data/funasr/models:/workspace/models把宿主机模型目录挂载进容器这个最关键。-v /data/funasr/audio:/workspace/audio挂载测试音频目录方便在容器内外共享文件。为什么强调挂载模型目录因为镜像里的文件系统是临时的容器一旦删掉重建里面的模型就全没了。模型放在宿主机目录挂载进去删除容器不丢数据升级镜像也方便新容器直接挂同一个目录就能用。2.3 进入容器启动识别服务容器起来后真正要做的是启动FunASR的识别服务docker exec -it funasr-asr bash cd /workspace/FunASR python -m funasr.bin.server \ --host 0.0.0.0 \ --port 10096 \ --model-dir /workspace/models \ --device cpu \ --backend torch解释一下几个参数。--host 0.0.0.0表示监听容器内所有网卡这样宿主机和外部才能访问到--port 10096要和docker映射的端口一致--model-dir指向挂载进来的模型目录--device指定cpu或gpu--backend指定推理后端CPU部署更推荐用onnxruntime速度明显比torch快。不同版本的启动入口可能不太一样老版本是python server.py新版本是python -m funasr.bin.server具体看镜像内README。如果想让容器启动后自动拉起服务可以把启动命令直接跟在镜像后面或者用Docker Compose的command字段这个我第6章给出完整配置。2.4 验证服务是否正常启动之后先看容器和日志docker ps | grep funasr docker logs -f funasr-asr看到类似listening on 0.0.0.0:10096或者Uvicorn running的日志说明服务已就绪。再用curl探测一下端口curl http://localhost:10096/如果返回了服务响应而不是连接拒绝说明端口映射和容器网络都没问题接下来就可以准备模型了。3. 模型资源管理模型下载、挂载与多模型切换3.1 模型下载的两种方式对比FunASR的模型托管在ModelScope社区部署前必须先把模型下好。有两种方式方式一宿主机下载后挂载进容器pip install modelscope modelscope download --model iic/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-pytorch --local_dir /data/funasr/models/paraformer-large方式二进入容器内下载docker exec -it funasr-asr bash pip install modelscope modelscope download --model iic/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-pytorch --local_dir /workspace/models我强烈推荐方式一。原因有三个容器是临时资源删了重建模型要重新下载容器内磁盘空间受Docker磁盘配额限制宿主机下载可以把模型目录同时挂载给多个容器复用。Paraformer-large模型文件大概1GB出头加上VAD模型和标点恢复模型建议预留5GB空间。3.2 模型目录结构与推理参数的关系一个常见的坑是模型目录结构不对导致服务启动时找不到模型文件。正确的结构是模型文件直接放在model_dir下包含config.yaml、model.pt、tokenizer相关文件等。用modelscope download --local_dir可以避免多套一层子目录的问题。启动FunASR服务时除了--model-dir还有几个参数跟模型配套参数说明建议值--model-dirASR主模型目录指向包含config.yaml的目录--devicecpu / gpu无GPU就cpu--backendtorch / onnxruntimeCPU推荐onnxruntime--vad-modelVAD模型目录需要单独下载--punc-model标点恢复模型目录需要单独下载如果只用ASR模型不配VAD和标点模型长音频的切分和语气停顿处理会差很多。生产环境建议三个模型都配上识别结果的可用性会有质的提升。3.3 多模型管理一套服务对应一个业务实际项目中经常出现同时需要中英文的场景。我的做法是每个模型一个独立目录/data/funasr/models/paraformer-zh/data/funasr/models/paraformer-en然后分别启动两个容器映射不同端口docker run -d --name funasr-zh -p 10096:10096 \ -v /data/funasr/models/paraformer-zh:/workspace/models \ funasr/funasr:latest docker run -d --name funasr-en -p 10097:10096 \ -v /data/funasr/models/paraformer-en:/workspace/models \ funasr/funasr:latest用容器名和端口区分业务比在一个服务里做模型热切换省心得多。不同业务的资源隔离也更清晰某个容器挂了不影响另一个。模型更新时把新模型下载到另一个目录切换挂载路径重启容器就行前后不过几十秒。4. 接口验证与业务接入HTTP和WebSocket实测4.1 服务日志怎么确认服务准备好了服务启动成功后日志中会出现模型加载完成的提示。如果加了VAD和标点模型还会看到对应的加载日志。用docker logs -f funasr-asr追踪日志看到监听地址打印出来就说明服务进程进入正常状态。任何启动阶段的报错比如model not found、config.yaml missing基本都是模型目录问题回到第3章检查。4.2 HTTP接口一条curl命令完成识别准备一个16k采样率、单声道、wav格式的音频文件放到挂载目录下然后执行curl -F audio/data/funasr/audio/test.wav http://localhost:10096/返回一般是JSON{code:0,text:今天天气怎么样,data:[{text:今天天气怎么样}]}不同版本返回结构会有点差异但识别文本基本都在text字段里。如果返回code非0优先检查音频格式。16k是模型训练的标准采样率44.1k的音乐或者8k的电话录音直接丢进去效果会很差最好先用ffmpeg转成16k单声道再送识别。生产环境接HTTP接口时注意设置超时时间。非流式识别长音频可能要跑几秒到几十秒客户端超时设太短会误判为服务故障。4.3 WebSocket接口流式识别的接入逻辑WebSocket接口地址是ws://localhost:10096/适合实时语音识别场景。接入逻辑是客户端建立WebSocket连接每次发送一段PCM二进制数据服务端流式返回中间结果和最终结果。FunASR支持实时半句结果的回传对实时字幕、语音助手这类场景非常有用。给一个最小的Python测试脚本参考import websocket ws websocket.create_connection(ws://localhost:10096/) with open(test.pcm, rb) as f: data f.read() ws.send(data, opcodewebsocket.ABNF.OPCODE_BINARY) result ws.recv() print(result) ws.close()实际项目中客户端要把音频按chunk切分比如16k、16bit、单声道每个chunk约3200字节对应100ms服务端对每个chunk都会返回识别结果。如果要接实时麦克风用pyaudio采集音频帧边采边发。WebSocket的具体帧格式不同版本协议有差异建议以官方runtime示例代码为准这里说的是通用的接入思路。4.4 WebUI演示页浏览器打开http://localhost:10096/如果镜像内带WebUI会看到一个简单的Demo页面可以上传wav文件直接出识别结果也可以模拟麦克风输入。这个对本地验证特别方便不用写任何客户端代码就能确认服务可用。页面打不开也不一定说明服务有问题可能是镜像没打包WebUI直接用curl测接口就行。5. 踩坑实录docker部署FunASR的典型问题与排查链路5.1 docker权限报错是入门第一道坎报错原文通常是permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock。原因是当前用户不在docker组。解决方法是sudo usermod -aG docker $USER然后退出当前会话重新登录。这个坑之所以普遍是因为很多安装教程默认用sudo装完就不再管权限了后面每次执行docker命令都要sudo或者直接报权限错误。我远程帮同事排查过一台部署失败的服务器折腾半天发现就是这个问题。所以要做的第一件事永远是确认docker ps能正常执行。5.2 镜像下载慢或超时镜像下载慢的原因基本只有一个没有配置镜像加速。在/etc/docker/daemon.json里加上registry-mirrors格式类似这样{ registry-mirrors: [https://docker.mirrors.ustc.edu.cn, https://registry.docker-cn.com] }具体地址以当时可用的为准配置完执行sudo systemctl restart docker再重新pull。如果服务器处于完全离线环境更稳妥的办法是在能联网的机器上先把镜像拉下来导出再导入docker save -o funasr.tar funasr/funasr:latest docker load -i funasr.tar一条save一条load整个镜像带着依赖一起搬家比在离线服务器上折腾源靠谱得多。5.3 GPU在容器里没生效启动容器时加了--gpus all进容器跑nvidia-smi却报命令找不到这种情况通常是宿主机缺少nvidia-container-toolkit。安装完驱动之后还要装这个工具包然后重启Dockersudo apt-get install -y nvidia-container-toolkit sudo systemctl restart docker再启动容器时加--gpus all。还有一个典型报错是CUDA error: no kernel image is available这基本是NVIDIA驱动版本和容器里的CUDA版本不匹配需要升级宿主机驱动或者换一个CUDA版本对应的镜像。5.4 端口占用与容器瞬间退出启动容器时如果报port is already allocated说明10096端口被占了。排查方法lsof -i:10096找到占用进程释放端口或者直接换宿主机映射端口比如-p 20096:10096。还有一种情况是容器启动后立刻退出docker ps看不到docker ps -a能看到残留容器。这个时候一定先看日志docker logs funasr-asr日志基本会直接告诉你原因绝大多数是模型目录不对或者Python包缺失。注意容器的主进程必须要常驻如果镜像的默认CMD是一个一次性命令docker run -d不加覆盖命令就会立刻退出。用-it方式进容器手动启动服务反而更容易定位问题。6. 从单容器到服务编排Compose部署与cosyvoice联动6.1 Docker Compose文件编写参考容器越来越多之后手动敲docker run就有些累了。我习惯用Docker Compose管理下面是完整可参考的docker-compose.ymlservices: funasr: image: funasr/funasr:latest container_name: funasr-asr ports: - 10096:10096 volumes: - /data/funasr/models:/workspace/models - /data/funasr/audio:/workspace/audio command: python -m funasr.bin.server --host 0.0.0.0 --port 10096 --model-dir /workspace/models --device cpu --backend onnxruntime restart: alwaysrestart: always保证Docker守护进程重启后自动拉起服务对生产环境很关键。启动命令docker compose up -d新版Docker用docker compose老版本用docker-compose效果一样。查看状态和日志docker compose ps docker compose logs -f funasrCompose的好处是环境定义全部固化在一个文件里开发、测试、生产可以复刻同一套配置新人接手也不需要追着问启动参数。6.2 与cosyvoice在同Compose里协同部署cosyvoice是开源语音合成项目同样可以用Docker部署。把ASR和TTS放在同一个Compose文件里就能搭起一套完整的语音交互链路services: funasr-asr: image: funasr/funasr:latest ports: - 10096:10096 volumes: - /data/funasr/models:/workspace/models cosyvoice: image: cosyvoice镜像 ports: - 50000:50000 volumes: - /data/cosyvoice:/workspaceASR负责把用户语音转成文本TTS负责把文本合成语音。同一个Compose网络内服务之间可以直接用服务名互相访问比如业务系统调用ASR用http://funasr-asr:10096调用TTS用http://cosyvoice:50000不需要关心容器的具体IP地址。这个组合很适合做语音对话机器人、会议转写加合成、智能客服这类项目。6.3 生产环境的容器管理建议最后加一点生产环境的细节建议。资源限制一定要加Compose里可以这样控制services: funasr: ... deploy: resources: limits: cpus: 4 memory: 8G日志要限制体积长期运行的容器日志文件可能占满磁盘logging: driver: json-file options: max-size: 100m max-file: 3模型目录最好别放本地磁盘用NAS或者对象存储挂载避免单机故障导致模型丢失。镜像升级时保留旧容器先起新容器映射另一个端口验证再切换流量回滚也方便。FunASR的版本和模型版本建议在镜像tag上体现出来方便回溯。最后分享一点个人体会。我远程帮同事排查过一台部署失败的服务器折腾半天发现就是用户不在docker组这种小问题日志里全是permission denied所以先别急着怀疑FunASR本身的配置把Docker环境基础打牢后面会顺很多。这套容器方案我跑了半年最值的地方就是换服务器再也不用重装CUDA和一堆Python包了模型目录一挂、compose一拉服务自己就起来了。我自己部署时的做法是第一次跑通不追求GPU先用CPU模式配合onnxruntime把流程走完确认接口、模型、数据流都没问题再考虑GPU加速这样排查范围会小很多。按照这个流程走一遍基本一小时内就能把一套可用的中文语音识别服务跑起来。