feat(monitoring): supervise l'API, la base et l'hôte avec Prometheus et Grafana
L'API exposait /metrics, mais aucun collecteur ne le lisait : monitoring/ ne contenait que des .gitkeep. Sous le profil Compose `monitoring` : prometheus, alertmanager, grafana, postgres-exporter, node-exporter et cadvisor. Tous ont un mem_limit, pour environ 700 Mo au total sur la VM de 8 Go, et leurs interfaces n'écoutent que sur 127.0.0.1. Le profil est actif en prod via COMPOSE_PROFILES, donc à chaque déploiement, et se lance à la demande ailleurs (make monitoring-up). - Neuf règles d'alerte (API, base, hôte, cibles). Chacune a un cas dans les tests joués par `promtool test rules`, en CI comme par make monitoring-check. - Alertmanager route les alertes par courriel vers Mailpit ; un critical masque le warning de la même cible. - Grafana est provisionné : sources Prometheus et TimescaleDB, et trois tableaux de bord (API, données et dérive du modèle, infrastructure). - Le rôle PostgreSQL `supervision` est en lecture seule sur les seules tables métier (db/roles/supervision.sql), posé par make db-ensure-supervision et par stack-up quand le profil est actif. - Le jeton de /metrics passe à Prometheus en secret Compose (APP_METRICS_TOKEN) ; provision-host.sh génère ce secret et les deux autres. Backend : - un APP_METRICS_TOKEN vide vaut absent ; - les sondes de santé ne comptent plus dans les métriques ; - seaux de latence fins autour de 500 ms ; - un registre Prometheus par application, sans quoi toute application créée après la première (dans les tests) ne mesurait rien. Réf : #26
This commit is contained in:
@@ -107,6 +107,7 @@ services:
|
||||
APP_SMTP_PORT: "1025"
|
||||
APP_SMTP_USE_TLS: "false"
|
||||
APP_SMTP_FROM_ADDRESS: ${APP_SMTP_FROM_ADDRESS:-no-reply@enervision.fr}
|
||||
APP_METRICS_TOKEN: ${APP_METRICS_TOKEN:-}
|
||||
ports:
|
||||
- "${BACKEND_PORT:-8000}:8000"
|
||||
restart: unless-stopped
|
||||
@@ -193,6 +194,111 @@ services:
|
||||
airflow-init:
|
||||
condition: service_completed_successfully
|
||||
|
||||
# Profil `monitoring` : actif en prod par COMPOSE_PROFILES, à la demande ailleurs (ADR 0016).
|
||||
# Aucun `depends_on` : `make monitoring-up` démarre en `--no-deps`, sans jamais recréer `db`.
|
||||
prometheus:
|
||||
image: prom/prometheus:v3.14.0
|
||||
profiles: ["monitoring"]
|
||||
command:
|
||||
- --config.file=/etc/prometheus/prometheus.yml
|
||||
- --storage.tsdb.path=/prometheus
|
||||
- --storage.tsdb.retention.time=15d
|
||||
- --storage.tsdb.retention.size=1GB
|
||||
volumes:
|
||||
- ./monitoring/prometheus:/etc/prometheus:ro
|
||||
- prometheus_data:/prometheus
|
||||
secrets:
|
||||
- metrics_token
|
||||
ports:
|
||||
- "127.0.0.1:${PROMETHEUS_PORT:-9090}:9090"
|
||||
mem_limit: 512m
|
||||
restart: unless-stopped
|
||||
|
||||
alertmanager:
|
||||
image: prom/alertmanager:v0.34.1
|
||||
profiles: ["monitoring"]
|
||||
command:
|
||||
- --config.file=/etc/alertmanager/alertmanager.yml
|
||||
- --storage.path=/alertmanager
|
||||
volumes:
|
||||
- ./monitoring/alertmanager:/etc/alertmanager:ro
|
||||
- alertmanager_data:/alertmanager
|
||||
ports:
|
||||
- "127.0.0.1:${ALERTMANAGER_PORT:-9093}:9093"
|
||||
mem_limit: 64m
|
||||
restart: unless-stopped
|
||||
|
||||
# Sans mot de passe, Grafana créerait un compte admin/admin : le conteneur refuse de démarrer.
|
||||
grafana:
|
||||
image: grafana/grafana:13.2.2
|
||||
profiles: ["monitoring"]
|
||||
entrypoint:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- ': "$${GF_SECURITY_ADMIN_PASSWORD:?GRAFANA_ADMIN_PASSWORD manquant dans .env}" && exec /run.sh'
|
||||
environment:
|
||||
GF_SECURITY_ADMIN_USER: admin
|
||||
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:-}
|
||||
GF_USERS_ALLOW_SIGN_UP: "false"
|
||||
GF_AUTH_ANONYMOUS_ENABLED: "false"
|
||||
GF_ANALYTICS_REPORTING_ENABLED: "false"
|
||||
GF_ANALYTICS_CHECK_FOR_UPDATES: "false"
|
||||
GF_ANALYTICS_CHECK_FOR_PLUGIN_UPDATES: "false"
|
||||
GF_NEWS_NEWS_FEED_ENABLED: "false"
|
||||
GF_DASHBOARDS_DEFAULT_HOME_DASHBOARD_PATH: /etc/grafana/dashboards/api.json
|
||||
POSTGRES_DB: ${POSTGRES_DB:-enervision}
|
||||
SUPERVISION_DB_PASSWORD: ${SUPERVISION_DB_PASSWORD:-}
|
||||
volumes:
|
||||
- ./monitoring/grafana/provisioning:/etc/grafana/provisioning:ro
|
||||
- ./monitoring/grafana/dashboards:/etc/grafana/dashboards:ro
|
||||
- grafana_data:/var/lib/grafana
|
||||
ports:
|
||||
- "127.0.0.1:${GRAFANA_PORT:-3001}:3000"
|
||||
mem_limit: 256m
|
||||
restart: unless-stopped
|
||||
|
||||
postgres-exporter:
|
||||
image: prometheuscommunity/postgres-exporter:v0.20.1
|
||||
profiles: ["monitoring"]
|
||||
environment:
|
||||
DATA_SOURCE_URI: db:5432/${POSTGRES_DB:-enervision}?sslmode=disable
|
||||
DATA_SOURCE_USER: supervision
|
||||
DATA_SOURCE_PASS: ${SUPERVISION_DB_PASSWORD:-}
|
||||
mem_limit: 64m
|
||||
restart: unless-stopped
|
||||
|
||||
node-exporter:
|
||||
image: prom/node-exporter:v1.12.1
|
||||
profiles: ["monitoring"]
|
||||
command:
|
||||
- --path.rootfs=/host
|
||||
pid: host
|
||||
volumes:
|
||||
- /:/host:ro,rslave
|
||||
mem_limit: 64m
|
||||
restart: unless-stopped
|
||||
|
||||
# Contrainte : cAdvisor lit les cgroups de tous les conteneurs de l'hôte, d'où `privileged` et
|
||||
# ses montages en lecture seule. Aucun port publié : seul Prometheus le joint.
|
||||
cadvisor:
|
||||
image: gcr.io/cadvisor/cadvisor:v0.55.1
|
||||
profiles: ["monitoring"]
|
||||
privileged: true
|
||||
devices:
|
||||
- /dev/kmsg
|
||||
command:
|
||||
- --docker_only=true
|
||||
- --housekeeping_interval=30s
|
||||
- --store_container_labels=false
|
||||
volumes:
|
||||
- /:/rootfs:ro
|
||||
- /var/run:/var/run:ro
|
||||
- /sys:/sys:ro
|
||||
- /var/lib/docker/:/var/lib/docker:ro
|
||||
- /dev/disk/:/dev/disk:ro
|
||||
mem_limit: 160m
|
||||
restart: unless-stopped
|
||||
|
||||
# Pourquoi : sur le réseau du projet, k6 joint `backend:8000` sans passer par nginx, dont la
|
||||
# limite par adresse (20 req/s) fausserait la mesure de l'API. `make load-*` le lance (ADR 0015).
|
||||
k6:
|
||||
@@ -214,3 +320,11 @@ volumes:
|
||||
pgdata:
|
||||
airflow_logs:
|
||||
airflow_ml_state:
|
||||
prometheus_data:
|
||||
alertmanager_data:
|
||||
grafana_data:
|
||||
|
||||
# Vide tant qu'APP_METRICS_TOKEN n'est pas posé : l'API n'exige alors aucun jeton.
|
||||
secrets:
|
||||
metrics_token:
|
||||
environment: APP_METRICS_TOKEN
|
||||
|
||||
Reference in New Issue
Block a user