Docker Tutorial #8: Praxis – Node.js-Anwendung mit Docker
Projekt-Setup
Wir erstellen eine vollständige Node.js/Express-API mit MongoDB, Redis und Nginx.
Projektstruktur
meine-api/
├── docker-compose.yml
├── Dockerfile
├── .dockerignore
├── .env.example
├── package.json
├── src/
│ ├── server.js
│ ├── routes/
│ └── models/
└── nginx/
└── default.conf
Dockerfile für Node.js
# Multi-Stage Build für optimale Größe
# Build Stage
FROM node:18-alpine AS builder
WORKDIR /app
# Dependencies installieren
COPY package*.json ./
RUN npm ci --only=production
# Source Code kopieren
COPY . .
# Production Stage
FROM node:18-alpine
# Non-root User erstellen
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
WORKDIR /app
# Nur Production Dependencies und Code
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --chown=nodejs:nodejs ./src ./src
COPY --chown=nodejs:nodejs package*.json ./
# Als nodejs User ausführen
USER nodejs
EXPOSE 3000
ENV NODE_ENV=production
HEALTHCHECK --interval=30s --timeout=3s \
CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
CMD ["node", "src/server.js"]
.dockerignore
node_modules
npm-debug.log
.git
.gitignore
.env
.env.*
*.md
.vscode
.idea
coverage
.nyc_output
dist
.dockerignore
Dockerfile
docker-compose.yml
package.json
{
"name": "meine-api",
"version": "1.0.0",
"scripts": {
"start": "node src/server.js",
"dev": "nodemon src/server.js"
},
"dependencies": {
"express": "^4.18.2",
"mongoose": "^7.5.0",
"redis": "^4.6.7",
"dotenv": "^16.3.1"
},
"devDependencies": {
"nodemon": "^3.0.1"
}
}
server.js
const express = require('express');
const mongoose = require('mongoose');
const redis = require('redis');
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(express.json());
// MongoDB Connection
mongoose.connect(process.env.MONGODB_URI || 'mongodb://mongo:27017/meine-api', {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => console.log('MongoDB verbunden'))
.catch(err => console.error('MongoDB Fehler:', err));
// Redis Client
const redisClient = redis.createClient({
url: process.env.REDIS_URL || 'redis://redis:6379'
});
redisClient.connect()
.then(() => console.log('Redis verbunden'))
.catch(err => console.error('Redis Fehler:', err));
// Health Check
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
// Beispiel-Route mit Caching
app.get('/api/data', async (req, res) => {
try {
// Prüfe Cache
const cached = await redisClient.get('data');
if (cached) {
return res.json({
source: 'cache',
data: JSON.parse(cached)
});
}
// Daten von DB holen (Beispiel)
const data = { message: 'Hallo von der API!', timestamp: new Date() };
// In Cache speichern (10 Minuten)
await redisClient.setEx('data', 600, JSON.stringify(data));
res.json({
source: 'database',
data
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(PORT, '0.0.0.0', () => {
console.log(`Server läuft auf Port ${PORT}`);
});
docker-compose.yml
version: '3.8'
services:
# Node.js API
api:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
NODE_ENV: production
MONGODB_URI: mongodb://mongo:27017/meine-api
REDIS_URL: redis://redis:6379
volumes:
- ./src:/app/src # Nur für Development
depends_on:
mongo:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
networks:
- app-network
# MongoDB
mongo:
image: mongo:7.0
ports:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: geheim123
MONGO_INITDB_DATABASE: meine-api
volumes:
- mongo_data:/data/db
healthcheck:
test: echo 'db.runCommand("ping").ok' | mongosh localhost:27017/test --quiet
interval: 10s
timeout: 5s
retries: 3
restart: unless-stopped
networks:
- app-network
# Redis Cache
redis:
image: redis:alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: redis-server --appendonly yes
restart: unless-stopped
networks:
- app-network
# Nginx Reverse Proxy
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- api
restart: unless-stopped
networks:
- app-network
volumes:
mongo_data:
redis_data:
networks:
app-network:
driver: bridge
Nginx Konfiguration
nginx/default.conf:
upstream api_backend {
server api:3000;
}
server {
listen 80;
server_name localhost;
# Logging
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# API Proxy
location /api/ {
proxy_pass http://api_backend/api/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
# Health Check
location /health {
proxy_pass http://api_backend/health;
}
# Static Files (falls vorhanden)
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ =404;
}
}
Development vs. Production
docker-compose.override.yml (Development)
version: '3.8'
services:
api:
build:
context: .
target: builder
command: npm run dev
volumes:
- ./src:/app/src
- ./package.json:/app/package.json
- /app/node_modules
environment:
NODE_ENV: development
ports:
- "9229:9229" # Debug Port
docker-compose.prod.yml (Production)
version: '3.8'
services:
api:
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
replicas: 3
restart_policy:
condition: on-failure
max_attempts: 3
environment:
NODE_ENV: production
Starten
Development
# Alle Services starten
docker compose up -d
# Logs verfolgen
docker compose logs -f api
# In Container einsteigen
docker compose exec api sh
Production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Testing
# Health Check
curl http://localhost/health
# API testen
curl http://localhost/api/data
# MongoDB Check
docker compose exec mongo mongosh -u admin -p geheim123
# Redis Check
docker compose exec redis redis-cli PING
Monitoring und Logging
Logs anzeigen
# Alle Logs
docker compose logs
# Nur API
docker compose logs -f api
# Letzte 100 Zeilen
docker compose logs --tail=100 api
# Logs mit Zeitstempel
docker compose logs -t api
Ressourcen überwachen
# Live Monitoring
docker compose stats
# Details zu einem Service
docker compose top api
Backup und Restore
MongoDB Backup
# Backup erstellen
docker compose exec mongo mongodump \
--username admin \
--password geheim123 \
--out /data/backup
# Backup herunterladen
docker cp $(docker compose ps -q mongo):/data/backup ./mongo-backup
MongoDB Restore
# Backup hochladen
docker cp ./mongo-backup $(docker compose ps -q mongo):/data/
# Restore durchführen
docker compose exec mongo mongorestore \
--username admin \
--password geheim123 \
/data/mongo-backup
Skalierung
# API auf 3 Instanzen skalieren
docker compose up -d --scale api=3
# Load Balancing konfigurieren (nginx anpassen)
CI/CD Integration
.gitlab-ci.yml
stages:
- build
- test
- deploy
build:
stage: build
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
test:
stage: test
script:
- docker compose -f docker-compose.test.yml up --abort-on-container-exit
deploy:
stage: deploy
script:
- docker compose -f docker-compose.prod.yml pull
- docker compose -f docker-compose.prod.yml up -d
only:
- main
Troubleshooting
Container startet nicht
# Logs prüfen
docker compose logs api
# Interaktiv debuggen
docker compose run --rm api sh
Verbindungsprobleme
# Netzwerk prüfen
docker compose exec api ping mongo
docker compose exec api ping redis
# DNS auflösen
docker compose exec api nslookup mongo
Performance-Probleme
# Ressourcen-Verbrauch
docker compose stats
# MongoDB Slow Queries
docker compose exec mongo mongosh --eval "db.currentOp()"
Best Practices
- Multi-Stage Builds: Kleinere Images
- Non-Root User: Sicherheit
- Health Checks: Automatische Neustarts
- Volumes für Daten: Persistenz
- .dockerignore: Schnellere Builds
- Environment Variables: Konfiguration
- Logging: Strukturiertes Logging
- Monitoring: Ressourcen überwachen
Zusammenfassung
Sie haben gelernt:
- Node.js-App mit Docker containerisieren
- Multi-Container-Setup mit Compose
- Development vs. Production Config
- Monitoring und Logging
- Backup und Restore
- Skalierung

Author: Andreas Lang
Andreas Lang konzentriert sich seit zwei Jahrzehnten auf die Webentwicklung und Webdesign mit dem Schwerpunkt PHP, Laravel und Javascript und betreut seine Kunden mit Herz und Seele in allen Bereichen von Entwicklung, Design, Suchmaschinenoptimierung, IT-Recht, IT-Sicherheit etc.

