Metriques et journaux (CHANGELOG 142). Le site pose les quatre interrupteurs TLS que chaque locataire pose dans ses group_vars : node_exporter et Loki servaient en HTTP sur les neuf VM, le certificat etait la, inutilise. test_parite_tls_site.py compare desormais le site aux locataires. Les hyperviseurs poussent leurs journaux en HTTPS verifie, avec la racine du site comme simple fichier. L'exportateur PostgreSQL sert en HTTPS partout, sur l'interrupteur de PostgreSQL lui-meme, que Prometheus lit pour choisir son schema. La frontiere (CHANGELOG 143). Une autorite subordonnee signee par la racine du site, bornee (pathlen 0, noms sous le domaine et adresses du site, contrainte critique), cle nee sur la boite (scripts/frontiere_pki.py). Syslog vers Loki en TLS mutuel (seule la frontiere peut ecrire), exportateur en HTTPS, interface web sur le certificat de services ; les clients d'API verifient la frontiere contre la racine du site. La lecture du magasin passe par config.xml : l'API de recherche renvoie les cles privees. Deploye et mesure au site, chez les deux locataires et sur la frontiere ; 95 preuves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
89 lines
4.3 KiB
Django/Jinja
89 lines
4.3 KiB
Django/Jinja
# Gere par Set-OPS (role serveur_prometheus). Ne pas editer a la main.
|
|
global:
|
|
scrape_interval: {{ serveur_prometheus_intervalle }}
|
|
|
|
scrape_configs:
|
|
- job_name: prometheus
|
|
static_configs:
|
|
- targets: ['localhost:9090']
|
|
|
|
- job_name: node
|
|
{% if serveur_prometheus_metriques_tls | default(false) %}
|
|
scheme: https
|
|
tls_config:
|
|
ca_file: {{ serveur_prometheus_metriques_ca }}
|
|
{% endif %}
|
|
static_configs:
|
|
{# `intersect` rend un ENSEMBLE : son ordre d'iteration n'est pas stable d'un
|
|
processus Python a l'autre. Sans `sort`, ce fichier se rendait differemment a
|
|
chaque passage — memes quatorze cibles, ordre different — et Prometheus etait
|
|
redemarre pour rien. Trouve par le passage d'idempotence du 2026-08-09.
|
|
On trie sur les HOTES : ordre deterministe et lisible par un humain. #}
|
|
- targets: {{ (groups.get(serveur_prometheus_groupe_metriques, []) | intersect(groups.get('hotes_actifs', [])) | sort)
|
|
| map('extract', hostvars)
|
|
| selectattr('ansible_host', 'defined')
|
|
| map(attribute='ansible_host')
|
|
| map('regex_replace', '$', ':' ~ serveur_prometheus_port_node)
|
|
| list | to_json }}
|
|
{# --- LES CIBLES QUE LES ROLES DECLARENT (2026-09-14) ---------------------------
|
|
Chaque `roles/<role>/meta/metriques.yml` decrit son exportateur ; le nom du DOSSIER
|
|
est le nom du GROUPE, donc les cibles sont les hotes actifs de ce groupe.
|
|
|
|
UN ROLE DECLARE SANS HOTE NE PRODUIT AUCUN JOB. Prometheus n'a pas a porter une cible
|
|
qui n'existe pas, et son journal n'a pas a se remplir de refus previsibles.
|
|
|
|
ORDRE STABLE : `sort` sur les hotes, comme pour `node` juste au-dessus. Sans lui, le
|
|
fichier se rend differemment a chaque passage et Prometheus redemarre pour rien —
|
|
defaut trouve le 2026-08-09, qu'on ne va pas reintroduire ici. #}
|
|
{% for decl in serveur_prometheus_declarations.results | default([]) %}
|
|
{% set role = decl.item.path | dirname | dirname | basename %}
|
|
{% set meta = decl.content | b64decode | from_yaml %}
|
|
{% set expo = meta.exportateur | default(none) %}
|
|
{% set cibles = (groups.get(role, []) | intersect(groups.get('hotes_actifs', [])) | sort)
|
|
| map('extract', hostvars) | selectattr('ansible_host', 'defined')
|
|
| map(attribute='ansible_host')
|
|
| map('regex_replace', '$', ':' ~ (expo.port | default(0))) | list %}
|
|
{# TLS SI LA DECLARATION LE DIT ET QUE CHAQUE CIBLE L'A (2026-10-12) : `tls_si` nomme
|
|
l'interrupteur que le role exportateur lit lui aussi. Une seule cible sans lui, et le
|
|
job reste en HTTP — mieux qu'un job HTTPS qui en perdrait une sans rien dire. #}
|
|
{% set tls = namespace(oui=(expo.tls_si | default('') | length > 0) if expo else false) %}
|
|
{% for h in (groups.get(role, []) | intersect(groups.get('hotes_actifs', [])) | sort) %}
|
|
{% if tls.oui and not (hostvars[h][expo.tls_si] | default(false) | bool) %}{% set tls.oui = false %}{% endif %}
|
|
{% endfor %}
|
|
{% if expo and cibles %}
|
|
|
|
- job_name: {{ expo.job }}
|
|
{% if tls.oui %}
|
|
scheme: https
|
|
tls_config:
|
|
ca_file: {{ serveur_prometheus_metriques_ca }}
|
|
{% endif %}
|
|
static_configs:
|
|
- targets: {{ cibles | to_json }}
|
|
{% endif %}
|
|
{% endfor %}
|
|
{# Les cibles ECRITES AU PLAN completent la derivation, elles ne la remplacent pas :
|
|
un equipement ou un service tiers n'a aucun role Set-OPS pour le declarer. #}
|
|
{% for job in serveur_prometheus_cibles_supplementaires %}
|
|
|
|
- job_name: {{ job.job }}
|
|
{# UNE CIBLE HORS FLOTTE PEUT SERVIR EN TLS (2026-10-12) : la frontiere, certifiee par son
|
|
autorite subordonnee, verifiee contre la meme racine que le reste. #}
|
|
{% if job.tls | default(false) %}
|
|
scheme: https
|
|
tls_config:
|
|
ca_file: {{ serveur_prometheus_metriques_ca }}
|
|
{% endif %}
|
|
static_configs:
|
|
{% if job.hotes | default({}) %}
|
|
{# UNE CIBLE NOMMEE PAR BLOC : l'etiquette `hote` s'attache a un `static_config`, pas a
|
|
une adresse. Les series disent alors « asgard », pas « 192.168.11.41:9100 ». #}
|
|
{% for cible in job.cibles %}
|
|
- targets: {{ [cible] | to_json }}
|
|
labels:
|
|
hote: {{ job.hotes[cible] | default(cible) | to_json }}
|
|
{% endfor %}
|
|
{% else %}
|
|
- targets: {{ job.cibles | to_json }}
|
|
{% endif %}
|
|
{% endfor %}
|