fix:
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
# DEPRECATED – Single Source of Truth moved to docker/.env
|
||||
#
|
||||
# This file is no longer used by Docker Compose or any build scripts.
|
||||
# Please configure environment variables in:
|
||||
# docker/.env (create from docker/.env.example)
|
||||
#
|
||||
# Reason: Avoid duplicated/conflicting configuration files.
|
||||
# Monitoring, Postgres, Redis, Keycloak, Gateway etc. read values from docker/.env.
|
||||
+53
-7
@@ -1,7 +1,53 @@
|
||||
# DEPRECATED – Single Source of Truth moved to docker/.env.example
|
||||
#
|
||||
# This file is no longer used by Docker Compose or any build scripts.
|
||||
# Please use and copy from:
|
||||
# docker/.env.example → docker/.env
|
||||
#
|
||||
# Reason: Avoid duplicated/conflicting configuration files.
|
||||
# ==========================================
|
||||
# Meldestelle – Docker Compose Environment
|
||||
# Single Source of Truth (SSoT)
|
||||
# ==========================================
|
||||
|
||||
# --- PROJECT ---
|
||||
# COMPOSE_PROJECT_NAME=meldestelle
|
||||
|
||||
# --- PORT MAPPINGS (host:container) ---
|
||||
POSTGRES_PORT=5432:5432
|
||||
REDIS_PORT=6379:6379
|
||||
KC_PORT=8180:8080
|
||||
CONSUL_PORT=8500:8500
|
||||
PROMETHEUS_PORT=9090:9090
|
||||
GF_PORT=3000:3000
|
||||
WEB_APP_PORT=4000:80
|
||||
PING_SERVICE_PORT=8082:8082
|
||||
PING_DEBUG_PORT=5006:5006
|
||||
GATEWAY_PORT=8081:8081
|
||||
GATEWAY_DEBUG_PORT=5005:5005
|
||||
GATEWAY_SERVER_PORT=8081
|
||||
DESKTOP_APP_VNC_PORT=5900:5900
|
||||
DESKTOP_APP_NOVNC_PORT=6080:6080
|
||||
|
||||
# Postgres
|
||||
POSTGRES_USER=meldestelle
|
||||
POSTGRES_PASSWORD=meldestelle
|
||||
POSTGRES_DB=meldestelle
|
||||
|
||||
# --- REDIS ---
|
||||
# Optional password for Redis; leave empty to disable authentication in dev
|
||||
REDIS_PASSWORD=
|
||||
|
||||
# --- KEYCLOAK ---
|
||||
KC_ADMIN_USER=admin
|
||||
KC_ADMIN_PASSWORD=admin
|
||||
KC_HOSTNAME=localhost
|
||||
|
||||
# --- PGADMIN ---
|
||||
PGADMIN_EMAIL=admin@example.com
|
||||
PGADMIN_PASSWORD=admin
|
||||
|
||||
# --- GRAFANA ---
|
||||
GF_ADMIN_USER=admin
|
||||
GF_ADMIN_PASSWORD=admin
|
||||
|
||||
|
||||
# Docker build versions (optional overrides)
|
||||
DOCKER_GRADLE_VERSION=9.1.0
|
||||
DOCKER_JAVA_VERSION=21
|
||||
DOCKER_NODE_VERSION=22.21.0
|
||||
DOCKER_NGINX_VERSION=1.28.0-alpine
|
||||
WEB_BUILD_PROFILE=dev
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# syntax=docker/dockerfile:1.8
|
||||
# ===================================================================
|
||||
# Production-Ready Keycloak Dockerfile
|
||||
# ===================================================================
|
||||
# Based on: quay.io/keycloak/keycloak:26.4
|
||||
# Features:
|
||||
# - Pre-built optimized image (faster startup)
|
||||
# - Security hardening
|
||||
# - Custom theme support
|
||||
# - Health monitoring
|
||||
# ===================================================================
|
||||
ARG KEYCLOAK_IMAGE_TAG
|
||||
|
||||
FROM quay.io/keycloak/keycloak:${KEYCLOAK_IMAGE_TAG}
|
||||
|
||||
LABEL maintainer="Meldestelle Development Team"
|
||||
LABEL description="Production-ready Keycloak for Meldestelle authentication"
|
||||
LABEL version="${KEYCLOAK_IMAGE_TAG}"
|
||||
|
||||
# Set environment variables for build
|
||||
ENV KC_HEALTH_ENABLED=true
|
||||
ENV KC_METRICS_ENABLED=true
|
||||
ENV KC_DB=postgres
|
||||
|
||||
WORKDIR /opt/keycloak
|
||||
|
||||
# Pre-build Keycloak for faster startup
|
||||
RUN /opt/keycloak/bin/kc.sh build \
|
||||
--db=postgres \
|
||||
--health-enabled=true \
|
||||
--metrics-enabled=true
|
||||
|
||||
# Set user
|
||||
USER 1000
|
||||
|
||||
ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]
|
||||
@@ -0,0 +1,296 @@
|
||||
{
|
||||
"realm": "meldestelle",
|
||||
"enabled": true,
|
||||
"displayName": "Meldestelle Authentication",
|
||||
"displayNameHtml": "<div class=\"kc-logo-text\"><span>Meldestelle</span></div>",
|
||||
"sslRequired": "external",
|
||||
"registrationAllowed": false,
|
||||
"registrationEmailAsUsername": false,
|
||||
"rememberMe": true,
|
||||
"verifyEmail": false,
|
||||
"loginWithEmailAllowed": true,
|
||||
"duplicateEmailsAllowed": false,
|
||||
"resetPasswordAllowed": true,
|
||||
"editUsernameAllowed": false,
|
||||
"bruteForceProtected": true,
|
||||
"permanentLockout": false,
|
||||
"maxFailureWaitSeconds": 900,
|
||||
"minimumQuickLoginWaitSeconds": 60,
|
||||
"waitIncrementSeconds": 60,
|
||||
"quickLoginCheckMilliSeconds": 1000,
|
||||
"maxDeltaTimeSeconds": 43200,
|
||||
"failureFactor": 5,
|
||||
"defaultSignatureAlgorithm": "RS256",
|
||||
"offlineSessionMaxLifespan": 5184000,
|
||||
"offlineSessionMaxLifespanEnabled": false,
|
||||
"accessTokenLifespan": 300,
|
||||
"accessTokenLifespanForImplicitFlow": 900,
|
||||
"ssoSessionIdleTimeout": 1800,
|
||||
"ssoSessionMaxLifespan": 36000,
|
||||
"refreshTokenMaxReuse": 0,
|
||||
"accessCodeLifespan": 60,
|
||||
"accessCodeLifespanUserAction": 300,
|
||||
"accessCodeLifespanLogin": 1800,
|
||||
"actionTokenGeneratedByAdminLifespan": 43200,
|
||||
"actionTokenGeneratedByUserLifespan": 300,
|
||||
"oauth2DeviceCodeLifespan": 600,
|
||||
"oauth2DevicePollingInterval": 5,
|
||||
"internationalizationEnabled": true,
|
||||
"supportedLocales": ["de", "en"],
|
||||
"defaultLocale": "de",
|
||||
"roles": {
|
||||
"realm": [
|
||||
{
|
||||
"name": "ADMIN",
|
||||
"description": "Administrator role with full system access",
|
||||
"composite": false,
|
||||
"clientRole": false
|
||||
},
|
||||
{
|
||||
"name": "USER",
|
||||
"description": "Standard user role with limited access",
|
||||
"composite": false,
|
||||
"clientRole": false
|
||||
},
|
||||
{
|
||||
"name": "MONITORING",
|
||||
"description": "Monitoring role for system health checks",
|
||||
"composite": false,
|
||||
"clientRole": false
|
||||
},
|
||||
{
|
||||
"name": "GUEST",
|
||||
"description": "Guest role with minimal access",
|
||||
"composite": false,
|
||||
"clientRole": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"clients": [
|
||||
{
|
||||
"clientId": "api-gateway",
|
||||
"name": "API Gateway Client",
|
||||
"description": "OAuth2 client for the Meldestelle API Gateway",
|
||||
"enabled": true,
|
||||
"alwaysDisplayInConsole": false,
|
||||
"clientAuthenticatorType": "client-secret",
|
||||
"secret": "K5RqonwVOaxPKaXVH4mbthSRbjRh5tOK",
|
||||
"redirectUris": [
|
||||
"http://localhost:8081/*",
|
||||
"http://localhost:3000/*",
|
||||
"https://app.meldestelle.at/*"
|
||||
],
|
||||
"webOrigins": [
|
||||
"http://localhost:8081",
|
||||
"http://localhost:3000",
|
||||
"https://app.meldestelle.at"
|
||||
],
|
||||
"protocol": "openid-connect",
|
||||
"bearerOnly": false,
|
||||
"publicClient": false,
|
||||
"standardFlowEnabled": true,
|
||||
"implicitFlowEnabled": false,
|
||||
"directAccessGrantsEnabled": true,
|
||||
"serviceAccountsEnabled": true,
|
||||
"authorizationServicesEnabled": false,
|
||||
"fullScopeAllowed": true,
|
||||
"frontchannelLogout": true,
|
||||
"attributes": {
|
||||
"access.token.lifespan": "300",
|
||||
"client.secret.creation.time": "0",
|
||||
"oauth2.device.authorization.grant.enabled": "false",
|
||||
"backchannel.logout.session.required": "true",
|
||||
"backchannel.logout.revoke.offline.tokens": "false"
|
||||
},
|
||||
"protocolMappers": [
|
||||
{
|
||||
"name": "realm-roles",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-realm-role-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"multivalued": "true",
|
||||
"userinfo.token.claim": "true",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "realm_access.roles",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "client-roles",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-client-role-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"multivalued": "true",
|
||||
"userinfo.token.claim": "true",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "resource_access.${client_id}.roles",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "username",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-property-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute": "username",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "preferred_username",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "email",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-property-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"userinfo.token.claim": "true",
|
||||
"user.attribute": "email",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "email",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "full-name",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-full-name-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"userinfo.token.claim": "true"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"clientId": "web-app",
|
||||
"name": "Web Application Client",
|
||||
"description": "Public client for web frontend",
|
||||
"enabled": true,
|
||||
"publicClient": true,
|
||||
"standardFlowEnabled": true,
|
||||
"implicitFlowEnabled": false,
|
||||
"directAccessGrantsEnabled": false,
|
||||
"redirectUris": [
|
||||
"http://localhost:4000/*",
|
||||
"http://localhost:3000/*",
|
||||
"https://app.meldestelle.at/*"
|
||||
],
|
||||
"webOrigins": [
|
||||
"http://localhost:4000",
|
||||
"http://localhost:3000",
|
||||
"https://app.meldestelle.at"
|
||||
],
|
||||
"protocol": "openid-connect",
|
||||
"attributes": {
|
||||
"pkce.code.challenge.method": "S256"
|
||||
}
|
||||
}
|
||||
],
|
||||
"users": [
|
||||
{
|
||||
"username": "admin",
|
||||
"enabled": true,
|
||||
"emailVerified": true,
|
||||
"firstName": "System",
|
||||
"lastName": "Administrator",
|
||||
"email": "admin@meldestelle.local",
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
"value": "Change_Me_In_Production!",
|
||||
"temporary": true
|
||||
}
|
||||
],
|
||||
"realmRoles": ["ADMIN", "USER"],
|
||||
"clientRoles": {
|
||||
"api-gateway": ["ADMIN"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"groups": [],
|
||||
"defaultRoles": ["USER", "GUEST"],
|
||||
"requiredCredentials": ["password"],
|
||||
"passwordPolicy": "length(8)",
|
||||
"otpPolicyType": "totp",
|
||||
"otpPolicyAlgorithm": "HmacSHA1",
|
||||
"otpPolicyInitialCounter": 0,
|
||||
"otpPolicyDigits": 6,
|
||||
"otpPolicyLookAheadWindow": 1,
|
||||
"otpPolicyPeriod": 30,
|
||||
"otpSupportedApplications": ["FreeOTP", "Google Authenticator"],
|
||||
"webAuthnPolicyRpEntityName": "meldestelle",
|
||||
"webAuthnPolicySignatureAlgorithms": ["ES256", "RS256"],
|
||||
"smtpServer": {},
|
||||
"eventsEnabled": true,
|
||||
"eventsListeners": ["jboss-logging"],
|
||||
"enabledEventTypes": [
|
||||
"LOGIN",
|
||||
"LOGIN_ERROR",
|
||||
"LOGOUT",
|
||||
"REGISTER",
|
||||
"REGISTER_ERROR",
|
||||
"UPDATE_PASSWORD",
|
||||
"UPDATE_PASSWORD_ERROR"
|
||||
],
|
||||
"adminEventsEnabled": true,
|
||||
"adminEventsDetailsEnabled": true,
|
||||
"identityProviders": [],
|
||||
"identityProviderMappers": [],
|
||||
"components": {
|
||||
"org.keycloak.keys.KeyProvider": [
|
||||
{
|
||||
"name": "rsa-generated",
|
||||
"providerId": "rsa-generated",
|
||||
"subComponents": {},
|
||||
"config": {
|
||||
"priority": ["100"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "hmac-generated",
|
||||
"providerId": "hmac-generated",
|
||||
"subComponents": {},
|
||||
"config": {
|
||||
"priority": ["100"],
|
||||
"algorithm": ["HS256"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "aes-generated",
|
||||
"providerId": "aes-generated",
|
||||
"subComponents": {},
|
||||
"config": {
|
||||
"priority": ["100"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"authenticationFlows": [],
|
||||
"authenticatorConfig": [],
|
||||
"requiredActions": [],
|
||||
"browserFlow": "browser",
|
||||
"registrationFlow": "registration",
|
||||
"directGrantFlow": "direct grant",
|
||||
"resetCredentialsFlow": "reset credentials",
|
||||
"clientAuthenticationFlow": "clients",
|
||||
"dockerAuthenticationFlow": "docker auth",
|
||||
"attributes": {
|
||||
"frontendUrl": "",
|
||||
"acr.loa.map": "{}",
|
||||
"clientOfflineSessionMaxLifespan": "0",
|
||||
"clientSessionIdleTimeout": "0",
|
||||
"clientSessionMaxLifespan": "0",
|
||||
"clientOfflineSessionIdleTimeout": "0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
# Prometheus configuration for Meldestelle project
|
||||
# Basic configuration to enable service monitoring
|
||||
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
|
||||
# Alertmanager configuration
|
||||
alerting:
|
||||
alertmanagers:
|
||||
- static_configs:
|
||||
- targets:
|
||||
- "alertmanager:9093"
|
||||
|
||||
rule_files:
|
||||
- "/etc/prometheus/rules/alerts.yaml"
|
||||
|
||||
scrape_configs:
|
||||
# Job 1: Prometheus überwacht sich selbst
|
||||
- job_name: 'prometheus'
|
||||
static_configs:
|
||||
- targets: [ 'localhost:9090' ]
|
||||
|
||||
# Job 2: API Gateway (Spring Boot Actuator)
|
||||
- job_name: 'api-gateway'
|
||||
metrics_path: '/actuator/prometheus'
|
||||
scrape_interval: "30s"
|
||||
static_configs:
|
||||
- targets: [ 'api-gateway:8081' ]
|
||||
|
||||
# Job 3: Postgres (ACHTUNG)
|
||||
# Postgres direkt auf 5432 zu scrapen geht nicht.
|
||||
# Entweder auskommentieren oder 'postgres-exporter' Container hinzufügen.
|
||||
# - job_name: 'postgres-exporter'
|
||||
# static_configs:
|
||||
# - targets: ['postgres-exporter:9187']
|
||||
|
||||
# Add consul for service discovery monitoring
|
||||
- job_name: 'consul'
|
||||
metrics_path: '/v1/agent/metrics'
|
||||
params:
|
||||
format: [ 'prometheus' ]
|
||||
static_configs:
|
||||
- targets: [ 'consul:8500' ]
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
groups:
|
||||
- name: meldestelle_alerts
|
||||
rules:
|
||||
# 1. Memory: Passt soweit, ist okay.
|
||||
- alert: HighMemoryUsage
|
||||
expr: (jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"}) * 100 > 85
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "High memory usage ({{ $value | humanize }}%)"
|
||||
description: "JVM Heap usage is above 85%.\n Instance: {{ $labels.instance }}"
|
||||
|
||||
# 2. CPU: Passt auch.
|
||||
- alert: HighCpuUsage
|
||||
expr: process_cpu_usage * 100 > 85
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "High CPU usage ({{ $value | humanize }}%)"
|
||||
description: "CPU usage is above 85%.\n Instance: {{ $labels.instance }}"
|
||||
|
||||
# 3. Error Rate: FIX - Division durch null abfangen & Rate nutzen
|
||||
- alert: HighErrorRate
|
||||
# Wir prüfen nur, wenn überhaupt Requests > 0 da sind, um DivByZero zu vermeiden
|
||||
expr: |
|
||||
(
|
||||
sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m]))
|
||||
/
|
||||
sum(rate(http_server_requests_seconds_count[5m]))
|
||||
) * 100 > 5
|
||||
for: 2m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "High error rate ({{ $value | humanize }}%)"
|
||||
description: "More than 5% of requests resulted in 5xx errors.\n Instance: {{ $labels.instance }}"
|
||||
|
||||
# 4. Service Down: FIX - Job Name Regex
|
||||
- alert: ServiceDown
|
||||
# Prüft alle Jobs, die du in prometheus.yml definiert hast (api-gateway, consul etc.),
|
||||
# 'up == 0' bedeutet: Target ist konfiguriert, aber nicht erreichbar.
|
||||
expr: up == 0
|
||||
for: 1m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Service {{ $labels.job }} is down"
|
||||
description: "Service instance {{ $labels.instance }} of job {{ $labels.job }} is not reachable."
|
||||
|
||||
# 5. Slow Response: FIX - 'rate' benutzen!
|
||||
- alert: SlowResponseTime
|
||||
# Berechnet die durchschnittliche Dauer pro request im 5-Minuten-Fenster
|
||||
expr: rate(http_server_requests_seconds_sum[5m]) / rate(http_server_requests_seconds_count[5m]) > 1
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Slow response time ({{ $value | humanizeDuration }})"
|
||||
description: "Average response time is > 1s for the last 5 minutes.\n Instance: {{ $labels.instance }}\n Path: {{ $labels.uri }}"
|
||||
|
||||
# 6. GC Pause: FIX - 'rate' benutzen!
|
||||
- alert: HighGcPauseTime
|
||||
# Zeigt an, wie viel Zeit PRO SEKUNDE für GC draufgeht (nicht pro GC Event, das ist oft aussagekräftiger)
|
||||
# Oder "Durchschnittliche Dauer pro GC Event im Zeitfenster":
|
||||
expr: rate(jvm_gc_pause_seconds_sum[5m]) / rate(jvm_gc_pause_seconds_count[5m]) > 0.5
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "High GC pause time ({{ $value | humanizeDuration }})"
|
||||
description: "Average GC pause is > 0.5s.\n Instance: {{ $labels.instance }}"
|
||||
@@ -0,0 +1,35 @@
|
||||
-- ===================================================================
|
||||
-- PostgreSQL Initialization Script for Keycloak
|
||||
-- ===================================================================
|
||||
-- Dieses Skript erstellt ein separates Schema für Keycloak-Daten innerhalb der
|
||||
-- meldestelle-Datenbank und sorgt so für Isolation und bessere Organisation.
|
||||
--
|
||||
-- Ausführung: Wird automatisch von PostgreSQL beim ersten Start ausgeführt
|
||||
-- über den docker-entrypoint-initdb.d-Mechanismus.
|
||||
-- ===================================================================
|
||||
|
||||
-- Erstellt das Keycloak-Schema, falls es noch nicht existiert.
|
||||
CREATE SCHEMA IF NOT EXISTS keycloak;
|
||||
|
||||
-- Da der "POSTGRES_USER" (Superuser) das Skript ausführt,
|
||||
-- gehört ihm das Schema automatisch oder er hat Zugriff.
|
||||
-- Explizite GRANTS auf "pg-user" entfernen, um .env-Unabhängigkeit zu wahren.
|
||||
|
||||
-- Falls du es explizit willst, nutze current_user (der ausführende User):
|
||||
GRANT ALL PRIVILEGES ON SCHEMA keycloak TO current_user;
|
||||
-- Gewährt dem Benutzer „meldestelle“ alle Berechtigungen für das Schema.
|
||||
-- GRANT ALL PRIVILEGES ON SCHEMA keycloak TO "pg-user";
|
||||
|
||||
-- Gewährt die Nutzung des Schemas
|
||||
GRANT USAGE ON SCHEMA keycloak TO "pg-user";
|
||||
|
||||
-- Standardberechtigungen für zukünftige Tabellen im Keycloak-Schema festlegen
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA keycloak GRANT ALL ON TABLES TO "pg-user";
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA keycloak GRANT ALL ON SEQUENCES TO "pg-user";
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA keycloak GRANT ALL ON FUNCTIONS TO "pg-user";
|
||||
|
||||
-- Log successful schema Erstellung
|
||||
DO $$
|
||||
BEGIN
|
||||
RAISE NOTICE 'Keycloak schema created successfully in database';
|
||||
END $$;
|
||||
@@ -0,0 +1,11 @@
|
||||
-- ===================================================================
|
||||
-- Keycloak Schema Init (No-Op)
|
||||
-- ===================================================================
|
||||
-- DEPRECATED: Schema-initialization erfolgt über die Datei 01-init-keycloak-schema.sql.
|
||||
-- Diese Datei dient lediglich der Sicherstellung der Ausführungsreihenfolge, führt aber keine Aktionen aus.
|
||||
-- ===================================================================
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
RAISE NOTICE '02-init-keycloak-schema.sql is a no-op (handled by 01-init-keycloak-schema.sql)';
|
||||
END $$;
|
||||
+3
-3
@@ -39,9 +39,9 @@ constraint_exclusion = partition
|
||||
|
||||
# Logging
|
||||
log_destination = 'stderr'
|
||||
logging_collector = on
|
||||
log_directory = 'log'
|
||||
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
|
||||
logging_collector = off
|
||||
# log_directory = 'log'
|
||||
# log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
|
||||
log_truncate_on_rotation = off
|
||||
log_rotation_age = 1d
|
||||
log_rotation_size = 100MB
|
||||
@@ -63,14 +63,17 @@ maxclients 10000
|
||||
# Security Settings
|
||||
rename-command FLUSHDB ""
|
||||
rename-command FLUSHALL ""
|
||||
# KEYS ist langsam, sperren ist okay (Admin tools funktionieren dann aber evtl. nicht mehr)
|
||||
rename-command KEYS ""
|
||||
rename-command CONFIG "CONFIG_b835c3f8a5d2e7f1"
|
||||
rename-command SHUTDOWN "SHUTDOWN_a9b4c2d1e3f5g6h7"
|
||||
rename-command DEBUG ""
|
||||
rename-command EVAL ""
|
||||
|
||||
# Disable dangerous commands in production
|
||||
rename-command DEL "DEL_prod_safe"
|
||||
# EVAL wird für Lua-Skripte benötigt (Locks, Rate Limiting etc.)
|
||||
# rename-command EVAL ""
|
||||
|
||||
# DEL wird benötigt, damit die App Cache-Einträge invalidieren kann!
|
||||
# rename-command DEL "DEL_prod_safe"
|
||||
|
||||
# TLS Configuration (uncomment and configure for TLS)
|
||||
# port 0
|
||||
@@ -0,0 +1,619 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "https://meldestelle.at/schemas/docker-versions.json",
|
||||
"title": "Docker Versions TOML Schema",
|
||||
"description": "Schema for docker/versions.toml - centralized Docker version management",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"versions": {
|
||||
"type": "object",
|
||||
"description": "Central version definitions for all Docker components",
|
||||
"properties": {
|
||||
"gradle": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$",
|
||||
"description": "Gradle version for build tools",
|
||||
"examples": ["9.0.0", "8.14.0"]
|
||||
},
|
||||
"java": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+$",
|
||||
"description": "Java version (LTS recommended)",
|
||||
"examples": ["21", "17", "11"]
|
||||
},
|
||||
"node": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$",
|
||||
"description": "Node.js version for client builds",
|
||||
"examples": ["20.12.0", "18.19.0"]
|
||||
},
|
||||
"nginx": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+\\.[0-9]+-alpine$",
|
||||
"description": "Nginx version with Alpine base",
|
||||
"examples": ["1.25-alpine", "1.24-alpine"]
|
||||
},
|
||||
"alpine": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+\\.[0-9]+$",
|
||||
"description": "Alpine Linux base image version",
|
||||
"examples": ["3.19", "3.18"]
|
||||
},
|
||||
"eclipse-temurin-jdk": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+-jdk-alpine$",
|
||||
"description": "Eclipse Temurin JDK image tag",
|
||||
"examples": ["21-jdk-alpine", "17-jdk-alpine"]
|
||||
},
|
||||
"eclipse-temurin-jre": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+-jre-alpine$",
|
||||
"description": "Eclipse Temurin JRE image tag",
|
||||
"examples": ["21-jre-alpine", "17-jre-alpine"]
|
||||
},
|
||||
"prometheus": {
|
||||
"type": "string",
|
||||
"pattern": "^v[0-9]+\\.[0-9]+\\.[0-9]+$",
|
||||
"description": "Prometheus monitoring version",
|
||||
"examples": ["v2.54.1", "v2.47.0"]
|
||||
},
|
||||
"grafana": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$",
|
||||
"description": "Grafana visualization version",
|
||||
"examples": ["11.3.0", "10.1.0"]
|
||||
},
|
||||
"keycloak": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$",
|
||||
"description": "Keycloak authentication version",
|
||||
"examples": ["26.0.7", "25.0.6"]
|
||||
},
|
||||
"spring-profiles-default": {
|
||||
"type": "string",
|
||||
"enum": ["default", "dev", "test", "prod"],
|
||||
"description": "Default Spring profile for infrastructure services"
|
||||
},
|
||||
"spring-profiles-docker": {
|
||||
"type": "string",
|
||||
"enum": ["docker", "dev", "test", "prod"],
|
||||
"description": "Spring profile for Docker containers"
|
||||
},
|
||||
"spring-profiles-prod": {
|
||||
"type": "string",
|
||||
"enum": ["prod", "production"],
|
||||
"description": "Spring profile for production environment"
|
||||
},
|
||||
"app-version": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$",
|
||||
"description": "Application version for all services",
|
||||
"examples": ["1.0.0", "2.1.3"]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"gradle",
|
||||
"java",
|
||||
"node",
|
||||
"nginx",
|
||||
"alpine",
|
||||
"prometheus",
|
||||
"grafana",
|
||||
"keycloak",
|
||||
"app-version"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"service-ports": {
|
||||
"type": "object",
|
||||
"description": "Centralized port definitions for all services",
|
||||
"properties": {
|
||||
"api-gateway": {
|
||||
"type": "integer",
|
||||
"minimum": 8081,
|
||||
"maximum": 8081,
|
||||
"description": "API Gateway port"
|
||||
},
|
||||
"auth-server": {
|
||||
"type": "integer",
|
||||
"minimum": 8087,
|
||||
"maximum": 8087,
|
||||
"description": "Authentication server port"
|
||||
},
|
||||
"monitoring-server": {
|
||||
"type": "integer",
|
||||
"minimum": 8088,
|
||||
"maximum": 8088,
|
||||
"description": "Monitoring server port"
|
||||
},
|
||||
"ping-service": {
|
||||
"type": "integer",
|
||||
"minimum": 8082,
|
||||
"maximum": 8082,
|
||||
"description": "Ping service port"
|
||||
},
|
||||
"members-service": {
|
||||
"type": "integer",
|
||||
"minimum": 8083,
|
||||
"maximum": 8083,
|
||||
"description": "Members service port"
|
||||
},
|
||||
"horses-service": {
|
||||
"type": "integer",
|
||||
"minimum": 8084,
|
||||
"maximum": 8084,
|
||||
"description": "Horses service port"
|
||||
},
|
||||
"events-service": {
|
||||
"type": "integer",
|
||||
"minimum": 8085,
|
||||
"maximum": 8085,
|
||||
"description": "Events service port"
|
||||
},
|
||||
"masterdata-service": {
|
||||
"type": "integer",
|
||||
"minimum": 8086,
|
||||
"maximum": 8086,
|
||||
"description": "Masterdata service port"
|
||||
},
|
||||
"postgres": {
|
||||
"type": "integer",
|
||||
"minimum": 5432,
|
||||
"maximum": 5432,
|
||||
"description": "PostgreSQL database port"
|
||||
},
|
||||
"redis": {
|
||||
"type": "integer",
|
||||
"minimum": 6379,
|
||||
"maximum": 6379,
|
||||
"description": "Redis cache port"
|
||||
},
|
||||
"keycloak": {
|
||||
"type": "integer",
|
||||
"minimum": 8180,
|
||||
"maximum": 8180,
|
||||
"description": "Keycloak authentication port"
|
||||
},
|
||||
"consul": {
|
||||
"type": "integer",
|
||||
"minimum": 8500,
|
||||
"maximum": 8500,
|
||||
"description": "Consul service discovery port"
|
||||
},
|
||||
"zookeeper": {
|
||||
"type": "integer",
|
||||
"minimum": 2181,
|
||||
"maximum": 2181,
|
||||
"description": "Zookeeper coordination port"
|
||||
},
|
||||
"kafka": {
|
||||
"type": "integer",
|
||||
"minimum": 9092,
|
||||
"maximum": 9092,
|
||||
"description": "Kafka messaging port"
|
||||
},
|
||||
"prometheus": {
|
||||
"type": "integer",
|
||||
"minimum": 9090,
|
||||
"maximum": 9090,
|
||||
"description": "Prometheus monitoring port"
|
||||
},
|
||||
"grafana": {
|
||||
"type": "integer",
|
||||
"minimum": 3000,
|
||||
"maximum": 3000,
|
||||
"description": "Grafana visualization port"
|
||||
},
|
||||
"web-app": {
|
||||
"type": "integer",
|
||||
"minimum": 4000,
|
||||
"maximum": 4000,
|
||||
"description": "Web application port"
|
||||
},
|
||||
"desktop-app-vnc": {
|
||||
"type": "integer",
|
||||
"minimum": 5901,
|
||||
"maximum": 5901,
|
||||
"description": "Desktop app VNC port"
|
||||
},
|
||||
"desktop-app-novnc": {
|
||||
"type": "integer",
|
||||
"minimum": 6080,
|
||||
"maximum": 6080,
|
||||
"description": "Desktop app noVNC web port"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"api-gateway",
|
||||
"auth-server",
|
||||
"monitoring-server",
|
||||
"ping-service",
|
||||
"postgres",
|
||||
"redis",
|
||||
"keycloak",
|
||||
"prometheus",
|
||||
"grafana",
|
||||
"web-app"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"port-ranges": {
|
||||
"type": "object",
|
||||
"description": "Port range definitions for service categories",
|
||||
"properties": {
|
||||
"infrastructure": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+-[0-9]+$",
|
||||
"description": "Port range for infrastructure services",
|
||||
"examples": ["8081-8088"]
|
||||
},
|
||||
"services": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+-[0-9]+$",
|
||||
"description": "Port range for application services",
|
||||
"examples": ["8082-8099"]
|
||||
},
|
||||
"monitoring": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+-[0-9]+$",
|
||||
"description": "Port range for monitoring services",
|
||||
"examples": ["9090-9099"]
|
||||
},
|
||||
"clients": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+-[0-9]+$",
|
||||
"description": "Port range for client applications",
|
||||
"examples": ["4000-4099"]
|
||||
},
|
||||
"vnc": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+-[0-9]+$",
|
||||
"description": "Port range for VNC connections",
|
||||
"examples": ["5901-5999"]
|
||||
},
|
||||
"debug": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+-[0-9]+$",
|
||||
"description": "Port range for debug connections",
|
||||
"examples": ["5005-5009"]
|
||||
},
|
||||
"system-reserved": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+-[0-9]+$",
|
||||
"description": "System reserved port range",
|
||||
"examples": ["0-1023"]
|
||||
},
|
||||
"ephemeral": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+-[0-9]+$",
|
||||
"description": "Ephemeral port range",
|
||||
"examples": ["32768-65535"]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"infrastructure",
|
||||
"services",
|
||||
"monitoring",
|
||||
"clients",
|
||||
"debug"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"build-args": {
|
||||
"type": "object",
|
||||
"description": "Build argument categories for different service types",
|
||||
"properties": {
|
||||
"global": {
|
||||
"type": "array",
|
||||
"description": "Global build arguments used by all services",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"GRADLE_VERSION",
|
||||
"JAVA_VERSION",
|
||||
"BUILD_DATE",
|
||||
"VERSION"
|
||||
]
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"spring-services": {
|
||||
"type": "array",
|
||||
"description": "Spring Boot service-specific build arguments",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"SPRING_PROFILES_ACTIVE",
|
||||
"SERVICE_PATH",
|
||||
"SERVICE_NAME",
|
||||
"SERVICE_PORT"
|
||||
]
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"web-clients": {
|
||||
"type": "array",
|
||||
"description": "Web client-specific build arguments",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"NODE_VERSION",
|
||||
"NGINX_VERSION",
|
||||
"CLIENT_PATH",
|
||||
"CLIENT_MODULE",
|
||||
"CLIENT_NAME"
|
||||
]
|
||||
},
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
"required": ["global"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"categories": {
|
||||
"type": "object",
|
||||
"description": "Service category configurations",
|
||||
"properties": {
|
||||
"services": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"default-spring-profile": {
|
||||
"type": "string",
|
||||
"enum": ["docker", "dev", "test", "prod"]
|
||||
},
|
||||
"default-port-start": {
|
||||
"type": "integer",
|
||||
"minimum": 8082,
|
||||
"maximum": 8099
|
||||
},
|
||||
"services": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ping-service",
|
||||
"members-service",
|
||||
"horses-service",
|
||||
"events-service",
|
||||
"masterdata-service"
|
||||
]
|
||||
},
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
"required": ["services"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"infrastructure": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"default-spring-profile": {
|
||||
"type": "string",
|
||||
"enum": ["default", "dev", "test", "prod"]
|
||||
},
|
||||
"services": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"gateway",
|
||||
"auth-server",
|
||||
"monitoring-server"
|
||||
]
|
||||
},
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
"required": ["services"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"clients": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"default-node-version": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
|
||||
},
|
||||
"default-nginx-version": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+\\.[0-9]+-alpine$"
|
||||
},
|
||||
"clients": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"web-app",
|
||||
"desktop-app"
|
||||
]
|
||||
},
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
"required": ["clients"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["services", "infrastructure", "clients"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"environment-mapping": {
|
||||
"type": "object",
|
||||
"description": "Maps internal version names to environment variable names",
|
||||
"properties": {
|
||||
"gradle-version": {
|
||||
"type": "string",
|
||||
"enum": ["DOCKER_GRADLE_VERSION"]
|
||||
},
|
||||
"java-version": {
|
||||
"type": "string",
|
||||
"enum": ["DOCKER_JAVA_VERSION"]
|
||||
},
|
||||
"node-version": {
|
||||
"type": "string",
|
||||
"enum": ["DOCKER_NODE_VERSION"]
|
||||
},
|
||||
"nginx-version": {
|
||||
"type": "string",
|
||||
"enum": ["DOCKER_NGINX_VERSION"]
|
||||
},
|
||||
"prometheus-version": {
|
||||
"type": "string",
|
||||
"enum": ["DOCKER_PROMETHEUS_VERSION"]
|
||||
},
|
||||
"grafana-version": {
|
||||
"type": "string",
|
||||
"enum": ["DOCKER_GRAFANA_VERSION"]
|
||||
},
|
||||
"keycloak-version": {
|
||||
"type": "string",
|
||||
"enum": ["DOCKER_KEYCLOAK_VERSION"]
|
||||
},
|
||||
"spring-profiles-default": {
|
||||
"type": "string",
|
||||
"enum": ["DOCKER_SPRING_PROFILES_DEFAULT"]
|
||||
},
|
||||
"spring-profiles-docker": {
|
||||
"type": "string",
|
||||
"enum": ["DOCKER_SPRING_PROFILES_DOCKER"]
|
||||
},
|
||||
"app-version": {
|
||||
"type": "string",
|
||||
"enum": ["DOCKER_APP_VERSION"]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"gradle-version",
|
||||
"java-version",
|
||||
"node-version",
|
||||
"nginx-version",
|
||||
"prometheus-version",
|
||||
"grafana-version",
|
||||
"keycloak-version",
|
||||
"app-version"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"environments": {
|
||||
"type": "object",
|
||||
"description": "Environment-specific configurations",
|
||||
"properties": {
|
||||
"development": {
|
||||
"$ref": "#/definitions/environment-config"
|
||||
},
|
||||
"production": {
|
||||
"$ref": "#/definitions/environment-config"
|
||||
},
|
||||
"testing": {
|
||||
"$ref": "#/definitions/environment-config"
|
||||
}
|
||||
},
|
||||
"required": ["development", "production", "testing"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"versions",
|
||||
"service-ports",
|
||||
"port-ranges",
|
||||
"build-args",
|
||||
"categories",
|
||||
"environment-mapping",
|
||||
"environments"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"environment-config": {
|
||||
"type": "object",
|
||||
"description": "Environment-specific configuration settings",
|
||||
"properties": {
|
||||
"spring-profiles": {
|
||||
"type": "string",
|
||||
"enum": ["dev", "test", "prod", "docker"],
|
||||
"description": "Default Spring profiles for this environment"
|
||||
},
|
||||
"debug-enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Whether debug mode is enabled"
|
||||
},
|
||||
"log-level": {
|
||||
"type": "string",
|
||||
"enum": ["TRACE", "DEBUG", "INFO", "WARN", "ERROR"],
|
||||
"description": "Default log level for services"
|
||||
},
|
||||
"health-check-interval": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+s$",
|
||||
"description": "Health check interval (e.g., '30s')"
|
||||
},
|
||||
"health-check-timeout": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+s$",
|
||||
"description": "Health check timeout (e.g., '5s')"
|
||||
},
|
||||
"health-check-retries": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 10,
|
||||
"description": "Number of health check retries"
|
||||
},
|
||||
"health-check-start-period": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+s$",
|
||||
"description": "Health check start period (e.g., '40s')"
|
||||
},
|
||||
"resource-limits": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to enforce resource limits"
|
||||
},
|
||||
"jvm-debug-port": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "integer",
|
||||
"minimum": 5005,
|
||||
"maximum": 5009
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"enum": [false]
|
||||
}
|
||||
],
|
||||
"description": "JVM debug port (5005-5009) or false to disable"
|
||||
},
|
||||
"hot-reload": {
|
||||
"type": "boolean",
|
||||
"description": "Whether hot reload is enabled for development"
|
||||
},
|
||||
"security-headers": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to add security headers (production)"
|
||||
},
|
||||
"tls-enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Whether TLS/SSL is enabled (production)"
|
||||
},
|
||||
"ephemeral-storage": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to use ephemeral storage (testing)"
|
||||
},
|
||||
"test-containers": {
|
||||
"type": "boolean",
|
||||
"description": "Whether test containers are used (testing)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"spring-profiles",
|
||||
"debug-enabled",
|
||||
"log-level",
|
||||
"health-check-interval",
|
||||
"health-check-timeout",
|
||||
"health-check-retries",
|
||||
"health-check-start-period",
|
||||
"resource-limits",
|
||||
"jvm-debug-port",
|
||||
"hot-reload"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
# ===================================================================
|
||||
# Multi-stage Dockerfile Template for Kotlin Multiplatform Web Client
|
||||
# Features: Kotlin/JS compilation, Nginx serving, development support, centralized version management
|
||||
# Version: 3.0.0 - Central Version Management Implementation
|
||||
# ===================================================================
|
||||
# IMPORTANT: Build arguments are now managed centrally via docker/versions.toml
|
||||
# Use: docker-compose build or scripts/docker-build.sh for automated version injection
|
||||
|
||||
# === CENTRALIZED BUILD ARGUMENTS ===
|
||||
# Values sourced from docker/versions.toml and docker/build-args/
|
||||
# Global arguments (docker/build-args/global.env)
|
||||
ARG GRADLE_VERSION
|
||||
ARG JAVA_VERSION
|
||||
ARG BUILD_DATE
|
||||
ARG VERSION
|
||||
|
||||
# Client-specific arguments (docker/build-args/clients.env)
|
||||
ARG NODE_VERSION
|
||||
ARG NGINX_VERSION
|
||||
|
||||
# Client-specific build arguments (can be overridden at build time)
|
||||
ARG CLIENT_PATH=client/web-app
|
||||
ARG CLIENT_MODULE=client:web-app
|
||||
ARG CLIENT_NAME=web-app
|
||||
|
||||
# ===================================================================
|
||||
# Build Stage - Kotlin/JS Compilation
|
||||
# ===================================================================
|
||||
FROM gradle:${GRADLE_VERSION}-jdk${JAVA_VERSION}-alpine AS kotlin-builder
|
||||
|
||||
# Re-declare build arguments for kotlin-builder stage
|
||||
ARG CLIENT_PATH=client/web-app
|
||||
ARG CLIENT_MODULE=client:web-app
|
||||
ARG CLIENT_NAME=web-app
|
||||
ARG NODE_VERSION
|
||||
|
||||
LABEL stage=kotlin-builder
|
||||
LABEL maintainer="Meldestelle Development Team"
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
# Install specific Node.js version for Kotlin/JS compatibility
|
||||
RUN apk add --no-cache wget ca-certificates && \
|
||||
wget -q -O - https://unofficial-builds.nodejs.org/download/release/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64-musl.tar.xz | \
|
||||
tar -xJ -C /usr/local --strip-components=1 && \
|
||||
apk del wget ca-certificates && \
|
||||
rm -rf /var/cache/apk/* && \
|
||||
npm config set cache /tmp/.npm-cache && \
|
||||
npm config set progress false && \
|
||||
npm config set audit false
|
||||
|
||||
# Gradle optimizations for Kotlin Multiplatform builds
|
||||
ENV GRADLE_OPTS="-Dorg.gradle.caching=true \
|
||||
-Dorg.gradle.daemon=false \
|
||||
-Dorg.gradle.parallel=true \
|
||||
-Dorg.gradle.configureondemand=true \
|
||||
-Dorg.gradle.jvmargs=-Xmx3g \
|
||||
-Dkotlin.compiler.execution.strategy=in-process"
|
||||
|
||||
# Kotlin/JS and Node.js environment variables
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096" \
|
||||
NPM_CONFIG_CACHE="/tmp/.npm-cache" \
|
||||
KOTLIN_JS_GENERATE_EXTERNALS=false
|
||||
|
||||
# Copy build configuration files first for optimal caching
|
||||
COPY gradlew gradlew.bat gradle.properties settings.gradle.kts ./
|
||||
COPY gradle/ gradle/
|
||||
COPY build.gradle.kts ./
|
||||
|
||||
# Copy platform and core dependencies
|
||||
COPY platform/ platform/
|
||||
COPY core/ core/
|
||||
|
||||
# Copy client modules in dependency order
|
||||
COPY client/common-ui/ client/common-ui/
|
||||
COPY ${CLIENT_PATH}/ ${CLIENT_PATH}/
|
||||
|
||||
# Clear npm cache and verify Node.js installation
|
||||
RUN npm cache clean --force && \
|
||||
node --version && npm --version
|
||||
|
||||
# Download dependencies in a separate layer
|
||||
RUN ./gradlew :${CLIENT_MODULE}:dependencies --no-daemon --info --stacktrace
|
||||
|
||||
# Build web application with production optimizations and better error handling
|
||||
RUN ./gradlew :${CLIENT_MODULE}:jsBrowserProductionWebpack --no-daemon --info --stacktrace --debug
|
||||
|
||||
# Verify build output
|
||||
RUN ls -la /workspace/${CLIENT_PATH}/build/dist/ || (echo "Build failed - no dist directory found" && exit 1)
|
||||
|
||||
# ===================================================================
|
||||
# Production Stage - Nginx serving
|
||||
# ===================================================================
|
||||
FROM nginx:${NGINX_VERSION} AS runtime
|
||||
|
||||
# Re-declare build arguments for runtime stage
|
||||
ARG CLIENT_PATH=client/web-app
|
||||
ARG CLIENT_MODULE=client:web-app
|
||||
ARG CLIENT_NAME=web-app
|
||||
|
||||
# Metadata
|
||||
LABEL service="${CLIENT_NAME}" \
|
||||
version="1.0.0" \
|
||||
description="Kotlin Multiplatform Web Client for Meldestelle" \
|
||||
maintainer="Meldestelle Development Team"
|
||||
|
||||
# Security and system setup
|
||||
RUN apk update && \
|
||||
apk upgrade && \
|
||||
apk add --no-cache curl jq && \
|
||||
rm -rf /var/cache/apk/*
|
||||
|
||||
# Remove default nginx content and logs
|
||||
RUN rm -rf /usr/share/nginx/html/* && \
|
||||
rm -f /var/log/nginx/*.log
|
||||
|
||||
# Copy built web application from builder stage
|
||||
COPY --from=kotlin-builder /workspace/${CLIENT_PATH}/build/dist/ /usr/share/nginx/html/
|
||||
|
||||
# Copy nginx configuration
|
||||
COPY ${CLIENT_PATH}/nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
# Set proper permissions for nginx
|
||||
RUN chown -R nginx:nginx /usr/share/nginx/html /var/cache/nginx /var/run /var/log/nginx && \
|
||||
chmod -R 755 /usr/share/nginx/html
|
||||
|
||||
# Switch to nginx user for security
|
||||
USER nginx
|
||||
|
||||
# Health check specifically for the web application
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
|
||||
CMD curl -f http://localhost/health || exit 1
|
||||
|
||||
# Expose HTTP port
|
||||
EXPOSE 80
|
||||
|
||||
# Start nginx with proper signal handling for graceful shutdowns
|
||||
STOPSIGNAL SIGQUIT
|
||||
|
||||
# Run nginx in foreground with error handling
|
||||
CMD ["sh", "-c", "nginx -t && exec nginx -g 'daemon off;'"]
|
||||
@@ -0,0 +1,147 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
|
||||
# ===================================================================
|
||||
# Multi-stage Dockerfile Template for Spring Boot Services
|
||||
# Features: Security hardening, monitoring support, optimal caching, centralized version management
|
||||
# Version: 3.0.0 - Central Version Management Implementation
|
||||
# ===================================================================
|
||||
# IMPORTANT: Build arguments are now managed centrally via docker/versions.toml
|
||||
# Use: docker-compose build or scripts/docker-build.sh for automated version injection
|
||||
|
||||
# === CENTRALIZED BUILD ARGUMENTS ===
|
||||
# Values sourced from docker/versions.toml and docker/build-args/
|
||||
# Global arguments (docker/build-args/global.env)
|
||||
ARG GRADLE_VERSION
|
||||
ARG JAVA_VERSION
|
||||
ARG BUILD_DATE
|
||||
ARG VERSION
|
||||
|
||||
# Service-specific arguments (docker/build-args/services.env or infrastructure.env)
|
||||
# Note: No runtime profiles/ports as build ARGs
|
||||
ARG SERVICE_PATH=.
|
||||
ARG SERVICE_NAME=spring-boot-service
|
||||
|
||||
# ===================================================================
|
||||
# Build Stage
|
||||
# ===================================================================
|
||||
FROM gradle:${GRADLE_VERSION}-jdk${JAVA_VERSION}-alpine AS builder
|
||||
|
||||
# Re-declare build arguments for this stage
|
||||
ARG SERVICE_PATH=.
|
||||
ARG SERVICE_NAME=spring-boot-service
|
||||
|
||||
LABEL stage=builder
|
||||
LABEL maintainer="Meldestelle Development Team"
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
# Gradle optimizations
|
||||
ENV GRADLE_OPTS="-Dorg.gradle.caching=true \
|
||||
-Dorg.gradle.daemon=false \
|
||||
-Dorg.gradle.parallel=true \
|
||||
-Dorg.gradle.configureondemand=true \
|
||||
-Xmx2g"
|
||||
|
||||
# Copy build files in optimal order for caching
|
||||
COPY gradlew gradlew.bat gradle.properties settings.gradle.kts ./
|
||||
COPY gradle/ gradle/
|
||||
COPY platform/ platform/
|
||||
COPY build.gradle.kts ./
|
||||
|
||||
# Create standalone project structure when using template generically
|
||||
RUN if [ "${SERVICE_PATH}" = "." ]; then \
|
||||
echo "Creating isolated standalone Spring Boot application..."; \
|
||||
mkdir -p /tmp/standalone-app/src/main/kotlin/com/example /tmp/standalone-app/src/main/resources; \
|
||||
cd /tmp/standalone-app; \
|
||||
echo 'plugins { id("org.springframework.boot") version "3.2.0"; id("io.spring.dependency-management") version "1.1.4"; kotlin("jvm") version "2.2.0"; kotlin("plugin.spring") version "2.2.0" }' > build.gradle.kts; \
|
||||
echo 'group = "com.example"; version = "1.0.0"; java { sourceCompatibility = JavaVersion.VERSION_21 }' >> build.gradle.kts; \
|
||||
echo 'repositories { mavenCentral() }' >> build.gradle.kts; \
|
||||
echo 'dependencies { implementation("org.springframework.boot:spring-boot-starter-web"); testImplementation("org.springframework.boot:spring-boot-starter-test") }' >> build.gradle.kts; \
|
||||
echo 'package com.example; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.runApplication; @SpringBootApplication class Application; fun main(args: Array<String>) { runApplication<Application>(*args) }' > src/main/kotlin/com/example/Application.kt; \
|
||||
echo 'rootProject.name = "standalone-app"' > settings.gradle.kts; \
|
||||
cp /workspace/gradlew /workspace/gradlew.bat .; \
|
||||
cp -r /workspace/gradle .; \
|
||||
echo "Building standalone application..."; \
|
||||
./gradlew bootJar --no-daemon --info; \
|
||||
cp build/libs/*.jar /workspace/app.jar; \
|
||||
else \
|
||||
echo "Building specific service: ${SERVICE_NAME}"; \
|
||||
./gradlew :${SERVICE_NAME}:dependencies --no-daemon --info; \
|
||||
./gradlew :${SERVICE_NAME}:bootJar --no-daemon --info; \
|
||||
cp ${SERVICE_PATH}/build/libs/*.jar /workspace/app.jar; \
|
||||
fi
|
||||
|
||||
# ===================================================================
|
||||
# Runtime Stage
|
||||
# ===================================================================
|
||||
FROM eclipse-temurin:${JAVA_VERSION}-jre-alpine AS runtime
|
||||
|
||||
# Metadata
|
||||
LABEL service="${SERVICE_NAME}" \
|
||||
version="1.0.0" \
|
||||
maintainer="Meldestelle Development Team" \
|
||||
java.version="${JAVA_VERSION}"
|
||||
|
||||
# Build arguments
|
||||
ARG APP_USER=appuser
|
||||
ARG APP_GROUP=appgroup
|
||||
ARG APP_UID=1001
|
||||
ARG APP_GID=1001
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# System setup
|
||||
RUN apk update && \
|
||||
apk upgrade && \
|
||||
apk add --no-cache curl jq tzdata && \
|
||||
rm -rf /var/cache/apk/*
|
||||
|
||||
# Non-root user creation
|
||||
RUN addgroup -g ${APP_GID} -S ${APP_GROUP} && \
|
||||
adduser -u ${APP_UID} -S ${APP_USER} -G ${APP_GROUP} -h /app -s /bin/sh
|
||||
|
||||
# Directory setup
|
||||
RUN mkdir -p /app/logs /app/tmp && \
|
||||
chown -R ${APP_USER}:${APP_GROUP} /app
|
||||
|
||||
# Re-declare build arguments for runtime stage
|
||||
ARG SERVICE_PATH=.
|
||||
ARG SERVICE_NAME=spring-boot-service
|
||||
|
||||
# Copy JAR (different locations for standalone vs service-specific builds)
|
||||
COPY --from=builder --chown=${APP_USER}:${APP_GROUP} \
|
||||
/workspace/app.jar app.jar
|
||||
|
||||
USER ${APP_USER}
|
||||
|
||||
# Expose ports (runtime port configured via environment)
|
||||
EXPOSE 8080 5005
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=15s --timeout=3s --start-period=40s --retries=3 \
|
||||
CMD curl -fsS --max-time 2 http://localhost:${SERVER_PORT:-8080}/actuator/health/readiness || exit 1
|
||||
|
||||
# JVM configuration
|
||||
ENV JAVA_OPTS="-XX:MaxRAMPercentage=80.0 \
|
||||
-XX:+UseG1GC \
|
||||
-XX:+UseStringDeduplication \
|
||||
-XX:+UseContainerSupport \
|
||||
-Djava.security.egd=file:/dev/./urandom \
|
||||
-Djava.awt.headless=true \
|
||||
-Dfile.encoding=UTF-8 \
|
||||
-Duser.timezone=UTC \
|
||||
-Dmanagement.endpoints.web.exposure.include=health,info,metrics,prometheus"
|
||||
|
||||
# Spring Boot configuration
|
||||
ENV SPRING_OUTPUT_ANSI_ENABLED=ALWAYS
|
||||
ENV LOGGING_LEVEL_ROOT=INFO
|
||||
ENV SERVER_PORT=8080
|
||||
|
||||
# Startup command with debug support
|
||||
ENTRYPOINT ["sh", "-c", "\
|
||||
if [ \"${DEBUG:-false}\" = \"true\" ]; then \
|
||||
echo 'Starting ${SERVICE_NAME} in DEBUG mode on port 5005...'; \
|
||||
exec java $JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -jar app.jar; \
|
||||
else \
|
||||
exec java $JAVA_OPTS -jar app.jar; \
|
||||
fi"]
|
||||
@@ -0,0 +1,153 @@
|
||||
name: meldestelle-hardcoded
|
||||
|
||||
services:
|
||||
# --- DATENBANK ---
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: meldestelle-postgres
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
POSTGRES_USER: pg-user
|
||||
POSTGRES_PASSWORD: pg-password
|
||||
POSTGRES_DB: meldestelle
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
# Falls du Init-Scripte hast, lassen wir die erstmal weg,
|
||||
# um Fehlerquellen zu reduzieren, oder lassen den Pfad, falls er existiert:
|
||||
- ./docker/core/postgres:/docker-entrypoint-initdb.d:Z
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "pg_isready -U pg-user -d meldestelle" ]
|
||||
interval: 1s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
networks:
|
||||
- meldestelle-network
|
||||
|
||||
# --- DATENBANK-MANAGEMENT-TOOL ---
|
||||
pgadmin:
|
||||
image: dpage/pgadmin4:8
|
||||
container_name: pgadmin4_container
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8888:80"
|
||||
environment:
|
||||
PGADMIN_DEFAULT_EMAIL: user@domain.com
|
||||
PGADMIN_DEFAULT_PASSWORD: strong-password
|
||||
volumes:
|
||||
- pgadmin-data:/var/lib/pgadmin
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "wget --spider -q http://localhost:80/ || exit 1" ]
|
||||
interval: 1s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
networks:
|
||||
- meldestelle-network
|
||||
|
||||
# --- CACHE ---
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: meldestelle-redis
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "6379:6379"
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
command: redis-server --appendonly yes
|
||||
healthcheck:
|
||||
test: [ "CMD", "redis-cli" ]
|
||||
interval: 1s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
networks:
|
||||
- meldestelle-network
|
||||
|
||||
# --- IDENTITY PROVIDER (Wartet auf Postgres) ---
|
||||
keycloak:
|
||||
image: quay.io/keycloak/keycloak:26.4
|
||||
container_name: meldestelle-keycloak
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
KC_HEALTH_ENABLED: true
|
||||
KC_METRICS_ENABLED: true
|
||||
KC_BOOTSTRAP_ADMIN_USERNAME: kc-admin
|
||||
KC_BOOTSTRAP_ADMIN_PASSWORD: kc-password
|
||||
KC_DB: postgres
|
||||
KC_DB_URL: jdbc:postgresql://postgres:5432/meldestelle
|
||||
KC_DB_USERNAME: pg-user
|
||||
KC_DB_PASSWORD: pg-password
|
||||
KC_HOSTNAME: localhost
|
||||
ports:
|
||||
- "8180:8080"
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./docker/core/keycloak:/opt/keycloak/data/import:Z
|
||||
command: start-dev --import-realm
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/9000" ]
|
||||
interval: 20s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 60s
|
||||
networks:
|
||||
- meldestelle-network
|
||||
|
||||
# --- MONITORING ---
|
||||
prometheus:
|
||||
image: prom/prometheus:v2.54.1
|
||||
container_name: meldestelle-prometheus
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "9090:9090"
|
||||
volumes:
|
||||
- prometheus-data:/prometheus
|
||||
- ./docker/monitoring/prometheus:/etc/prometheus:Z
|
||||
command:
|
||||
- --config.file=/etc/prometheus/prometheus.yaml
|
||||
- --storage.tsdb.retention.time=15d
|
||||
healthcheck:
|
||||
test: [ "CMD", "wget", "--spider", "-q", "http://localhost:9090/-/healthy" ]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
networks:
|
||||
- meldestelle-network
|
||||
|
||||
grafana:
|
||||
image: grafana/grafana:11.3.0
|
||||
container_name: meldestelle-grafana
|
||||
environment:
|
||||
GF_SECURITY_ADMIN_USER: gf-admin
|
||||
GF_SECURITY_ADMIN_PASSWORD: gf-password
|
||||
ports:
|
||||
- "3000:3000"
|
||||
volumes:
|
||||
- grafana-data:/var/lib/grafana
|
||||
- ./docker/monitoring/grafana:/etc/grafana/provisioning:Z
|
||||
depends_on:
|
||||
- prometheus
|
||||
healthcheck:
|
||||
test: [ "CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/api/health" ]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
networks:
|
||||
- meldestelle-network
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
pgadmin-data:
|
||||
redis-data:
|
||||
prometheus-data:
|
||||
grafana-data:
|
||||
|
||||
networks:
|
||||
meldestelle-network:
|
||||
driver: bridge
|
||||
@@ -0,0 +1 @@
|
||||
services: {}
|
||||
@@ -0,0 +1 @@
|
||||
services: {}
|
||||
@@ -0,0 +1,368 @@
|
||||
name: "${PROJECT_NAME:-meldestelle}"
|
||||
|
||||
services:
|
||||
# ==========================================
|
||||
# CORE INFRASTRUCTURE
|
||||
# ==========================================
|
||||
|
||||
# --- DATABASE: PostgreSQL ---
|
||||
postgres:
|
||||
image: "postgres:16-alpine"
|
||||
container_name: "${PROJECT_NAME}-postgres"
|
||||
restart: "${RESTART_POLICY}"
|
||||
ports:
|
||||
- "${POSTGRES_PORT}"
|
||||
environment:
|
||||
POSTGRES_USER: "${POSTGRES_USER}"
|
||||
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
|
||||
POSTGRES_DB: "${POSTGRES_DB}"
|
||||
volumes:
|
||||
- "postgres-data:/var/lib/postgresql/data"
|
||||
- "../config/backend/infrastructure/postgres:/docker-entrypoint-initdb.d:Z"
|
||||
- "../config/backend/infrastructure/postgres/postgresql.conf:/etc/postgresql/postgresql.conf:Z"
|
||||
command: [ "postgres", "-c", "config_file=/etc/postgresql/postgresql.conf" ]
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}" ]
|
||||
interval: "5s"
|
||||
timeout: "5s"
|
||||
retries: "5"
|
||||
start_period: "10s"
|
||||
networks:
|
||||
meldestelle-network:
|
||||
aliases:
|
||||
- "postgres"
|
||||
|
||||
# --- CACHE: Redis ---
|
||||
redis:
|
||||
image: "redis:8.4-alpine"
|
||||
container_name: "${PROJECT_NAME}-redis"
|
||||
restart: "${RESTART_POLICY}"
|
||||
ports:
|
||||
- "${REDIS_PORT}"
|
||||
volumes:
|
||||
- "redis-data:/data"
|
||||
- "../config/backend/infrastructure/redis/redis.conf:/usr/local/etc/redis/redis.conf:Z"
|
||||
command: [ "sh", "-lc", "exec redis-server /usr/local/etc/redis/redis.conf ${REDIS_PASSWORD:+--requirepass $REDIS_PASSWORD}" ]
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "redis-cli -a \"$REDIS_PASSWORD\" ping | grep PONG" ]
|
||||
interval: "5s"
|
||||
timeout: "5s"
|
||||
retries: "3"
|
||||
networks:
|
||||
meldestelle-network:
|
||||
aliases:
|
||||
- "redis"
|
||||
|
||||
# --- IAM: Keycloak ---
|
||||
keycloak:
|
||||
image: "meldestelle-keycloak:latest"
|
||||
container_name: "${PROJECT_NAME}-keycloak"
|
||||
restart: "${RESTART_POLICY}"
|
||||
build:
|
||||
context: "../config/backend/infrastructure/keycloak"
|
||||
args:
|
||||
KEYCLOAK_IMAGE_TAG: "26.4"
|
||||
environment:
|
||||
# Admin Credentials aus .env
|
||||
KC_BOOTSTRAP_ADMIN_USERNAME: "${KC_ADMIN_USER}"
|
||||
KC_BOOTSTRAP_ADMIN_PASSWORD: "${KC_ADMIN_PASSWORD}"
|
||||
|
||||
# DB Verbindung (Nutzt interne Docker-Namen, daher fest 'postgres')
|
||||
KC_DB: "postgres"
|
||||
KC_DB_URL: "jdbc:postgresql://postgres:5432/pg-${PROJECT_NAME}-db"
|
||||
KC_DB_USERNAME: "${POSTGRES_USER}"
|
||||
KC_DB_PASSWORD: "${POSTGRES_PASSWORD}"
|
||||
|
||||
# Hostname & Proxy (Wichtig für Dev!)
|
||||
KC_HOSTNAME: "localhost"
|
||||
# Erlaubte Backend-Calls via http://keycloak:8080 im Docker Netzwerk
|
||||
KC_HOSTNAME_STRICT: "false"
|
||||
KC_HOSTNAME_STRICT_HTTPS: "false"
|
||||
KC_HTTP_ENABLED: "true"
|
||||
KC_PROXY_HEADERS: "xforwarded"
|
||||
|
||||
# Health & Metrics sind schon im Image gebaut, aber env schadet nicht
|
||||
KC_HEALTH_ENABLED: "true"
|
||||
KC_METRICS_ENABLED: "true"
|
||||
ports:
|
||||
- "${KC_PORT}"
|
||||
- "9000:9000"
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: "service_healthy"
|
||||
volumes:
|
||||
# Import Realm
|
||||
- "../config/backend/infrastructure/keycloak:/opt/keycloak/data/import:Z"
|
||||
command: "start --optimized --import-realm"
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/9000" ]
|
||||
interval: "10s"
|
||||
timeout: "5s"
|
||||
retries: "5"
|
||||
start_period: "60s"
|
||||
networks:
|
||||
meldestelle-network:
|
||||
aliases:
|
||||
- "keycloak"
|
||||
|
||||
# --- DATENBANK-MANAGEMENT-TOOL: pgAdmin4 ---
|
||||
pgadmin:
|
||||
image: "dpage/pgadmin4:8"
|
||||
container_name: "${PROJECT_NAME}-pgadmin"
|
||||
restart: "${RESTART_POLICY}"
|
||||
ports:
|
||||
- "${PGADMIN_PORT:-8888:80}"
|
||||
environment:
|
||||
PGADMIN_DEFAULT_EMAIL: "${PGADMIN_EMAIL}"
|
||||
PGADMIN_DEFAULT_PASSWORD: "${PGADMIN_PASSWORD}"
|
||||
volumes:
|
||||
- "pgadmin-data:/var/lib/pgadmin"
|
||||
networks:
|
||||
meldestelle-network:
|
||||
aliases:
|
||||
- "pgadmin"
|
||||
|
||||
# --- MONITORING: Prometheus ---
|
||||
prometheus:
|
||||
image: "prom/prometheus:v3.7.3"
|
||||
container_name: "${PROJECT_NAME}-prometheus"
|
||||
restart: "${RESTART_POLICY}"
|
||||
ports:
|
||||
- "${PROMETHEUS_PORT}"
|
||||
volumes:
|
||||
- "prometheus-data:/prometheus"
|
||||
- "../config/backend/infrastructure/monitoring/prometheus:/etc/prometheus:Z"
|
||||
command:
|
||||
- --web.enable-lifecycle
|
||||
- --config.file=/etc/prometheus/prometheus.yaml
|
||||
- --storage.tsdb.retention.time=15d
|
||||
healthcheck:
|
||||
test: [ "CMD", "wget", "--spider", "-q", "http://localhost:9090/-/healthy" ]
|
||||
interval: "30s"
|
||||
timeout: "10s"
|
||||
retries: "3"
|
||||
start_period: "30s"
|
||||
networks:
|
||||
meldestelle-network:
|
||||
aliases:
|
||||
- "prometheus"
|
||||
|
||||
# --- MONITORING: Grafana ---
|
||||
grafana:
|
||||
image: grafana/grafana:12.3
|
||||
container_name: ${PROJECT_NAME}-grafana
|
||||
restart: "${RESTART_POLICY}"
|
||||
environment:
|
||||
GF_SECURITY_ADMIN_USER: ${GF_ADMIN_USER}
|
||||
GF_SECURITY_ADMIN_PASSWORD: ${GF_ADMIN_PASSWORD}
|
||||
ports:
|
||||
- "${GF_PORT}"
|
||||
volumes:
|
||||
- grafana-data:/var/lib/grafana
|
||||
# Provisioning (datasources/dashboards) from central config
|
||||
- ../config/backend/infrastructure/monitoring/grafana/provisioning:/etc/grafana/provisioning:Z
|
||||
# Dashboards directory (referenced by provisioning file path: /var/lib/grafana/dashboards)
|
||||
- ../config/backend/infrastructure/monitoring/grafana/dashboards:/var/lib/grafana/dashboards:Z
|
||||
depends_on:
|
||||
- prometheus
|
||||
healthcheck:
|
||||
test: [ "CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/api/health" ]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
networks:
|
||||
meldestelle-network:
|
||||
aliases:
|
||||
- grafana
|
||||
|
||||
# --- CONSUL ---
|
||||
consul:
|
||||
image: "hashicorp/consul:1.22.1"
|
||||
container_name: "${PROJECT_NAME}-consul"
|
||||
restart: "${RESTART_POLICY}"
|
||||
ports:
|
||||
- "${CONSUL_PORT}"
|
||||
- "${CONSUL_UDP_PORT}"
|
||||
command: "agent -server -bootstrap-expect=1 -ui -client=0.0.0.0"
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl", "-f", "http://localhost:8500/v1/status/leader" ]
|
||||
interval: "30s"
|
||||
timeout: "10s"
|
||||
retries: "3"
|
||||
networks:
|
||||
meldestelle-network:
|
||||
aliases:
|
||||
- "consul"
|
||||
|
||||
# --- API-GATEWAY: Spring Cloud Gateway ---
|
||||
api-gateway:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: backend/infrastructure/gateway/Dockerfile
|
||||
args:
|
||||
# Build-Args aus deinen .env Dateien (werden hier statisch benötigt für den Build)
|
||||
GRADLE_VERSION: "9.1.0"
|
||||
JAVA_VERSION: "21"
|
||||
VERSION: "1.0.0-SNAPSHOT"
|
||||
BUILD_DATE: "2025-12-04"
|
||||
container_name: "${PROJECT_NAME}-gateway"
|
||||
restart: "${RESTART_POLICY}"
|
||||
ports:
|
||||
- "${GATEWAY_SERVER_PORT}"
|
||||
- "${GATEWAY_DEBUG_PORT}"
|
||||
environment:
|
||||
# server.port must be an integer. Do not pass host:container mapping here.
|
||||
SERVER_PORT: "8081"
|
||||
SPRING_PROFILES_ACTIVE: "docker"
|
||||
DEBUG: "true"
|
||||
|
||||
# --- KEYCLOAK ---
|
||||
# Container-zu-Container Kommunikation (intern)
|
||||
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: "http://keycloak:8080/realms/meldestelle"
|
||||
# JWK Set Uri erzwingen, damit er nicht über den Issuer (localhost vs keycloak) stolpert
|
||||
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI: "http://keycloak:8080/realms/meldestelle/protocol/openid-connect/certs"
|
||||
|
||||
# --- CONSUL ---
|
||||
SPRING_CLOUD_CONSUL_HOST: "consul"
|
||||
# Consul port must be an integer (container internal port)
|
||||
SPRING_CLOUD_CONSUL_PORT: "8500"
|
||||
# WICHTIG: Das Gateway muss wissen, wie es von anderen Containern erreicht wird (nicht localhost!)
|
||||
SPRING_CLOUD_CONSUL_DISCOVERY_HOSTNAME: "api-gateway"
|
||||
# Wichtig für Docker: Wir wollen IP-Adressen registrieren, keine Hostnames, die DNS brauchen
|
||||
SPRING_CLOUD_CONSUL_DISCOVERY_PREFER_IP_ADDRESS: "true"
|
||||
|
||||
# --- POSTGRES ---
|
||||
SPRING_DATASOURCE_URL: "jdbc:postgresql://postgres:5432/${POSTGRES_DB}"
|
||||
SPRING_DATASOURCE_USERNAME: "${POSTGRES_USER}"
|
||||
SPRING_DATASOURCE_PASSWORD: "${POSTGRES_PASSWORD}"
|
||||
|
||||
# --- LOGGING ---
|
||||
LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_GATEWAY: "DEBUG"
|
||||
LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_SECURITY: "DEBUG"
|
||||
|
||||
depends_on:
|
||||
consul:
|
||||
condition: "service_healthy"
|
||||
postgres:
|
||||
condition: "service_healthy"
|
||||
keycloak:
|
||||
condition: "service_healthy"
|
||||
networks:
|
||||
meldestelle-network:
|
||||
aliases:
|
||||
- "api-gateway"
|
||||
|
||||
# ==========================================
|
||||
# MICROSERVICES
|
||||
# ==========================================
|
||||
ping-service:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: backend/services/ping/Dockerfile
|
||||
args:
|
||||
GRADLE_VERSION: 9.1.0
|
||||
JAVA_VERSION: 21
|
||||
VERSION: 1.0.0
|
||||
BUILD_DATE: "2025-11-29"
|
||||
container_name: ${PROJECT_NAME}-ping-service
|
||||
restart: "${RESTART_POLICY}"
|
||||
ports:
|
||||
- "${PING_PORT}"
|
||||
- "${PING_DEBUG_PORT}"
|
||||
environment:
|
||||
SPRING_PROFILES_ACTIVE: docker
|
||||
DEBUG: "true"
|
||||
SERVER_PORT: 8082
|
||||
|
||||
# --- CONSUL ---
|
||||
SPRING_CLOUD_CONSUL_HOST: consul
|
||||
SPRING_CLOUD_CONSUL_PORT: 8500
|
||||
SPRING_CLOUD_CONSUL_DISCOVERY_HOSTNAME: ping-service
|
||||
|
||||
# - DATENBANK VERBINDUNG -
|
||||
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/${POSTGRES_DB}
|
||||
SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER}
|
||||
SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
SPRING_JPA_HIBERNATE_DDL_AUTO: validate
|
||||
|
||||
# --- REDIS ---
|
||||
SPRING_DATA_REDIS_HOST: redis
|
||||
SPRING_DATA_REDIS_PORT: 6379
|
||||
# Optional: if REDIS_PASSWORD is set in .env, forward it to Spring
|
||||
SPRING_DATA_REDIS_PASSWORD: "${REDIS_PASSWORD}"
|
||||
# Make initial connection a bit more tolerant on startup races
|
||||
SPRING_DATA_REDIS_CONNECT_TIMEOUT: 5s
|
||||
depends_on:
|
||||
consul:
|
||||
condition: service_healthy
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
keycloak:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
meldestelle-network:
|
||||
aliases:
|
||||
- ping-service
|
||||
|
||||
# ==========================================
|
||||
# FRONTEND
|
||||
# ==========================================
|
||||
|
||||
# --- WEB-APP ---
|
||||
web-app:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: ../config/frontends/web-app/Dockerfile
|
||||
args:
|
||||
GRADLE_VERSION: ${DOCKER_GRADLE_VERSION:-9.1.0}
|
||||
JAVA_VERSION: ${DOCKER_JAVA_VERSION:-21}
|
||||
NODE_VERSION: ${DOCKER_NODE_VERSION:-22.21.0}
|
||||
NGINX_IMAGE_TAG: ${DOCKER_NGINX_VERSION:-1.28.0-alpine}
|
||||
WEB_BUILD_PROFILE: ${WEB_BUILD_PROFILE:-dev}
|
||||
container_name: ${PROJECT_NAME}-web-app
|
||||
restart: "${RESTART_POLICY}"
|
||||
ports:
|
||||
- "${WEB_APP_PORT}"
|
||||
volumes:
|
||||
# Mount production nginx config (can be adjusted per env)
|
||||
- ../config/nginx/nginx.prod.conf:/etc/nginx/nginx.conf:Z,ro
|
||||
depends_on:
|
||||
api-gateway:
|
||||
condition: service_started
|
||||
networks:
|
||||
meldestelle-network:
|
||||
aliases:
|
||||
- web-app
|
||||
|
||||
desktop-app:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: ../config/frontends/desktop-app/Dockerfile
|
||||
container_name: ${PROJECT_NAME}-desktop-app
|
||||
restart: "${RESTART_POLICY}"
|
||||
environment:
|
||||
API_BASE_URL: http://api-gateway:8081
|
||||
ports:
|
||||
- "${DESKTOP_APP_VNC_PORT}"
|
||||
- "${DESKTOP_APP_NOVNC_PORT}"
|
||||
depends_on:
|
||||
api-gateway:
|
||||
condition: service_started
|
||||
networks:
|
||||
meldestelle-network:
|
||||
aliases:
|
||||
- desktop-app
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
pgadmin-data:
|
||||
redis-data:
|
||||
prometheus-data:
|
||||
grafana-data:
|
||||
|
||||
networks:
|
||||
meldestelle-network:
|
||||
driver: bridge
|
||||
@@ -0,0 +1,94 @@
|
||||
# ===================================================================
|
||||
# Multi-Stage Dockerfile für Meldestelle Desktop-App (VNC)
|
||||
# ===================================================================
|
||||
|
||||
# ===================================================================
|
||||
# Stage 1: Build Stage - Kotlin Desktop-App kompilieren
|
||||
# ===================================================================
|
||||
FROM gradle:8-jdk21-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Kopiere Gradle-Konfiguration und Wrapper
|
||||
COPY build.gradle.kts settings.gradle.kts gradle.properties ./
|
||||
COPY gradle ./gradle
|
||||
COPY gradlew ./
|
||||
|
||||
# Kopiere alle notwendigen Module für Multi-Modul-Projekt
|
||||
COPY frontend ./frontend
|
||||
COPY backend ./backend
|
||||
COPY core ./core
|
||||
COPY domains ./domains
|
||||
COPY platform ./platform
|
||||
COPY docs ./docs
|
||||
|
||||
# Setze Gradle-Wrapper Berechtigung
|
||||
RUN chmod +x ./gradlew
|
||||
|
||||
# Dependencies downloaden (für besseres Caching)
|
||||
RUN ./gradlew :frontend:shells:meldestelle-portal:dependencies --no-configure-on-demand
|
||||
|
||||
# Desktop-App kompilieren (createDistributable für native Distribution)
|
||||
RUN ./gradlew :frontend:shells:meldestelle-portal:createDistributable --no-configure-on-demand
|
||||
|
||||
# ===================================================================
|
||||
# Stage 2: Runtime Stage - Ubuntu mit VNC + noVNC
|
||||
# ===================================================================
|
||||
FROM ubuntu:22.04
|
||||
|
||||
# Verhindere interaktive Installationen
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Installiere System-Dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
openjdk-21-jdk \
|
||||
xvfb \
|
||||
x11vnc \
|
||||
novnc \
|
||||
websockify \
|
||||
xfce4 \
|
||||
xfce4-goodies \
|
||||
curl \
|
||||
wget \
|
||||
unzip \
|
||||
supervisor \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Arbeitsverzeichnis
|
||||
WORKDIR /app
|
||||
|
||||
# Kopiere kompilierte Desktop-App von Build-Stage
|
||||
COPY --from=builder /app/frontend/shells/meldestelle-portal/build/compose/binaries/main/desktop/ ./desktop-app/
|
||||
|
||||
# Kopiere Scripts
|
||||
COPY dockerfiles/clients/desktop-app/entrypoint.sh /entrypoint.sh
|
||||
COPY dockerfiles/clients/desktop-app/health-check.sh /opt/health-check.sh
|
||||
COPY dockerfiles/clients/desktop-app/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||
|
||||
# Setze Permissions
|
||||
RUN chmod +x /entrypoint.sh /opt/health-check.sh
|
||||
|
||||
# Erstelle VNC-User
|
||||
RUN useradd -m -s /bin/bash vncuser && \
|
||||
mkdir -p /home/vncuser/.vnc && \
|
||||
chown -R vncuser:vncuser /home/vncuser && \
|
||||
chown -R vncuser:vncuser /app
|
||||
|
||||
# VNC und noVNC Ports
|
||||
EXPOSE 5901 6080
|
||||
|
||||
# Environment Variables
|
||||
ENV DISPLAY=:99
|
||||
ENV VNC_PORT=5901
|
||||
ENV NOVNC_PORT=6080
|
||||
ENV API_BASE_URL=http://api-gateway:8081
|
||||
|
||||
# Health-Check
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
||||
CMD /opt/health-check.sh
|
||||
|
||||
# User wechseln
|
||||
USER vncuser
|
||||
|
||||
# Entrypoint
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
@@ -0,0 +1,104 @@
|
||||
#!/bin/bash
|
||||
# ===================================================================
|
||||
# Entrypoint-Script für Meldestelle Desktop-App (VNC)
|
||||
# ===================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# Logging-Funktion
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
|
||||
}
|
||||
|
||||
log "Starting Meldestelle Desktop-App VNC Container..."
|
||||
|
||||
# Environment-Variablen setzen
|
||||
export DISPLAY=${DISPLAY:-:99}
|
||||
export VNC_PORT=${VNC_PORT:-5901}
|
||||
export NOVNC_PORT=${NOVNC_PORT:-6080}
|
||||
export API_BASE_URL=${API_BASE_URL:-http://api-gateway:8081}
|
||||
|
||||
log "Environment:"
|
||||
log " DISPLAY: $DISPLAY"
|
||||
log " VNC_PORT: $VNC_PORT"
|
||||
log " NOVNC_PORT: $NOVNC_PORT"
|
||||
log " API_BASE_URL: $API_BASE_URL"
|
||||
|
||||
# Erstelle .Xauthority wenn nicht vorhanden
|
||||
touch /home/vncuser/.Xauthority
|
||||
|
||||
# 1. Starte X11 Virtual Display (Xvfb)
|
||||
log "Starting Xvfb on display $DISPLAY..."
|
||||
Xvfb $DISPLAY -screen 0 1280x1024x24 -ac +extension GLX +render -noreset &
|
||||
XVFB_PID=$!
|
||||
|
||||
# Warte bis X11 bereit ist
|
||||
sleep 3
|
||||
|
||||
# 2. Starte Desktop Environment (XFCE4)
|
||||
log "Starting XFCE4 desktop environment..."
|
||||
startxfce4 &
|
||||
XFCE_PID=$!
|
||||
|
||||
# Warte bis Desktop bereit ist
|
||||
sleep 5
|
||||
|
||||
# 3. Starte VNC Server
|
||||
log "Starting VNC server on port $VNC_PORT..."
|
||||
x11vnc -display $DISPLAY -forever -usepw -create -rfbport $VNC_PORT -nopw -shared -bg
|
||||
VNC_PID=$!
|
||||
|
||||
# 4. Starte noVNC Web Interface
|
||||
log "Starting noVNC web interface on port $NOVNC_PORT..."
|
||||
websockify --web=/usr/share/novnc/ $NOVNC_PORT localhost:$VNC_PORT &
|
||||
NOVNC_PID=$!
|
||||
|
||||
# 5. Warte bis Services bereit sind
|
||||
sleep 10
|
||||
|
||||
# 6. Starte Desktop-App
|
||||
log "Starting Meldestelle Desktop-App..."
|
||||
cd /app/desktop-app
|
||||
export API_BASE_URL=$API_BASE_URL
|
||||
|
||||
# Finde die ausführbare Datei
|
||||
if [ -f "client/bin/client" ]; then
|
||||
DESKTOP_APP="client/bin/client"
|
||||
elif [ -f "bin/client" ]; then
|
||||
DESKTOP_APP="bin/client"
|
||||
elif [ -f "client" ]; then
|
||||
DESKTOP_APP="client"
|
||||
else
|
||||
log "ERROR: Desktop-App executable not found!"
|
||||
log "Contents of /app/desktop-app:"
|
||||
ls -la /app/desktop-app/
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Found desktop app: $DESKTOP_APP"
|
||||
chmod +x "$DESKTOP_APP"
|
||||
|
||||
# Starte Desktop-App
|
||||
./"$DESKTOP_APP" &
|
||||
APP_PID=$!
|
||||
|
||||
log "All services started successfully!"
|
||||
log "VNC: vnc://localhost:$VNC_PORT"
|
||||
log "noVNC: http://localhost:$NOVNC_PORT/vnc.html"
|
||||
|
||||
# Cleanup-Funktion
|
||||
cleanup() {
|
||||
log "Shutting down services..."
|
||||
kill $APP_PID 2>/dev/null || true
|
||||
kill $NOVNC_PID 2>/dev/null || true
|
||||
kill $VNC_PID 2>/dev/null || true
|
||||
kill $XFCE_PID 2>/dev/null || true
|
||||
kill $XVFB_PID 2>/dev/null || true
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Signal-Handler
|
||||
trap cleanup SIGTERM SIGINT
|
||||
|
||||
# Warten auf Prozesse
|
||||
wait $APP_PID
|
||||
@@ -0,0 +1,63 @@
|
||||
#!/bin/bash
|
||||
# ===================================================================
|
||||
# Health-Check-Script für Meldestelle Desktop-App (VNC)
|
||||
# ===================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# Environment-Variablen
|
||||
VNC_PORT=${VNC_PORT:-5901}
|
||||
NOVNC_PORT=${NOVNC_PORT:-6080}
|
||||
DISPLAY=${DISPLAY:-:99}
|
||||
|
||||
# Logging-Funktion
|
||||
log() {
|
||||
echo "[HEALTH] $1"
|
||||
}
|
||||
|
||||
# 1. Überprüfe X11 Display
|
||||
if ! xdpyinfo -display $DISPLAY >/dev/null 2>&1; then
|
||||
log "ERROR: X11 display $DISPLAY is not running"
|
||||
exit 1
|
||||
fi
|
||||
log "✓ X11 display $DISPLAY is running"
|
||||
|
||||
# 2. Überprüfe VNC Server
|
||||
if ! netstat -ln | grep -q ":$VNC_PORT "; then
|
||||
log "ERROR: VNC server is not listening on port $VNC_PORT"
|
||||
exit 1
|
||||
fi
|
||||
log "✓ VNC server is running on port $VNC_PORT"
|
||||
|
||||
# 3. Überprüfe noVNC Web Interface
|
||||
if ! curl -f -s "http://localhost:$NOVNC_PORT/" > /dev/null 2>&1; then
|
||||
log "ERROR: noVNC web interface is not responding on port $NOVNC_PORT"
|
||||
exit 1
|
||||
fi
|
||||
log "✓ noVNC web interface is running on port $NOVNC_PORT"
|
||||
|
||||
# 4. Überprüfe ob Desktop-App läuft (optional, da sie crashen könnte)
|
||||
if pgrep -f "client" >/dev/null 2>&1; then
|
||||
log "✓ Desktop-App is running"
|
||||
else
|
||||
log "⚠ Desktop-App is not running (may have crashed or not started yet)"
|
||||
# Nicht als Fehler behandeln, da die App crashen könnte
|
||||
fi
|
||||
|
||||
# 5. Überprüfe Xvfb
|
||||
if ! pgrep -f "Xvfb" >/dev/null 2>&1; then
|
||||
log "ERROR: Xvfb is not running"
|
||||
exit 1
|
||||
fi
|
||||
log "✓ Xvfb is running"
|
||||
|
||||
# 6. Überprüfe XFCE4
|
||||
if ! pgrep -f "xfce4" >/dev/null 2>&1; then
|
||||
log "WARNING: XFCE4 desktop might not be running"
|
||||
# Nicht als kritischer Fehler behandeln
|
||||
else
|
||||
log "✓ XFCE4 desktop environment is running"
|
||||
fi
|
||||
|
||||
log "All critical services are healthy"
|
||||
exit 0
|
||||
@@ -0,0 +1,54 @@
|
||||
[supervisord]
|
||||
nodaemon=true
|
||||
user=root
|
||||
logfile=/var/log/supervisor/supervisord.log
|
||||
pidfile=/var/run/supervisord.pid
|
||||
|
||||
[program:xvfb]
|
||||
command=Xvfb :99 -screen 0 1280x1024x24 -ac +extension GLX +render -noreset
|
||||
user=vncuser
|
||||
autostart=true
|
||||
autorestart=true
|
||||
priority=100
|
||||
stdout_logfile=/var/log/supervisor/xvfb.log
|
||||
stderr_logfile=/var/log/supervisor/xvfb.log
|
||||
|
||||
[program:xfce4]
|
||||
command=startxfce4
|
||||
user=vncuser
|
||||
environment=DISPLAY=":99"
|
||||
autostart=true
|
||||
autorestart=true
|
||||
priority=200
|
||||
stdout_logfile=/var/log/supervisor/xfce4.log
|
||||
stderr_logfile=/var/log/supervisor/xfce4.log
|
||||
|
||||
[program:vnc]
|
||||
command=x11vnc -display :99 -forever -usepw -create -rfbport 5901 -nopw -shared
|
||||
user=vncuser
|
||||
environment=DISPLAY=":99"
|
||||
autostart=true
|
||||
autorestart=true
|
||||
priority=300
|
||||
stdout_logfile=/var/log/supervisor/vnc.log
|
||||
stderr_logfile=/var/log/supervisor/vnc.log
|
||||
|
||||
[program:novnc]
|
||||
command=websockify --web=/usr/share/novnc/ 6080 localhost:5901
|
||||
user=vncuser
|
||||
autostart=true
|
||||
autorestart=true
|
||||
priority=400
|
||||
stdout_logfile=/var/log/supervisor/novnc.log
|
||||
stderr_logfile=/var/log/supervisor/novnc.log
|
||||
|
||||
[program:desktop-app]
|
||||
command=/app/desktop-app/client/bin/client
|
||||
user=vncuser
|
||||
environment=DISPLAY=":99",API_BASE_URL="http://api-gateway:8081"
|
||||
directory=/app/desktop-app
|
||||
autostart=true
|
||||
autorestart=false
|
||||
priority=500
|
||||
stdout_logfile=/var/log/supervisor/desktop-app.log
|
||||
stderr_logfile=/var/log/supervisor/desktop-app.log
|
||||
@@ -0,0 +1,83 @@
|
||||
# ===================================================================
|
||||
# Multi-Stage Dockerfile für Meldestelle Web-App (Kotlin/JS)
|
||||
# ===================================================================
|
||||
|
||||
# ===================================================================
|
||||
# Stage 1: Build Stage - Kotlin/JS kompilieren
|
||||
# ===================================================================
|
||||
# Build args (build-time only)
|
||||
ARG GRADLE_VERSION
|
||||
ARG JAVA_VERSION
|
||||
ARG NODE_VERSION
|
||||
ARG NGINX_IMAGE_TAG=1.28.0-alpine
|
||||
# Toggle build profile: dev (default) or prod
|
||||
ARG WEB_BUILD_PROFILE=dev
|
||||
FROM gradle:${GRADLE_VERSION}-jdk${JAVA_VERSION} AS builder
|
||||
|
||||
# Install Node.js (version aligned with versions.toml)
|
||||
# Derive major version from NODE_VERSION (e.g., 22.21.0 -> setup_22.x)
|
||||
RUN apt-get update && apt-get install -y curl && \
|
||||
NODE_MAJOR=$(echo "$NODE_VERSION" | cut -d. -f1) && \
|
||||
curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash - && \
|
||||
apt-get install -y nodejs && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Kopiere Gradle-Konfiguration und Wrapper
|
||||
COPY build.gradle.kts settings.gradle.kts gradle.properties ./
|
||||
COPY gradle ./gradle
|
||||
COPY gradlew ./
|
||||
|
||||
# Kopiere alle notwendigen Module für Multi-Modul-Projekt
|
||||
COPY frontend ./frontend
|
||||
COPY backend ./backend
|
||||
COPY core ./core
|
||||
COPY domains ./domains
|
||||
COPY platform ./platform
|
||||
COPY docs ./docs
|
||||
|
||||
# Setze Gradle-Wrapper Berechtigung
|
||||
RUN chmod +x ./gradlew
|
||||
|
||||
# Dependencies downloaden (für besseres Caching)
|
||||
RUN ./gradlew :frontend:shells:meldestelle-portal:dependencies --no-configure-on-demand
|
||||
|
||||
# Kotlin/JS Web-App kompilieren (Profil wählbar über WEB_BUILD_PROFILE)
|
||||
# - dev → jsBrowserDevelopmentExecutable (schneller, Source Maps)
|
||||
# - prod → jsBrowserDistribution (minifiziert, optimiert)
|
||||
RUN if [ "$WEB_BUILD_PROFILE" = "prod" ]; then \
|
||||
./gradlew :frontend:shells:meldestelle-portal:jsBrowserDistribution --no-configure-on-demand -Pproduction=true; \
|
||||
mkdir -p /app/web-dist && cp -r frontend/shells/meldestelle-portal/build/dist/js/productionExecutable/* /app/web-dist/; \
|
||||
else \
|
||||
./gradlew :frontend:shells:meldestelle-portal:jsBrowserDevelopmentExecutable --no-configure-on-demand; \
|
||||
mkdir -p /app/web-dist && cp -r frontend/shells/meldestelle-portal/build/dist/js/developmentExecutable/* /app/web-dist/; \
|
||||
fi
|
||||
|
||||
# ===================================================================
|
||||
# Stage 2: Runtime Stage - Nginx für Static Files + API Proxy
|
||||
# ===================================================================
|
||||
# Build arg controls runtime base image tag (declared globally to allow usage in FROM)
|
||||
FROM nginx:${NGINX_IMAGE_TAG}
|
||||
|
||||
# Installiere curl für Health-Checks
|
||||
RUN apk add --no-cache curl
|
||||
|
||||
# Kopiere kompilierte Web-App von Build-Stage (vereinheitlichtes Ausgabeverzeichnis)
|
||||
COPY --from=builder /app/web-dist/ /usr/share/nginx/html/
|
||||
|
||||
# Kopiere Nginx-Konfiguration
|
||||
COPY dockerfiles/clients/web-app/nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
# Exponiere Port 4000 (statt Standard 80)
|
||||
EXPOSE 4000
|
||||
|
||||
# Downloads (Platzhalter) ausliefern lassen
|
||||
COPY dockerfiles/clients/web-app/downloads/ /usr/share/nginx/html/downloads/
|
||||
|
||||
# Health-Check für Container
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
|
||||
CMD curl -f http://localhost:4000/ || exit 1
|
||||
|
||||
# Starte Nginx
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
@@ -0,0 +1,30 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Meldestelle – Desktop Downloads (Platzhalter)</title>
|
||||
<style>
|
||||
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, 'Helvetica Neue', Arial, 'Noto Sans', 'Liberation Sans', sans-serif; margin: 2rem; }
|
||||
h1 { margin-bottom: .25rem; }
|
||||
.muted { color: #666; }
|
||||
ul { line-height: 1.8; }
|
||||
.card { border: 1px solid #e5e7eb; border-radius: 8px; padding: 1rem 1.25rem; max-width: 720px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Desktop Downloads</h1>
|
||||
<p class="muted">Platzhalter-Verzeichnis. Hier können zukünftig Installer/Archive der Desktop-App bereitgestellt werden.</p>
|
||||
<div class="card">
|
||||
<p>Lege deine Dateien in dieses Verzeichnis im Repository:</p>
|
||||
<pre><code>dockerfiles/clients/web-app/downloads/</code></pre>
|
||||
<p>Oder mounte in Docker Compose ein Host-Verzeichnis auf <code>/usr/share/nginx/html/downloads</code>.</p>
|
||||
<p>Beispiele (geplant):</p>
|
||||
<ul>
|
||||
<li>Meldestelle-Setup-1.0.0.msi (Windows)</li>
|
||||
<li>Meldestelle-1.0.0.dmg (macOS)</li>
|
||||
<li>Meldestelle-1.0.0.deb (Linux)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,101 @@
|
||||
# ===================================================================
|
||||
# Nginx-Konfiguration für Meldestelle Web-App
|
||||
# Static Files + API Proxy zu Gateway
|
||||
# ===================================================================
|
||||
|
||||
# Worker-Prozesse (konfigurierbar über NGINX_WORKER_PROCESSES)
|
||||
worker_processes auto;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
use epoll;
|
||||
multi_accept on;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
# Logging
|
||||
access_log /var/log/nginx/access.log;
|
||||
error_log /var/log/nginx/error.log;
|
||||
|
||||
# Performance Optimizations
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 65;
|
||||
types_hash_max_size 2048;
|
||||
|
||||
# Gzip Kompression für bessere Performance
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_comp_level 6;
|
||||
gzip_types
|
||||
text/plain
|
||||
text/css
|
||||
text/xml
|
||||
text/javascript
|
||||
application/javascript
|
||||
application/xml+rss
|
||||
application/json
|
||||
application/wasm;
|
||||
|
||||
# Upstream für API Gateway
|
||||
upstream api-gateway {
|
||||
server api-gateway:8081;
|
||||
}
|
||||
|
||||
# Server-Block für Web-App
|
||||
server {
|
||||
listen 4000;
|
||||
server_name localhost;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Serve static files (Kotlin/JS compiled files)
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
|
||||
# Cache-Headers für statische Assets
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# WASM Files mit korrektem MIME-Type
|
||||
location ~* \.wasm$ {
|
||||
add_header Content-Type application/wasm;
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
}
|
||||
|
||||
# Proxy API calls zu Gateway
|
||||
location /api/ {
|
||||
proxy_pass http://api-gateway;
|
||||
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;
|
||||
|
||||
# CORS Headers für API-Calls
|
||||
add_header Access-Control-Allow-Origin *;
|
||||
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
|
||||
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
|
||||
|
||||
# Handle preflight requests
|
||||
if ($request_method = 'OPTIONS') {
|
||||
return 204;
|
||||
}
|
||||
}
|
||||
|
||||
# Health-Check Endpoint
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "healthy\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
default: true
|
||||
|
||||
# Entspannte Regeln für Entwickler-Dokumentation
|
||||
MD013: false # Line length - zu restriktiv für technische Docs
|
||||
MD033: false # Inline HTML - manchmal nützlich für Diagramme/Badges
|
||||
MD041: false # First line heading - README.md hat oft Badges
|
||||
|
||||
# Erlaubte Heading-Duplikate (wichtig für API-Docs)
|
||||
MD024:
|
||||
siblings_only: true
|
||||
|
||||
# Lockere Listen-Indentation
|
||||
MD007:
|
||||
indent: 2
|
||||
|
||||
# Code-Blöcke brauchen Language-Tags
|
||||
MD040: true
|
||||
MD046:
|
||||
style: fenced
|
||||
@@ -0,0 +1,17 @@
|
||||
# Generierte Dateien ignorieren
|
||||
docs/api/generated/
|
||||
build/
|
||||
node_modules/
|
||||
kotlin-js-store/
|
||||
|
||||
# Externe Dokumentation
|
||||
.junie/
|
||||
|
||||
# Temporäre/Legacy-Dateien
|
||||
.vscode/
|
||||
.idea/
|
||||
.fleet/
|
||||
|
||||
# Spezielle Markdown-Dateien
|
||||
CHANGELOG.md
|
||||
LICENSE
|
||||
@@ -0,0 +1,6 @@
|
||||
extends:
|
||||
- spectral:oas
|
||||
|
||||
rules:
|
||||
# Keep defaults from spectral:oas; tweak minimal project-specific preferences here if needed
|
||||
info-contact: warn
|
||||
@@ -0,0 +1,5 @@
|
||||
StylesPath = .junie/vale
|
||||
MinAlertLevel = warning
|
||||
|
||||
[*.md]
|
||||
BasedOnStyles = Vale, MoCode
|
||||
@@ -1,185 +0,0 @@
|
||||
# ===================================================================
|
||||
# Prometheus Development Configuration
|
||||
# Enhanced monitoring for Meldestelle development environment
|
||||
# ===================================================================
|
||||
|
||||
global:
|
||||
scrape_interval: 15s # More frequent scraping for development
|
||||
evaluation_interval: 15s # Faster rule evaluation
|
||||
external_labels:
|
||||
cluster: 'meldestelle-dev'
|
||||
environment: 'development'
|
||||
|
||||
# Rule files for alerting (development-friendly)
|
||||
rule_files:
|
||||
- "/etc/prometheus/rules.yml"
|
||||
|
||||
# Scrape configurations for development services
|
||||
scrape_configs:
|
||||
# ===================================================================
|
||||
# Infrastructure Services
|
||||
# ===================================================================
|
||||
|
||||
# Prometheus self-monitoring
|
||||
- job_name: 'prometheus'
|
||||
static_configs:
|
||||
- targets: ['localhost:9090']
|
||||
scrape_interval: 15s
|
||||
metrics_path: '/metrics'
|
||||
|
||||
# ===================================================================
|
||||
# Application Services (Spring Boot)
|
||||
# ===================================================================
|
||||
|
||||
# API Gateway
|
||||
- job_name: 'api-gateway'
|
||||
static_configs:
|
||||
- targets: ['api-gateway:8081']
|
||||
metrics_path: '/actuator/prometheus'
|
||||
scrape_interval: 10s # More frequent for gateway
|
||||
scrape_timeout: 5s
|
||||
params:
|
||||
format: ['prometheus']
|
||||
|
||||
# Auth Server
|
||||
- job_name: 'auth-server'
|
||||
static_configs:
|
||||
- targets: ['auth-server:8081']
|
||||
metrics_path: '/actuator/prometheus'
|
||||
scrape_interval: 15s
|
||||
scrape_timeout: 5s
|
||||
|
||||
# Monitoring Server (self-monitoring)
|
||||
- job_name: 'monitoring-server'
|
||||
static_configs:
|
||||
- targets: ['monitoring-server:8083']
|
||||
metrics_path: '/actuator/prometheus'
|
||||
scrape_interval: 15s
|
||||
scrape_timeout: 5s
|
||||
|
||||
# Ping Service
|
||||
- job_name: 'ping-service'
|
||||
static_configs:
|
||||
- targets: ['ping-service:8082']
|
||||
metrics_path: '/actuator/prometheus'
|
||||
scrape_interval: 10s # Frequent for testing
|
||||
scrape_timeout: 3s
|
||||
|
||||
# ===================================================================
|
||||
# Infrastructure Monitoring
|
||||
# ===================================================================
|
||||
|
||||
# PostgreSQL Exporter (if deployed)
|
||||
- job_name: 'postgres'
|
||||
static_configs:
|
||||
- targets: ['postgres-exporter:9187']
|
||||
scrape_interval: 30s
|
||||
scrape_timeout: 10s
|
||||
|
||||
# Redis Exporter (if deployed)
|
||||
- job_name: 'redis'
|
||||
static_configs:
|
||||
- targets: ['redis-exporter:9121']
|
||||
scrape_interval: 30s
|
||||
scrape_timeout: 10s
|
||||
|
||||
# ===================================================================
|
||||
# Container and Host Metrics
|
||||
# ===================================================================
|
||||
|
||||
# Docker container metrics via cAdvisor (if deployed)
|
||||
- job_name: 'cadvisor'
|
||||
static_configs:
|
||||
- targets: ['cadvisor:8080']
|
||||
scrape_interval: 30s
|
||||
scrape_timeout: 10s
|
||||
|
||||
# Node Exporter for host metrics (if deployed)
|
||||
- job_name: 'node-exporter'
|
||||
static_configs:
|
||||
- targets: ['node-exporter:9100']
|
||||
scrape_interval: 30s
|
||||
scrape_timeout: 10s
|
||||
|
||||
# ===================================================================
|
||||
# Service Discovery (Consul Integration)
|
||||
# ===================================================================
|
||||
|
||||
# Consul service discovery for dynamic services
|
||||
- job_name: 'consul-services'
|
||||
consul_sd_configs:
|
||||
- server: 'consul:8500'
|
||||
services: []
|
||||
relabel_configs:
|
||||
# Only scrape services that have prometheus.scrape=true
|
||||
- source_labels: [__meta_consul_service_metadata_prometheus_scrape]
|
||||
action: keep
|
||||
regex: "true"
|
||||
|
||||
# Use service name as job name
|
||||
- source_labels: [__meta_consul_service]
|
||||
target_label: job
|
||||
|
||||
# Use custom metrics path if specified
|
||||
- source_labels: [__meta_consul_service_metadata_prometheus_path]
|
||||
target_label: __metrics_path__
|
||||
regex: "(.+)"
|
||||
|
||||
# Use custom port if specified
|
||||
- source_labels: [__address__, __meta_consul_service_metadata_prometheus_port]
|
||||
target_label: __address__
|
||||
regex: "([^:]+)(?::\d+)?;(\d+)"
|
||||
replacement: $1:$2
|
||||
|
||||
# ===================================================================
|
||||
# Development-Specific Configurations
|
||||
# ===================================================================
|
||||
|
||||
# Health check endpoints monitoring
|
||||
- job_name: 'health-checks'
|
||||
static_configs:
|
||||
- targets:
|
||||
- 'api-gateway:8081'
|
||||
- 'auth-server:8081'
|
||||
- 'monitoring-server:8083'
|
||||
- 'ping-service:8082'
|
||||
metrics_path: '/actuator/health'
|
||||
scrape_interval: 30s
|
||||
scrape_timeout: 5s
|
||||
|
||||
# JVM metrics (additional detail for development)
|
||||
- job_name: 'jvm-metrics'
|
||||
static_configs:
|
||||
- targets:
|
||||
- 'api-gateway:8081'
|
||||
- 'auth-server:8081'
|
||||
- 'monitoring-server:8083'
|
||||
- 'ping-service:8082'
|
||||
metrics_path: '/actuator/prometheus'
|
||||
scrape_interval: 30s
|
||||
params:
|
||||
match[]:
|
||||
- 'jvm_*'
|
||||
- 'process_*'
|
||||
- 'system_*'
|
||||
|
||||
# ===================================================================
|
||||
# Alerting Configuration (Development-friendly)
|
||||
# ===================================================================
|
||||
alerting:
|
||||
alertmanagers:
|
||||
- static_configs:
|
||||
- targets:
|
||||
# AlertManager not typically used in development
|
||||
# - alertmanager:9093
|
||||
|
||||
# ===================================================================
|
||||
# Remote Write Configuration (for development data persistence)
|
||||
# ===================================================================
|
||||
# Uncomment if you want to send metrics to external storage
|
||||
# remote_write:
|
||||
# - url: "http://prometheus-remote-write:8080/api/v1/write"
|
||||
# queue_config:
|
||||
# max_samples_per_send: 1000
|
||||
# max_shards: 200
|
||||
# capacity: 2500
|
||||
@@ -1,123 +0,0 @@
|
||||
# Prometheus Production Configuration
|
||||
# =============================================================================
|
||||
# This configuration provides production-ready monitoring setup with
|
||||
# security, performance optimizations, and comprehensive service discovery
|
||||
# =============================================================================
|
||||
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
external_labels:
|
||||
monitor: 'meldestelle-prod'
|
||||
environment: 'production'
|
||||
|
||||
# Alertmanager configuration
|
||||
alerting:
|
||||
alertmanagers:
|
||||
- static_configs:
|
||||
- targets:
|
||||
- alertmanager:9093
|
||||
|
||||
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
|
||||
rule_files:
|
||||
- "alert_rules.yml"
|
||||
- "recording_rules.yml"
|
||||
|
||||
# Scrape configuration
|
||||
scrape_configs:
|
||||
# Prometheus itself
|
||||
- job_name: 'prometheus'
|
||||
static_configs:
|
||||
- targets: ['localhost:9090']
|
||||
scrape_interval: 5s
|
||||
metrics_path: /metrics
|
||||
|
||||
# Application metrics
|
||||
- job_name: 'meldestelle-api'
|
||||
static_configs:
|
||||
- targets: ['host.docker.internal:8081']
|
||||
scrape_interval: 10s
|
||||
metrics_path: /actuator/prometheus
|
||||
basic_auth:
|
||||
username: 'admin'
|
||||
password: 'CHANGE_ME_METRICS_PASSWORD'
|
||||
|
||||
# PostgreSQL metrics (using postgres_exporter)
|
||||
- job_name: 'postgres'
|
||||
static_configs:
|
||||
- targets: ['postgres-exporter:9187']
|
||||
scrape_interval: 30s
|
||||
|
||||
# Redis metrics (using redis_exporter)
|
||||
- job_name: 'redis'
|
||||
static_configs:
|
||||
- targets: ['redis-exporter:9121']
|
||||
scrape_interval: 30s
|
||||
|
||||
# Kafka metrics (using kafka_exporter)
|
||||
- job_name: 'kafka'
|
||||
static_configs:
|
||||
- targets: ['kafka-exporter:9308']
|
||||
scrape_interval: 30s
|
||||
|
||||
# Zookeeper metrics (using zookeeper_exporter)
|
||||
- job_name: 'zookeeper'
|
||||
static_configs:
|
||||
- targets: ['zookeeper-exporter:9141']
|
||||
scrape_interval: 30s
|
||||
|
||||
# Keycloak metrics
|
||||
- job_name: 'keycloak'
|
||||
static_configs:
|
||||
- targets: ['keycloak:8443']
|
||||
scrape_interval: 30s
|
||||
metrics_path: /auth/realms/master/metrics
|
||||
scheme: https
|
||||
tls_config:
|
||||
insecure_skip_verify: true
|
||||
|
||||
# Nginx metrics (using nginx-prometheus-exporter)
|
||||
- job_name: 'nginx'
|
||||
static_configs:
|
||||
- targets: ['nginx-exporter:9113']
|
||||
scrape_interval: 30s
|
||||
|
||||
# Node exporter for system metrics
|
||||
- job_name: 'node'
|
||||
static_configs:
|
||||
- targets: ['node-exporter:9100']
|
||||
scrape_interval: 30s
|
||||
|
||||
# cAdvisor for container metrics
|
||||
- job_name: 'cadvisor'
|
||||
static_configs:
|
||||
- targets: ['cadvisor:8080']
|
||||
scrape_interval: 30s
|
||||
|
||||
# Grafana metrics
|
||||
- job_name: 'grafana'
|
||||
static_configs:
|
||||
- targets: ['grafana:3000']
|
||||
scrape_interval: 30s
|
||||
metrics_path: /metrics
|
||||
|
||||
# Zipkin metrics
|
||||
- job_name: 'zipkin'
|
||||
static_configs:
|
||||
- targets: ['zipkin:9411']
|
||||
scrape_interval: 30s
|
||||
metrics_path: /actuator/prometheus
|
||||
|
||||
# Remote write configuration (for long-term storage)
|
||||
# remote_write:
|
||||
# - url: "https://your-remote-storage/api/v1/write"
|
||||
# basic_auth:
|
||||
# username: "your-username"
|
||||
# password: "your-password"
|
||||
|
||||
# Storage configuration
|
||||
storage:
|
||||
tsdb:
|
||||
retention.time: 30d
|
||||
retention.size: 10GB
|
||||
wal-compression: true
|
||||
@@ -1,41 +0,0 @@
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
|
||||
# Alertmanager configuration
|
||||
alerting:
|
||||
alertmanagers:
|
||||
- static_configs:
|
||||
- targets:
|
||||
- alertmanager:9093
|
||||
|
||||
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
|
||||
rule_files:
|
||||
- "/etc/prometheus/rules/alerts.yml"
|
||||
|
||||
# A scrape configuration containing exactly one endpoint to scrape:
|
||||
scrape_configs:
|
||||
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
|
||||
- job_name: "prometheus"
|
||||
# metrics_path defaults to '/metrics'
|
||||
# scheme defaults to 'http'.
|
||||
static_configs:
|
||||
- targets: ["localhost:9090"]
|
||||
|
||||
# Scrape configuration for the Meldestelle application
|
||||
- job_name: "meldestelle-api"
|
||||
metrics_path: /actuator/prometheus
|
||||
scrape_interval: 10s
|
||||
basic_auth:
|
||||
username: ${METRICS_USER:-metrics}
|
||||
password: ${METRICS_PASSWORD:-metrics-password-dev}
|
||||
static_configs:
|
||||
- targets: ["server:8081"]
|
||||
labels:
|
||||
application: "meldestelle"
|
||||
service: "api-gateway"
|
||||
|
||||
# Node exporter for host metrics (if added later)
|
||||
# - job_name: "node-exporter"
|
||||
# static_configs:
|
||||
# - targets: ["node-exporter:9100"]
|
||||
@@ -1,62 +0,0 @@
|
||||
groups:
|
||||
- name: meldestelle_alerts
|
||||
rules:
|
||||
# Alert for high memory usage
|
||||
- alert: HighMemoryUsage
|
||||
expr: (jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"}) * 100 > 85
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "High memory usage ({{ $value }}%)"
|
||||
description: "JVM memory usage is above 85% for 5 minutes.\n Instance: {{ $labels.instance }}\n Service: {{ $labels.service }}"
|
||||
|
||||
# Alert for high CPU usage
|
||||
- alert: HighCpuUsage
|
||||
expr: process_cpu_usage > 0.85
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "High CPU usage ({{ $value }})"
|
||||
description: "CPU usage is above 85% for 5 minutes.\n Instance: {{ $labels.instance }}\n Service: {{ $labels.service }}"
|
||||
|
||||
# Alert for high error rate
|
||||
- alert: HighErrorRate
|
||||
expr: sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m])) / sum(rate(http_server_requests_seconds_count[5m])) * 100 > 5
|
||||
for: 2m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "High error rate ({{ $value }}%)"
|
||||
description: "Error rate is above 5% for 2 minutes.\n Instance: {{ $labels.instance }}\n Service: {{ $labels.service }}"
|
||||
|
||||
# Alert for service unavailability
|
||||
- alert: ServiceUnavailable
|
||||
expr: up{job="meldestelle-server"} == 0
|
||||
for: 1m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Service unavailable"
|
||||
description: "Meldestelle service is down.\n Instance: {{ $labels.instance }}"
|
||||
|
||||
# Alert for slow response time
|
||||
- alert: SlowResponseTime
|
||||
expr: http_server_requests_seconds_sum / http_server_requests_seconds_count > 1
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Slow response time ({{ $value }}s)"
|
||||
description: "Average response time is above 1 second for 5 minutes.\n Instance: {{ $labels.instance }}\n Path: {{ $labels.uri }}"
|
||||
|
||||
# Alert for high GC pause time
|
||||
- alert: HighGcPauseTime
|
||||
expr: jvm_gc_pause_seconds_sum / jvm_gc_pause_seconds_count > 0.5
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "High GC pause time ({{ $value }}s)"
|
||||
description: "Average GC pause time is above 0.5 seconds for 5 minutes.\n Instance: {{ $labels.instance }}"
|
||||
@@ -0,0 +1,204 @@
|
||||
# ===================================================================
|
||||
# Docker Versions Catalog - Single Source of Truth
|
||||
# Analogous to gradle/libs.versions.toml for centralized version management
|
||||
# ===================================================================
|
||||
# Last updated: 2025-09-13
|
||||
# Eliminates version redundancy across 12+ Dockerfiles
|
||||
|
||||
[versions]
|
||||
# --- Build Tools ---
|
||||
gradle = "9.1.0"
|
||||
java = "21"
|
||||
node = "22.21.0"
|
||||
|
||||
# --- Base Images ---
|
||||
nginx = "1.28.0-alpine"
|
||||
alpine = "3.22"
|
||||
eclipse-temurin-jdk = "21-jdk-alpine"
|
||||
eclipse-temurin-jre = "21-jre-alpine"
|
||||
|
||||
# --- Datastore Images ---
|
||||
postgres = "16-alpine"
|
||||
redis = "7-alpine"
|
||||
|
||||
# --- Additional Infrastructure Images ---
|
||||
consul = "1.15"
|
||||
zookeeper = "7.4.0"
|
||||
kafka = "7.4.0"
|
||||
|
||||
# --- Monitoring & Infrastructure Services ---
|
||||
prometheus = "v2.54.1"
|
||||
grafana = "11.3.0"
|
||||
keycloak = "26.4.2"
|
||||
|
||||
# --- Spring Configuration ---
|
||||
spring-profiles-default = "default"
|
||||
spring-profiles-docker = "docker"
|
||||
spring-profiles-prod = "prod"
|
||||
|
||||
# --- Application Versions ---
|
||||
app-version = "1.0.0"
|
||||
|
||||
# --- Zentrale Port-Verwaltung ---
|
||||
# Single Source of Truth für alle Service-Ports
|
||||
|
||||
[service-ports]
|
||||
# --- Infrastructure Services ---
|
||||
api-gateway = 8081
|
||||
auth-server = 8087
|
||||
monitoring-server = 8088
|
||||
|
||||
# --- Application Services ---
|
||||
ping-service = 8082
|
||||
members-service = 8083
|
||||
horses-service = 8084
|
||||
events-service = 8085
|
||||
masterdata-service = 8086
|
||||
|
||||
# --- External Services ---
|
||||
postgres = 5432
|
||||
redis = 6379
|
||||
keycloak = 8180
|
||||
consul = 8500
|
||||
zookeeper = 2181
|
||||
kafka = 9092
|
||||
|
||||
# --- Monitoring Stack ---
|
||||
prometheus = 9090
|
||||
grafana = 3000
|
||||
|
||||
# --- Client Applications ---
|
||||
web-app = 4000
|
||||
desktop-app-vnc = 5901
|
||||
desktop-app-novnc = 6080
|
||||
|
||||
[port-ranges]
|
||||
# --- Port-Range-Definitionen für automatische Port-Zuweisung ---
|
||||
infrastructure = "8081-8088"
|
||||
services = "8082-8099"
|
||||
monitoring = "9090-9099"
|
||||
clients = "4000-4099"
|
||||
vnc = "5901-5999"
|
||||
debug = "5005-5009"
|
||||
|
||||
# --- Reserved Port Ranges ---
|
||||
system-reserved = "0-1023"
|
||||
ephemeral = "32768-65535"
|
||||
|
||||
[build-args]
|
||||
# --- Global Build Arguments (used across all categories) ---
|
||||
global = [
|
||||
"GRADLE_VERSION",
|
||||
"JAVA_VERSION",
|
||||
"BUILD_DATE",
|
||||
"VERSION"
|
||||
]
|
||||
|
||||
# --- Spring Boot Services (dockerfiles/services/* and infrastructure/*) ---
|
||||
spring-services = [
|
||||
"SPRING_PROFILES_ACTIVE",
|
||||
"SERVICE_PATH",
|
||||
"SERVICE_NAME",
|
||||
"SERVICE_PORT"
|
||||
]
|
||||
|
||||
# --- Kotlin/JS Web Clients (dockerfiles/clients/*) ---
|
||||
web-clients = [
|
||||
"NODE_VERSION",
|
||||
"NGINX_VERSION",
|
||||
"CLIENT_PATH",
|
||||
"CLIENT_MODULE",
|
||||
"CLIENT_NAME"
|
||||
]
|
||||
|
||||
[categories]
|
||||
# --- Services Configuration ---
|
||||
[categories.services]
|
||||
default-spring-profile = "docker"
|
||||
default-port-start = 8082
|
||||
services = [
|
||||
"ping-service",
|
||||
"members-service",
|
||||
"horses-service",
|
||||
"events-service",
|
||||
"masterdata-service"
|
||||
]
|
||||
|
||||
# --- Infrastructure Configuration ---
|
||||
[categories.infrastructure]
|
||||
default-spring-profile = "default"
|
||||
services = [
|
||||
"gateway",
|
||||
"auth-server",
|
||||
"monitoring-server"
|
||||
]
|
||||
|
||||
# --- Client Applications Configuration ---
|
||||
[categories.clients]
|
||||
clients = [
|
||||
"web-app",
|
||||
"desktop-app"
|
||||
]
|
||||
|
||||
[environment-mapping]
|
||||
# --- Environment Variable Names for Docker Compose ---
|
||||
# Maps internal version names to environment variable names
|
||||
gradle-version = "DOCKER_GRADLE_VERSION"
|
||||
java-version = "DOCKER_JAVA_VERSION"
|
||||
node-version = "DOCKER_NODE_VERSION"
|
||||
nginx-version = "DOCKER_NGINX_VERSION"
|
||||
postgres-version = "DOCKER_POSTGRES_VERSION"
|
||||
redis-version = "DOCKER_REDIS_VERSION"
|
||||
prometheus-version = "DOCKER_PROMETHEUS_VERSION"
|
||||
grafana-version = "DOCKER_GRAFANA_VERSION"
|
||||
keycloak-version = "DOCKER_KEYCLOAK_VERSION"
|
||||
consul-version = "DOCKER_CONSUL_VERSION"
|
||||
zookeeper-version = "DOCKER_ZOOKEEPER_VERSION"
|
||||
kafka-version = "DOCKER_KAFKA_VERSION"
|
||||
spring-profiles-default = "DOCKER_SPRING_PROFILES_DEFAULT"
|
||||
spring-profiles-docker = "DOCKER_SPRING_PROFILES_DOCKER"
|
||||
app-version = "DOCKER_APP_VERSION"
|
||||
|
||||
[environments]
|
||||
# --- Environment-spezifische Konfigurationen ---
|
||||
# Zentrale Verwaltung für dev/test/prod Umgebungen
|
||||
|
||||
[environments.development]
|
||||
spring-profiles = "dev"
|
||||
debug-enabled = true
|
||||
log-level = "DEBUG"
|
||||
health-check-interval = "30s"
|
||||
health-check-timeout = "5s"
|
||||
health-check-retries = 3
|
||||
health-check-start-period = "40s"
|
||||
resource-limits = false
|
||||
jvm-debug-port = 5005
|
||||
hot-reload = true
|
||||
|
||||
[environments.production]
|
||||
spring-profiles = "prod"
|
||||
debug-enabled = false
|
||||
log-level = "INFO"
|
||||
health-check-interval = "15s"
|
||||
health-check-timeout = "3s"
|
||||
health-check-retries = 3
|
||||
health-check-start-period = "30s"
|
||||
resource-limits = true
|
||||
jvm-debug-port = false
|
||||
hot-reload = false
|
||||
security-headers = true
|
||||
tls-enabled = true
|
||||
|
||||
[environments.testing]
|
||||
spring-profiles = "test"
|
||||
debug-enabled = true
|
||||
log-level = "DEBUG"
|
||||
health-check-interval = "10s"
|
||||
health-check-timeout = "5s"
|
||||
health-check-retries = 2
|
||||
health-check-start-period = "20s"
|
||||
resource-limits = false
|
||||
jvm-debug-port = 5005
|
||||
hot-reload = false
|
||||
ephemeral-storage = true
|
||||
test-containers = true
|
||||
Reference in New Issue
Block a user