From 9acd91a7cd0b25f4e333ff8d957444a2ca37cc46 Mon Sep 17 00:00:00 2001 From: Gabriel Radureau Date: Tue, 15 Sep 2026 12:25:06 +0200 Subject: [PATCH] =?UTF-8?q?feat(system)=20=E2=80=94=20masquer=20gvfs-udisk?= =?UTF-8?q?s2=20sur=20les=203=20Pi=20:=20un=20d=C3=A9mon=20du=20bureau=20f?= =?UTF-8?q?uyait=202,5=20Gi=20sur=20pi2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sur pi2, gvfs-udisks2-volume-monitor (session de bureau de `pi`) occupait 2,54 Gi de PSS après 154 jours, contre 5 à 9 Mi sur pi1 et pi3. pi2 saturé, le moteur Longhorn a manqué son délai de 8 s et ext4 a abandonné son journal sur quatre volumes (tools#49). Son redémarrage a rendu 2,6 Gi. Le playbook masque l'unité utilisateur en portée globale (le bus de session tourne en --systemd-activation, donc le bus ne peut plus la réveiller), arrête l'instance en cours, puis relit le masque et l'absence de processus. Masquer plutôt que retirer `gvfs-daemons`, dont dépend tout gvfs et le bureau de Raspberry Pi OS. Refs arcodange-org/tools#52 Co-Authored-By: Claude Opus 5 --- .../factory/playbooks/system/gvfs_udisks2.yml | 88 +++++++++++++++++++ .../factory/playbooks/system/system.yml | 4 + 2 files changed, 92 insertions(+) create mode 100644 ansible/arcodange/factory/playbooks/system/gvfs_udisks2.yml diff --git a/ansible/arcodange/factory/playbooks/system/gvfs_udisks2.yml b/ansible/arcodange/factory/playbooks/system/gvfs_udisks2.yml new file mode 100644 index 0000000..a7e5bd2 --- /dev/null +++ b/ansible/arcodange/factory/playbooks/system/gvfs_udisks2.yml @@ -0,0 +1,88 @@ +--- +# Couper le moniteur de disques du bureau (gvfs-udisks2-volume-monitor). +# +# Incident arcodange-org/tools#52 (2026-09-15) : sur pi2, ce démon de la +# session de bureau de `pi` occupait 2,54 Gi de PSS après 154 jours de marche, +# contre 5 à 9 Mi sur pi1 et pi3 — un tiers de la RAM du nœud, invisible du +# scheduler k8s. pi2 saturé, le moteur Longhorn a manqué son délai de 8 s, et +# ext4 a abandonné son journal sur quatre volumes (tools#49). Son redémarrage +# a rendu 2,6 Gi d'un coup (`free -m` : available 1 728 → 4 340 Mi). +# +# Il surveille les périphériques bloc pour le gestionnaire de fichiers : +# chaque attachement iSCSI de Longhorn en est un. Les Pi sont des serveurs, on +# n'y monte pas de clé USB depuis le bureau : on le coupe au lieu de le +# redémarrer périodiquement. +# +# Pourquoi MASQUER l'unité et pas retirer le paquet : le binaire vit dans +# `gvfs-daemons`, dont dépend tout gvfs (et, par lui, le bureau de Raspberry Pi +# OS) — le retirer arracherait des paquets sans rapport. Masquer est ciblé, +# réversible (`systemctl --global unmask`), et suffit : le bus de session tourne +# en `--systemd-activation`, et le fichier D-Bus du service porte +# `SystemdService=gvfs-udisks2-volume-monitor.service` — une unité masquée ne +# peut donc plus être réveillée par le bus. +# +# `--global` (et pas `--user`) : le masque vit dans /etc/systemd/user/ et vaut +# pour tout utilisateur, sans dépendre du $HOME de `pi`. + +- name: Couper gvfs-udisks2-volume-monitor (moniteur de disques du bureau) + hosts: raspberries:&local + gather_facts: no + + vars: + gvfs_udisks2_unit: gvfs-udisks2-volume-monitor.service + gvfs_udisks2_desktop_user: pi + + tasks: + + - name: Masquer l'unité utilisateur pour tous les utilisateurs + become: yes + ansible.builtin.systemd: + name: "{{ gvfs_udisks2_unit }}" + scope: global + masked: yes + + - name: Relever l'uid de l'utilisateur du bureau + ansible.builtin.command: id -u {{ gvfs_udisks2_desktop_user }} + register: gvfs_udisks2_uid + changed_when: false + + - name: Savoir si une session systemd utilisateur tourne + ansible.builtin.stat: + path: "/run/user/{{ gvfs_udisks2_uid.stdout }}/systemd/private" + register: gvfs_udisks2_user_manager + + # On se connecte déjà en `pi` : pas de become, le gestionnaire utilisateur + # se joint par son XDG_RUNTIME_DIR. + - name: Arrêter l'instance en cours dans la session de bureau + when: gvfs_udisks2_user_manager.stat.exists + environment: + XDG_RUNTIME_DIR: "/run/user/{{ gvfs_udisks2_uid.stdout }}" + ansible.builtin.systemd: + name: "{{ gvfs_udisks2_unit }}" + scope: user + state: stopped + + # On relit le système vivant, pas la tâche : le masque doit être posé ET + # aucun processus ne doit survivre (ni avoir été réveillé par le bus). + - name: Relire le masque global + ansible.builtin.command: systemctl --global is-enabled {{ gvfs_udisks2_unit }} + register: gvfs_udisks2_mask + changed_when: false + failed_when: false + + - name: Chercher un processus survivant + ansible.builtin.command: pgrep -x gvfs-udisks2-vo + register: gvfs_udisks2_pgrep + changed_when: false + failed_when: false + + - name: Vérifier que le moniteur est masqué et arrêté + ansible.builtin.assert: + that: + - gvfs_udisks2_mask.stdout == "masked" + - gvfs_udisks2_pgrep.rc == 1 + fail_msg: >- + gvfs-udisks2 n'est pas coupé sur {{ inventory_hostname }} : + masque={{ gvfs_udisks2_mask.stdout }}, pgrep rc={{ gvfs_udisks2_pgrep.rc }} + ({{ gvfs_udisks2_pgrep.stdout }}). + success_msg: "gvfs-udisks2 masqué et arrêté sur {{ inventory_hostname }}" diff --git a/ansible/arcodange/factory/playbooks/system/system.yml b/ansible/arcodange/factory/playbooks/system/system.yml index 3d5e9e2..74cf701 100644 --- a/ansible/arcodange/factory/playbooks/system/system.yml +++ b/ansible/arcodange/factory/playbooks/system/system.yml @@ -8,6 +8,10 @@ - name: Filet mémoire earlyoom ansible.builtin.import_playbook: earlyoom.yml +# Un démon du bureau qui fuyait 2,5 Gi sur pi2 (tools#52). +- name: Couper le moniteur de disques du bureau + ansible.builtin.import_playbook: gvfs_udisks2.yml + - name: dns ansible.builtin.import_playbook: ../dns/dns.yml -- 2.54.0