Introduction
Apache Kafka is a powerful event streaming platform, but setting it up in a Docker environment while ensuring seamless connectivity with an external Node.js service can be challenging. This article explores two key challenges:
- Connecting a Local Node.js Server as an External Host Machine with a Kafka Broker in Docker
- Understanding and Fixing Docker DNS Resolution Issues for Local Machine Communication
We'll walk through the problems faced, their root causes, and step-by-step solutions.
Setting Up Kafka in a Docker Container
We are using Bitnami's Kafka image, which supports KRaft mode (Kafka's built-in metadata management) without requiring ZooKeeper.
1. Create a Docker Network
To ensure seamless communication between services, create a dedicated network:
docker network create app-tier
2. Run Kafka in KRaft Mode
docker run -d --name kafka-server -p 9092:9092 --hostname kafka-server \
--network app-tier \
-e KAFKA_CFG_NODE_ID=0 \
-e KAFKA_CFG_PROCESS_ROLES=controller,broker \
-e KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093 \
-e KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT \
-e KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://192.168.1.10:9092 \
-e KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka-server:9093 \
-e KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER \
bitnami/kafka:latest
Understanding advertised.listeners
-
listeners=PLAINTEXT://:9092→ Kafka listens on all interfaces inside the container. -
advertised.listeners=PLAINTEXT://192.168.1.10:9092→ External clients connect using the host machine's IP.
This prevents Kafka from advertising its internal container IP, which would make it unreachable from the local machine.
Challenge 1: Connecting a Local Node.js Server to Docker Kafka
By default, a Docker container runs in an isolated network. This means localhost:9092 inside the container does not refer to the host machine’s localhost. If a Node.js service runs outside Docker, it cannot connect to Kafka using localhost:9092 unless Kafka explicitly advertises the host machine's IP.
Solution: Use the Host IP
Find your machine's IP address:
ip a | grep inet
Then, update Kafka’s advertised.listeners to point to this IP.
Challenge 2: Docker DNS Resolution and Communication
Even after setting the correct advertised.listeners, issues may arise if:
- Kafka and Node.js services are on different networks.
- Docker’s DNS resolution prevents local services from reaching Kafka.
Solution: Use Docker’s Built-in DNS
- Containers in the same network can resolve each other by name.
- Use
kafka-server:9092inside Docker. - Use
192.168.1.10:9092outside Docker.
Fixing Docker Name Resolution for Node.js
If your Node.js service runs inside a container, ensure it is in the same network:
docker network connect app-tier nodejs-container
Then, use:
const kafka = new Kafka({
clientId: 'cart.service',
brokers: ['localhost:9092'],
retry: { retries: 3 }
});
Final Working Node.js Kafka Integration
Kafka Producer & Consumer Code
import express from "express";
import { Kafka, Partitioners } from "kafkajs";
const app = express();
app.use(express.json());
const kafka = new Kafka({
clientId: 'cart.service',
brokers: ['localhost:9092'],
retry: { retries: 3 }
});
const producer = kafka.producer({ createPartitioner: Partitioners.DefaultPartitioner });
const consumer = kafka.consumer({ groupId: 'cart.service' });
const runConsumer = async () => {
await consumer.connect();
await consumer.subscribe({ topic: "cart.item.add" });
await consumer.run({
eachMessage: async ({ message }) => {
console.log("Received message:", message.value?.toString());
}
});
};
runConsumer().catch(console.error);
export const kafkaProducer = async (topic: string, message: Record<string, any>) => {
await producer.connect();
await producer.send({
topic,
messages: [{ value: JSON.stringify(message) }]
});
};
app.get("/health-check", (_, res) => res.send("OK"));
app.post("/cart", async (req, res) => {
const { productId, quantity } = req.body;
await kafkaProducer("cart.item.add", { productId, quantity });
res.send("OK");
});
app.listen(3000, () => console.log("Cart service is running on port 3000"));
app.on("close", async () => {
await consumer.disconnect();
await producer.disconnect();
});
Conclusion
By addressing Kafka’s networking challenges in Docker, we ensured seamless communication between a local Node.js service and a Kafka broker running in a container. Key takeaways:
✅ Use advertised.listeners to expose Kafka correctly
✅ Leverage Docker DNS for intra-container communication
✅ Ensure Kafka and Node.js services are in communication with the docker container kafka broker
Photo by Mélanie THESE on Unsplash
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.