refactor(infra): Restrukturierung Config-Ordner & Einführung von Docker-Profilen
Umfangreiches Refactoring der Projektkonfiguration zur klaren Trennung von Build-, Runtime- und Applikations-Logik. Änderungen im Detail: - Struktur: Neuorganisation des `config/` Verzeichnisses in logische Bereiche: - `config/docker`: Reine Infrastruktur-Configs (Postgres, Redis, Nginx, Monitoring). - `config/quality`: Statische Code-Analyse (Detekt, Lint). - `config/app`: Gemeinsame Spring-Boot-Konfigurationen. - Docker Compose: - Einführung von Profilen (`infra`, `backend`, `ops`, `gui`, `tools`) für gezieltes Starten von Teilbereichen. - Anpassung aller Volume-Pfade auf die neue Struktur. - Spring Boot Config: - Zentralisierung gemeinsamer Einstellungen (Datasource, Redis, JPA) in `config/app/base-application.yml`. - Parametrisierung der Hosts für nahtlosen Wechsel zwischen Docker und Localhost. - Bereinigung der service-spezifischen `application.yaml` Dateien (z.B. Ping-Service). - Cleanup: Entfernen redundanter "Ghost-Files" (`versions.toml`, `central.toml`, `config/.env`), um eine echte Single Source of Truth (SSoT) zu gewährleisten.
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
// Kafka JAAS Configuration for Production
|
||||
// =============================================================================
|
||||
// This file configures SASL authentication for Kafka in production
|
||||
// Change the passwords to strong, randomly generated values
|
||||
// =============================================================================
|
||||
|
||||
KafkaServer {
|
||||
org.apache.kafka.common.security.plain.PlainLoginModule required
|
||||
username="admin"
|
||||
password="CHANGE_ME_STRONG_KAFKA_ADMIN_PASSWORD"
|
||||
user_admin="CHANGE_ME_STRONG_KAFKA_ADMIN_PASSWORD"
|
||||
user_producer="CHANGE_ME_STRONG_KAFKA_PRODUCER_PASSWORD"
|
||||
user_consumer="CHANGE_ME_STRONG_KAFKA_CONSUMER_PASSWORD";
|
||||
};
|
||||
|
||||
Client {
|
||||
org.apache.kafka.common.security.plain.PlainLoginModule required
|
||||
username="admin"
|
||||
password="CHANGE_ME_STRONG_KAFKA_ADMIN_PASSWORD";
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
// Zookeeper JAAS Configuration for Production
|
||||
// =============================================================================
|
||||
// This file configures SASL authentication for Zookeeper in production
|
||||
// Change the passwords to strong, randomly generated values
|
||||
// =============================================================================
|
||||
|
||||
Server {
|
||||
org.apache.zookeeper.server.auth.DigestLoginModule required
|
||||
user_admin="CHANGE_ME_STRONG_ZOOKEEPER_ADMIN_PASSWORD"
|
||||
user_kafka="CHANGE_ME_STRONG_ZOOKEEPER_KAFKA_PASSWORD";
|
||||
};
|
||||
|
||||
Client {
|
||||
org.apache.zookeeper.server.auth.DigestLoginModule required
|
||||
username="kafka"
|
||||
password="CHANGE_ME_STRONG_ZOOKEEPER_KAFKA_PASSWORD";
|
||||
};
|
||||
@@ -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,26 @@
|
||||
global:
|
||||
resolve_timeout: 5m
|
||||
# FIX: Hier müssen echte Werte stehen, keine Variablen!
|
||||
# Wenn du noch keinen SMTP hast, trag Dummy-Werte ein, damit der Container startet.
|
||||
smtp_smarthost: 'smtp.gmail.com:587'
|
||||
smtp_from: 'alertmanager@meldestelle.at'
|
||||
smtp_auth_username: 'deine-email@gmail.com'
|
||||
smtp_auth_password: 'dein-passwort'
|
||||
smtp_require_tls: true
|
||||
|
||||
route:
|
||||
receiver: 'email-notifications'
|
||||
# ... (Rest bleibt gleich)
|
||||
|
||||
receivers:
|
||||
- name: 'email-notifications'
|
||||
email_configs:
|
||||
- to: 'admin@meldestelle.at'
|
||||
send_resolved: true
|
||||
|
||||
- name: 'slack-critical'
|
||||
slack_configs:
|
||||
# FIX: Auch hier die echte Webhook URL eintragen oder den Block entfernen, wenn nicht genutzt
|
||||
- api_url: 'https://hooks.slack.com/services/example'
|
||||
channel: '#alerts-critical'
|
||||
# ...
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
## Default Elasticsearch configuration
|
||||
cluster.name: "meldestelle-elk"
|
||||
network.host: 0.0.0.0
|
||||
|
||||
# Minimum memory requirements
|
||||
discovery.type: single-node
|
||||
|
||||
# X-Pack security disabled for development
|
||||
xpack.security.enabled: false
|
||||
|
||||
# Enable monitoring
|
||||
xpack.monitoring.collection.enabled: true
|
||||
@@ -0,0 +1,51 @@
|
||||
input {
|
||||
# TCP input for logback appender
|
||||
tcp {
|
||||
port => 5000
|
||||
codec => json_lines
|
||||
}
|
||||
|
||||
# File input for server logs
|
||||
file {
|
||||
path => "/var/log/meldestelle/*.log"
|
||||
start_position => "beginning"
|
||||
sincedb_path => "/dev/null"
|
||||
}
|
||||
}
|
||||
|
||||
filter {
|
||||
if [type] == "syslog" {
|
||||
grok {
|
||||
match => { "message" => "%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{DATA:syslog_program}(?:\[%{POSINT:syslog_pid}\])?: %{GREEDYDATA:syslog_message}" }
|
||||
add_field => [ "received_at", "%{@timestamp}" ]
|
||||
add_field => [ "received_from", "%{host}" ]
|
||||
}
|
||||
date {
|
||||
match => [ "syslog_timestamp", "MMM d HH:mm:ss", "MMM dd HH:mm:ss" ]
|
||||
}
|
||||
}
|
||||
|
||||
# Parse JSON logs
|
||||
if [message] =~ /^\{.*\}$/ {
|
||||
json {
|
||||
source => "message"
|
||||
}
|
||||
}
|
||||
|
||||
# Add application name
|
||||
mutate {
|
||||
add_field => { "application" => "meldestelle" }
|
||||
}
|
||||
}
|
||||
|
||||
output {
|
||||
elasticsearch {
|
||||
hosts => ["elasticsearch:9200"]
|
||||
index => "meldestelle-logs-%{+YYYY.MM.dd}"
|
||||
}
|
||||
|
||||
# For debugging
|
||||
stdout {
|
||||
codec => rubydebug
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,389 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": {
|
||||
"type": "grafana",
|
||||
"uid": "-- Grafana --"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "Meldestelle Application Overview Dashboard - Key metrics and health indicators",
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"id": null,
|
||||
"links": [],
|
||||
"liveNow": false,
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 10,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"vis": false
|
||||
},
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "reqps"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 1,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom"
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"expr": "rate(http_server_requests_seconds_count{application=\"meldestelle\"}[5m])",
|
||||
"interval": "",
|
||||
"legendFormat": "{{method}} {{uri}}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "HTTP Request Rate",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 4,
|
||||
"w": 6,
|
||||
"x": 12,
|
||||
"y": 0
|
||||
},
|
||||
"id": 2,
|
||||
"options": {
|
||||
"colorMode": "background",
|
||||
"graphMode": "none",
|
||||
"justifyMode": "auto",
|
||||
"orientation": "auto",
|
||||
"reduceOptions": {
|
||||
"values": false,
|
||||
"calcs": [
|
||||
"lastNotNull"
|
||||
],
|
||||
"fields": ""
|
||||
},
|
||||
"textMode": "auto"
|
||||
},
|
||||
"pluginVersion": "8.5.0",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"expr": "up{application=\"meldestelle\"}",
|
||||
"interval": "",
|
||||
"legendFormat": "{{instance}}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Application Status",
|
||||
"type": "stat"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 10,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"vis": false
|
||||
},
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "ms"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 8
|
||||
},
|
||||
"id": 3,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom"
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"expr": "histogram_quantile(0.95, rate(http_server_requests_seconds_bucket{application=\"meldestelle\"}[5m])) * 1000",
|
||||
"interval": "",
|
||||
"legendFormat": "95th percentile",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"expr": "histogram_quantile(0.50, rate(http_server_requests_seconds_bucket{application=\"meldestelle\"}[5m])) * 1000",
|
||||
"interval": "",
|
||||
"legendFormat": "50th percentile",
|
||||
"refId": "B"
|
||||
}
|
||||
],
|
||||
"title": "HTTP Response Times",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 10,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"vis": false
|
||||
},
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "percent"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 6,
|
||||
"x": 12,
|
||||
"y": 8
|
||||
},
|
||||
"id": 4,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom"
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"expr": "rate(http_server_requests_seconds_count{application=\"meldestelle\",status=~\"[45].*\"}[5m]) / rate(http_server_requests_seconds_count{application=\"meldestelle\"}[5m]) * 100",
|
||||
"interval": "",
|
||||
"legendFormat": "Error Rate",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Error Rate",
|
||||
"type": "timeseries"
|
||||
}
|
||||
],
|
||||
"refresh": "30s",
|
||||
"schemaVersion": 36,
|
||||
"style": "dark",
|
||||
"tags": [
|
||||
"meldestelle",
|
||||
"application",
|
||||
"overview"
|
||||
],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-1h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "",
|
||||
"title": "Meldestelle - Application Overview",
|
||||
"uid": "meldestelle-app-overview",
|
||||
"version": 1,
|
||||
"weekStart": ""
|
||||
}
|
||||
@@ -0,0 +1,599 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": {
|
||||
"type": "grafana",
|
||||
"uid": "-- Grafana --"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "Infrastructure Components Dashboard - Monitoring of PostgreSQL, Redis, Kafka, and other supporting services",
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"id": null,
|
||||
"links": [],
|
||||
"liveNow": false,
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 4,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 1,
|
||||
"options": {
|
||||
"colorMode": "background",
|
||||
"graphMode": "none",
|
||||
"justifyMode": "auto",
|
||||
"orientation": "horizontal",
|
||||
"reduceOptions": {
|
||||
"values": false,
|
||||
"calcs": [
|
||||
"lastNotNull"
|
||||
],
|
||||
"fields": ""
|
||||
},
|
||||
"textMode": "auto"
|
||||
},
|
||||
"pluginVersion": "8.5.0",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"expr": "up{job=\"postgres\"}",
|
||||
"interval": "",
|
||||
"legendFormat": "PostgreSQL",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"expr": "up{job=\"redis\"}",
|
||||
"interval": "",
|
||||
"legendFormat": "Redis",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"expr": "up{job=\"kafka\"}",
|
||||
"interval": "",
|
||||
"legendFormat": "Kafka",
|
||||
"refId": "C"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"expr": "up{job=\"keycloak\"}",
|
||||
"interval": "",
|
||||
"legendFormat": "Keycloak",
|
||||
"refId": "D"
|
||||
}
|
||||
],
|
||||
"title": "Infrastructure Services Status",
|
||||
"type": "stat"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 10,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"vis": false
|
||||
},
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "percent"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 4
|
||||
},
|
||||
"id": 2,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom"
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"expr": "100 - (avg(rate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100)",
|
||||
"interval": "",
|
||||
"legendFormat": "CPU Usage",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "System CPU Usage",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 10,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"vis": false
|
||||
},
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "bytes"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 4
|
||||
},
|
||||
"id": 3,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom"
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"expr": "node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes",
|
||||
"interval": "",
|
||||
"legendFormat": "Memory Used",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"expr": "node_memory_MemTotal_bytes",
|
||||
"interval": "",
|
||||
"legendFormat": "Memory Total",
|
||||
"refId": "B"
|
||||
}
|
||||
],
|
||||
"title": "System Memory Usage",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 10,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"vis": false
|
||||
},
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "short"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 8,
|
||||
"x": 0,
|
||||
"y": 12
|
||||
},
|
||||
"id": 4,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom"
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"expr": "pg_stat_database_numbackends{job=\"postgres\"}",
|
||||
"interval": "",
|
||||
"legendFormat": "{{datname}}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "PostgreSQL Active Connections",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 10,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"vis": false
|
||||
},
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "short"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 8,
|
||||
"x": 8,
|
||||
"y": 12
|
||||
},
|
||||
"id": 5,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom"
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"expr": "redis_connected_clients{job=\"redis\"}",
|
||||
"interval": "",
|
||||
"legendFormat": "Connected Clients",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Redis Connected Clients",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 10,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"vis": false
|
||||
},
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "short"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 8,
|
||||
"x": 16,
|
||||
"y": 12
|
||||
},
|
||||
"id": 6,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom"
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "prometheus"
|
||||
},
|
||||
"expr": "kafka_server_brokertopicmetrics_messagesin_total{job=\"kafka\"}",
|
||||
"interval": "",
|
||||
"legendFormat": "{{topic}}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Kafka Messages In",
|
||||
"type": "timeseries"
|
||||
}
|
||||
],
|
||||
"refresh": "30s",
|
||||
"schemaVersion": 36,
|
||||
"style": "dark",
|
||||
"tags": [
|
||||
"meldestelle",
|
||||
"infrastructure",
|
||||
"postgres",
|
||||
"redis",
|
||||
"kafka"
|
||||
],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-1h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "",
|
||||
"title": "Meldestelle - Infrastructure Components",
|
||||
"uid": "meldestelle-infrastructure",
|
||||
"version": 1,
|
||||
"weekStart": ""
|
||||
}
|
||||
@@ -0,0 +1,659 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": {
|
||||
"type": "grafana",
|
||||
"uid": "-- Grafana --"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"id": 1,
|
||||
"links": [],
|
||||
"liveNow": false,
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "PBFA97CFB590B2093"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 10,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "bytes"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 1,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "PBFA97CFB590B2093"
|
||||
},
|
||||
"editorMode": "code",
|
||||
"expr": "jvm_memory_used_bytes{area=\"heap\"}",
|
||||
"legendFormat": "Used Heap",
|
||||
"range": true,
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "PBFA97CFB590B2093"
|
||||
},
|
||||
"editorMode": "code",
|
||||
"expr": "jvm_memory_committed_bytes{area=\"heap\"}",
|
||||
"hide": false,
|
||||
"legendFormat": "Committed Heap",
|
||||
"range": true,
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "PBFA97CFB590B2093"
|
||||
},
|
||||
"editorMode": "code",
|
||||
"expr": "jvm_memory_max_bytes{area=\"heap\"}",
|
||||
"hide": false,
|
||||
"legendFormat": "Max Heap",
|
||||
"range": true,
|
||||
"refId": "C"
|
||||
}
|
||||
],
|
||||
"title": "JVM Heap Memory",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "PBFA97CFB590B2093"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 10,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "bytes"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 0
|
||||
},
|
||||
"id": 2,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "PBFA97CFB590B2093"
|
||||
},
|
||||
"editorMode": "code",
|
||||
"expr": "jvm_memory_used_bytes{area=\"nonheap\"}",
|
||||
"legendFormat": "Used Non-Heap",
|
||||
"range": true,
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "PBFA97CFB590B2093"
|
||||
},
|
||||
"editorMode": "code",
|
||||
"expr": "jvm_memory_committed_bytes{area=\"nonheap\"}",
|
||||
"hide": false,
|
||||
"legendFormat": "Committed Non-Heap",
|
||||
"range": true,
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "PBFA97CFB590B2093"
|
||||
},
|
||||
"editorMode": "code",
|
||||
"expr": "jvm_memory_max_bytes{area=\"nonheap\"}",
|
||||
"hide": false,
|
||||
"legendFormat": "Max Non-Heap",
|
||||
"range": true,
|
||||
"refId": "C"
|
||||
}
|
||||
],
|
||||
"title": "JVM Non-Heap Memory",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "PBFA97CFB590B2093"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 10,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 8
|
||||
},
|
||||
"id": 3,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "PBFA97CFB590B2093"
|
||||
},
|
||||
"editorMode": "code",
|
||||
"expr": "jvm_threads_live_threads",
|
||||
"legendFormat": "Live Threads",
|
||||
"range": true,
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "PBFA97CFB590B2093"
|
||||
},
|
||||
"editorMode": "code",
|
||||
"expr": "jvm_threads_daemon_threads",
|
||||
"hide": false,
|
||||
"legendFormat": "Daemon Threads",
|
||||
"range": true,
|
||||
"refId": "B"
|
||||
}
|
||||
],
|
||||
"title": "JVM Threads",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "PBFA97CFB590B2093"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 10,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "s"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 8
|
||||
},
|
||||
"id": 4,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "PBFA97CFB590B2093"
|
||||
},
|
||||
"editorMode": "code",
|
||||
"expr": "jvm_gc_pause_seconds_sum / jvm_gc_pause_seconds_count",
|
||||
"legendFormat": "GC Pause Time",
|
||||
"range": true,
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "JVM GC Pause Time",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "PBFA97CFB590B2093"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 10,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 16
|
||||
},
|
||||
"id": 5,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "PBFA97CFB590B2093"
|
||||
},
|
||||
"editorMode": "code",
|
||||
"expr": "rate(http_server_requests_seconds_count[1m])",
|
||||
"legendFormat": "{{method}} {{uri}} {{status}}",
|
||||
"range": true,
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "HTTP Request Rate",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "PBFA97CFB590B2093"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 10,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "s"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 16
|
||||
},
|
||||
"id": 6,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "PBFA97CFB590B2093"
|
||||
},
|
||||
"editorMode": "code",
|
||||
"expr": "http_server_requests_seconds_sum / http_server_requests_seconds_count",
|
||||
"legendFormat": "{{method}} {{uri}} {{status}}",
|
||||
"range": true,
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "HTTP Request Duration",
|
||||
"type": "timeseries"
|
||||
}
|
||||
],
|
||||
"refresh": "5s",
|
||||
"schemaVersion": 38,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-1h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "",
|
||||
"title": "Meldestelle JVM Metrics",
|
||||
"uid": "meldestelle-jvm",
|
||||
"version": 1,
|
||||
"weekStart": ""
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: 1
|
||||
|
||||
providers:
|
||||
- name: 'Meldestelle Dashboards'
|
||||
orgId: 1
|
||||
folder: 'Meldestelle'
|
||||
type: file
|
||||
disableDeletion: false
|
||||
editable: true
|
||||
options:
|
||||
path: /var/lib/grafana/dashboards
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Prometheus
|
||||
type: prometheus
|
||||
access: proxy
|
||||
url: http://prometheus:9090
|
||||
isDefault: true
|
||||
editable: false
|
||||
version: 1
|
||||
@@ -0,0 +1,68 @@
|
||||
# ===================================================================
|
||||
# Prometheus Configuration for Meldestelle
|
||||
# Features: Consul Service Discovery, Spring Boot Actuator support
|
||||
# ===================================================================
|
||||
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
|
||||
# Alertmanager configuration
|
||||
alerting:
|
||||
alertmanagers:
|
||||
- static_configs:
|
||||
- targets:
|
||||
# Da wir Alertmanager noch nicht im Docker Compose haben (kommt noch!),
|
||||
# lassen wir das vorerst auskommentiert oder fügen den Container hinzu.
|
||||
- "alertmanager:9093"
|
||||
|
||||
rule_files:
|
||||
- "/etc/prometheus/rules/alerts.yaml"
|
||||
|
||||
scrape_configs:
|
||||
# 1. Prometheus Self-Monitoring
|
||||
- job_name: 'prometheus'
|
||||
static_configs:
|
||||
- targets: [ 'localhost:9090' ]
|
||||
|
||||
# 2. Consul Self-Monitoring
|
||||
- job_name: 'consul'
|
||||
metrics_path: '/v1/agent/metrics'
|
||||
params:
|
||||
format: [ 'prometheus' ]
|
||||
static_configs:
|
||||
- targets: [ 'consul:8500' ]
|
||||
|
||||
# 3. Spring Boot Services via Consul Discovery.
|
||||
# Das ist die Magie: Prometheus fragt Consul nach allen Services.
|
||||
# Wenn ein Service das Tag 'metrics' oder 'spring-boot' hat (oder einfach alle), wird er scraped.
|
||||
- job_name: 'consul-services'
|
||||
consul_sd_configs:
|
||||
- server: 'consul:8500'
|
||||
services: [ ] # Leere Liste = Alle Services
|
||||
|
||||
relabel_configs:
|
||||
# Nur Services scrapen, die NICHT consul selbst sind (das haben wir oben schon)
|
||||
- source_labels: [ __meta_consul_service ]
|
||||
regex: consul
|
||||
action: drop
|
||||
|
||||
# Setze den Pfad auf /actuator/prometheus für Spring Boot Apps
|
||||
# Optional: Man kann das auch über Consul Tags steuern
|
||||
- source_labels: [ __meta_consul_service ]
|
||||
target_label: __metrics_path__
|
||||
replacement: /actuator/prometheus
|
||||
|
||||
# Übernehme den Service-Namen als 'application' Label
|
||||
- source_labels: [ __meta_consul_service ]
|
||||
target_label: application
|
||||
|
||||
# Behalte die Instanz (IP: Port)
|
||||
- source_labels: [ __meta_consul_address, __meta_consul_service_port ]
|
||||
separator: ':'
|
||||
target_label: instance
|
||||
|
||||
# Job 4: Postgres Exporter (Statisch, da kein Consul-Client im Image)
|
||||
- job_name: 'postgres-exporter'
|
||||
static_configs:
|
||||
- targets: [ 'postgres-exporter:9187' ]
|
||||
@@ -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,109 @@
|
||||
# ===================================================================
|
||||
# Multi-Stage Dockerfile für Meldestelle Desktop-App (VNC)
|
||||
# ===================================================================
|
||||
|
||||
# 1. Build Stage (Debian-basiert für Stabilität bei Desktop-Builds)
|
||||
FROM gradle:8-jdk21 AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy Configs
|
||||
COPY build.gradle.kts settings.gradle.kts gradle.properties ./
|
||||
COPY gradle/ gradle/
|
||||
COPY gradlew ./
|
||||
|
||||
# Fix Permissions
|
||||
RUN chmod +x gradlew
|
||||
|
||||
# Copy Sources (Struktur wie im Web-App Fix)
|
||||
COPY platform/ platform/
|
||||
COPY core/ core/
|
||||
COPY backend/ backend/
|
||||
COPY frontend/ frontend/
|
||||
COPY docs/ docs/
|
||||
# Falls du 'domains' oder andere Ordner hast, die in settings.gradle.kts stehen:
|
||||
# COPY domains/ domains/
|
||||
|
||||
# Dependencies laden
|
||||
RUN ./gradlew :frontend:shells:meldestelle-portal:dependencies --no-daemon
|
||||
|
||||
# Desktop-App Distribution erstellen
|
||||
# Wir nutzen 'packageDistributionForCurrentOS' oder 'createDistributable'
|
||||
RUN ./gradlew :frontend:shells:meldestelle-portal:createDistributable --no-daemon
|
||||
|
||||
# ===================================================================
|
||||
# Stage 2: Runtime Stage - Ubuntu mit VNC + noVNC
|
||||
# ===================================================================
|
||||
FROM ubuntu:22.04
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Installiere System-Dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
openjdk-21-jdk \
|
||||
xvfb \
|
||||
x11vnc \
|
||||
python3 \
|
||||
python3-numpy \
|
||||
xfce4 \
|
||||
xfce4-goodies \
|
||||
curl \
|
||||
wget \
|
||||
unzip \
|
||||
supervisor \
|
||||
net-tools \
|
||||
git \
|
||||
# WICHTIG: GUI Libraries für Java/Compose \
|
||||
libgl1-mesa-glx \
|
||||
libgl1-mesa-dri \
|
||||
libgtk-3-0 \
|
||||
libasound2 \
|
||||
libxcursor1 \
|
||||
libxi6 \
|
||||
libxtst6 \
|
||||
libxrandr2 \
|
||||
&& apt-get remove -y xfce4-power-manager \
|
||||
&& apt-get autoremove -y \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Manuelle Installation von noVNC und Websockify
|
||||
# Wir klonen Websockify an den Ort, wo noVNC es erwartet
|
||||
RUN mkdir -p /usr/share/novnc \
|
||||
&& git clone --depth 1 https://github.com/novnc/noVNC.git /usr/share/novnc \
|
||||
&& git clone --depth 1 https://github.com/novnc/websockify /usr/share/novnc/utils/websockify \
|
||||
&& ln -s /usr/share/novnc/vnc.html /usr/share/novnc/index.html
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Kopiere Build-Ergebnis
|
||||
# HINWEIS: Der Pfad muss exakt stimmen. Compose Gradle Plugin Output ist oft verschachtelt.
|
||||
# Wir kopieren den Inhalt nach /app/desktop-app
|
||||
COPY --from=builder /app/frontend/shells/meldestelle-portal/build/compose/binaries/main/app/ /app/desktop-app/
|
||||
|
||||
# Scripts (Achte darauf, dass die Pfade im Host stimmen!)
|
||||
COPY config/frontends/desktop-app/entrypoint.sh /entrypoint.sh
|
||||
COPY config/frontends/desktop-app/health-check.sh /opt/health-check.sh
|
||||
# Wir nutzen vorerst dein Entrypoint-Script, Supervisor Config ist optional wenn Script alles macht
|
||||
# COPY config/frontends/desktop-app/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||
|
||||
RUN chmod +x /entrypoint.sh /opt/health-check.sh
|
||||
|
||||
# User Setup
|
||||
RUN useradd -m -s /bin/bash vncuser && \
|
||||
mkdir -p /home/vncuser/.vnc && \
|
||||
chown -R vncuser:vncuser /home/vncuser && \
|
||||
chown -R vncuser:vncuser /app
|
||||
|
||||
EXPOSE 5901 6080
|
||||
|
||||
ENV DISPLAY=:99 \
|
||||
VNC_PORT=5901 \
|
||||
NOVNC_PORT=6080 \
|
||||
API_BASE_URL="http://api-gateway:8081"
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
||||
CMD /opt/health-check.sh
|
||||
|
||||
USER vncuser
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
@@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"; }
|
||||
|
||||
log "Starting Meldestelle Desktop-App..."
|
||||
|
||||
export DISPLAY=${DISPLAY:-:99}
|
||||
export VNC_PORT=${VNC_PORT:-5901}
|
||||
export NOVNC_PORT=${NOVNC_PORT:-6080}
|
||||
export VNC_PW=${VNC_PW:-meldestelle}
|
||||
export API_BASE_URL=${API_BASE_URL:-http://api-gateway:8081}
|
||||
# Grafik-Optimierungen für Docker (Software Rendering erzwingen)
|
||||
export SKIKO_RENDER_API="SOFTWARE"
|
||||
export LIBGL_ALWAYS_SOFTWARE=1
|
||||
export MESA_GL_VERSION_OVERRIDE=3.3
|
||||
|
||||
# 0. VNC Passwort Setup
|
||||
mkdir -p /home/vncuser/.vnc
|
||||
x11vnc -storepasswd "$VNC_PW" /home/vncuser/.vnc/passwd
|
||||
|
||||
# 1. Start Xvfb
|
||||
log "Starting Xvfb..."
|
||||
rm -f /tmp/.X99-lock
|
||||
Xvfb $DISPLAY -screen 0 1280x1024x24 -ac +extension GLX +render -noreset &
|
||||
sleep 2
|
||||
|
||||
# 2. Start Window Manager
|
||||
log "Starting XFCE4..."
|
||||
startxfce4 &
|
||||
sleep 2
|
||||
|
||||
# 3. Start VNC Server
|
||||
log "Starting x11vnc on port $VNC_PORT..."
|
||||
x11vnc -display $DISPLAY -forever -rfbauth /home/vncuser/.vnc/passwd -create -rfbport $VNC_PORT -shared -bg
|
||||
|
||||
# 4. Start noVNC (Fix für Websockify Pfad)
|
||||
log "Starting noVNC on port $NOVNC_PORT..."
|
||||
# Wir nutzen das mitgelieferte Proxy-Script, das ist robuster als websockify direkt aufzurufen
|
||||
/usr/share/novnc/utils/novnc_proxy --vnc localhost:$VNC_PORT --listen $NOVNC_PORT &
|
||||
NOVNC_PID=$!
|
||||
|
||||
# 5. Start Desktop App (Fix für Pfad-Probleme)
|
||||
log "Searching for App binary..."
|
||||
|
||||
# Wir suchen rekursiv nach der Datei "Meldestelle" im Ordner "bin", die ausführbar ist
|
||||
# Screenshot zeigte: /app/desktop-app/Meldestelle/bin/Meldestelle
|
||||
APP_PATH=$(find /app/desktop-app -type f -path "*/bin/Meldestelle" | head -n 1)
|
||||
|
||||
if [ -z "$APP_PATH" ]; then
|
||||
# Fallback: Suche irgendeine Datei im bin Ordner, die KEIN Shellscript/Bat ist und executable
|
||||
APP_PATH=$(find /app/desktop-app -type f -path "*/bin/*" ! -name "*.sh" ! -name "*.bat" | head -n 1)
|
||||
fi
|
||||
|
||||
if [ -f "$APP_PATH" ]; then
|
||||
log "🚀 Launching App from: $APP_PATH"
|
||||
chmod +x "$APP_PATH"
|
||||
"$APP_PATH" &
|
||||
APP_PID=$!
|
||||
else
|
||||
log "❌ CRITICAL ERROR: App binary not found!"
|
||||
log "Files in /app/desktop-app:"
|
||||
ls -R /app/desktop-app
|
||||
fi
|
||||
|
||||
log "Ready! Access: http://localhost:6080/vnc.html"
|
||||
|
||||
# Cleanup bei Stop
|
||||
cleanup() {
|
||||
log "Stopping..."
|
||||
if [ -n "$APP_PID" ]; then kill $APP_PID 2>/dev/null || true; fi
|
||||
kill $NOVNC_PID 2>/dev/null || true
|
||||
pkill x11vnc || true
|
||||
exit 0
|
||||
}
|
||||
trap cleanup SIGTERM SIGINT
|
||||
|
||||
# Loggt Websockify Output in die Konsole
|
||||
tail -f /dev/null
|
||||
@@ -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,133 @@
|
||||
# Nginx Production Configuration
|
||||
# =============================================================================
|
||||
# This configuration provides secure reverse proxy with SSL termination,
|
||||
# security headers, and performance optimizations for production
|
||||
# =============================================================================
|
||||
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
# Performance and Security Settings
|
||||
worker_rlimit_nofile 65535;
|
||||
|
||||
events {
|
||||
worker_connections 4096;
|
||||
use epoll;
|
||||
multi_accept on;
|
||||
}
|
||||
|
||||
http {
|
||||
# Basic Settings
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
# Performance Settings
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 65;
|
||||
types_hash_max_size 2048;
|
||||
server_tokens off;
|
||||
|
||||
# Buffer Settings
|
||||
client_body_buffer_size 128k;
|
||||
client_max_body_size 10m;
|
||||
client_header_buffer_size 1k;
|
||||
large_client_header_buffers 4 4k;
|
||||
output_buffers 1 32k;
|
||||
postpone_output 1460;
|
||||
|
||||
# Timeout Settings
|
||||
client_body_timeout 12;
|
||||
client_header_timeout 12;
|
||||
send_timeout 10;
|
||||
|
||||
# Gzip Compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 10240;
|
||||
gzip_proxied expired no-cache no-store private must-revalidate auth;
|
||||
gzip_types
|
||||
text/plain
|
||||
text/css
|
||||
text/xml
|
||||
text/javascript
|
||||
application/x-javascript
|
||||
application/xml+rss
|
||||
application/javascript
|
||||
application/json
|
||||
application/xml
|
||||
application/rss+xml
|
||||
application/atom+xml
|
||||
image/svg+xml;
|
||||
|
||||
# Security Headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
|
||||
# Rate Limiting
|
||||
limit_req_zone $binary_remote_addr zone=login:10m rate=10r/m;
|
||||
limit_req_zone $binary_remote_addr zone=api:10m rate=100r/m;
|
||||
limit_req_zone $binary_remote_addr zone=general:10m rate=1000r/m;
|
||||
|
||||
# Logging Format
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for" '
|
||||
'$request_time $upstream_response_time';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
# SSL Configuration
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
ssl_session_tickets off;
|
||||
ssl_stapling on;
|
||||
ssl_stapling_verify on;
|
||||
|
||||
# Upstream Definitions
|
||||
upstream keycloak {
|
||||
server keycloak:8443;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
upstream grafana {
|
||||
server grafana:3000;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
upstream prometheus {
|
||||
server prometheus:9090;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
# HTTP to HTTPS Redirect
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
# Health Check Endpoint
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "healthy\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
}
|
||||
|
||||
# Include additional server configurations
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
# syntax=docker/dockerfile:1.8
|
||||
# ===================================================================
|
||||
# Multi-Stage Dockerfile for Meldestelle Web-App (Kotlin/JS)
|
||||
# Version: 2.1.0 - Fix: Use Debian for Build (Glibc), Alpine for Run
|
||||
# ===================================================================
|
||||
|
||||
# === GLOBAL ARGS ===
|
||||
ARG GRADLE_VERSION
|
||||
ARG JAVA_VERSION
|
||||
ARG NODE_VERSION
|
||||
ARG NGINX_IMAGE_TAG
|
||||
ARG WEB_BUILD_PROFILE
|
||||
ARG VERSION
|
||||
ARG BUILD_DATE
|
||||
|
||||
# ===================================================================
|
||||
# Stage 1: Build Stage (Debian-based for Node.js compatibility)
|
||||
# ===================================================================
|
||||
# WICHTIG: Wir nutzen hier NICHT Alpine, damit die Node-Binaries funktionieren!
|
||||
FROM gradle:${GRADLE_VERSION}-jdk${JAVA_VERSION} AS builder
|
||||
|
||||
ARG WEB_BUILD_PROFILE
|
||||
ARG VERSION
|
||||
ARG BUILD_DATE
|
||||
|
||||
LABEL stage=builder
|
||||
WORKDIR /workspace
|
||||
|
||||
# 1. Gradle Optimizations
|
||||
# Hinweis: Wir lassen Node/Yarn Download Flags weg, da Gradle auf Debian
|
||||
# die Standard-Binaries problemlos laden und ausführen kann.
|
||||
ENV GRADLE_OPTS="-Dorg.gradle.caching=true \
|
||||
-Dorg.gradle.daemon=false \
|
||||
-Dorg.gradle.parallel=true \
|
||||
-Dorg.gradle.workers.max=2 \
|
||||
-Dorg.gradle.jvmargs=-Xmx2g \
|
||||
-XX:+UseParallelGC \
|
||||
-XX:MaxMetaspaceSize=512m"
|
||||
ENV GRADLE_USER_HOME=/home/gradle/.gradle
|
||||
|
||||
# 2. Copy Build Configs
|
||||
COPY gradlew gradlew.bat gradle.properties settings.gradle.kts ./
|
||||
COPY gradle/ gradle/
|
||||
COPY build.gradle.kts ./
|
||||
|
||||
RUN chmod +x gradlew
|
||||
|
||||
# 3. Copy Sources (Monorepo Structure)
|
||||
COPY platform/ platform/
|
||||
COPY core/ core/
|
||||
COPY backend/ backend/
|
||||
COPY frontend/ frontend/
|
||||
# FIX: Docs Ordner ist notwendig für die Gradle-Konfigurationsphase
|
||||
COPY docs/ docs/
|
||||
|
||||
# 4. Resolve Dependencies
|
||||
# Wir lassen Gradle Node.js selbst herunterladen (funktioniert jetzt, da wir auf Debian sind)
|
||||
RUN --mount=type=cache,target=/home/gradle/.gradle/caches \
|
||||
--mount=type=cache,target=/home/gradle/.gradle/wrapper \
|
||||
./gradlew :frontend:shells:meldestelle-portal:dependencies --no-daemon
|
||||
|
||||
# 5. Build Web App
|
||||
RUN --mount=type=cache,target=/home/gradle/.gradle/caches \
|
||||
--mount=type=cache,target=/home/gradle/.gradle/wrapper \
|
||||
if [ "$WEB_BUILD_PROFILE" = "prod" ]; then \
|
||||
echo "Building for PRODUCTION..."; \
|
||||
./gradlew :frontend:shells:meldestelle-portal:jsBrowserDistribution -Pproduction=true --no-daemon; \
|
||||
mkdir -p /app/dist && \
|
||||
cp -r frontend/shells/meldestelle-portal/build/dist/js/productionExecutable/* /app/dist/; \
|
||||
else \
|
||||
echo "Building for DEVELOPMENT..."; \
|
||||
./gradlew :frontend:shells:meldestelle-portal:jsBrowserDevelopmentExecutable --no-daemon; \
|
||||
mkdir -p /app/dist && \
|
||||
cp -r frontend/shells/meldestelle-portal/build/dist/js/developmentExecutable/* /app/dist/; \
|
||||
fi
|
||||
|
||||
# ===================================================================
|
||||
# Stage 2: Runtime Stage (Alpine Nginx)
|
||||
# ===================================================================
|
||||
FROM nginx:${NGINX_IMAGE_TAG}
|
||||
|
||||
ARG VERSION
|
||||
ARG BUILD_DATE
|
||||
ARG JAVA_VERSION
|
||||
|
||||
# Metadata
|
||||
LABEL service="web-app" \
|
||||
version="${VERSION}" \
|
||||
maintainer="Meldestelle Development Team" \
|
||||
build.date="${BUILD_DATE}" \
|
||||
org.opencontainers.image.title="Meldestelle Web-App"
|
||||
|
||||
# Tools & User Setup
|
||||
RUN apk add --no-cache curl && \
|
||||
rm /etc/nginx/conf.d/default.conf && \
|
||||
mkdir -p /usr/share/nginx/html/downloads
|
||||
|
||||
# Copy Artifacts
|
||||
COPY config/frontends/web-app/nginx.conf /etc/nginx/nginx.conf
|
||||
COPY --from=builder /app/dist/ /usr/share/nginx/html/
|
||||
COPY config/frontends/web-app/downloads/ /usr/share/nginx/html/downloads/
|
||||
|
||||
# Permissions
|
||||
RUN chown -R nginx:nginx /usr/share/nginx/html && \
|
||||
chmod -R 755 /usr/share/nginx/html && \
|
||||
chown -R nginx:nginx /var/cache/nginx /var/log/nginx /etc/nginx/conf.d && \
|
||||
touch /var/run/nginx.pid && \
|
||||
chown -R nginx:nginx /var/run/nginx.pid
|
||||
|
||||
USER nginx
|
||||
EXPOSE 4000
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||
CMD curl -f http://localhost:4000/health || exit 1
|
||||
|
||||
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,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 $$;
|
||||
@@ -0,0 +1,90 @@
|
||||
# PostgreSQL Configuration File
|
||||
# Optimized for Meldestelle application
|
||||
|
||||
# Connection Settings
|
||||
listen_addresses = '*'
|
||||
max_connections = 100
|
||||
superuser_reserved_connections = 3
|
||||
|
||||
# Memory Settings
|
||||
# These will be overridden by environment variables in docker-compose.yaml
|
||||
shared_buffers = 256MB # min 128kB
|
||||
work_mem = 16MB # min 64kB
|
||||
maintenance_work_mem = 64MB # min 1MB
|
||||
effective_cache_size = 768MB
|
||||
|
||||
# Write-Ahead Log (WAL)
|
||||
wal_level = replica # minimal, replica, or logical
|
||||
max_wal_size = 1GB
|
||||
min_wal_size = 80MB
|
||||
wal_buffers = 16MB # min 32kB, -1 sets based on shared_buffers
|
||||
checkpoint_completion_target = 0.9 # checkpoint target duration, 0.0 - 1.0
|
||||
random_page_cost = 1.1 # For SSD storage
|
||||
|
||||
# Background Writer
|
||||
bgwriter_delay = 200ms
|
||||
bgwriter_lru_maxpages = 100
|
||||
bgwriter_lru_multiplier = 2.0
|
||||
|
||||
# Asynchronous Behavior
|
||||
effective_io_concurrency = 200 # For SSD storage
|
||||
max_worker_processes = 8
|
||||
max_parallel_workers_per_gather = 4
|
||||
max_parallel_workers = 8
|
||||
max_parallel_maintenance_workers = 4
|
||||
|
||||
# Query Planner
|
||||
default_statistics_target = 100
|
||||
constraint_exclusion = partition
|
||||
|
||||
# Logging
|
||||
log_destination = 'stderr'
|
||||
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
|
||||
log_min_duration_statement = 250ms # Log slow queries (250ms or slower)
|
||||
log_checkpoints = on
|
||||
log_connections = on
|
||||
log_disconnections = on
|
||||
log_lock_waits = on
|
||||
log_temp_files = 0
|
||||
log_autovacuum_min_duration = 250ms
|
||||
log_line_prefix = '%m [%p] %q%u@%d '
|
||||
|
||||
# Autovacuum
|
||||
autovacuum = on
|
||||
autovacuum_max_workers = 3
|
||||
autovacuum_naptime = 1min
|
||||
autovacuum_vacuum_threshold = 50
|
||||
autovacuum_analyze_threshold = 50
|
||||
autovacuum_vacuum_scale_factor = 0.05
|
||||
autovacuum_analyze_scale_factor = 0.025
|
||||
autovacuum_vacuum_cost_delay = 20ms
|
||||
autovacuum_vacuum_cost_limit = 2000
|
||||
|
||||
# Statement Behavior
|
||||
search_path = '"$user", public'
|
||||
row_security = on
|
||||
|
||||
# Client Connection Defaults
|
||||
client_min_messages = notice
|
||||
statement_timeout = 60000 # 60 seconds, prevents long-running queries
|
||||
lock_timeout = 10000 # 10 seconds, prevents lock contention
|
||||
idle_in_transaction_session_timeout = 600000 # 10 minutes, prevents idle transactions
|
||||
|
||||
# Disk
|
||||
temp_file_limit = 1GB # Limits temp file size
|
||||
|
||||
# SSL
|
||||
ssl = off
|
||||
ssl_prefer_server_ciphers = on
|
||||
|
||||
# Performance Monitoring
|
||||
track_activities = on
|
||||
track_counts = on
|
||||
track_io_timing = on
|
||||
track_functions = pl # none, pl, all
|
||||
track_activity_query_size = 2048
|
||||
@@ -0,0 +1,149 @@
|
||||
# Redis Production Configuration
|
||||
# =============================================================================
|
||||
# This configuration file contains production-ready settings for Redis
|
||||
# with security, performance, and reliability optimizations.
|
||||
# =============================================================================
|
||||
|
||||
# Network and Security
|
||||
bind 0.0.0.0
|
||||
protected-mode yes
|
||||
port 6379
|
||||
|
||||
# Authentication (password will be set via command line)
|
||||
# requirepass will be set via --requirepass flag in docker-compose
|
||||
|
||||
# General Settings
|
||||
timeout 300
|
||||
tcp-keepalive 300
|
||||
tcp-backlog 511
|
||||
|
||||
# Memory Management
|
||||
maxmemory 256mb
|
||||
maxmemory-policy allkeys-lru
|
||||
maxmemory-samples 5
|
||||
|
||||
# Persistence Settings
|
||||
save 900 1
|
||||
save 300 10
|
||||
save 60 10000
|
||||
stop-writes-on-bgsave-error yes
|
||||
rdbcompression yes
|
||||
rdbchecksum yes
|
||||
dbfilename dump.rdb
|
||||
dir /data
|
||||
|
||||
# Append Only File (AOF)
|
||||
appendonly yes
|
||||
appendfilename "appendonly.aof"
|
||||
appendfsync everysec
|
||||
no-appendfsync-on-rewrite no
|
||||
auto-aof-rewrite-percentage 100
|
||||
auto-aof-rewrite-min-size 64mb
|
||||
aof-load-truncated yes
|
||||
aof-use-rdb-preamble yes
|
||||
|
||||
# Logging
|
||||
loglevel notice
|
||||
logfile ""
|
||||
syslog-enabled no
|
||||
|
||||
# Database Settings
|
||||
databases 16
|
||||
|
||||
# Slow Log
|
||||
slowlog-log-slower-than 10000
|
||||
slowlog-max-len 128
|
||||
|
||||
# Latency Monitoring
|
||||
latency-monitor-threshold 100
|
||||
|
||||
# Client Settings
|
||||
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 ""
|
||||
|
||||
# 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
|
||||
# tls-port 6380
|
||||
# tls-cert-file /tls/redis.crt
|
||||
# tls-key-file /tls/redis.key
|
||||
# tls-ca-cert-file /tls/ca.crt
|
||||
# tls-dh-params-file /tls/redis.dh
|
||||
# tls-protocols "TLSv1.2 TLSv1.3"
|
||||
# tls-ciphers "ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS"
|
||||
# tls-ciphersuites "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256"
|
||||
# tls-prefer-server-ciphers yes
|
||||
# tls-session-caching no
|
||||
# tls-session-cache-size 5000
|
||||
# tls-session-cache-timeout 60
|
||||
|
||||
# Performance Tuning
|
||||
hash-max-ziplist-entries 512
|
||||
hash-max-ziplist-value 64
|
||||
list-max-ziplist-size -2
|
||||
list-compress-depth 0
|
||||
set-max-intset-entries 512
|
||||
zset-max-ziplist-entries 128
|
||||
zset-max-ziplist-value 64
|
||||
hll-sparse-max-bytes 3000
|
||||
stream-node-max-bytes 4096
|
||||
stream-node-max-entries 100
|
||||
|
||||
# Active Rehashing
|
||||
activerehashing yes
|
||||
|
||||
# Client Output Buffer Limits
|
||||
client-output-buffer-limit normal 0 0 0
|
||||
client-output-buffer-limit replica 256mb 64mb 60
|
||||
client-output-buffer-limit pubsub 32mb 8mb 60
|
||||
|
||||
# Client Query Buffer
|
||||
client-query-buffer-limit 1gb
|
||||
|
||||
# Protocol Buffer
|
||||
proto-max-bulk-len 512mb
|
||||
|
||||
# Replication (for Redis cluster/replica setup)
|
||||
# replica-serve-stale-data yes
|
||||
# replica-read-only yes
|
||||
# repl-diskless-sync no
|
||||
# repl-diskless-sync-delay 5
|
||||
# repl-ping-replica-period 10
|
||||
# repl-timeout 60
|
||||
# repl-disable-tcp-nodelay no
|
||||
# repl-backlog-size 1mb
|
||||
# repl-backlog-ttl 3600
|
||||
|
||||
# Security: Disable potentially dangerous features
|
||||
enable-protected-configs no
|
||||
enable-debug-command no
|
||||
enable-module-command no
|
||||
|
||||
# Notifications (disable for performance)
|
||||
notify-keyspace-events ""
|
||||
|
||||
# Advanced Configuration
|
||||
hz 10
|
||||
dynamic-hz yes
|
||||
aof-rewrite-incremental-fsync yes
|
||||
rdb-save-incremental-fsync yes
|
||||
|
||||
# Jemalloc Configuration
|
||||
jemalloc-bg-thread yes
|
||||
|
||||
# Threading (Redis 6.0+)
|
||||
# io-threads 4
|
||||
# io-threads-do-reads yes
|
||||
@@ -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,269 @@
|
||||
# SSL/TLS Zertifikat-Setup für die Produktionsumgebung
|
||||
|
||||
Dieses Verzeichnis enthält SSL/TLS-Zertifikate und Schlüssel zur Absicherung der Meldestelle-Anwendung in der Produktionsumgebung.
|
||||
|
||||
## Verzeichnisstruktur
|
||||
|
||||
```
|
||||
config/ssl/
|
||||
├── postgres/ # PostgreSQL SSL-Zertifikate
|
||||
├── redis/ # Redis TLS-Zertifikate
|
||||
├── keycloak/ # Keycloak HTTPS-Zertifikate
|
||||
├── prometheus/ # Prometheus HTTPS-Zertifikate
|
||||
├── grafana/ # Grafana HTTPS-Zertifikate
|
||||
├── nginx/ # Nginx SSL-Zertifikate
|
||||
└── README.md # Diese Datei
|
||||
```
|
||||
|
||||
## Zertifikat-Anforderungen
|
||||
|
||||
### 1. PostgreSQL SSL-Zertifikate
|
||||
|
||||
Platzieren Sie die folgenden Dateien in `config/ssl/postgres/`:
|
||||
|
||||
- `server.crt` - Server-Zertifikat
|
||||
- `server.key` - Privater Server-Schlüssel
|
||||
- `ca.crt` - Certificate Authority-Zertifikat
|
||||
|
||||
### 2. Redis TLS-Zertifikate
|
||||
|
||||
Platzieren Sie die folgenden Dateien in `config/ssl/redis/`:
|
||||
|
||||
- `redis.crt` - Redis Server-Zertifikat
|
||||
- `redis.key` - Privater Redis Server-Schlüssel
|
||||
- `ca.crt` - Certificate Authority-Zertifikat
|
||||
- `redis.dh` - Diffie-Hellman Parameter
|
||||
|
||||
### 3. Keycloak HTTPS-Zertifikate
|
||||
|
||||
Platzieren Sie die folgenden Dateien in `config/ssl/keycloak/`:
|
||||
|
||||
- `server.crt.pem` - Server-Zertifikat im PEM-Format
|
||||
- `server.key.pem` - Privater Server-Schlüssel im PEM-Format
|
||||
|
||||
### 4. Prometheus HTTPS-Zertifikate
|
||||
|
||||
Platzieren Sie die folgenden Dateien in `config/ssl/prometheus/`:
|
||||
|
||||
- `prometheus.crt` - Prometheus Server-Zertifikat
|
||||
- `prometheus.key` - Privater Prometheus Server-Schlüssel
|
||||
- `web.yml` - Prometheus Web-Konfigurationsdatei
|
||||
|
||||
### 5. Grafana HTTPS-Zertifikate
|
||||
|
||||
Platzieren Sie die folgenden Dateien in `config/ssl/grafana/`:
|
||||
|
||||
- `server.crt` - Grafana Server-Zertifikat
|
||||
- `server.key` - Privater Grafana Server-Schlüssel
|
||||
|
||||
### 6. Nginx SSL-Zertifikate
|
||||
|
||||
Platzieren Sie die folgenden Dateien in `config/ssl/nginx/`:
|
||||
|
||||
- `server.crt` - Haupt-SSL-Zertifikat
|
||||
- `server.key` - Privater Haupt-SSL-Schlüssel
|
||||
- `dhparam.pem` - Diffie-Hellman Parameter
|
||||
|
||||
## Generierung selbstsignierter Zertifikate (Entwicklung/Test)
|
||||
|
||||
⚠️ **Warnung**: Verwenden Sie selbstsignierte Zertifikate nur für Entwicklung und Tests. Nutzen Sie ordnungsgemäß von einer CA signierte Zertifikate in der Produktion.
|
||||
|
||||
### CA-Zertifikat generieren
|
||||
|
||||
```bash
|
||||
# CA privaten Schlüssel erstellen
|
||||
openssl genrsa -out ca.key 4096
|
||||
|
||||
# CA-Zertifikat erstellen
|
||||
openssl req -new -x509 -days 365 -key ca.key -out ca.crt \
|
||||
-subj "/C=AT/ST=Vienna/L=Vienna/O=Meldestelle/OU=IT/CN=Meldestelle-CA"
|
||||
```
|
||||
|
||||
### Server-Zertifikate generieren
|
||||
|
||||
```bash
|
||||
# Für jeden Service privaten Schlüssel und Certificate Signing Request generieren
|
||||
openssl genrsa -out server.key 2048
|
||||
openssl req -new -key server.key -out server.csr \
|
||||
-subj "/C=AT/ST=Vienna/L=Vienna/O=Meldestelle/OU=IT/CN=ihre-domain.com"
|
||||
|
||||
# Zertifikat mit CA signieren
|
||||
openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key \
|
||||
-CAcreateserial -out server.crt
|
||||
|
||||
# Aufräumen
|
||||
rm server.csr
|
||||
```
|
||||
|
||||
### Diffie-Hellman Parameter generieren
|
||||
|
||||
```bash
|
||||
openssl dhparam -out dhparam.pem 2048
|
||||
```
|
||||
|
||||
## Produktions-Zertifikat Setup
|
||||
|
||||
### Option 1: Let's Encrypt (Empfohlen)
|
||||
|
||||
Verwenden Sie Certbot, um kostenlose SSL-Zertifikate zu erhalten:
|
||||
|
||||
```bash
|
||||
# Certbot installieren
|
||||
sudo apt-get install certbot
|
||||
|
||||
# Zertifikate erhalten
|
||||
sudo certbot certonly --standalone -d ihre-domain.com -d www.ihre-domain.com
|
||||
|
||||
# Zertifikate in entsprechende Verzeichnisse kopieren
|
||||
sudo cp /etc/letsencrypt/live/ihre-domain.com/fullchain.pem config/ssl/nginx/server.crt
|
||||
sudo cp /etc/letsencrypt/live/ihre-domain.com/privkey.pem config/ssl/nginx/server.key
|
||||
```
|
||||
|
||||
### Option 2: Kommerzielle CA
|
||||
|
||||
1. Certificate Signing Requests (CSRs) generieren
|
||||
2. CSRs an Ihre Certificate Authority übermitteln
|
||||
3. Signierte Zertifikate herunterladen
|
||||
4. Zertifikate in entsprechende Verzeichnisse platzieren
|
||||
|
||||
### Option 3: Interne CA
|
||||
|
||||
Bei Verwendung einer internen Certificate Authority:
|
||||
|
||||
1. CSRs für jeden Service generieren
|
||||
2. Zertifikate mit Ihrer internen CA signieren
|
||||
3. CA-Zertifikat an alle Clients verteilen
|
||||
|
||||
## Dateiberechtigungen
|
||||
|
||||
Stellen Sie ordnungsgemäße Dateiberechtigungen für die Sicherheit sicher:
|
||||
|
||||
```bash
|
||||
# Restriktive Berechtigungen für private Schlüssel setzen
|
||||
chmod 600 config/ssl/*/server.key
|
||||
chmod 600 config/ssl/*/redis.key
|
||||
chmod 600 config/ssl/*/prometheus.key
|
||||
|
||||
# Lesbare Berechtigungen für Zertifikate setzen
|
||||
chmod 644 config/ssl/*/server.crt
|
||||
chmod 644 config/ssl/*/ca.crt
|
||||
|
||||
# Verzeichnisberechtigungen setzen
|
||||
chmod 755 config/ssl/*/
|
||||
```
|
||||
|
||||
## Docker Volume Mounts
|
||||
|
||||
Die Zertifikate werden als schreibgeschützte Volumes in die Docker-Container eingebunden:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- ./config/ssl/nginx:/etc/ssl/nginx:ro
|
||||
- ./config/ssl/keycloak:/opt/keycloak/conf:ro
|
||||
# ... weitere Mounts
|
||||
```
|
||||
|
||||
## Zertifikat-Erneuerung
|
||||
|
||||
### Automatisierte Erneuerung (Let's Encrypt)
|
||||
|
||||
Richten Sie einen Cron-Job für automatische Erneuerung ein:
|
||||
|
||||
```bash
|
||||
# Zu Crontab hinzufügen
|
||||
0 12 * * * /usr/bin/certbot renew --quiet --post-hook "docker-compose -f docker-compose.prod.yml restart nginx"
|
||||
```
|
||||
|
||||
### Manuelle Erneuerung
|
||||
|
||||
1. Neue Zertifikate generieren
|
||||
2. Alte Zertifikate in SSL-Verzeichnissen ersetzen
|
||||
3. Betroffene Services neu starten:
|
||||
|
||||
```bash
|
||||
docker-compose -f docker-compose.prod.yml restart nginx keycloak grafana prometheus
|
||||
```
|
||||
|
||||
## Sicherheits-Best-Practices
|
||||
|
||||
1. **Starke Verschlüsselung verwenden**: Mindestens 2048-Bit RSA-Schlüssel oder 256-Bit ECDSA-Schlüssel verwenden
|
||||
2. **Regelmäßige Rotation**: Zertifikate regelmäßig rotieren (jährlich oder halbjährlich)
|
||||
3. **Sichere Speicherung**: Private Schlüssel sicher speichern und Zugriff beschränken
|
||||
4. **Ablauf überwachen**: Überwachung für Zertifikat-Ablauf einrichten
|
||||
5. **HSTS verwenden**: HTTP Strict Transport Security aktivieren
|
||||
6. **Perfect Forward Secrecy**: ECDHE-Cipher-Suites verwenden
|
||||
7. **Certificate Transparency**: CT-Logs auf unbefugte Zertifikate überwachen
|
||||
|
||||
## Fehlerbehebung
|
||||
|
||||
### Häufige Probleme
|
||||
|
||||
1. **Berechtigung verweigert**
|
||||
|
||||
```bash
|
||||
# Dateiberechtigungen korrigieren
|
||||
sudo chown -R $USER:$USER config/ssl/
|
||||
chmod -R 755 config/ssl/
|
||||
chmod 600 config/ssl/*/server.key
|
||||
```
|
||||
|
||||
2. **Zertifikat-Verifizierung fehlgeschlagen**
|
||||
|
||||
```bash
|
||||
# Zertifikat verifizieren
|
||||
openssl x509 -in config/ssl/nginx/server.crt -text -noout
|
||||
|
||||
# Zertifikatskette prüfen
|
||||
openssl verify -CAfile config/ssl/nginx/ca.crt config/ssl/nginx/server.crt
|
||||
```
|
||||
|
||||
3. **TLS-Handshake-Fehler**
|
||||
- Gültigkeitsdaten des Zertifikats prüfen
|
||||
- Verifizieren, dass Zertifikat zum Hostnamen passt
|
||||
- Ordnungsgemäße Cipher-Suite-Konfiguration sicherstellen
|
||||
|
||||
### SSL-Konfiguration testen
|
||||
|
||||
```bash
|
||||
# SSL-Zertifikat testen
|
||||
openssl s_client -connect ihre-domain.com:443 -servername ihre-domain.com
|
||||
|
||||
# Mit spezifischem Protokoll testen
|
||||
openssl s_client -connect ihre-domain.com:443 -tls1_2
|
||||
|
||||
# Zertifikat-Ablauf prüfen
|
||||
openssl x509 -in config/ssl/nginx/server.crt -noout -dates
|
||||
|
||||
# Zertifikat-Details anzeigen
|
||||
openssl x509 -in config/ssl/nginx/server.crt -text -noout
|
||||
```
|
||||
|
||||
## Monitoring und Wartung
|
||||
|
||||
### Zertifikat-Überwachung
|
||||
|
||||
Implementieren Sie Überwachung für:
|
||||
|
||||
- Zertifikat-Ablaufdaten
|
||||
- Zertifikat-Gültigkeit
|
||||
- SSL/TLS-Handshake-Erfolg
|
||||
- Cipher-Suite-Verwendung
|
||||
|
||||
### Wartungsaufgaben
|
||||
|
||||
- Regelmäßige Überprüfung der Zertifikat-Gültigkeit
|
||||
- Aktualisierung der Cipher-Suites
|
||||
- Überwachung der Sicherheitsupdates
|
||||
- Backup der Zertifikate und privaten Schlüssel
|
||||
|
||||
## Weitere Ressourcen
|
||||
|
||||
- [Mozilla SSL Configuration Generator](https://ssl-config.mozilla.org/)
|
||||
- [SSL Labs Server Test](https://www.ssllabs.com/ssltest/)
|
||||
- [Let's Encrypt Dokumentation](https://letsencrypt.org/docs/)
|
||||
- [OpenSSL Dokumentation](https://www.openssl.org/docs/)
|
||||
|
||||
---
|
||||
|
||||
**Letzte Aktualisierung**: 25. Juli 2025
|
||||
|
||||
@@ -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"]
|
||||
Reference in New Issue
Block a user