Ubuntu20.04生产环境部署:Fish Speech+Nginx负载均衡配置
Ubuntu20.04生产环境部署:Fish Speech+Nginx负载均衡配置
最近在帮一个做有声内容的朋友搭建语音合成服务,他们之前用单机部署的Fish Speech,平时用着还行,一到业务高峰期,比如晚上集中生成有声书章节的时候,服务器就卡得不行,并发一高就崩。他们问我有没有办法在不加太多硬件成本的情况下,把服务撑起来。
我看了下他们的配置,一台RTX 4090的服务器,跑着Fish Speech 1.5,平时也就处理几十个并发请求。问题其实不在模型本身,而是部署方式太“裸奔”了,资源没管好,请求来了也没个调度。折腾了两天,给他们搞了一套基于Nginx负载均衡的生产级部署方案,硬是把单台服务器的并发处理能力从原来的50请求/秒,提到了300+。整个过程踩了不少坑,也总结了一些经验,今天就跟大家详细聊聊怎么操作。
这套方案的核心思路很简单:把一台服务器的潜力榨干。不是靠堆硬件,而是通过GPU资源隔离、请求负载均衡、以及一些运维上的优化,让现有的设备跑得更稳、更快。如果你也在为TTS服务的性能瓶颈发愁,或者打算把Fish Speech用到正式的业务里,那接下来的内容应该能帮到你。
1. 基础环境与Fish Speech部署
咱们先从最基础的开始,把Fish Speech在Ubuntu 20.04上跑起来。虽然网上有很多一键脚本,但生产环境我建议还是手动走一遍,心里有底,后面排查问题也方便。
1.1 系统准备与依赖安装
首先,确保你的系统是Ubuntu 20.04 LTS,并且有NVIDIA显卡驱动。用nvidia-smi命令检查一下驱动和CUDA版本,推荐驱动版本在525以上,CUDA 12.1或更高。
# 更新系统包
sudo apt update && sudo apt upgrade -y
# 安装基础编译工具和依赖
sudo apt install -y build-essential cmake git wget curl libsox-dev ffmpeg python3-pip python3-venv
# 创建专门的用户来运行服务(安全起见,别用root)
sudo useradd -m -s /bin/bash fishspeech
sudo usermod -aG audio fishspeech
接下来,配置Python环境。我习惯用Miniconda来管理,比较干净。
# 下载并安装Miniconda
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh -b -p $HOME/miniconda3
echo 'export PATH="$HOME/miniconda3/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
# 为Fish Speech创建独立的虚拟环境
conda create -n fish-speech python=3.10 -y
conda activate fish-speech
# 安装PyTorch(匹配你的CUDA版本)
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
1.2 拉取并配置Fish Speech
现在来获取Fish Speech的代码和模型。建议直接从官方仓库拉取,模型从Hugging Face下载。
# 切换到我们创建的用户
sudo -u fishspeech bash
cd /home/fishspeech
# 克隆代码仓库
git clone https://github.com/fishaudio/fish-speech.git
cd fish-speech
# 安装项目依赖
pip install -e .
# 下载预训练模型(1.5版本)
# 这里需要git-lfs,如果没安装请先运行:sudo apt install git-lfs
git lfs install
git clone https://huggingface.co/fishaudio/fish-speech-1.5 models/
下载模型可能会花点时间,毕竟好几个GB。完成后,你可以先简单测试一下模型是否能正常工作。
# 一个快速测试脚本,检查核心功能
python -c "
from fish_speech.models.text2semantic import Text2Semantic
model = Text2Semantic.from_pretrained('models/fish-speech-1.5')
print('模型加载成功!')
"
如果没报错,说明基础环境没问题了。
1.3 启动基础API服务
Fish Speech官方提供了WebUI,但生产环境我们更需要一个稳定的API服务。我们可以用他们提供的服务器模块,稍作调整。
先创建一个简单的启动脚本 /home/fishspeech/start_api.py:
#!/usr/bin/env python3
import argparse
from fish_speech.server import serve
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--host", type=str, default="127.0.0.1")
parser.add_argument("--port", type=int, default=8000)
parser.add_argument("--model-path", type=str, default="/home/fishspeech/fish-speech/models/fish-speech-1.5")
parser.add_argument("--device", type=str, default="cuda")
args = parser.parse_args()
serve(
host=args.host,
port=args.port,
model_path=args.model_path,
device=args.device,
max_workers=4 # 初始工作进程数,后面会调整
)
给脚本执行权限,并创建一个Systemd服务来管理它,这样服务崩溃了能自动重启。
sudo nano /etc/systemd/system/fish-speech-api.service
写入以下内容:
[Unit]
Description=Fish Speech API Service
After=network.target
[Service]
Type=simple
User=fishspeech
WorkingDirectory=/home/fishspeech/fish-speech
Environment="PATH=/home/fishspeech/miniconda3/bin"
ExecStart=/home/fishspeech/miniconda3/envs/fish-speech/bin/python /home/fishspeech/start_api.py --host 0.0.0.0 --port 8000
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
然后启动服务:
sudo systemctl daemon-reload
sudo systemctl start fish-speech-api
sudo systemctl enable fish-speech-api
sudo systemctl status fish-speech-api # 检查状态
现在,你的Fish Speech API应该已经在本地8000端口跑起来了。可以用curl简单测一下:
curl -X POST http://127.0.0.1:8000/generate \
-H "Content-Type: application/json" \
-d '{"text": "你好,这是一个测试语音。", "language": "zh"}'
如果返回一个任务ID或者音频数据,那就恭喜你,基础服务部署成功了。不过,这只是单机裸奔模式,扛不住多少压力。接下来,我们要开始上“生产级”的硬菜了。
2. GPU资源隔离与优化
单卡服务器想要同时处理多个语音合成请求,最大的瓶颈就是GPU内存和计算核心的争抢。一个长文本的合成任务可能就把显存占满了,其他请求只能干等着。我们的目标是把这块GPU“划出几个隔间”,让不同的任务互不干扰。
2.1 使用NVIDIA MPS提升并发
NVIDIA Multi-Process Service (MPS) 是个好东西,它允许GPU上的多个进程共享上下文,减少进程切换的开销,特别适合像我们这种需要同时跑多个模型推理进程的场景。
首先,确保你的NVIDIA驱动支持MPS(大多数现代驱动都支持)。然后按以下步骤设置:
# 停止可能正在运行的X Server或显示管理器(如果是无头服务器,通常没有)
sudo systemctl stop gdm # 或 lightdm,根据你的桌面环境
# 设置GPU为独占模式,为MPS做准备
sudo nvidia-smi -i 0 -c EXCLUSIVE_PROCESS # -i 0 指定第一块GPU,如果你有多块请调整
# 启动MPS服务
sudo nvidia-cuda-mps-control -d
验证MPS是否启动成功:
echo "status" | nvidia-cuda-mps-control
你应该能看到MPS is alive之类的信息。接下来,我们需要修改Fish Speech的启动方式,让它的进程在MPS环境下运行。这步很关键,能显著降低每个进程的显存开销。
修改之前的Systemd服务文件,在[Service]部分添加环境变量:
Environment="CUDA_VISIBLE_DEVICES=0"
Environment="CUDA_MPS_PIPE_DIRECTORY=/tmp/nvidia-mps"
Environment="CUDA_MPS_LOG_DIRECTORY=/tmp/nvidia-log"
然后重启服务:
sudo systemctl daemon-reload
sudo systemctl restart fish-speech-api
2.2 配置进程池与显存限制
即使开了MPS,如果一个进程失控把显存吃光,其他进程还是会饿死。我们需要在应用层面控制每个工作进程的资源使用。
修改/home/fishspeech/start_api.py,利用PyTorch的内存管理功能:
# 在serve函数调用前,或模型加载后,添加以下代码
import torch
torch.cuda.set_per_process_memory_fraction(0.3) # 每个进程最多使用30%的显存
torch.cuda.empty_cache() # 清空缓存
同时,调整启动脚本,允许我们控制工作进程的数量,并让每个进程绑定到不同的CPU核心,减少上下文切换。
# 创建一个新的启动脚本 /home/fishspeech/start_api_optimized.sh
#!/bin/bash
export CUDA_VISIBLE_DEVICES=0
export OMP_NUM_THREADS=2 # 限制每个进程的OpenMP线程数
# 使用taskset将进程绑定到特定的CPU核心(例如核心0和1)
taskset -c 0,1 /home/fishspeech/miniconda3/envs/fish-speech/bin/python /home/fishspeech/start_api.py --host 0.0.0.0 --port 8000 --max-workers 4
然后更新Systemd服务的ExecStart指向这个新脚本。别忘了给脚本执行权限。
这样配置后,你的单个API服务进程就不会霸占全部GPU资源了。但真正的并发提升,靠单个服务实例是不够的。接下来,我们要在单机上启动多个Fish Speech服务实例,让它们监听不同的端口。
2.3 启动多服务实例
思路是启动多个相同的API服务进程,每个进程使用不同的端口和略微不同的资源限制,让Nginx在后面做负载均衡。
创建另一个Systemd服务文件,比如fish-speech-api-8001.service,将端口改为8001,并且可以绑定到不同的CPU核心(例如核心2,3)。
# /etc/systemd/system/fish-speech-api-8001.service
[Service]
...
Environment="CUDA_VISIBLE_DEVICES=0"
Environment="OMP_NUM_THREADS=2"
ExecStart=/bin/bash -c 'taskset -c 2,3 /home/fishspeech/miniconda3/envs/fish-speech/bin/python /home/fishspeech/start_api.py --host 0.0.0.0 --port 8001'
...
我建议根据你GPU的显存大小来决定启动几个实例。对于一块24GB显存的RTX 4090,在做了上述内存限制后,启动3-4个实例是比较安全的。你可以创建fish-speech-api-8002.service、fish-speech-api-8003.service等。
启动并启用所有实例:
sudo systemctl start fish-speech-api-8001
sudo systemctl enable fish-speech-api-8001
# ... 对其他实例重复此操作
现在,你本机的8000、8001、8002等端口上,都运行着独立的Fish Speech服务了。它们共享同一块GPU,但通过MPS和资源限制,实现了基本的隔离。下一步,就是请出“调度员”Nginx,把外部的请求合理地分发给这些“工人”。
3. Nginx负载均衡配置
Nginx在这里扮演两个角色:一是作为反向代理,把外部请求转发到后端的多个Fish Speech实例;二是作为负载均衡器,决定哪个请求发给哪个后端,避免某个实例过载。
3.1 安装与基础配置
首先安装Nginx:
sudo apt install nginx -y
我们先为Fish Speech服务创建一个独立的Nginx配置文件,这样管理起来清晰。
sudo nano /etc/nginx/sites-available/fish-speech
写入以下配置内容。这里假设你的服务器公网IP是your_server_ip,或者你有一个域名tts.yourcompany.com。
upstream fish_speech_backend {
# 配置负载均衡后端服务器列表
# 这里列出我们刚才启动的所有本地实例
server 127.0.0.1:8000 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8001 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8002 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8003 max_fails=3 fail_timeout=30s;
# 使用最少连接负载均衡算法,谁当前处理的请求少,新请求就给谁
least_conn;
# 保持连接,提升性能
keepalive 32;
}
server {
listen 80;
server_name your_server_ip; # 或你的域名 tts.yourcompany.com
# 客户端请求体大小限制(根据你的需求调整)
client_max_body_size 50M;
# 增加超时时间,语音合成可能比较耗时
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
location / {
# 拒绝直接访问根路径,或者可以返回一个简单的状态页
return 404;
}
location /api/ {
# 将 /api/ 开头的请求代理到后端集群
proxy_pass http://fish_speech_backend;
# 重要的头部信息转发
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 禁用缓冲,对于长时间运行的生成任务,让数据流式返回可能更好
# 但对于TTS,通常是一次性返回整个音频,所以缓冲没问题。
# proxy_buffering off;
}
# 可选:添加一个健康检查端点
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
保存后,创建符号链接启用这个站点配置,并测试Nginx配置是否正确。
sudo ln -s /etc/nginx/sites-available/fish-speech /etc/nginx/sites-enabled/
sudo nginx -t # 测试配置
sudo systemctl reload nginx # 重新加载配置
现在,外部客户端访问 http://your_server_ip/api/generate 的请求,就会被Nginx均匀地分发到后端的8000-8003端口服务上。你可以用压测工具简单试一下,看看多个并发请求是不是被分到了不同的端口。
3.2 配置HTTPS(SSL/TLS)
生产环境必须上HTTPS,保证数据传输安全,而且很多现代的API客户端也要求使用HTTPS。这里我们使用Let‘s Encrypt的免费证书。
安装Certbot工具:
sudo apt install certbot python3-certbot-nginx -y
获取并安装证书(如果你有域名的话):
sudo certbot --nginx -d tts.yourcompany.com
Certbot会自动修改你的Nginx配置文件,添加SSL相关设置,并设置自动续期。如果你只是用IP地址,那么可能需要使用自签名证书,或者考虑使用支持IP证书的其它服务商,但自签名证书客户端需要手动信任,比较麻烦。
配置完成后,你的Nginx server块会多出一个监听443端口的配置,并且将HTTP请求重定向到HTTPS。确保在配置中,proxy_pass仍然指向后端的HTTP服务(http://fish_speech_backend),因为Nginx和后端服务在同一台机器,内网通信可以不用HTTPS。
3.3 高级调优:缓存与限流
为了进一步提升性能和稳定性,我们可以加入两个小功能。
微缓存: 对于完全相同的文本和参数合成请求,短时间内没必要让GPU再算一次。我们可以用Nginx的proxy_cache功能,在内存里缓存一小段时间的结果。
在Nginx的http块内(通常在/etc/nginx/nginx.conf)添加缓存路径定义:
http {
...
# 定义缓存路径和参数
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=fish_cache:10m max_size=1g inactive=60m use_temp_path=off;
...
}
然后在刚才的server配置的location /api/块内添加缓存规则:
location /api/ {
proxy_cache fish_cache;
proxy_cache_key "$scheme$request_method$host$request_uri$request_body";
proxy_cache_valid 200 302 5m; # 成功响应缓存5分钟
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://fish_speech_backend;
... # 其他proxy_set_header等设置保持不变
}
限流: 防止恶意用户或失控客户端瞬间发送大量请求,拖垮后端服务。
在location /api/块内添加限流:
location /api/ {
# 限制每个IP地址每秒最多10个请求,突发不超过20个
limit_req zone=req_limit burst=20 nodelay;
limit_req_status 429;
proxy_pass http://fish_speech_backend;
...
}
同时需要在http块定义这个限流区域:
http {
...
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
...
}
做完这些优化,重启Nginx:
sudo systemctl restart nginx
现在,你的Fish Speech服务前面就有了一个具备负载均衡、HTTPS、缓存和限流能力的强大网关。但这还不够,一个健壮的生产系统还需要考虑日志和监控。
4. 生产运维关键点
服务跑起来只是第一步,让它长期稳定运行,出了问题能快速定位,才是运维的难点。
4.1 结构化日志与切割
默认的日志可能都混在一起,我们需要把Nginx访问日志、错误日志,以及每个Fish Speech后端实例的日志分开管理,并定期切割,避免单个日志文件过大。
首先,配置Nginx的日志格式,加入更多信息。在http块内:
log_format fish_speech_json '{ "time": "$time_iso8601", '
'"remote_addr": "$remote_addr", '
'"request": "$request", '
'"status": $status, '
'"body_bytes_sent": $body_bytes_sent, '
'"request_time": $request_time, '
'"upstream_addr": "$upstream_addr", '
'"upstream_response_time": "$upstream_response_time", '
'"upstream_cache_status": "$upstream_cache_status" }';
access_log /var/log/nginx/fish-speech-access.log fish_speech_json;
error_log /var/log/nginx/fish-speech-error.log warn;
然后,使用logrotate工具来管理日志切割。创建配置文件:
sudo nano /etc/logrotate.d/fish-speech-nginx
内容如下:
/var/log/nginx/fish-speech-*.log {
daily
missingok
rotate 30
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
}
对于Fish Speech后端服务的日志(输出到stdout/stderr,被Systemd捕获),我们同样可以配置Journald的日志限制和转发。修改或创建Systemd服务的[Service]部分:
[Service]
...
StandardOutput=journal
StandardError=journal
# 限制日志大小
LogRateLimitIntervalSec=30s
LogRateLimitBurst=1000
4.2 监控与告警
基础监控可以用简单的脚本来完成。创建一个健康检查脚本/usr/local/bin/check_fish_speech.sh:
#!/bin/bash
# 检查后端服务端口是否存活
PORTS=(8000 8001 8002 8003)
ALL_HEALTHY=true
for port in "${PORTS[@]}"; do
if ! curl -f -s http://127.0.0.1:$port/health > /dev/null; then
echo "后端服务端口 $port 健康检查失败!"
ALL_HEALTHY=false
# 可以尝试重启对应的服务
sudo systemctl restart fish-speech-api-$port 2>/dev/null || true
fi
done
# 检查Nginx状态
if ! systemctl is-active --quiet nginx; then
echo "Nginx服务未运行!"
sudo systemctl restart nginx
ALL_HEALTHY=false
fi
if [ "$ALL_HEALTHY" = true ]; then
echo "所有服务状态正常。"
exit 0
else
exit 1
fi
给脚本执行权限,然后把它加入Crontab,每分钟执行一次,并将错误输出发送到你的邮箱或监控系统。
sudo chmod +x /usr/local/bin/check_fish_speech.sh
sudo crontab -e
# 添加一行:
* * * * * /usr/local/bin/check_fish_speech.sh >> /var/log/fish-speech-monitor.log 2>&1
对于更直观的资源监控,比如GPU使用率、显存占用、请求QPS等,我推荐使用Prometheus + Grafana。你可以部署nvidia-gpu-exporter来暴露GPU指标,用nginx-exporter来暴露Nginx指标,然后在Grafana里制作一个仪表盘。这部分内容稍微复杂一些,但对于长期运维来说非常值得投入。
4.3 压力测试与性能验证
配置都做完后,到底效果如何?我们需要压测一下。可以用wrk或ab这样的工具。
# 安装wrk
sudo apt install wrk -y
# 进行一个简单的压测,30个线程,100个连接,持续30秒
wrk -t30 -c100 -d30s --timeout 2s -s post.lua http://your_server_ip/api/generate
你需要准备一个post.lua文件,里面定义POST请求的body:
wrk.method = "POST"
wrk.headers["Content-Type"] = "application/json"
wrk.body = '{"text": "这是一个用于压力测试的示例文本,内容需要足够长以模拟真实负载。", "language": "zh"}'
压测时,同时用nvidia-smi、htop和sudo tail -f /var/log/nginx/fish-speech-access.log来观察系统状态。目标是找到系统的瓶颈:是GPU算力到头了?还是某个后端进程崩溃了?或者是Nginx或网络成了瓶颈?
根据压测结果,你可能需要回头调整一些参数,比如:
- 增加或减少后端实例数量 (
max_workers) - 调整Nginx的
keepalive连接数 - 修改GPU进程的内存分数 (
set_per_process_memory_fraction) - 优化内核网络参数 (
sysctl.conf)
5. 总结
折腾完这一套,朋友那边的服务稳定多了,晚上高峰期的请求排队现象基本消失,合成任务的平均响应时间也降了下来。回顾一下,这套方案的核心其实就是**“分而治之”和“精细化管理”**。
把单个重型模型服务拆分成多个轻量级实例,用Nginx做智能调度,再配上资源隔离和运维优化,单台服务器的处理能力就有了质的提升。从50到300+的并发提升,并不是模型突然变快了,而是我们让请求排队和处理的效率变高了。
当然,这套方案也不是银弹。如果业务量继续暴涨,最终还是要走向真正的多机集群。但在此之前,把单机性能榨干,无疑是成本最低、见效最快的选择。如果你正准备把Fish Speech或其他AI模型投入生产,不妨试试这些方法。过程中可能会遇到各种环境依赖、版本冲突的问题,耐心点,一步步排查,总能搞定。
最后,再提一句安全。本文重点在性能部署,但生产环境的安全配置同样重要,包括防火墙规则、服务账户权限、数据库加密(如果你存了用户数据)等,千万别忽略了。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
更多推荐

所有评论(0)