Set-OPS-Public/roles/serveur_artefacts/tasks/main.yml
Daniel Allaire a5d9040a11 cache-apt scindee — et la scission a revele que moteur aurait alarme a tort
LA SCISSION. cache-apt fondait deux causes aux DELAIS differents : « ne
repond pas » arrete tout apt de l ecosysteme, on agit dans la minute ;
« volume a 90 % » est un billet pour demain. Les fondre obligeait soit a
reveiller quelqu un pour un disque, soit a traiter une panne comme un
billet. Deux sondes, chacune avec son etat, son historique, son
acquittement — et prouvees INDEPENDANTES :

  port ferme       -> cache-apt CRITIQUE, cache-apt-volume OK
  seuil impossible -> cache-apt OK, cache-apt-volume AVERTISSEMENT

CE QUE LA VERIFICATION A REVELE. Un resultat pousse et accepte (code 200)
n apparaissait pas en base. Hypothese testee et confirmee : IcingaDB n
ECRIT service_state QUE SUR CHANGEMENT D ETAT. Un OK identique repete ne
produit aucune ecriture ; un AVERTISSEMENT pousse ensuite est ecrit en 13 s.

Or la sonde moteur, ecrite quelques heures plus tot, lisait exactement
max(last_update) de service_state pour juger de la fraicheur. Elle
mesurait le CHANGEMENT. Sur un ecosysteme parfaitement STABLE — celui
qu on veut — plus rien ne change, last_update vieillit, et elle serait
passee en avertissement a 15 min puis en critique a 90. Une alarme qui se
declenche PARCE QUE tout va bien, avec un delai qui l aurait rendue
difficile a rattacher a sa cause.

La bonne source existait : icingadb_instance porte le battement du
synchroniseur, ecrit en continu. Releve a 1 s sur un systeme sain. Seuils
ramenes de 15/90 min a 1/5 min. Controle negatif rejoue.

Dix-huit services, tous verts sauf sauvegarde 7/9 (deja connu).

make prouver : CONFORME, 64 OK, 0 echec, 0 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-10 03:27:52 -04:00

147 lines
6.1 KiB
YAML

---
# LE CACHE NE DOIT PAS DÉPENDRE DE LUI-MÊME POUR SE RÉPARER.
#
# La première version faisait `update_cache: true` à chaque passage. Sur l'hôte qui PORTE
# le cache, cet `apt update` passe par le cache lui-même — et en mode hors ligne, il est
# refusé. Le rôle qui devait remettre le service en ligne ne pouvait donc plus s'exécuter :
#
# « Failed to update apt cache after 5 retries » (mesuré le 2026-08-23, après
# l'épreuve hors ligne : il a fallu réparer la machine à la main)
#
# Un service dont la remise en route exige qu'il fonctionne déjà est un piège. On ne
# rafraîchit donc l'index qu'à la PREMIÈRE installation, quand aucun cache n'existe encore
# et qu'`apt` va forcément à l'amont.
- name: Le cache est-il déjà installé ?
ansible.builtin.stat:
path: /usr/sbin/apt-cacher-ng
register: serveur_artefacts_present
- name: Installer le cache d'artefacts
ansible.builtin.apt:
name: "{{ serveur_artefacts_paquets }}"
state: present
update_cache: "{{ not serveur_artefacts_present.stat.exists }}"
when: not ansible_check_mode
- name: Assurer le répertoire de cache
ansible.builtin.file:
path: "{{ serveur_artefacts_cache }}"
state: directory
owner: apt-cacher-ng
group: apt-cacher-ng
mode: "0755"
when: not ansible_check_mode
# apt-cacher-ng lit TOUS les `*.conf` de son répertoire, par ordre alphabétique, et le
# dernier lu gagne. Le préfixe `zzz-` place donc notre configuration après celle du
# paquet Debian et celle de debconf, sans avoir à les éditer — on ajoute, on ne remplace
# pas. Une valeur posée ici l'emporte, et `apt purge` rend la machine à son état d'origine.
# --- LE FILET : NE PAS POSER UN AMONT INJOIGNABLE ----------------------------
#
# CE CACHE EST UN MAILLON DONT DEPEND SA PROPRE RECONSTRUCTION (mesure du 2026-08-31).
#
# Toute la flotte du tenant prend ses paquets ici. Si ce cache pointe vers un amont mort,
# `apt` echoue sur les QUINZE machines — donc le socle echoue, donc le deploiement
# n'atteint jamais la couche qui poserait le bon amont. Le correctif se retrouve dans la
# couche que la panne empeche d'atteindre, et il faut une main pour en sortir.
#
# C'est exactement ce qui est arrive : l'amont pointait sur le cache de patient 0, eteint
# la veille pour liberer de la RAM. `apt` rendait `503 Connection timeout` EN CITANT
# L'ADRESSE DU CACHE LOCAL, jamais celle de l'amont manquant — la panne accusait le
# maillon visible.
#
# On mesure donc AVANT d'ecrire. Un amont injoignable est refuse : mieux vaut un cache
# qui garde sa configuration precedente et un message clair, qu'un cache qu'on vient de
# rendre inutilisable pour toute la flotte.
- name: L'amont declare repond-il ?
ansible.builtin.wait_for:
host: "{{ serveur_artefacts_amont | urlsplit('hostname') }}"
port: "{{ serveur_artefacts_amont | urlsplit('port') | default(3142, true) | int }}"
timeout: 10
register: serveur_artefacts_amont_sonde
changed_when: false
# `ignore_errors` ET NON `failed_when: false` — LA DIFFERENCE EST TOUTE LA GARDE.
#
# `failed_when: false` REECRIT le verdict : la tache n'est plus jamais `failed`, donc
# `is not failed` est toujours vrai, donc l'assertion qui suit ne peut PAS tirer. Ecrite
# ainsi, elle a declare « joignable » un amont mesure MUET la seconde d'avant
# (2026-08-31). Une garde qui ne peut pas echouer ne garde rien.
#
# `ignore_errors` laisse le verdict intact et se contente de ne pas arreter le play :
# c'est ce qu'il faut pour SONDER puis DECIDER.
ignore_errors: true
when:
- serveur_artefacts_amont | length > 0
- not ansible_check_mode
- name: Refuser un amont injoignable plutot que de rendre ce cache inutilisable
ansible.builtin.assert:
that:
- serveur_artefacts_amont_sonde is not failed
fail_msg: >-
L'amont declare ({{ serveur_artefacts_amont }}) ne repond pas. Le poser rendrait ce
cache — et donc `apt` sur TOUTE la flotte de cet ecosysteme — inutilisable, et le
deploiement ne pourrait plus atteindre la couche qui le corrigerait.
Verifier que la machine d'amont tourne, ou vider `serveur_artefacts_amont` pour
aller directement chez Debian (c'est une emancipation : voir
docs/filiation-emancipation.md).
success_msg: "Amont {{ serveur_artefacts_amont }} joignable."
when:
- serveur_artefacts_amont | length > 0
- not ansible_check_mode
- name: Déployer la configuration Set-OPS
ansible.builtin.template:
src: zzz-setops.conf.j2
dest: /etc/apt-cacher-ng/zzz-setops.conf
owner: root
group: root
mode: "0644"
notify: Redémarrer le cache d'artefacts
- name: Activer et démarrer le cache d'artefacts
ansible.builtin.systemd:
name: "{{ serveur_artefacts_service }}"
enabled: true
state: started
when: not ansible_check_mode
- name: Appliquer les redémarrages avant de vérifier
ansible.builtin.meta: flush_handlers
# ÉCRIRE, PUIS RELIRE (D-68). Un service « active » qui n'écoute pas sur le port attendu
# est un vert sur un périmètre vide : les clients le découvriraient à leur premier
# `apt update`, c'est-à-dire au pire moment.
- name: Le cache écoute-t-il vraiment ?
ansible.builtin.wait_for:
host: "127.0.0.1"
port: "{{ serveur_artefacts_port }}"
timeout: 30
when: not ansible_check_mode
# --- 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 « cache-apt »
ansible.builtin.template:
src: sonde-cache-apt.sh.j2
dest: /usr/local/lib/setops/sondes/cache-apt.sh
owner: root
group: root
mode: "0750"
- name: Deposer la sonde « cache-apt-volume »
ansible.builtin.template:
src: sonde-cache-apt-volume.sh.j2
dest: /usr/local/lib/setops/sondes/cache-apt-volume.sh
owner: root
group: root
mode: "0750"