算力任务队列搭建,排队执行AI推理任务:算力任务队列搭建
当多个人或程序同时向GPU服务器提交AI推理请求时,如果没有排队机制,很容易出现显存溢出、进程崩溃或任务互相抢占资源。
搭建一个算力任务队列,让请求按顺序排队执行,是低成本且稳定的解决方案。
什么场景需要任务队列
以下情况建议部署队列:
- 多人共用一台GPU服务器跑推理任务
- 上游系统并发提交请求,但GPU显存只够跑一个模型
- 需要记录每个任务的执行状态和结果
- 希望任务失败后能重试,而不是直接丢弃
队列的核心思路是:提交任务只写入队列,后台Worker按顺序取出并执行,同一时间只有一个推理进程占用GPU。
环境准备与组件选择
以Ubuntu 20.04/22.04 + NVIDIA GPU为例,需要准备:
- Python 3.8以上环境
- Redis服务(作为任务队列中间件)
- 推理脚本(如调用PyTorch或ONNX模型)
- 进程守护工具(Supervisor或systemd)
安装Redis和Python依赖:
sudo apt update
sudo apt install redis-server -y
sudo systemctl enable redis-server
sudo systemctl start redis-server
pip install redis rq
这里选用RQ(Redis Queue),它比Celery更轻量,适合单机GPU排队场景。
编写推理任务与入队脚本
先写一个模拟推理函数,实际使用时替换成你的模型调用代码:
# tasks.py
import time
def run_inference(prompt):
# 模拟模型加载与推理耗时
time.sleep(5)
result = f"processed: {prompt}"
return result
再写提交任务的脚本:
# submit.py
from redis import Redis
from rq import Queue
from tasks import run_inference
redis_conn = Redis(host='localhost', port=6379)
q = Queue('gpu_queue', connection=redis_conn)
job = q.enqueue(run_inference, 'hello ai')
print('任务ID:', job.id)
执行python submit.py后,任务进入gpu_queue队列,但还没有被消费。
启动Worker并限制并发
关键点:启动Worker时必须设置并发为1,否则多个任务仍会同时占用GPU。
rq worker gpu_queue --max-jobs 1
更稳妥的方式是用Supervisor守护,配置文件/etc/supervisor/conf.d/gpu_worker.conf:
[program:gpu_worker]
command=rq worker gpu_queue --max-jobs 1
directory=/home/ubuntu/ai_queue
autostart=true
autorestart=true
stderr_logfile=/var/log/gpu_worker.err.log
stdout_logfile=/var/log/gpu_worker.out.log
加载配置:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start gpu_worker
避坑与注意事项
- 并发数必须为1:这是排队执行AI推理任务的核心,设为2以上会重新引发显存竞争。
- 任务超时设置:RQ默认任务超时为180秒,长推理任务需在
enqueue时加job_timeout=600,否则会被误杀。 - Redis持久化:建议开启AOF,避免服务器重启后队列任务丢失。
- GPU显存释放:如果推理脚本没有正确释放显存,连续任务可能累积占用,建议每个任务结束后调用
torch.cuda.empty_cache()。 - Worker目录一致:Supervisor的
directory必须与提交任务时的模块路径一致,否则会报ImportError。
效果验证
提交3个任务后,观察日志:
tail -f /var/log/gpu_worker.out.log
应看到任务依次执行,前一个完成后才开始下一个。
同时用nvidia-smi确认同一时刻只有一个Python进程占用GPU。
查看队列状态:
from redis import Redis
from rq import Queue
q = Queue('gpu_queue', connection=Redis())
print('排队中:', len(q))
如果任务一直不执行,先检查Worker是否存活:supervisorctl status gpu_worker;
如果报连接错误,确认Redis服务是否启动:redis-cli ping应返回PONG。
常见疑问
队列和直接加锁有什么区别?
加锁只能防止并发,但请求会阻塞在客户端;队列可以缓存大量请求,客户端提交后立即返回,体验更好。
多张GPU怎么处理?
可以启动多个Worker,每个Worker绑定不同的GPU,用CUDA_VISIBLE_DEVICES=0和CUDA_VISIBLE_DEVICES=1分别指定,队列名可以相同或分开。
任务结果怎么获取?
通过job.result读取,或监听job.meta写入自定义状态到Redis。生产环境建议把结果存数据库,避免Redis内存膨胀。
按照以上步骤,你可以在半小时内搭好一个稳定的算力任务队列,让AI推理请求有序排队执行。
后续可根据任务量调整Worker数量或引入优先级队列。