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**
This commit is contained in:
@@ -0,0 +1,440 @@
|
||||
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
|
||||
Reference in New Issue
Block a user