Docker运行RabbitMQ时Celery报Connection refused连接拒绝错误
问题现象
测试将长耗时任务投递至Celery worker处理队列的API,使用Docker容器部署的RabbitMQ作为消息队列,发送消息时抛出报错:Error: [Errno 111] Connection refused
复现步骤
- 启动RabbitMQ容器:
docker run -d -p 5672:5672 rabbitmq - 启动Celery服务:
celery -A celery worker --loglevel=INFO - 构建FastAPI镜像:
docker build -t fastapi . - 运行FastAPI容器:
docker run -it -p 8000:8000 fastapi
相关配置文件内容
Dockerfile
FROM python:3.9 WORKDIR / COPY . . RUN pip install --no-cache-dir --upgrade -r ./requirements.txt EXPOSE 8000 CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
requirements.txt
anyio==3.6.1 asgiref==3.5.2 celery==5.2.7 click==8.1.3 colorama==0.4.4 fastapi==0.78.0 h11==0.13.0 httptools==0.4.0 idna==3.3 pydantic==1.9.1 python-dotenv==0.20.0 PyYAML==6.0 sniffio==1.2.0 starlette==0.19.1 typing_extensions==4.2.0 uvicorn==0.17.6 watchgod==0.8.2 websockets==10.3
app.py
from fastapi import FastAPI import tasks @app.get("/{num}") async def root(num): tasks.update_db.delay(num) return {"success": True}
tasks.py
from celery import Celery import time celery = Celery('tasks', broker='amqp://') @celery.task(name='update_db') def update_db(num: int) -> None: time.sleep(30) return
故障原因
连接拒绝的核心诱因是Docker默认网络隔离规则,叠加两处配置错误:
tasks.py中配置的broker地址为amqp://,该地址默认解析为localhost:5672。FastAPI运行在独立Docker容器中,容器内的localhost指向容器自身的网络命名空间,既不代表宿主机,也不代表RabbitMQ所在容器,自然无法访问到RabbitMQ的5672端口。- Celery启动命令参数错误:
celery -A celery worker指定的应用入口为celery模块,但实际代码中Celery实例定义在tasks.py中,worker根本加载不到正确的broker配置和任务定义,就算网络通也无法正常消费任务。 - 官方默认RabbitMQ镜像启动后需要10-30秒完成插件加载、端口绑定初始化流程,刚执行完run命令立刻发起连接,也会因为端口还没开始监听触发连接拒绝。
排查解决步骤
按以下顺序操作即可修复问题:
- 先创建Docker自定义桥接网络,避免默认网络的DNS解析、访问限制问题:
docker network create celery-net - 启动RabbitMQ容器时指定固定容器名、加入自定义网络,启动后等待初始化完成:
docker run -d --name rabbitmq --network celery-net -p 5672:5672 rabbitmq
执行完命令后等待20-30秒,执行docker logs rabbitmq查看到Server startup complete字样的日志,说明RabbitMQ已经完成初始化,5672端口正常监听。 - 修改
tasks.py中的Celery broker配置,用RabbitMQ的容器名作为连接地址(同一自定义网络下Docker内置DNS会自动把容器名解析到对应容器IP):
from celery import Celery import time # 默认RabbitMQ的账号密码是guest/guest,地址直接写容器名rabbitmq celery = Celery('tasks', broker='amqp://guest:guest@rabbitmq:5672//') @celery.task(name='update_db') def update_db(num: int) -> None: time.sleep(30) return
- 修正Celery启动参数,指定正确的应用实例路径:如果Celery worker跑在宿主机上,需要单独把broker地址改成
amqp://guest:guest@127.0.0.1:5672//(宿主机可以直接访问映射到本地的5672端口),启动命令改为celery -A tasks.celery worker --loglevel=INFO;如果Celery也容器化部署,直接把Celery容器加入celery-net网络,用上面的容器名broker配置即可。 - 重新构建FastAPI镜像后,启动容器时加入自定义网络:
docker run -it --network celery-net -p 8000:8000 fastapi
避坑提示:不要在FastAPI容器内用
localhost/127.0.0.1尝试连接RabbitMQ,容器网络隔离下这个地址永远指向FastAPI容器自身,不可能连通。如果不想用自定义网络,需要通过宿主机中转连接,Windows/Mac环境可以用host.docker.internal作为宿主机访问地址,Linux环境启动容器时额外加--add-host=host.docker.internal:host-gateway参数,再把broker地址对应修改即可,该方案稳定性弱于自定义网络方案,优先选前者。
内容的提问来源于stack exchange,提问作者JSON_Derulo
相关产品推荐
相关产品推荐

