实战指南:使用Docker部署ChatTTS文字转语音服务并指定端口8666
背景与痛点
做语音合成项目时,最怕的不是模型跑不动,而是“跑起来却没人能访问”。
我最初在物理机直接装 ChatTTS,结果遇到三件糟心事:
- 默认 8080 端口被公司 Jenkins 占了,改一次端口就要重写 systemd 服务文件,来回重启。
- 依赖里既有 PyTorch 又有 espeak-ng,不同系统版本编译时间 20 min 起跳,CI 流水线直接超时。
- 同事 A 把 Python 升到 3.11,我这边还在 3.9,一合并代码就崩,回滚又花掉半天。
一句话:环境不一致、端口冲突、构建慢——这三座大山把“想快速上线 Demo”的激情磨得精光。Docker 镜像化正是为了把“能跑”变成“随时能跑”,再把“端口”这件事提前说清楚,让后来者不再踩坑。
技术选型:为什么最终选了 Docker
| 方案 | 优点 | 缺点 | 结论 |
|---|---|---|---|
| 裸机部署 | 性能极限高 | 污染宿主机、回滚难 | 放弃 |
| Conda 环境 | 科学栈友好 | 端口仍冲突、CI 镜像大 | 放弃 |
| 传统虚拟机 | 隔离彻底 | 镜像 2 GB+、启动慢 | 放弃 |
| Docker | 秒级启动、分层缓存、端口映射灵活 | 需要学 Dockerfile | 采用 |
Docker 的“分层缓存”对 ChatTTS 这种“模型权重 > 代码”的项目尤其友好:
只要 requirements.txt 不动,PyTorch 那一层就永远复用,二次构建 10 秒搞定。
再加上-p 8666:8666一行命令就能把端口钉死,谁占都不怕。
核心实现:Dockerfile + 端口映射一步到位
下面这份 Dockerfile 是我迭代 5 次后的“最小可跑”版本,已推到公司仓库稳定运行两周。
思路:先装系统依赖 → 再装 Python 包 → 再拷模型 → 最后换非 root 用户,每层尽量一条 RUN,减少层数。
# 1. 基础镜像:官方 PyTorch,已带 CUDA 11.8,省掉 1.5 GB 编译时间 FROM pytorch/pytorch:2.1.2-cuda11.8-cudnn8-runtime # 2. 系统依赖:espeak-ng 用于音素化,ffmpeg 用于音频格式转换 RUN apt-get update && \ apt-get install -y --no-install-recommends \ espeak-ng \ ffmpeg \ && rm -rf /var/lib/apt/lists/* # 3. Python 依赖:提前单独复制,最大化缓存 WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 4. 拷贝源码与模型(.dockerignore 里把 .git、__pycache__ 都排除了) COPY . . # 5. 创建非 root 用户,降低容器逃逸风险 RUN groupadd -r chat && useradd -r -g chat chat RUN chown -R chat:chat /app USER chat # 6. 声明端口,方便 docker ps 看得到 EXPOSE 8666 # 7. 启动命令:uvicorn 单 worker,多 worker 见下一节调优 CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8666"]构建 & 运行:
# 构建(别忘记末尾的点) docker build -t chattts:1.0 . # 运行:把容器 8666 映射到宿主机 8666,后台常驻 docker run -d --name chattts \ -p 8666:8666 \ --restart unless-stopped \ chattts:1.0验证:
curl -X POST http://:8666/tts \ -H "Content-Type: application/json" \ -d '{"text":"你好 Docker"}' \ --output out.wav听到声音那一刻,你就知道端口 8666 真的被“钉”死了。
性能与安全:高并发下的两条土办法
性能
ChatTTS 模型一次前向大约 1.2~1.5 s(GPU 版,句长 20 字)。单 worker 显然撑不住突发。
但多进程又吃显存,实测 4 worker 把 8 GB 显存直接打满。折中方案:- 容器里起gunicorn + 1 个 uvicorn worker,再挂--workers 2(=2 进程)。
- 前面用Nginx做 10 路限流,超出的请求直接返回 503,保护 GPU 不被打爆。
这样压测 50 并发,平均延迟 2.1 s,显存占用 6.3 GB,算力刚好打满但不 OOM。
安全
- 非 root 用户已在 Dockerfile 体现。
- 把模型权重目录
/app/models挂成只读卷:docker run -v /host/models:/app/models:ro ... - 如果对外暴露,再加一层basic auth或JWT,别让陌生人免费点歌你的 GPU。
避坑指南:我替你们踩过的 4 个坑
端口被占用却看不到进程
Linux 会复用 TIME_WAIT,用ss -ltnp | grep 8666比netstat更快定位。构建时卡在 “Downloading torch-2.x.whl”
国内机器把pip.conf换成清华源,或者 Dockerfile 里加RUN pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple容器里中文乱码
宿主机 locale 没传进去,Dockerfile 加两行:ENV LANG=C.UTF-8 LC_ALL=C.UTF-8语音出来全是“沙沙”噪音
espeak-ng 版本不一致导致音素错位,锁定espeak-ng=1.50+dfsg-8即可。
实践建议:下一步你可以这样玩
优化镜像体积
把pytorch/pytorch:2.1.2-cuda11.8-runtime换成nvidia/cuda:11.8.0-base-ubuntu22.04,再手动装 Miniconda + PyTorch,最终镜像从 5.6 GB 降到 3.1 GB,推送仓库省一半时间。尝试不同语音模型
ChatTTS 官方默认 400 M 参数,其实还有 1.1 B 大模型,把models/目录整包替换后,改一行MODEL_NAME环境变量即可 A/B 测试,Docker 卷挂载让切换只需 10 秒。做成 docker-compose 一键开发
把 TTS、Nginx、Redis(做缓存)写成三服务,docker-compose up直接拿到一套可横向扩展的“语音工厂”。
写在最后
整套流程跑通后,我把 Dockerfile 和 compose 模板丢到团队 GitLab,新人入职当天就能docker run出自己的语音接口。
端口 8666 再也没人抢,CI 构建稳定在 2 分钟,回滚直接docker image ls找旧 tag。
对我来说,Docker 不只是“容器”,而是把 ChatTTS 从“论文里的模型”变成“产品线随时可调用”的最后一公里。
如果你也在为环境、端口、依赖头疼,不妨把这篇笔记当成 baseline,先跑起来,再慢慢剪枝优化——祝你早点听到自己容器里传出的第一声“Hello Docker”。