Production-grade Kafka infrastructure for multi-channel notifications (FCM, Slack, Email) and centralized logging with Elasticsearch indexing.
- Overview
- Architecture
- Kafka Infrastructure
- Services
- Quick Start
- Configuration
- Usage
- Security
- Monitoring
- Troubleshooting
This repository provides a complete, production-ready Kafka cluster with consumer services for:
-
Multi-Channel Notifications
- π± Firebase Cloud Messaging (FCM) - Push notifications
- π¬ Slack - Team alerts and notifications
- π§ Email - SMTP-based notifications
-
Centralized Logging
- Consume logs from Kafka
- Index to Elasticsearch
- Searchable log aggregation
β
Secure Kafka Cluster - 3 brokers + 2 Zookeeper nodes with SASL_SSL
β
Multi-Channel Notifications - FCM, Slack, Email in one service
β
Centralized Logging - Kafka β Elasticsearch pipeline
β
Prometheus Monitoring - JMX exporters for all Kafka brokers
β
Automated Certificate Management - Scripts for SSL/TLS setup
β
Production Tested - Used in cryptocurrency exchange
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β KAFKA CLUSTER β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β Zookeeper-1 β β Zookeeper-2 β β β β
β β Port: 22181 β β Port: 32181 β β β β
β ββββββββββββββββ ββββββββββββββββ β β β
β β β β β β
β ββββββββ΄ββββββββββββββββββ΄ββββββββ β β β
β β β β β β
β β βββββββββββ βββββββββββ ββββββββββ β β
β β β Broker-1β β Broker-2β β Broker-3β β β
β β β :2095 β β :2096 β β :2097 β β β
β β β JMX:9401β β JMX:9402β β JMX:9403β β β
β β βββββββββββ βββββββββββ ββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββ β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
β Topics:
β β’ push-notification-user
β β’ push-notification-all
β β’ slack-notifications
β β’ email-notifications
β β’ logging
β
βββββββββββββββββββ΄ββββββββββββββββββ
β β
βββββββββΌβββββββββ βββββββββββββΌββββββββββββ
β NOTIFICATION β β LOGGING SERVICE β
β SERVICE β β β
ββββββββββββββββββ€ βββββββββββββββββββββββββ€
β β’ FCM Handler β β β’ Kafka Consumer β
β β’ Slack β β β’ ES Indexing β
β β’ Email (SMTP) β β β’ Log Aggregation β
β β’ Redis Cache β β β
β β’ MongoDB β β β
ββββββββββββββββββ βββββββββββββββββββββββββ
β β
βΌ βΌ
ββββββββββββ βββββββββββββββββββ
β Mobile β β Elasticsearch β
β Devices β β (External) β
ββββββββββββ€ βββββββββββββββββββ
β Slack β
ββββββββββββ€
β Email β
ββββββββββββ
| Component | Count | Purpose |
|---|---|---|
| Zookeeper | 2 nodes | Cluster coordination |
| Kafka Brokers | 3 brokers | Message streaming |
| JMX Exporters | 3 (1 per broker) | Prometheus metrics |
| Service | Port | Description |
|---|---|---|
| Zookeeper-1 | 22181 | Client connections |
| Zookeeper-2 | 32181 | Client connections |
| Kafka Broker-1 (External) | 2095 | SASL_SSL external access |
| Kafka Broker-2 (External) | 2096 | SASL_SSL external access |
| Kafka Broker-3 (External) | 2097 | SASL_SSL external access |
| Kafka Broker-1 (Internal) | 19093 | Inter-broker communication |
| Kafka Broker-2 (Internal) | 29093 | Inter-broker communication |
| Kafka Broker-3 (Internal) | 39093 | Inter-broker communication |
| JMX Exporter-1 | 9401 | Prometheus metrics |
| JMX Exporter-2 | 9402 | Prometheus metrics |
| JMX Exporter-3 | 9403 | Prometheus metrics |
Authentication: SASL/PLAIN
Encryption: SSL/TLS (SASL_SSL)
Client Auth: Required (mutual TLS)
- β SASL authentication for clients
- β SSL/TLS encryption for data in transit
- β Mutual TLS (client certificates required)
- β Zookeeper SASL authentication
- β Separate credentials for brokers and clients
| Topic Name | Purpose | Consumers |
|---|---|---|
push-notification-user |
Single user FCM notifications | push-notifications |
push-notification-all |
Broadcast FCM notifications | push-notifications |
slack-notifications |
Slack messages and alerts | push-notifications |
email-notifications |
Email delivery | push-notifications |
logging |
Application logs | logging-service |
# Retention Policy
KAFKA_LOG_RETENTION_BYTES: 5GB per broker
# Security
KAFKA_SASL_MECHANISM_INTER_BROKER_PROTOCOL: PLAIN
KAFKA_SSL_CLIENT_AUTH: required
KAFKA_INTER_BROKER_LISTENER_NAME: SASL_SSL
# Listeners
SASL_SSL:// # Secure external access
LOCAL:// # Plaintext for localhost (debugging)Location: push-notifications/
Kafka consumer that handles multiple notification channels:
-
FCM (Firebase Cloud Messaging)
- Mobile push notifications (iOS + Android)
- Desktop notifications
- Batch processing (500 devices per request)
- User preference checking via Redis
-
Slack
- Channel notifications
- Alert messages with severity levels
- Formatted attachments with fields
- Webhook integration
-
Email (SMTP)
- Single and bulk email delivery
- HTML templated emails
- Attachment support
- Template system (welcome, alert, verify, etc.)
- MongoDB - Store FCM tokens and user data
- Redis - Cache user preferences and FCM tokens
- Firebase Admin SDK - Send FCM notifications
- Nodemailer - SMTP email delivery
- Axios - Slack webhook calls
push-notifications/
βββ index.js # Main consumer with topic routing
βββ config/
β βββ vars.js # Environment configuration
β βββ logger.js # Winston logger
β βββ redis.js # Redis client
βββ utils/
β βββ firebase.js # FCM notification handlers
β βββ slack.js # Slack notification handlers (NEW)
β βββ email.js # Email handlers (NEW)
β βββ redis.js # Redis operations
β βββ kafka.js # Kafka consumer setup
βββ package.json
Location: logging/
Kafka consumer that indexes logs to Elasticsearch:
- Consumes from
loggingtopic - Validates and parses JSON logs
- Indexes to Elasticsearch with daily indices
- Falls back to file logging if ES unavailable
- Elasticsearch - Log indexing (external)
- KafkaJS - Kafka consumer
logging/
βββ index.js # Main consumer
βββ config/
β βββ vars.js # Environment configuration
β βββ logger.js # Winston logger
βββ utils/
β βββ elastic.js # Elasticsearch operations
β βββ kafka.js # Kafka consumer setup
βββ package.json
- Docker & Docker Compose
- Node.js 18+ (for services)
- External dependencies:
- MongoDB (for push-notifications)
- Redis (for push-notifications)
- Elasticsearch (for logging)
# Clone repository
git clone https://github.com/your-org/notification-logging-service.git
cd notification-logging-service
# Generate certificates and passwords
cd secrets
cp .env-sample .env
# Edit .env with your configuration
nano .env
# Generate SSL certificates
./create-certs.sh
# Generate JAAS configuration files
./create-pass.sh
cd ..# Set external host (your server IP/domain)
export HOST=your-server.com
# Start Kafka cluster
docker-compose up -d
# Verify all containers are running
docker-compose ps
# Check Zookeeper
docker-compose logs zookeeper-1
# Check Kafka brokers
docker-compose logs kafka-ssl-1# Check broker health
docker exec -it $(docker ps -qf "name=kafka-ssl-1") kafka-broker-api-versions \
--bootstrap-server localhost:19092
# List topics (from local access)
docker exec -it $(docker ps -qf "name=kafka-ssl-1") kafka-topics \
--bootstrap-server localhost:19092 --listcd push-notifications
# Copy environment template
cp .env.example .env
# Edit with your credentials
nano .env
# Install dependencies
npm install
# Start service
npm startcd logging
# Copy environment template
cp .env.example .env
# Edit with your Elasticsearch credentials
nano .env
# Install dependencies
npm install
# Start service
npm start# From outside Docker (using SSL client)
# You'll need kafka-topics command with proper SSL config
# Or from inside a Kafka container
docker exec -it $(docker ps -qf "name=kafka-ssl-1") kafka-topics \
--bootstrap-server localhost:19092 \
--create --topic push-notification-user --partitions 3 --replication-factor 2
docker exec -it $(docker ps -qf "name=kafka-ssl-1") kafka-topics \
--bootstrap-server localhost:19092 \
--create --topic push-notification-all --partitions 3 --replication-factor 2
docker exec -it $(docker ps -qf "name=kafka-ssl-1") kafka-topics \
--bootstrap-server localhost:19092 \
--create --topic slack-notifications --partitions 3 --replication-factor 2
docker exec -it $(docker ps -qf "name=kafka-ssl-1") kafka-topics \
--bootstrap-server localhost:19092 \
--create --topic email-notifications --partitions 3 --replication-factor 2
docker exec -it $(docker ps -qf "name=kafka-ssl-1") kafka-topics \
--bootstrap-server localhost:19092 \
--create --topic logging --partitions 3 --replication-factor 2File: secrets/.env
# Certificate name
CANAME=CA
# Keystore/Truststore password
PASS=your-secure-password
# External hostname (for advertised listeners)
HOST=your-server.com
# Broker credentials
KS_BROKER_USERNAME=broker
KS_BROKER_PASS=broker-password
# Client credentials
KS_CLIENT_USERNAME=client
KS_CLIENT_PASS=client-password
# Zookeeper credentials
C_CLIENT_USER=kafka
C_CLIENT_PASS=kafka-secretFile: push-notifications/.env
Key configurations:
# Kafka
KAFKA_BROKERS=your-server.com:2095,your-server.com:2096,your-server.com:2097
SASL_USER=client
SASL_PASS=client-password
# MongoDB
MONGO_URI=mongodb://user:pass@host:port/database?authSource=admin
# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=redis-password
# Firebase (FCM)
FIREBASE_PROJECT_ID=your-project
FIREBASE_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n
FIREBASE_CLIENT_EMAIL=firebase-adminsdk@your-project.iam.gserviceaccount.com
# Slack
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/YOUR/WEBHOOK/URL
SLACK_ALERTS_CHANNEL=#alerts
# Email (SMTP)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-passwordFile: logging/.env
# Kafka
KAFKA_BROKERS=your-server.com:2095,your-server.com:2096,your-server.com:2097
SASL_USER=client
SASL_PASS=client-password
# Elasticsearch
ELASTIC_SEARCH_NODE=https://your-es-host:9200
ELASTIC_USERNAME=elastic
ELASTIC_PASSWORD=your-elastic-password
ELASTIC_CA_PATH=./elastic-ca.crtPublish to: push-notification-user
// Message format
{
"userid": "user123",
"title": "New Message",
"body": "You have a new notification",
"data": {
"type": "message",
"id": "msg_456"
}
}Publish to: push-notification-all
{
"title": "System Announcement",
"body": "Scheduled maintenance at 2 AM UTC",
"data": {
"type": "announcement",
"priority": "high"
}
}Publish to: slack-notifications
// Simple notification
{
"channel": "#general",
"title": "Deployment Complete",
"body": "Version 2.5.0 deployed successfully",
"color": "good"
}
// With fields
{
"channel": "#alerts",
"title": "High CPU Usage",
"body": "Server load exceeding threshold",
"color": "warning",
"fields": [
{ "title": "Server", "value": "app-server-01", "short": true },
{ "title": "CPU", "value": "85%", "short": true }
]
}Publish to: slack-notifications
{
"type": "alert", // Triggers alert handler
"severity": "critical",
"service": "API Gateway",
"message": "Service unavailable - 503 errors",
"metadata": {
"error_count": 150,
"region": "us-east-1",
"duration": "5 minutes"
}
}Publish to: email-notifications
// Simple text email
{
"to": "user@example.com",
"subject": "Account Verification",
"body": "Please verify your email address",
"html": false
}
// HTML email
{
"to": "user@example.com",
"subject": "Welcome!",
"body": "<h1>Welcome to LCX</h1><p>Thank you for signing up.</p>",
"html": true
}
// Bulk email
{
"type": "bulk",
"recipients": ["user1@example.com", "user2@example.com"],
"subject": "Newsletter",
"body": "<h1>Monthly Newsletter</h1>",
"html": true
}
// Templated email
{
"template": "welcome",
"to": "user@example.com",
"variables": {
"name": "John Doe",
"appName": "LCX Exchange"
}
}Publish to: logging
{
"timestamp": "2024-12-02T10:30:00Z",
"level": "INFO",
"service": "api-gateway",
"message": "Request processed successfully",
"userId": "user123",
"requestId": "req_abc123",
"duration": 45,
"metadata": {
"endpoint": "/api/v1/trades",
"method": "GET",
"statusCode": 200
}
}const { Kafka } = require('kafkajs');
const fs = require('fs');
const kafka = new Kafka({
clientId: 'my-producer',
brokers: ['your-server.com:2095', 'your-server.com:2096', 'your-server.com:2097'],
ssl: {
rejectUnauthorized: true,
ca: [fs.readFileSync('./CA.crt', 'utf-8')],
key: fs.readFileSync('./client.key', 'utf-8'),
cert: fs.readFileSync('./client.crt', 'utf-8')
},
sasl: {
mechanism: 'plain',
username: 'client',
password: 'client-password'
}
});
const producer = kafka.producer();
async function sendNotification() {
await producer.connect();
// Send FCM notification
await producer.send({
topic: 'push-notification-user',
messages: [{
value: JSON.stringify({
userid: 'user123',
title: 'Test Notification',
body: 'This is a test message'
})
}]
});
// Send Slack alert
await producer.send({
topic: 'slack-notifications',
messages: [{
value: JSON.stringify({
channel: '#alerts',
title: 'Test Alert',
body: 'Testing Slack integration',
color: 'good'
})
}]
});
await producer.disconnect();
}
sendNotification();Generate certificates:
cd secrets
./create-certs.shGenerated files:
CA.crt/CA.key- Certificate Authoritykafka.broker1.keystore.jks- Broker 1 keystorekafka.broker1.truststore.jks- Broker 1 truststorekafka.broker2.keystore.jks- Broker 2 keystorekafka.broker2.truststore.jks- Broker 2 truststorekafka.broker3.keystore.jks- Broker 3 keystorekafka.broker3.truststore.jks- Broker 3 truststore*_keystore_creds- Keystore passwords*_truststore_creds- Truststore passwords
Generate JAAS files:
cd secrets
./create-pass.shGenerated files:
broker_jaas.conf- Broker authentication configzookeeper_jaas.conf- Zookeeper authentication configclient_jaas.conf- Client authentication config (optional)
- β
Change default passwords in
secrets/.env - β Rotate certificates every 365 days
- β Use strong passwords (20+ characters)
- β Restrict firewall - Only expose necessary ports
- β Monitor access logs - Track failed authentication attempts
- β Enable encryption at rest - For Kafka log directories
- β Separate credentials - Different passwords for brokers vs clients
Required open ports:
2095-2097- Kafka external access (SASL_SSL)9401-9403- Prometheus metrics (optional, restrict to monitoring server)
Should be blocked:
19092-39092- Kafka internal listeners19093-39093- Kafka inter-broker communication22181, 32181- Zookeeper clients22888-23888, 32888-33888- Zookeeper internal
Each Kafka broker exposes Prometheus metrics:
- Broker 1: http://your-server:9401/metrics
- Broker 2: http://your-server:9402/metrics
- Broker 3: http://your-server:9403/metrics
Broker Metrics:
kafka_server_brokertopicmetrics_messagesinpersec- Message ratekafka_server_brokertopicmetrics_bytesinpersec- Throughputkafka_server_replicamanager_underreplicatedpartitions- Replication lagkafka_controller_kafkacontroller_activecontrollercount- Controller status
Consumer Metrics:
- Consumer lag (monitor in services)
- Processing rate
- Error rate
scrape_configs:
- job_name: 'kafka-broker-1'
static_configs:
- targets: ['your-server:9401']
- job_name: 'kafka-broker-2'
static_configs:
- targets: ['your-server:9402']
- job_name: 'kafka-broker-3'
static_configs:
- targets: ['your-server:9403']Both services use Winston logging:
Log levels: error, warn, info, debug
Log files:
push-notifications/logs/- Notification service logslogging/logs/- Logging service logs
Key logs to monitor:
- Failed notifications
- Kafka connection errors
- Elasticsearch indexing failures
- Redis connection issues
# Check Zookeeper first
docker-compose logs zookeeper-1 zookeeper-2
# Check broker logs
docker-compose logs kafka-ssl-1
# Common issues:
# 1. Certificate files missing
cd secrets && ls -la *.jks *.crt *.key
# 2. JAAS configuration missing
ls -la *_jaas.conf
# 3. Ports already in use
netstat -tulpn | grep -E "2095|2096|2097"# Verify HOST variable is set correctly
echo $HOST
# Test connectivity
telnet your-server.com 2095
# Check advertised listeners in broker logs
docker-compose logs kafka-ssl-1 | grep ADVERTISED
# Common issue: HOST not matching actual server address
# Fix: Update secrets/.env and restart
export HOST=correct-server.com
docker-compose down && docker-compose up -d# Verify CA certificate
openssl x509 -in secrets/CA.crt -text -noout
# Check keystore contents
keytool -list -v -keystore secrets/kafka.broker1.keystore.jks -storepass $(cat secrets/broker1_keystore_creds)
# Regenerate certificates
cd secrets
./delete-certs.sh
./create-certs.sh
cd .. && docker-compose restart# Check credentials
cat push-notifications/.env | grep SASL
# Verify broker addresses
cat push-notifications/.env | grep BROKERS
# Test from container
npm start 2>&1 | grep -i kafka# Verify Firebase credentials
cat push-notifications/.env | grep FIREBASE
# Check Redis connection
redis-cli -h localhost -p 6379 -a your-password PING
# Check MongoDB connection
mongosh "mongodb://user:pass@host:port/database?authSource=admin"
# Check logs
tail -f push-notifications/logs/combined.log# Test webhook URL
curl -X POST YOUR_SLACK_WEBHOOK_URL \
-H 'Content-Type: application/json' \
-d '{"text":"Test message"}'
# Check environment variable
cat push-notifications/.env | grep SLACK_WEBHOOK_URL
# Verify in logs
tail -f push-notifications/logs/combined.log | grep -i slack# Test SMTP connection
telnet smtp.gmail.com 587
# For Gmail: Verify App Password is used (not account password)
# Enable 2FA -> Generate App Password
# Check credentials
cat push-notifications/.env | grep SMTP
# Test with nodemailer
node -e "require('./push-notifications/utils/email').verifyConnection()"# Check Elasticsearch connection
curl -k -u elastic:password https://your-es-host:9200/_cluster/health
# Verify index exists
curl -k -u elastic:password https://your-es-host:9200/_cat/indices?v | grep logging
# Check service logs
tail -f logging/logs/combined.log
# Test manual indexing
node -e "require('./logging/utils/elastic').checkConnection()"# Check partition assignment
docker exec -it $(docker ps -qf "name=kafka-ssl-1") kafka-consumer-groups \
--bootstrap-server localhost:19092 \
--describe --group logging-consumer
# Increase consumers (if needed)
# Start multiple instances of logging service
cd logging && npm start &
cd logging && npm start &# Check all services status
docker-compose ps
systemctl status notification-service # If using systemd
systemctl status logging-service
# View real-time logs
docker-compose logs -f --tail=100
# Check disk space (Kafka logs can grow large)
df -h
du -sh /var/lib/docker/volumes/
# Check memory usage
free -h
docker stats
# Network connectivity
netstat -tulpn | grep -E "2095|2096|2097|9401|9402|9403"docker-compose.yml:
environment:
# Increase retention
KAFKA_LOG_RETENTION_BYTES: 10737418240 # 10GB
# Tune flush intervals
KAFKA_LOG_FLUSH_INTERVAL_MESSAGES: 10000
KAFKA_LOG_FLUSH_INTERVAL_MS: 1000
# Increase threads
KAFKA_NUM_NETWORK_THREADS: 8
KAFKA_NUM_IO_THREADS: 16Notification Service:
// Increase batch size
await consumer.run({
eachBatch: async ({ batch }) => {
// Process in batches instead of eachMessage
}
});Logging Service:
// Bulk Elasticsearch indexing
const bulkOps = messages.map(msg => [
{ index: { _index: 'logging-2024-12-02' } },
msg
]).flat();
await client.bulk({ operations: bulkOps });- Change all default passwords
- Generate production SSL certificates
- Configure proper hostnames in
.env - Set up monitoring (Prometheus + Grafana)
- Configure log rotation
- Set up automated backups for Kafka data
- Configure firewall rules
- Set up systemd services for Node.js apps
- Configure resource limits (CPU, memory)
- Test failover scenarios
- Document runbooks
- Set up alerting
Notification Service: /etc/systemd/system/notification-service.service
[Unit]
Description=Notification Service
After=network.target docker.service
[Service]
Type=simple
User=app-user
WorkingDirectory=/opt/notification-service/push-notifications
Environment=NODE_ENV=production
ExecStart=/usr/bin/npm start
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.targetLogging Service: /etc/systemd/system/logging-service.service
[Unit]
Description=Logging Service
After=network.target docker.service
[Service]
Type=simple
User=app-user
WorkingDirectory=/opt/notification-service/logging
Environment=NODE_ENV=production
ExecStart=/usr/bin/npm start
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.targetUse separate compose file for production:
docker-compose -f docker-compose.prod.yml up -dMIT License
DevSecOps Team - LCX
- Fork the repository
- Create feature branch (
git checkout -b feature/amazing) - Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing) - Open Pull Request
- Issues: GitHub Issues
- Documentation: See individual service READMEs
- Kafka Docs: https://kafka.apache.org/documentation/
Production-grade Kafka infrastructure for multi-channel notifications ππ