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
147 lines
6.1 KiB
YAML
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"
|