I am trying to test an API that sends long-running jobs to a queue processed by Celery workers.. I am using RabbitMQ running in a Docker container as the message queue. However, when sending a message to the queue I get the following error: Error: [Errno 111] Connection refused
Steps to reproduce:
- Start RabbitMQ container:
docker run -d -p 5672:5672 rabbitmq
- Start Celery server:
celery -A celery worker --loglevel=INFO
- Build docker image:
docker build -t fastapi .
- Run container
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
2
Answers
Update tasks.py
You can’t connect to rabbitmq on
localhost
; it’s not running in the same container as your Python app. Since you’ve exposed rabbit on your host, you can connect to it using the address of your host. One way of doing that is starting the app container like this:And then modify your code like this:
With that code in place, let’s re-run your example:
There’s no reason to publish the rabbitmq ports on your host if you only need to access it from within a container. When building an application with multiple containers, using something like docker-compose can make your life easier.
If you used the following
docker-compose.yaml
:And modified your code to connect to
rabbitmq
:You could then run
docker-compose up
to bring up both containers. Your app would be exposed on host port8000
, but rabbitmq would only be available to your app container.Incidentally, rather than hardcoding the broker uri in your code, you might want to get that from an environment variable instead:
That allows you to use different connection strings without needing to rebuild your image every time. We’d need to modify the
docker-compose.yaml
to include the appropriate variable: