INSIGHT-MVP/docker-compose.yml
Thomas Reitz 4d5d5bac88 fix: use wget-based healthcheck for Traefik
The `traefik healthcheck` CLI command doesn't reliably detect the
ping configuration. Using wget against the /ping endpoint instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 18:11:32 +01:00

304 lines
9.7 KiB
YAML

# ============================================================
# INSIGHT MVP - Docker Compose (Basis-Services)
# ============================================================
# Startet alle Kerndienste der INSIGHT-Plattform.
# Observability-Stack separat: docker-compose.observability.yml
#
# Nutzung:
# docker compose up -d
# docker compose logs -f core
# docker compose ps
# ============================================================
networks:
insight-web:
driver: bridge
name: insight-web
insight-db:
driver: bridge
name: insight-db
internal: true
insight-cache:
driver: bridge
name: insight-cache
internal: true
volumes:
postgres-data:
name: insight-postgres-data
redis-data:
name: insight-redis-data
step-ca-data:
name: insight-step-ca-data
traefik-certs:
name: insight-traefik-certs
services:
# --------------------------------------------------------
# Traefik - API Gateway / Reverse Proxy
# --------------------------------------------------------
traefik:
image: traefik:3
container_name: insight-traefik
restart: unless-stopped
command:
# API & Dashboard
- "--api.dashboard=true"
- "--api.insecure=true"
# Entrypoints (nur HTTP fuer Alpha/Dev mit IP-Zugang)
- "--entrypoints.web.address=:80"
# Docker Provider
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.network=insight-web"
# File Provider (fuer dynamische Konfiguration)
- "--providers.file.directory=/etc/traefik/dynamic"
- "--providers.file.watch=true"
# Logging
- "--log.level=INFO"
- "--accesslog=true"
- "--accesslog.format=json"
# Ping (fuer Healthcheck)
- "--ping=true"
# Metrics fuer Prometheus
- "--metrics.prometheus=true"
- "--metrics.prometheus.entryPoint=metrics"
- "--entrypoints.metrics.address=:8082"
ports:
- "80:80"
- "8080:8080" # Dashboard (nur intern)
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./config/traefik/dynamic:/etc/traefik/dynamic:ro
- traefik-certs:/certs
networks:
- insight-web
labels:
- "traefik.enable=true"
# Dashboard Route (nur intern, via Port 8080)
- "traefik.http.routers.dashboard.rule=Host(`172.20.10.59`) && PathPrefix(`/dashboard`)"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.entrypoints=web"
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/ping || exit 1"]
interval: 30s
timeout: 5s
retries: 3
# --------------------------------------------------------
# PostgreSQL - Datenbank
# --------------------------------------------------------
postgres:
image: postgres:16-alpine
container_name: insight-postgres
restart: unless-stopped
environment:
POSTGRES_USER: ${DB_USER:-insight}
POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD muss gesetzt sein}
POSTGRES_DB: ${DB_NAME:-platform_core}
# Performance-Tuning fuer 8GB RAM VM
POSTGRES_INITDB_ARGS: "--data-checksums"
volumes:
- postgres-data:/var/lib/postgresql/data
- ./config/postgres/init:/docker-entrypoint-initdb.d:ro
networks:
- insight-db
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-insight} -d ${DB_NAME:-platform_core}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
# Performance-Tuning via Command
command:
- "postgres"
- "-c"
- "shared_buffers=1GB"
- "-c"
- "effective_cache_size=4GB"
- "-c"
- "work_mem=16MB"
- "-c"
- "maintenance_work_mem=256MB"
- "-c"
- "max_connections=200"
- "-c"
- "log_min_duration_statement=500"
- "-c"
- "log_statement=ddl"
# --------------------------------------------------------
# PgBouncer - Connection Pooling
# --------------------------------------------------------
pgbouncer:
image: edoburu/pgbouncer:latest
container_name: insight-pgbouncer
restart: unless-stopped
environment:
DATABASE_URL: "postgres://${DB_USER:-insight}:${DB_PASSWORD}@postgres:5432/${DB_NAME:-platform_core}"
POOL_MODE: transaction
MAX_CLIENT_CONN: 500
DEFAULT_POOL_SIZE: 25
MIN_POOL_SIZE: 5
RESERVE_POOL_SIZE: 5
SERVER_RESET_QUERY: "DISCARD ALL"
SERVER_CHECK_DELAY: 30
SERVER_CHECK_QUERY: "SELECT 1"
AUTH_TYPE: scram-sha-256
networks:
- insight-db
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -p 5432"]
interval: 10s
timeout: 5s
retries: 3
# --------------------------------------------------------
# Redis - Cache, Sessions, Event Bus
# --------------------------------------------------------
redis:
image: redis:7-alpine
container_name: insight-redis
restart: unless-stopped
command: >
redis-server
--requirepass ${REDIS_PASSWORD:-}
--maxmemory 512mb
--maxmemory-policy allkeys-lru
--appendonly yes
--appendfsync everysec
--save 60 1000
--save 300 100
volumes:
- redis-data:/data
networks:
- insight-cache
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD:-}", "ping"]
interval: 10s
timeout: 5s
retries: 3
# --------------------------------------------------------
# step-ca - Interne Certificate Authority (mTLS)
# --------------------------------------------------------
step-ca:
image: smallstep/step-ca:latest
container_name: insight-step-ca
restart: unless-stopped
environment:
DOCKER_STEPCA_INIT_NAME: "INSIGHT Internal CA"
DOCKER_STEPCA_INIT_DNS_NAMES: "step-ca,localhost"
DOCKER_STEPCA_INIT_REMOTE_MANAGEMENT: "true"
DOCKER_STEPCA_INIT_ACME: "true"
volumes:
- step-ca-data:/home/step
networks:
- insight-web
- insight-db
- insight-cache
healthcheck:
test: ["CMD", "step", "ca", "health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
# --------------------------------------------------------
# Core-Service - NestJS Backend
# --------------------------------------------------------
core:
build:
context: ./packages/core-service
dockerfile: Dockerfile
target: development
container_name: insight-core
restart: unless-stopped
environment:
NODE_ENV: ${NODE_ENV:-development}
APP_PORT: ${APP_PORT:-3000}
APP_URL: ${APP_URL:-http://172.20.10.59}
FRONTEND_URL: ${FRONTEND_URL:-http://172.20.10.59}
LOG_LEVEL: ${LOG_LEVEL:-info}
# Database (via PgBouncer)
DATABASE_URL: "postgresql://${DB_USER:-insight}:${DB_PASSWORD}@pgbouncer:5432/${DB_NAME:-platform_core}"
# Database (direkt fuer Migrations)
DATABASE_URL_DIRECT: "postgresql://${DB_USER:-insight}:${DB_PASSWORD}@postgres:5432/${DB_NAME:-platform_core}"
# Redis
REDIS_HOST: redis
REDIS_PORT: 6379
REDIS_PASSWORD: ${REDIS_PASSWORD:-}
# JWT
JWT_PRIVATE_KEY_PATH: ${JWT_PRIVATE_KEY_PATH:-/app/keys/jwt-private.pem}
JWT_PUBLIC_KEY_PATH: ${JWT_PUBLIC_KEY_PATH:-/app/keys/jwt-public.pem}
JWT_ACCESS_TOKEN_EXPIRY: ${JWT_ACCESS_TOKEN_EXPIRY:-15m}
JWT_REFRESH_TOKEN_EXPIRY: ${JWT_REFRESH_TOKEN_EXPIRY:-7d}
JWT_ISSUER: ${JWT_ISSUER:-insight-platform}
# Bcrypt
BCRYPT_COST: ${BCRYPT_COST:-12}
# CORS
CORS_ORIGINS: ${CORS_ORIGINS:-http://172.20.10.59}
# Rate Limiting
THROTTLE_TTL: ${THROTTLE_TTL:-60000}
THROTTLE_LIMIT: ${THROTTLE_LIMIT:-200}
volumes:
- ./keys:/app/keys:ro
networks:
- insight-web
- insight-db
- insight-cache
depends_on:
pgbouncer:
condition: service_healthy
redis:
condition: service_healthy
labels:
- "traefik.enable=true"
# API Routing
- "traefik.http.routers.core-api.rule=Host(`172.20.10.59`) && PathPrefix(`/api`)"
- "traefik.http.routers.core-api.entrypoints=web"
- "traefik.http.routers.core-api.service=core-api"
- "traefik.http.services.core-api.loadbalancer.server.port=3000"
# Health-Endpunkt (ohne Auth)
- "traefik.http.routers.core-health.rule=Host(`172.20.10.59`) && Path(`/health`)"
- "traefik.http.routers.core-health.entrypoints=web"
- "traefik.http.routers.core-health.service=core-api"
# Rate Limiting Middleware
- "traefik.http.middlewares.api-ratelimit.ratelimit.average=100"
- "traefik.http.middlewares.api-ratelimit.ratelimit.burst=50"
- "traefik.http.routers.core-api.middlewares=api-ratelimit"
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:3000/health || exit 1"]
interval: 15s
timeout: 5s
retries: 3
start_period: 30s
# --------------------------------------------------------
# Frontend - React App (via Nginx)
# --------------------------------------------------------
frontend:
build:
context: ./packages/frontend
dockerfile: Dockerfile
target: development
container_name: insight-frontend
restart: unless-stopped
networks:
- insight-web
labels:
- "traefik.enable=true"
# Frontend Routing (Catch-All nach API)
- "traefik.http.routers.frontend.rule=Host(`172.20.10.59`)"
- "traefik.http.routers.frontend.entrypoints=web"
- "traefik.http.routers.frontend.service=frontend"
- "traefik.http.routers.frontend.priority=1"
- "traefik.http.services.frontend.loadbalancer.server.port=8080"
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/ || exit 1"]
interval: 30s
timeout: 5s
retries: 3