Ajout de meta/supervision.yml a six roles, chacun deposant sa propre sonde selon le contrat des greffons Nagios. DEUX PRINCIPES POSES AU DOCUMENT DE CONCEPTION, parce que la premiere sonde les a imposes : 1. UNE SONDE DOIT POUVOIR ETRE MISE EN DEFAUT PAR PARAMETRE. Cible et seuils sont des variables du role : on la prouve rouge avec un port ferme ou un seuil impossible, sur une machine reelle, sans rien casser, et aussi souvent qu on veut. Une sonde qu on ne peut prouver qu en cassant un service ne sera prouvee qu une fois. 2. LA SONDE VIT LA OU VIT LA VERITE. « Ce noeud est-il collecte ? » est une sonde de serveur_prometheus, pas de client_metrique : une seule y voit les N noeuds, et surtout elle voit le cas SILENCIEUX — celui qui a cesse d etre collecte ne peut pas s en plaindre. LES SIX : cache-apt (artefacts, repond + place), resolution (resolveur, zone interne ET Internet — deux chemins distincts), forge (forgejo, son propre /api/healthz), collecte (prometheus, 15/15 cibles), tableaux (grafana, base ok), ingestion (loki, PRET a ingerer, pas seulement en ecoute). TROIS FOIS J AI ECRIT LA SONDE AVANT DE MESURER, ET TROIS FOIS ELLE A EU TORT. La forge : port 443 et chemin des depots INVENTES — elle ecoute en 3000 derriere l edge et n a legitimement aucun depot. Loki : j ai conclu « panne persistante » sur deux lectures prises a quelques secondes d intervalle, juste apres un redemarrage ; l anneau etait ACTIVE et la reponse est passee a ready moins d une minute plus tard. Le delai de stabilisation est desormais un AVERTISSEMENT nomme, pas une panne. On demande au service ce qu il pense de lui-meme quand il sait le dire (healthz, /ready, /api/health) plutot que d inventer un critere de l exterieur. make prouver : CONFORME. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
243 lines
9.4 KiB
YAML
243 lines
9.4 KiB
YAML
---
|
|
|
|
# L'URL PUBLIQUE de l'IdP se derive de l'exposition declaree au plan, elle ne se
|
|
# fabrique pas : `keycloak.<domaine>` n'est publie nulle part (voir resoudre_idp).
|
|
- name: Resoudre le fournisseur d'identite (role partage)
|
|
ansible.builtin.include_role:
|
|
name: resoudre_idp
|
|
vars:
|
|
resoudre_idp_realm: "{{ serveur_grafana_oidc_realm }}"
|
|
- name: Exiger le mot de passe admin Grafana (Vault)
|
|
ansible.builtin.assert:
|
|
that:
|
|
- serveur_grafana_admin_password | length > 0
|
|
fail_msg: "serveur_grafana_admin_password est requis (Ansible Vault)."
|
|
|
|
- name: Assurer le repertoire des trousseaux apt
|
|
ansible.builtin.file:
|
|
path: /etc/apt/keyrings
|
|
state: directory
|
|
owner: root
|
|
group: root
|
|
mode: "0755"
|
|
|
|
# Recuperee UNE FOIS. Sans garde, chaque deploiement recontactait le serveur du
|
|
# fournisseur : cinq cles x quatorze hotes = soixante-dix allers-retours externes pour
|
|
# des cles deja installees, et autant d'occasions qu'un tiers lent fasse tomber le
|
|
# deploiement. Arbitrage rendu le 2026-08-09 : une plateforme souveraine ne depend pas
|
|
# de six serveurs etrangers pour redeployer ce qu'elle possede deja.
|
|
#
|
|
# CONSEQUENCE ASSUMEE : une rotation de cle amont n'est plus recuperee toute seule. Elle
|
|
# ne passe pas inapercue pour autant — `apt` refuse alors le depot, bruyamment. Pour
|
|
# forcer le rafraichissement : supprimer le fichier et rejouer le role.
|
|
- name: Cette ressource est-elle deja recuperee ? (Telecharger la cle de signature Gr)
|
|
ansible.builtin.stat:
|
|
path: "{{ serveur_grafana_depot_cle_fichier }}"
|
|
register: telecharger_la_cle_de_signature_grafana_present
|
|
|
|
- name: Telecharger la cle de signature Grafana
|
|
when: not telecharger_la_cle_de_signature_grafana_present.stat.exists
|
|
ansible.builtin.get_url:
|
|
url: "{{ serveur_grafana_depot_cle_url }}"
|
|
dest: "{{ serveur_grafana_depot_cle_fichier }}"
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
|
|
- name: Ajouter le depot apt Grafana
|
|
ansible.builtin.apt_repository:
|
|
repo: "{{ serveur_grafana_depot_source }}"
|
|
filename: grafana
|
|
state: present
|
|
|
|
# LE CACHE DU CONTROLEUR D'ABORD, LE DEPOT DISTANT POUR LE RESTE (2026-09-03).
|
|
#
|
|
# Les depots tiers sont en HTTPS, et `client_artefacts` pose
|
|
# `Acquire::https::Proxy "DIRECT"` — ils CONTOURNENT donc le cache du site et sortent sur
|
|
# Internet a chaque construction de VM. `make cacher-paquets` les tire une fois, versions
|
|
# epinglees et empreintes verifiees ; ce role les depose depuis ce cache.
|
|
- name: Poser les paquets tiers depuis le cache du controleur
|
|
ansible.builtin.include_role:
|
|
name: paquets_tiers
|
|
vars:
|
|
paquets_tiers_noms: "{{ serveur_grafana_paquets }}"
|
|
|
|
- name: Installer Grafana
|
|
ansible.builtin.apt:
|
|
# CE QUE LE CACHE N'A PAS FOURNI, ET RIEN D'AUTRE. Les dependances Debian passent
|
|
# par le cache du site en HTTP ; seuls les paquets tiers non caches exigent Internet.
|
|
# Reinstaller ce que le cache vient de poser ferait un `update_cache` inutile — et
|
|
# hors ligne, il echouerait APRES un travail deja fait.
|
|
name: "{{ serveur_grafana_paquets | difference((paquets_tiers_disponibles | default({})).keys() | list) }}"
|
|
state: present
|
|
update_cache: true
|
|
|
|
when: (serveur_grafana_paquets
|
|
| difference((paquets_tiers_disponibles | default({})).keys() | list)) | length > 0
|
|
- name: Provisionner les datasources (Prometheus + Loki)
|
|
ansible.builtin.template:
|
|
src: datasources.yaml.j2
|
|
dest: /etc/grafana/provisioning/datasources/setops.yaml
|
|
owner: root
|
|
group: grafana
|
|
mode: "0640"
|
|
notify: Redemarrer grafana
|
|
|
|
- name: Assurer le repertoire des dashboards
|
|
ansible.builtin.file:
|
|
path: "{{ serveur_grafana_dashboards_dir }}"
|
|
state: directory
|
|
owner: root
|
|
group: grafana
|
|
mode: "0750"
|
|
|
|
- name: Deployer le provisioning des dashboards
|
|
ansible.builtin.template:
|
|
src: dashboards.yaml.j2
|
|
dest: /etc/grafana/provisioning/dashboards/setops.yaml
|
|
owner: root
|
|
group: grafana
|
|
mode: "0640"
|
|
notify: Redemarrer grafana
|
|
|
|
- name: Deployer les dashboards Set-OPS
|
|
ansible.builtin.copy:
|
|
src: dashboards/
|
|
dest: "{{ serveur_grafana_dashboards_dir }}/"
|
|
owner: root
|
|
group: grafana
|
|
mode: "0640"
|
|
directory_mode: "0750"
|
|
notify: Redemarrer grafana
|
|
|
|
- name: Assurer le repertoire de drop-in systemd
|
|
ansible.builtin.file:
|
|
path: /etc/systemd/system/grafana-server.service.d
|
|
state: directory
|
|
owner: root
|
|
group: root
|
|
mode: "0755"
|
|
|
|
- name: Deployer la configuration d'environnement (domaine + secret)
|
|
ansible.builtin.template:
|
|
src: setops.conf.j2
|
|
dest: /etc/systemd/system/grafana-server.service.d/setops.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0600"
|
|
no_log: true
|
|
notify: Redemarrer grafana
|
|
|
|
- name: Activer et demarrer Grafana
|
|
when: not ansible_check_mode
|
|
ansible.builtin.systemd:
|
|
name: "{{ serveur_grafana_service }}"
|
|
enabled: true
|
|
state: started
|
|
daemon_reload: true
|
|
|
|
# --- ROTATION DU COMPTE DE SECOURS -------------------------------------------
|
|
# `GF_SECURITY_ADMIN_PASSWORD` n'agit qu'a la CREATION du compte : changer la voute
|
|
# ne changeait rien, et la voute se mettait a mentir en silence. Le runbook de
|
|
# reprise (docs/autorisation.md §6) promet pourtant cette rotation — c'est ce qui
|
|
# transforme une livraison en transfert. Constate le 2026-08-07.
|
|
#
|
|
# Idempotence par EMPREINTE : on garde le sha256 du secret applique. Tant qu'il ne
|
|
# change pas, on ne touche a rien ; des qu'il change, on reapplique. On ne peut pas
|
|
# lire le mot de passe en place, donc on memorise ce qu'on a pose.
|
|
# EXIGER QUE LE SERVEUR REPONDE AVANT DE TOUCHER A SA BASE. La CLI qui suit echouait
|
|
# sinon cinq fois de suite sous `no_log`, en annoncant « the output has been hidden » —
|
|
# alors que la vraie cause etait ailleurs et parfaitement lisible : `grafana-server` ne
|
|
# demarrait pas, sa base ayant echoue une migration au premier lancement (2026-08-10,
|
|
# `no such column: uid`). Cinq reprises censurees contre un message clair : on prefere
|
|
# le message.
|
|
- name: Attendre que Grafana réponde avant de toucher au compte de secours
|
|
ansible.builtin.wait_for:
|
|
host: 127.0.0.1
|
|
port: "{{ serveur_grafana_port | default(3000) }}"
|
|
timeout: 120
|
|
register: serveur_grafana_pret
|
|
failed_when: false
|
|
when: not ansible_check_mode
|
|
|
|
- name: Dire que Grafana ne répond pas, plutôt que d'échouer sur sa CLI
|
|
ansible.builtin.fail:
|
|
msg: >-
|
|
Grafana n'écoute pas sur le port {{ serveur_grafana_port | default(3000) }} après
|
|
120 s : la rotation du compte de secours ne peut pas aboutir, et sa CLI ne dirait
|
|
rien d'utile. Regarder `journalctl -u grafana-server` — et y chercher la PREMIÈRE
|
|
erreur, pas la dernière : `Restart=on-failure` en empile plusieurs.
|
|
when:
|
|
- not ansible_check_mode
|
|
- serveur_grafana_pret is failed
|
|
|
|
- name: Empreinte du mot de passe admin en place
|
|
ansible.builtin.slurp:
|
|
src: "{{ serveur_grafana_marqueur_admin }}"
|
|
register: serveur_grafana_marque
|
|
failed_when: false
|
|
changed_when: false
|
|
no_log: true
|
|
|
|
- name: Appliquer le mot de passe du compte de secours (rotation)
|
|
ansible.builtin.command:
|
|
argv:
|
|
- grafana-cli
|
|
- "--homepath={{ serveur_grafana_homepath }}"
|
|
# `paths.data` DOIT etre impose : la CLI le prend par defaut a
|
|
# `<homepath>/data`, alors que le paquet Debian range la base dans
|
|
# /var/lib/grafana. Sans lui, la CLI cree une base FANTOME, y ecrit, et
|
|
# annonce « Admin password changed successfully » — pendant que le serveur
|
|
# lit l'autre. Constate le 2026-08-07 : le compte n'avait pas bouge depuis
|
|
# le deploiement initial, malgre quatre reinitialisations « reussies ».
|
|
- "--configOverrides=cfg:default.paths.data={{ serveur_grafana_donnees }}"
|
|
- admin
|
|
- reset-admin-password
|
|
- "{{ serveur_grafana_admin_password }}"
|
|
no_log: true
|
|
# La tache ne s'execute QUE si l'empreinte differe : quand elle tourne, elle
|
|
# change reellement le mot de passe.
|
|
changed_when: true
|
|
# PATIENTE : sur une machine neuve, cette commande suit de peu le premier
|
|
# demarrage de grafana-server, qui cree encore sa base. La CLI echoue alors sur
|
|
# une base absente ou verrouillee. Constate le 2026-08-08 pendant la premiere
|
|
# reconstruction from-zero ; rejouee seule quelques minutes plus tard, la meme
|
|
# commande passe. Meme classe que l'ecriture Keycloak qui suit la synchro LDAP :
|
|
# une COURSE de premier demarrage, pas une panne. On attend une condition, pas
|
|
# une duree.
|
|
register: serveur_grafana_reset
|
|
retries: 5
|
|
delay: 10
|
|
until: serveur_grafana_reset is succeeded
|
|
when: >-
|
|
(serveur_grafana_marque.content | default('') | b64decode | trim)
|
|
!= (serveur_grafana_admin_password | hash('sha256'))
|
|
notify: Redemarrer grafana
|
|
|
|
- name: Retenir l'empreinte appliquee
|
|
ansible.builtin.copy:
|
|
content: "{{ serveur_grafana_admin_password | hash('sha256') }}\n"
|
|
dest: "{{ serveur_grafana_marqueur_admin }}"
|
|
owner: root
|
|
group: root
|
|
mode: "0600"
|
|
no_log: true
|
|
|
|
# --- Sonde de supervision (docs/supervision-conception.md) --------------------------
|
|
# Le role qui possede la verite depose sa propre sonde ; le porteur (`client_sante`) la
|
|
# fait tourner et pousse le verdict, sans savoir ce qu'elle mesure.
|
|
- name: Assurer le repertoire des sondes de supervision
|
|
ansible.builtin.file:
|
|
path: /usr/local/lib/setops/sondes
|
|
state: directory
|
|
owner: root
|
|
group: root
|
|
mode: "0755"
|
|
|
|
- name: Deposer la sonde « tableaux »
|
|
ansible.builtin.template:
|
|
src: sonde-tableaux.sh.j2
|
|
dest: /usr/local/lib/setops/sondes/tableaux.sh
|
|
owner: root
|
|
group: root
|
|
mode: "0750"
|