veylant/docker-compose.yml
2026-02-23 13:35:04 +01:00

236 lines
9.7 KiB
YAML

services:
# ─────────────────────────────────────────────
# PostgreSQL 16 — primary datastore
# ─────────────────────────────────────────────
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: veylant
POSTGRES_USER: veylant
POSTGRES_PASSWORD: veylant_dev
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U veylant -d veylant"]
interval: 5s
timeout: 5s
retries: 10
start_period: 10s
# ─────────────────────────────────────────────
# Redis 7 — sessions, rate limiting, PII pseudonymization mappings
# No persistence in dev (AOF/RDB disabled for fast startup)
# ─────────────────────────────────────────────
redis:
image: redis:7-alpine
command: redis-server --save "" --appendonly no
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
# ─────────────────────────────────────────────
# ClickHouse 24.3 LTS — append-only audit logs and analytics
# Pinned to LTS for stability
# ─────────────────────────────────────────────
clickhouse:
image: clickhouse/clickhouse-server:24.3-alpine
environment:
CLICKHOUSE_DB: veylant_logs
CLICKHOUSE_USER: veylant
CLICKHOUSE_PASSWORD: veylant_dev
CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: 1
ports:
- "8123:8123" # HTTP interface (used for health check and dashboard queries)
- "9000:9000" # Native TCP (used by Go driver)
volumes:
- clickhouse_data:/var/lib/clickhouse
healthcheck:
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:8123/ping || exit 1"]
interval: 5s
timeout: 5s
retries: 20
start_period: 15s
ulimits:
nofile:
soft: 262144
hard: 262144
# ─────────────────────────────────────────────
# Keycloak 24 — IAM, OIDC, SAML 2.0
# start-dev: in-memory DB, no TLS — development only
# Realm is auto-imported from deploy/keycloak/realm-export.json
# ─────────────────────────────────────────────
keycloak:
image: quay.io/keycloak/keycloak:24.0
command: ["start-dev", "--import-realm"]
environment:
KC_BOOTSTRAP_ADMIN_USERNAME: admin
KC_BOOTSTRAP_ADMIN_PASSWORD: admin
KC_DB: dev-mem
KC_HEALTH_ENABLED: "true"
ports:
- "8080:8080"
volumes:
- ./deploy/keycloak:/opt/keycloak/data/import:ro
healthcheck:
test: ["CMD-SHELL", "curl -sf http://localhost:8080/health/ready || exit 1"]
interval: 10s
timeout: 10s
retries: 20
start_period: 30s # Keycloak takes ~20s to start in dev mode
# ─────────────────────────────────────────────
# Veylant proxy — Go application
# ─────────────────────────────────────────────
proxy:
build:
context: .
dockerfile: Dockerfile
ports:
- "8090:8090"
environment:
VEYLANT_SERVER_PORT: "8090"
VEYLANT_SERVER_ENV: "development"
VEYLANT_DATABASE_URL: "postgres://veylant:veylant_dev@postgres:5432/veylant?sslmode=disable"
VEYLANT_REDIS_URL: "redis://redis:6379"
VEYLANT_KEYCLOAK_BASE_URL: "http://keycloak:8080"
VEYLANT_KEYCLOAK_REALM: "veylant"
VEYLANT_KEYCLOAK_CLIENT_ID: "veylant-proxy"
VEYLANT_PII_ENABLED: "true"
VEYLANT_PII_SERVICE_ADDR: "pii:50051"
VEYLANT_PII_TIMEOUT_MS: "100"
VEYLANT_PII_FAIL_OPEN: "true"
VEYLANT_LOG_FORMAT: "console"
VEYLANT_LOG_LEVEL: "debug"
# Provider API keys — set via a .env file or shell environment.
# Only providers with an API key set will be enabled at runtime.
VEYLANT_PROVIDERS_OPENAI_API_KEY: "${OPENAI_API_KEY:-}"
VEYLANT_PROVIDERS_ANTHROPIC_API_KEY: "${ANTHROPIC_API_KEY:-}"
VEYLANT_PROVIDERS_MISTRAL_API_KEY: "${MISTRAL_API_KEY:-}"
# Azure OpenAI requires resource name + deployment ID + API key.
VEYLANT_PROVIDERS_AZURE_API_KEY: "${AZURE_OPENAI_API_KEY:-}"
VEYLANT_PROVIDERS_AZURE_RESOURCE_NAME: "${AZURE_OPENAI_RESOURCE_NAME:-}"
VEYLANT_PROVIDERS_AZURE_DEPLOYMENT_ID: "${AZURE_OPENAI_DEPLOYMENT_ID:-}"
# Ollama — defaults to localhost:11434 (use host.docker.internal in Docker Desktop).
VEYLANT_PROVIDERS_OLLAMA_BASE_URL: "${OLLAMA_BASE_URL:-http://host.docker.internal:11434/v1}"
VEYLANT_METRICS_ENABLED: "true"
# ClickHouse audit log (Sprint 6).
VEYLANT_CLICKHOUSE_DSN: "clickhouse://veylant:veylant_dev@clickhouse:9000/veylant_logs"
# AES-256-GCM key for prompt encryption — generate: openssl rand -base64 32
# In production, inject via Vault or secret manager. Leave empty to disable.
VEYLANT_CRYPTO_AES_KEY_BASE64: "${VEYLANT_CRYPTO_AES_KEY_BASE64:-}"
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
clickhouse:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:8090/healthz || exit 1"]
interval: 5s
timeout: 3s
retries: 10
start_period: 5s
# ─────────────────────────────────────────────
# PII detection service — Python (Sprint 3: full pipeline)
# Layer 1: regex (IBAN/email/phone/SSN/CB)
# Layer 2: Presidio + spaCy NER (PERSON/LOC/ORG)
# Pseudonymization: AES-256-GCM in Redis
# ─────────────────────────────────────────────
pii:
build:
context: ./services/pii
dockerfile: Dockerfile
ports:
- "50051:50051" # gRPC
- "8000:8000" # HTTP health
environment:
PII_GRPC_PORT: "50051"
PII_HTTP_PORT: "8000"
PII_REDIS_URL: "redis://redis:6379"
# PII_ENCRYPTION_KEY must be set to a 32-byte base64-encoded key in production.
# The default dev key is used if unset (NOT safe for production).
PII_ENCRYPTION_KEY: "${PII_ENCRYPTION_KEY:-}"
PII_NER_ENABLED: "true"
PII_NER_CONFIDENCE: "0.85"
PII_TTL_SECONDS: "3600"
depends_on:
redis:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:8000/healthz || exit 1"]
interval: 10s
timeout: 5s
retries: 10
start_period: 60s # spaCy fr_core_news_lg model load takes ~30s on first start
# ─────────────────────────────────────────────
# Prometheus — metrics collection
# Scrapes the proxy /metrics endpoint every 15s
# ─────────────────────────────────────────────
prometheus:
image: prom/prometheus:v2.53.0
ports:
- "9090:9090"
volumes:
- ./deploy/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--web.console.libraries=/etc/prometheus/console_libraries"
- "--web.console.templates=/etc/prometheus/consoles"
depends_on:
proxy:
condition: service_healthy
# ─────────────────────────────────────────────
# Grafana — metrics visualisation
# Auto-provisioned datasource (Prometheus) + Veylant dashboard
# Default credentials: admin / admin
# ─────────────────────────────────────────────
grafana:
image: grafana/grafana:11.3.0
ports:
- "3001:3000"
environment:
GF_SECURITY_ADMIN_PASSWORD: admin
GF_USERS_ALLOW_SIGN_UP: "false"
volumes:
- ./deploy/grafana/provisioning:/etc/grafana/provisioning:ro
- ./deploy/grafana/dashboards:/var/lib/grafana/dashboards:ro
depends_on:
- prometheus
# ─────────────────────────────────────────────
# Veylant Dashboard — React SPA (Sprint 7)
# Dev server only — production uses dist/ served by nginx
# ─────────────────────────────────────────────
web:
image: node:20-alpine
working_dir: /app
command: sh -c "npm install && npm run dev -- --host"
ports:
- "3000:3000"
volumes:
- ./web:/app
- /app/node_modules
environment:
VITE_AUTH_MODE: "dev"
VITE_KEYCLOAK_URL: "http://localhost:8080/realms/veylant"
depends_on:
proxy:
condition: service_healthy
volumes:
postgres_data:
clickhouse_data: