meldestelle/docker-compose.prod.yml
stefan e7b18da45d refactor: Migrate from monolithic to modular architecture
1. **Docker-Compose für Entwicklung optimieren**
2. **Umgebungsvariablen für lokale Entwicklung**
3. **Service-Abhängigkeiten**
4. **Docker-Compose für Produktion**
5. **Dokumentation**
2025-07-24 14:20:48 +02:00

441 lines
13 KiB
YAML

version: '3.8'
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
# Production security settings
POSTGRES_INITDB_ARGS: "--auth-host=scram-sha-256 --auth-local=scram-sha-256"
ports:
# Only expose internally, not to host
- "5432"
volumes:
- postgres-data:/var/lib/postgresql/data
- ./docker/services/postgres:/docker-entrypoint-initdb.d
# TLS certificates for PostgreSQL
- ./config/ssl/postgres:/var/lib/postgresql/ssl:ro
networks:
- meldestelle-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 20s
restart: unless-stopped
# Security: Run as non-root user
user: postgres
# Resource limits
deploy:
resources:
limits:
memory: 1G
cpus: '0.5'
reservations:
memory: 512M
cpus: '0.25'
redis:
image: redis:7-alpine
environment:
# Redis with authentication
REDIS_PASSWORD: ${REDIS_PASSWORD}
ports:
# Only expose internally
- "6379"
volumes:
- redis-data:/data
- ./config/redis/redis.conf:/usr/local/etc/redis/redis.conf:ro
# TLS certificates for Redis
- ./config/ssl/redis:/tls:ro
command: >
redis-server /usr/local/etc/redis/redis.conf
--requirepass ${REDIS_PASSWORD}
--appendonly yes
--appendfsync everysec
--save 900 1
--save 300 10
--save 60 10000
--maxmemory 256mb
--maxmemory-policy allkeys-lru
--tcp-keepalive 300
--timeout 0
--tcp-backlog 511
--databases 16
--stop-writes-on-bgsave-error yes
--rdbcompression yes
--rdbchecksum yes
--dir /data
networks:
- meldestelle-network
healthcheck:
test: ["CMD", "redis-cli", "--no-auth-warning", "-a", "${REDIS_PASSWORD}", "ping"]
interval: 10s
timeout: 5s
retries: 3
start_period: 10s
restart: unless-stopped
# Security: Run as non-root user
user: redis
# Resource limits
deploy:
resources:
limits:
memory: 512M
cpus: '0.25'
reservations:
memory: 256M
cpus: '0.1'
keycloak:
image: quay.io/keycloak/keycloak:23.0
environment:
KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN}
KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
KC_DB: ${KC_DB}
KC_DB_URL: ${KC_DB_URL}
KC_DB_USERNAME: ${KC_DB_USERNAME}
KC_DB_PASSWORD: ${KC_DB_PASSWORD}
# Production settings
KC_HOSTNAME: ${KC_HOSTNAME}
KC_HOSTNAME_STRICT: true
KC_HOSTNAME_STRICT_HTTPS: true
KC_HTTP_ENABLED: false
KC_HTTPS_PORT: 8443
KC_HTTPS_CERTIFICATE_FILE: /opt/keycloak/conf/server.crt.pem
KC_HTTPS_CERTIFICATE_KEY_FILE: /opt/keycloak/conf/server.key.pem
KC_PROXY: edge
KC_LOG_LEVEL: WARN
KC_METRICS_ENABLED: true
KC_HEALTH_ENABLED: true
# Security headers
KC_HTTP_RELATIVE_PATH: /auth
ports:
# HTTPS only in production
- "8443:8443"
depends_on:
postgres:
condition: service_healthy
volumes:
- ./docker/services/keycloak:/opt/keycloak/data/import
# TLS certificates
- ./config/ssl/keycloak:/opt/keycloak/conf:ro
command: start --import-realm --optimized
networks:
- meldestelle-network
healthcheck:
test: ["CMD", "curl", "--fail", "--insecure", "https://localhost:8443/auth/health/ready"]
interval: 10s
timeout: 5s
retries: 5
start_period: 60s
restart: unless-stopped
# Resource limits
deploy:
resources:
limits:
memory: 1G
cpus: '0.5'
reservations:
memory: 512M
cpus: '0.25'
zookeeper:
image: confluentinc/cp-zookeeper:7.5.0
environment:
ZOOKEEPER_CLIENT_PORT: ${ZOOKEEPER_CLIENT_PORT}
ZOOKEEPER_TICK_TIME: 2000
ZOOKEEPER_SYNC_LIMIT: 2
ZOOKEEPER_INIT_LIMIT: 5
ZOOKEEPER_MAX_CLIENT_CNXNS: 60
ZOOKEEPER_AUTOPURGE_SNAP_RETAIN_COUNT: 3
ZOOKEEPER_AUTOPURGE_PURGE_INTERVAL: 24
# Security settings
ZOOKEEPER_AUTH_PROVIDER_SASL: org.apache.zookeeper.server.auth.SASLAuthenticationProvider
ZOOKEEPER_REQUIRE_CLIENT_AUTH_SCHEME: sasl
KAFKA_OPTS: "-Djava.security.auth.login.config=/etc/kafka/secrets/zookeeper_jaas.conf"
ports:
# Only expose internally
- "2181"
volumes:
- zookeeper-data:/var/lib/zookeeper/data
- zookeeper-logs:/var/lib/zookeeper/log
- ./config/kafka/secrets:/etc/kafka/secrets:ro
networks:
- meldestelle-network
healthcheck:
test: ["CMD", "nc", "-z", "localhost", "2181"]
interval: 10s
timeout: 5s
retries: 3
start_period: 10s
restart: unless-stopped
# Resource limits
deploy:
resources:
limits:
memory: 512M
cpus: '0.25'
reservations:
memory: 256M
cpus: '0.1'
kafka:
image: confluentinc/cp-kafka:7.5.0
depends_on:
zookeeper:
condition: service_healthy
ports:
# Only expose internally
- "9092"
- "9093"
environment:
KAFKA_BROKER_ID: ${KAFKA_BROKER_ID}
KAFKA_ZOOKEEPER_CONNECT: ${KAFKA_ZOOKEEPER_CONNECT}
# Production security settings with SASL/SSL
KAFKA_ADVERTISED_LISTENERS: SASL_SSL://kafka:9093,SASL_PLAINTEXT://kafka:9092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: SASL_SSL:SASL_SSL,SASL_PLAINTEXT:SASL_PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: SASL_SSL
KAFKA_SECURITY_INTER_BROKER_PROTOCOL: SASL_SSL
KAFKA_SASL_MECHANISM_INTER_BROKER_PROTOCOL: PLAIN
KAFKA_SASL_ENABLED_MECHANISMS: PLAIN
# SSL Configuration
KAFKA_SSL_KEYSTORE_FILENAME: kafka.server.keystore.jks
KAFKA_SSL_KEYSTORE_CREDENTIALS: kafka_keystore_creds
KAFKA_SSL_KEY_CREDENTIALS: kafka_ssl_key_creds
KAFKA_SSL_TRUSTSTORE_FILENAME: kafka.server.truststore.jks
KAFKA_SSL_TRUSTSTORE_CREDENTIALS: kafka_truststore_creds
KAFKA_SSL_CLIENT_AUTH: required
# Performance and reliability settings
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 3
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 3
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 2
KAFKA_DEFAULT_REPLICATION_FACTOR: 3
KAFKA_MIN_INSYNC_REPLICAS: 2
KAFKA_NUM_PARTITIONS: 3
KAFKA_LOG_RETENTION_HOURS: 168
KAFKA_LOG_SEGMENT_BYTES: 1073741824
KAFKA_LOG_RETENTION_CHECK_INTERVAL_MS: 300000
KAFKA_AUTO_CREATE_TOPICS_ENABLE: false
# JVM settings
KAFKA_HEAP_OPTS: "-Xmx512M -Xms512M"
KAFKA_JVM_PERFORMANCE_OPTS: "-server -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35"
# Security
KAFKA_OPTS: "-Djava.security.auth.login.config=/etc/kafka/secrets/kafka_jaas.conf"
volumes:
- kafka-data:/var/lib/kafka/data
- ./config/kafka/secrets:/etc/kafka/secrets:ro
networks:
- meldestelle-network
healthcheck:
test: ["CMD", "kafka-topics", "--bootstrap-server", "localhost:9092", "--list"]
interval: 10s
timeout: 5s
retries: 3
start_period: 30s
restart: unless-stopped
# Resource limits
deploy:
resources:
limits:
memory: 1G
cpus: '0.5'
reservations:
memory: 512M
cpus: '0.25'
zipkin:
image: openzipkin/zipkin:2
ports:
# Only expose internally
- "9411"
environment:
# Production settings
JAVA_OPTS: "-Xms256m -Xmx512m"
STORAGE_TYPE: elasticsearch
ES_HOSTS: http://elasticsearch:9200
networks:
- meldestelle-network
healthcheck:
test: ["CMD", "wget", "-q", "-O", "-", "http://localhost:9411/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 15s
restart: unless-stopped
# Resource limits
deploy:
resources:
limits:
memory: 512M
cpus: '0.25'
reservations:
memory: 256M
cpus: '0.1'
# Production monitoring services
prometheus:
image: prom/prometheus:latest
volumes:
- ./config/monitoring/prometheus.prod.yml:/etc/prometheus/prometheus.yml:ro
- prometheus-data:/prometheus
# TLS certificates
- ./config/ssl/prometheus:/etc/ssl/prometheus:ro
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=30d'
- '--storage.tsdb.retention.size=10GB'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
- '--web.enable-lifecycle'
- '--web.enable-admin-api'
- '--web.external-url=https://${PROMETHEUS_HOSTNAME}'
- '--web.config.file=/etc/ssl/prometheus/web.yml'
ports:
# Only expose internally
- "9090"
networks:
- meldestelle-network
healthcheck:
test: ["CMD", "wget", "-q", "-O", "-", "http://localhost:9090/-/healthy"]
interval: 10s
timeout: 5s
retries: 3
start_period: 15s
restart: unless-stopped
# Security: Run as non-root user
user: "65534:65534"
# Resource limits
deploy:
resources:
limits:
memory: 1G
cpus: '0.5'
reservations:
memory: 512M
cpus: '0.25'
grafana:
image: grafana/grafana:latest
volumes:
- ./config/monitoring/grafana/provisioning:/etc/grafana/provisioning:ro
- ./config/monitoring/grafana/dashboards:/var/lib/grafana/dashboards:ro
- grafana-data:/var/lib/grafana
# TLS certificates
- ./config/ssl/grafana:/etc/ssl/grafana:ro
environment:
# Security settings
- GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER}
- GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD}
- GF_USERS_ALLOW_SIGN_UP=false
- GF_USERS_ALLOW_ORG_CREATE=false
- GF_AUTH_ANONYMOUS_ENABLED=false
# HTTPS settings
- GF_SERVER_PROTOCOL=https
- GF_SERVER_CERT_FILE=/etc/ssl/grafana/server.crt
- GF_SERVER_CERT_KEY=/etc/ssl/grafana/server.key
- GF_SERVER_DOMAIN=${GRAFANA_HOSTNAME}
- GF_SERVER_ROOT_URL=https://${GRAFANA_HOSTNAME}
# Security headers
- GF_SECURITY_STRICT_TRANSPORT_SECURITY=true
- GF_SECURITY_STRICT_TRANSPORT_SECURITY_MAX_AGE_SECONDS=31536000
- GF_SECURITY_CONTENT_TYPE_PROTECTION=true
- GF_SECURITY_X_CONTENT_TYPE_OPTIONS=nosniff
- GF_SECURITY_X_XSS_PROTECTION=true
# Session settings
- GF_SESSION_PROVIDER=redis
- GF_SESSION_PROVIDER_CONFIG=addr=redis:6379,pool_size=100,db=2,password=${REDIS_PASSWORD}
- GF_SESSION_COOKIE_SECURE=true
- GF_SESSION_COOKIE_SAMESITE=strict
# Logging
- GF_LOG_MODE=console
- GF_LOG_LEVEL=warn
ports:
# HTTPS only
- "3443:3000"
networks:
- meldestelle-network
depends_on:
prometheus:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-q", "-O", "-", "--no-check-certificate", "https://localhost:3000/api/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 20s
restart: unless-stopped
# Security: Run as non-root user
user: "472:472"
# Resource limits
deploy:
resources:
limits:
memory: 512M
cpus: '0.25'
reservations:
memory: 256M
cpus: '0.1'
# Reverse proxy for production
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./config/nginx/nginx.prod.conf:/etc/nginx/nginx.conf:ro
- ./config/nginx/conf.d:/etc/nginx/conf.d:ro
- ./config/ssl/nginx:/etc/ssl/nginx:ro
- ./logs/nginx:/var/log/nginx
networks:
- meldestelle-network
depends_on:
- keycloak
- grafana
healthcheck:
test: ["CMD", "wget", "-q", "-O", "-", "http://localhost/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 10s
restart: unless-stopped
# Resource limits
deploy:
resources:
limits:
memory: 256M
cpus: '0.1'
reservations:
memory: 128M
cpus: '0.05'
volumes:
postgres-data:
driver: local
redis-data:
driver: local
kafka-data:
driver: local
zookeeper-data:
driver: local
zookeeper-logs:
driver: local
prometheus-data:
driver: local
grafana-data:
driver: local
networks:
meldestelle-network:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16