--- # `lock_timeout` sur CHAQUE tache apt : au premier demarrage, l'image Debian lance ses # propres mises a jour (apt-daily, unattended-upgrades) et tient le verrou dpkg par # vagues. Sans attente, la premiere tache echoue sur un verrou — pas sur une vraie # erreur — et le deploiement d'une VM neuve devient un tirage au sort. # # Attendre ici plutot que seulement avant le playbook : le verrou peut etre repris # ENTRE deux taches, ce qu'une verification ponctuelle en amont ne peut pas empecher. # APT-GET ATTEND LE VERROU, LUI AUSSI (2026-09-30). # # `lock_timeout` ne protege que la verification faite par le MODULE apt. Pour une mise a # niveau, le module lance ensuite `apt-get dist-upgrade` — qui, lui, n'attend pas : APT 3.0 # ne donne d'attente (120 s) qu'a la commande `apt` (`Binary::apt::DPkg::Lock::Timeout`), # pas a `apt-get`. Entre la verification et l'appel, `unattended-upgrades` a repris le # verrou sur `mon-01`, ne une minute plus tot, et la reconstruction de Technolibre s'est # arretee : # # E: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 1198 (unattended-upgr) # # On le dit donc a APT LUI-MEME, pour tout appel : c'est un fichier, pas une operation apt, # il peut passer en tout premier. - name: Faire attendre le verrou dpkg a tout appel d'APT (apt-get compris) ansible.builtin.copy: dest: /etc/apt/apt.conf.d/10setops-verrou content: | // Gere par Set-OPS (role common_packages). Voir tasks/main.yml. DPkg::Lock::Timeout "{{ common_packages_lock_timeout }}"; owner: root group: root mode: "0644" # DÉSARMER AVANT TOUTE OPÉRATION APT, ET NON APRÈS. # # Une tâche apt placée avant celle-ci attendrait le verrou que celle-ci doit justement # libérer — l'ordre n'est pas cosmétique. `state: stopped` en plus de `masked` : masquer # empêche un démarrage futur, pas celui qui est déjà en cours. - name: Désarmer les mises à jour automatiques de Debian (Set-OPS les applique lui-même) ansible.builtin.systemd_service: name: "{{ item }}" state: stopped enabled: false masked: true loop: "{{ common_packages_unites_maj_auto }}" when: common_packages_desarmer_maj_auto | bool failed_when: false # une unite absente n'est pas une faute : elle est desarmee # EFFACER LA TRACE QUE LE DESARMEMENT LAISSE (mesure du 2026-08-30). # # Masquer le service pendant que son timer TOURNE lui fait perdre sa cible. systemd le # note, et le garde : # # apt-daily.timer: Unit to trigger vanished. # apt-daily.timer: Failed with result 'resources'. # # `state: stopped` n'efface pas un etat `failed` — seul `reset-failed` le fait. Sans lui, # CHAQUE machine porte deux unites en echec permanent. Constate sur les quinze de # Chezlepro : `systemctl list-units --state=failed` en rend deux partout. # # CE N'EST PAS COSMETIQUE. Une supervision qui compte les unites en echec compte ces # deux-la pour toujours — et le jour ou une VRAIE panne s'ajoute, elle se noie dans un # bruit qu'on a appris a ignorer. C'est le meme defaut que le journal de la frontiere # noye sous 982 000 entrees : ce qui ment le plus n'est pas ce qui se tait, c'est ce qui # crie sans raison. - name: Effacer l'etat d'echec laisse par le desarmement ansible.builtin.command: argv: ["systemctl", "reset-failed", "{{ item }}"] loop: "{{ common_packages_unites_maj_auto }}" register: common_packages_reset changed_when: false failed_when: false # rien a effacer n'est pas une faute when: common_packages_desarmer_maj_auto | bool # REARMER NE SE DEDUIT PAS DE NE PLUS DESARMER (2026-09-11). # # Passer `common_packages_desarmer_maj_auto` a `false` SAUTE la tache de desarmement — # il ne defait rien. Une machine deja masquee le resterait pour toujours, et le drapeau # dirait le contraire de l'etat reel. Le desarmement laisse une trace ; il faut donc une # tache qui la retire, et elle doit exister le jour meme ou l'on inverse la decision. # # L'ORDRE COMPTE, COMME POUR LE DESARMEMENT : on reamorce AVANT les operations apt de ce # role. Un demarrage d'`apt-daily` declenche pendant nos propres taches se disputerait le # verrou au pire moment ; declenche avant, il est attendu par `lock_timeout`. # ON ARME, ON NE DECLENCHE PAS — ET LA NUANCE A COUTE DIX MACHINES (2026-09-11). # # La premiere version faisait `state: started` sur les trois unites. Demarrer # `unattended-upgrades.service` apres des semaines de masquage lance son RATTRAPAGE # immediatement — en plein deploiement. Il a pris le verrou dpkg et l'a garde plus # longtemps que `lock_timeout` (300 s) : # # 'apt-get autoremove' failed: E: Could not get lock /var/lib/dpkg/lock-frontend. # It is held by process 185135 (unattended-upgr) # # Exactement la course qui avait motive le desarmement en aout, rejouee par la tache # censee le defaire. Dix machines sur vingt et une. # # LE TRAVAIL QUOTIDIEN NE PASSE PAS PAR CE SERVICE : `apt-daily-upgrade.timer` appelle # `apt.systemd.daily`, qui invoque `unattended-upgrade` lui-meme. Le `.service` ne sert # qu'aux passages de demarrage et d'extinction. L'ACTIVER suffit donc — il partira au # prochain redemarrage, sur une machine au repos, et non pendant qu'Ansible tient apt. # # Demarrer les MINUTEURS, en revanche, est sans danger : armer un minuteur ne declenche # pas sa cible, il la programme. - name: Réarmer les mises à jour automatiques de Debian (sans les déclencher) ansible.builtin.systemd_service: name: "{{ item }}" masked: false enabled: true state: "{{ 'started' if item.endswith('.timer') else omit }}" loop: "{{ common_packages_unites_maj_auto }}" when: not (common_packages_desarmer_maj_auto | bool) failed_when: false # une unite absente sur cette image n'est pas une faute - name: Mettre à jour le cache APT ansible.builtin.apt: update_cache: true cache_valid_time: 3600 lock_timeout: "{{ common_packages_lock_timeout }}" # LES ÉPINGLES AVANT LA MISE À JOUR (2026-10-03). `upgrade: full` montait les paquets # tiers au-dela de `paquets-tiers.yml` : au site, `icinga-php-thirdparty` etait passe de # 1.0.0 (pose par le cache) a 1.0.1 sans que personne l'ait decide. Les preferences apt de # TOUTES les epingles sont posees ici, sur toute machine du socle : une epingle sur un # paquet absent ne fait rien, et fixe la version de sa premiere installation. - name: Épingler tous les paquets tiers avant la mise à jour ansible.builtin.include_role: name: paquets_tiers tasks_from: epingles.yml vars: paquets_tiers_toutes_epingles: true - name: Appliquer les mises à jour disponibles ansible.builtin.apt: upgrade: full lock_timeout: "{{ common_packages_lock_timeout }}" - name: Installer les paquets communs ansible.builtin.apt: name: "{{ common_packages_list }}" state: present lock_timeout: "{{ common_packages_lock_timeout }}" - name: Supprimer les dépendances devenues inutiles ansible.builtin.apt: autoremove: true lock_timeout: "{{ common_packages_lock_timeout }}" # LA SONDE SE DEPOSE ICI, ELLE SE DECLARE AILLEURS (`roles/serveur_debian/meta/`). # Ce role est celui qui APPLIQUE les mises a jour ; il est donc le bon porteur. Mais la # derivation des services d'Icinga croise les GROUPES, et `common_packages` n'en est pas # un — d'ou la separation, expliquee dans la declaration. - 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: Assurer le repertoire d etat des sondes ansible.builtin.file: # 0755, COMME PARTOUT AILLEURS (2026-10-01) : quatre roles tenaient ce repertoire, trois # en 0750 et `serveur_artefacts` en 0755 — apt-cacher-ng doit le traverser pour servir # le depot de binaires du site. Le dernier deploye gagnait : au site, le 2026-10-01, il # rendait 403, et chaque runner allait chercher 500 Mo sur Internet. Le repertoire ne # tient que des etats de sondes, deja lisibles. Garde : test_repertoires_partages.py. path: /var/lib/setops state: directory owner: root group: root mode: "0755" - name: Deposer la sonde des correctifs de securite ansible.builtin.template: src: sonde-correctifs.sh.j2 dest: /usr/local/lib/setops/sondes/correctifs.sh owner: root group: root mode: "0750"