I am trying to set up a simple docker-compose file which includes a rabbitmq container and a simple express server which connects to rabbitmq. I'm getting the following error when trying to connect to rabbitmq from my express application:
Error: connect ECONNREFUSED 172.19.0.2:5672
at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1247:16) {
errno: -111,
code: 'ECONNREFUSED',
syscall: 'connect',
address: '172.19.0.2',
port: 5672
}
I checked the IP-adress of the docker network manually to verify that 172.19.0.2 is indeed the rabbitmq process, which it is.
Here is my docker-compose:
version: '3'
services:
rabbitmq:
image: rabbitmq:3-management-alpine
container_name: 'rabbitmq'
environment:
- RABBITMQ_DEFAULT_USER=admin
- RABBITMQ_DEFAULT_PASS=pass
ports:
- 5672:5672
- 15672:15672
producerexpress:
build: ./service1
container_name: producerexpress
ports:
- 3000:3000
environment:
- PORT=3000
depends_on:
- rabbitmq
and the express app and its docker file:
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
const amqp = require('amqplib');
const amqpUrl = process.env.AMQP_URL || 'amqp://admin:[email protected]:5672';
let channel;
let connection;
connect();
async function connect(){
try{
connection = await amqp.connect(amqpUrl);
channel = await connection.createChannel();
await channel.assertQueue('chatExchange', {durable: false});
} catch (err) {
console.log(err);
}
}
function sendRabbitMessage(msg) {
channel.sendToQueue('chatExchange', Buffer.from(msg));
}
app.get('/', (req, res) => {
let msg = 'Triggered by get request';
sendRabbitMessage(msg);
res.send('Sent rabbitmq message!');
});
app.listen(port, () => {
console.log(`Server started on port ${port}`);
} );
FROM node:16
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
ENV PORT=3000
ENV AMQP_URL=amqp://admin:[email protected]:5672
EXPOSE 3000
CMD ["npm", "start"]
This is my first time using docker compose and all the fixes I found on here seem to suggest I did everything correctly. What am I missing?
CodePudding user response:
TL;DR
The depends_on
guarantes the order in which the services will start up, but that doesn't guarante anything for the processes they inititate.
In these cases, you should expand the depends_on
statement in order to take into account the health status
of the process of interest
Firstly, you should avoid making the communication of cointainers depend on their IP address but instead rely on their service names, since you are using docker compose.
Meaning, instead of amqp://admin:[email protected]:5672
You should use amqp://admin:pass@rabbitmq:5672
Moving on to the core issue, your producerexpress
relies on to rabbitmq
in order to function.
As a result, you added the depends_on
statement to producerexpress
to resolve this. But this is not enough, quotting from https://docs.docker.com/compose/startup-order/
You can control the order of service startup and shutdown with the depends_on option. Compose always starts and stops containers in dependency order, .... However,
for startup Compose does not wait until a container is “ready” (whatever that means for your particular application) - only until it’s running
.
As a result, you need to add a health check
in order to guarantee that the rabbitmq process has started
successfully, not just the container
.
In order to achieve that you should alter your compose
file
version: '3'
services:
rabbitmq:
build: ./rabbitmq
container_name: 'rabbitmq'
environment:
- RABBITMQ_DEFAULT_USER=admin
- RABBITMQ_DEFAULT_PASS=pass
ports:
- 5672:5672
- 15672:15672
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:15672"]
interval: 30s
timeout: 10s
retries: 5
producerexpress:
build: ./service1
restart: on-failure
container_name: producerexpress
ports:
- 3000:3000
environment:
- PORT=3000
depends_on:
rabbitmq:
condition: service_healthy
In order to make the healthcheck, we need the curl
package in the rabbitmq image, so add the following Dockerfile
FROM rabbitmq:3-management-alpine
RUN apk update
RUN apk add curl
EXPOSE 5672 15672
Finally, to make this change compatible create the following directory structure
./docker-compose.yml
./rabbitmq/
--- Dockerfile
./service1/
--- Dockerfile