22 KiB
Dossier technique pour le deuxième widget Glance de monitoring d’agents Rust
Conclusion opérationnelle
Oui, ton deuxième widget est réalisable, et il est même cohérent avec l’architecture de Glance à condition de le traiter comme une application externe intégrée dans Glance, pas comme un widget natif compilé “dans” Glance en Rust. Le dépôt principal de Glance est un projet Go avec un go.mod, et sa mécanique de widgets personnalisés repose aujourd’hui sur quatre portes d’entrée documentées par le projet lui-même : iframe, html, extension et custom-api. Glance précise aussi que les pages ne se mettent pas à jour automatiquement en arrière-plan et qu’un rechargement de page est normalement nécessaire pour récupérer les nouvelles données. Pour ton besoin — tuiles live, popup latéral, auto-découverte, installation d’agent, mise à jour, métriques à fréquences différentes et états de veille/arrêt/actif — l’option la plus robuste est donc : backend Rust + petite UI web servie par ce backend + intégration dans Glance via iframe. citeturn36view0turn33view1turn35view0turn34view0
Il y a aussi une nuance importante de vocabulaire : le projet étudié ici est Glance, pas Glances. La documentation de Glance dit d’ailleurs explicitement que le widget server-stats est encore “under development”, qu’il peut s’appuyer sur le Glance Agent pour des serveurs distants, et que le support d’autres providers “such as Glances” viendra plus tard. Autrement dit, ton besoin avancé dépasse clairement ce que le widget natif server-stats couvre aujourd’hui. citeturn3view0
Mon verdict est donc simple : oui pour Rust, mais en service externe consommé par Glance, pas comme module interne du binaire Go de Glance. Si tu veux un rendu vraiment vivant et administrable, je te recommande de bâtir un petit produit séparé, par exemple fleet-collector et fleet-agent, puis de l’afficher dans une page Glance dédiée. Cette approche servira d’ailleurs très bien ton premier widget IP réseau et ton deuxième widget agents avec un même modèle d’inventaire. citeturn33view1turn34view0turn31view0turn35view0
Ce que Glance permet réellement pour ce besoin
Glance organise l’interface autour de pages et de colonnes. Une page peut jouer le rôle de “tab” au sens fonctionnel, avec un layout wide et des colonnes full ou small. Si tu veux réserver un onglet complet à ton monitoring, Glance le permet nativement côté configuration, sans toucher au code source du projet. citeturn3view0
Pour ton cas, les trois mécanismes utiles sont les suivants. D’abord, custom-api sait appeler une API JSON avec GET, POST, PUT, PATCH, DELETE, gérer des headers, un body, des subrequests, puis rendre le résultat avec un template basé sur Go html/template et gjson. C’est puissant pour des widgets “server-rendered” relativement riches, mais la doc de Glance dit aussi explicitement que ce mode demande des notions de programmation, HTML, CSS et templates Go. Ensuite, extension est un protocole HTTP simple où un service externe renvoie du HTML et quelques headers spécifiques ; Glance documente néanmoins que cette API est work in progress, que le cache par défaut d’une extension est de 30 minutes, qu’il est configurable, et que le seul type de contenu supporté actuellement est html. Enfin, iframe embarque simplement une application externe à partir d’une URL et d’une hauteur donnée. Pour un écran de supervision avec popups, tuiles dynamiques, tri intelligent, interaction fine et mises à jour live, iframe est de loin la meilleure option. citeturn35view0turn31view0turn34view0turn33view1
La principale raison est le modèle d’actualisation de Glance. Le README officiel dit que les widgets ne font pas de requêtes périodiques en arrière-plan et que les données sont récupérées au chargement puis mises en cache ; il faut normalement recharger la page pour voir les changements. Il existe bien une pull request ouverte ajoutant des “live-events” avec polling serveur toutes les 15 secondes et SSE pour monitor et custom-api, mais elle est encore ouverte au 19 mai 2026 et n’appartient donc pas au comportement stable documenté du main actuel. Ton besoin de CPU/RAM à 1 seconde ne doit pas dépendre d’une PR non mergée. citeturn33view1turn33view0
Glance reste toutefois très bon comme conteneur, car tu peux lui faire servir des assets via assets-path, injecter du HTML dans le <head> avec document.head, ajouter un custom-css-file, et cibler les widgets par widget-type-* ou css-class. Autrement dit, même si le vrai widget vit dans un service Rust séparé, tu peux le “fondre” visuellement dans l’univers Glance. citeturn32view0turn32view1turn32view2turn32view3
Un squelette de configuration Glance pour ce deuxième widget ressemblerait à ceci :
pages:
- name: Infrastructure
width: wide
columns:
- size: full
widgets:
- type: iframe
title: Fleet Monitor
source: https://dash.example.net/widgets/agents
height: 820
css-class: fleet-monitor
Si tu veux garder un mode “dégradé” purement Glance, tu peux aussi fournir un petit widget custom-api qui n’affiche qu’un résumé global : nombre d’agents, nombre offline, charge CPU moyenne, machines en alerte, lien d’installation et lien “ouvrir la vue détaillée”. Mais la vue principale riche doit rester dans l’iframe. citeturn35view0turn34view0turn32view3
Architecture recommandée pour le widget agents
Je te recommande une architecture en trois briques. Première brique : un fleet-agent Rust installé sur chaque VM ou machine physique. Deuxième brique : un fleet-collector Rust central qui reçoit les enregistrements, stocke l’inventaire, expose une API et un flux temps réel. Troisième brique : une UI web légère, servie par le collector, affichée dans Glance via iframe. Cette séparation colle au modèle de Glance, contourne proprement l’absence d’auto-refresh généralisé, et te laisse toute latitude pour le placement intelligent des tuiles, les popups, les liens personnalisés et les workflows d’installation/mise à jour. citeturn33view1turn34view0turn35view0turn31view0
[ VM / Bare metal ]
└─ fleet-agent (Rust)
├─ collecte CPU / RAM / disque / process
├─ inventaire matériel
├─ écoute veille / reboot / shutdown
└─ push JSON vers collector
[ Serveur central ]
└─ fleet-collector (Rust)
├─ API d’enregistrement des agents
├─ stockage inventaire + séries courtes
├─ stream SSE / WebSocket pour l’UI
├─ génération commandes install / update
└─ UI web du widget
[ Glance ]
└─ page "Infrastructure"
└─ widget iframe -> UI du collector
Je te conseille un modèle push depuis les agents vers le collector, parce que ton cahier des charges inclut des événements qui se prêtent mal à un modèle purement pull : arrêt propre, passage en veille, reprise, changement d’état, heartbeat, auto-enregistrement au boot, et publication de métriques avec des fréquences hétérogènes. Comme Glance n’est pas un moteur de polling continu des widgets, et comme server-stats est encore explicitement en développement, vouloir faire ça directement “dans Glance” te mettrait rapidement à contre-courant du produit. citeturn33view1turn3view0
La bonne granularité, à mon sens, est la suivante. Inventaire statique au démarrage puis deux fois par jour : hostname, OS, carte mère, modèle, vendor, serial, interfaces réseau, icône, emplacement, groupe, parent, liens. Métriques rapides toutes les 1 à 5 secondes : CPU, RAM, état de disponibilité, éventuellement charge GPU si la machine en a une. Métriques lentes toutes les 30 minutes : capacité disque, occupation des volumes, état SMART synthétique. Événements à la volée : démarrage, arrêt, veille, reprise, changement d’IP, agent obsolète, échec d’un module. Cette séparation colle bien aux capacités des APIs système disponibles et évite de transmettre trop de bruit. citeturn22view0turn24search7turn8search0turn8search4turn12search0turn11search0turn10search0turn30search6
Pour le volet secondaire de tes tuiles, je te conseille de ne pas tout remonter à 1 seconde. Les données de popup — SMBIOS/DMI, services, icône, emplacement, relations de parenté, liste des top processus, état détaillé des disques — peuvent être stockées côté collector et réactualisées plus lentement, voire à la demande. Les docs de sysinfo montrent explicitement que le CPU s’appuie sur des différences temporelles, qu’il faut garder une même instance System, et qu’il vaut mieux utiliser des refresh ciblés pour les performances. Cette philosophie va exactement dans le sens de ton besoin “fréquences différentes selon les familles de données”. citeturn22view0turn26view0
Stack Rust recommandée
Pour le runtime et l’API réseau, la combinaison la plus naturelle est Tokio + Axum. Tokio est le runtime asynchrone standard de fait pour les applications réseau Rust ; sa primitive interval est adaptée à des boucles planifiées de collecte. Axum est conçu pour fonctionner avec Tokio et Hyper, sait gérer des APIs HTTP, les WebSockets, et fournit nativement des réponses SSE. Pour ton widget, SSE suffit souvent très bien : le collector pousse les changements de tuiles, et l’UI met à jour la grille sans refresh. citeturn16search22turn20search2turn20search5turn16search2turn20search0turn20search4
Pour la collecte système générale, je recommande sysinfo comme base. La doc officielle montre que la crate couvre processus, mémoire, CPU, disques, réseaux, températures de composants, et qu’elle dispose de RefreshKind, ProcessRefreshKind, DiskRefreshKind, ainsi que d’un MINIMUM_CPU_UPDATE_INTERVAL. Elle expose aussi des informations de carte mère, par exemple nom, vendor, version, numéro de série et asset tag via Motherboard. Pour les top processus, elle fournit notamment cpu_usage(), memory(), status(), parent(), exe() et cmd(). C’est une excellente base pour un agent Rust Linux-first. citeturn22view0turn25view0turn25view1turn26view0turn26view1
Pour le matériel et les identifiants, tu as deux étages. D’abord sysinfo, qui sait remonter une partie de l’inventaire matériel. Ensuite, pour les informations plus complètes ou plus “admin” — SMBIOS/DMI, châssis, BIOS, vendor, références — tu peux compléter avec dmidecode ou directement les fichiers sysfs. La doc de dmidecode rappelle toutefois que ces informations viennent du firmware SMBIOS/DMI et peuvent être rapides et sûres à lire mais parfois peu fiables, car elles dépendent de ce que le firmware expose. C’est important pour ton design : il faut afficher ces champs comme de l’inventaire déclaratif, pas comme une vérité absolue. citeturn25view0turn9search12turn9search6
Pour les disques, je te conseille de séparer “occupation des volumes” et “santé physique”. L’occupation se fait très bien avec sysinfo::Disks. La santé disque et les attributs SMART doivent venir de smartctl, car la documentation officielle indique que smartctl contrôle et surveille le système SMART des disques ATA/SATA, SCSI/SAS et SSD, et son code source expose un mode --json/-j pour une sortie JSON ou YAML. C’est le meilleur compromis Rust aujourd’hui : exécuter smartctl, parser le JSON, et normaliser un petit résumé par disque pour ton popup ou tes alertes. citeturn24search7turn8search0turn8search4
Pour les GPU, je te recommande un design par provider optionnel. Côté NVIDIA, la référence est clairement NVML ; NVIDIA documente NVML comme une API C de monitoring et management des GPU, thread-safe, et base de nvidia-smi. En Rust, nvml-wrapper permet d’y accéder proprement. Côté AMD, la doc ROCm indique que AMD SMI est l’interface unifiée vers laquelle AMD pousse désormais les usages de monitoring/management GPU et qu’elle est la successeure de ROCm SMI. Traduction pratique : phase 1 NVIDIA si tu veux aller vite, phase 2 AMD SMI si ton parc le justifie. citeturn10search0turn10search1turn6search4turn30search1turn30search6
Pour la base de données centrale, tu as deux bons choix. Si ton parc reste modeste et si tu veux un déploiement très simple, SQLite en WAL est parfaitement défendable avec un collector unique et un modèle “une écriture logique à la fois”. La doc SQLite rappelle comment activer WAL et souligne que ce mode permet, en règle générale, que les écrivains ne bloquent pas les lecteurs et inversement, tout en expliquant le rôle des checkpoints. En revanche, la doc SQLite signale aussi un bug rare WAL-reset corrigé en 3.51.3 le 13 mars 2026, ainsi que dans certains backports. Donc si tu pars sur SQLite/WAL, veille à utiliser une version corrigée. Si tu veux monter en charge, multi-utilisateur, ou faire de la rétention longue avec analytics, PostgreSQL devient plus confortable. Côté Rust, SQLx est adapté aux deux mondes : crate async, support Tokio, support SQLite/PostgreSQL, requêtes vérifiables à la compilation. citeturn27view0turn19search0turn19search1
Pour les mises à jour de l’agent, deux options sont sérieuses. Soit tu fais du self-update in-place avec une crate dédiée ; la doc de self_update précise qu’elle sait mettre à jour des exécutables Rust en place à partir de plusieurs backends de distribution. Soit, ce que je préfère dans ton contexte, tu gardes un installeur shell et des binaires versionnés, ce qui te donne un chemin d’upgrade plus transparent, plus compatible avec systemd et plus facile à signer/vérifier. Pour la signature, Minisign est une option légère et robuste, officiellement documentée comme un outil simple de signature/vérification basé sur Ed25519. citeturn16search1turn17search0
Déploiement, découverte et mises à jour
Pour la distribution des artefacts, Gitea est suffisant. La documentation officielle du Generic Package Registry explique qu’il peut publier des fichiers génériques comme des binaires de release, via PUT, et les télécharger via GET sur des URLs versionnées. Gitea documente aussi l’authentification API par Basic auth, query token, ou surtout Authorization: token .... Concrètement, tu peux stocker dans Gitea : le binaire par plateforme, le fichier .minisig, un manifest.json, et le install.sh. citeturn28view0turn28view2
Dans ce modèle, ton widget central peut générer la commande d’installation exactement dans l’esprit que tu veux, par exemple :
curl -fsSL https://git.example.net/api/packages/infra/generic/fleet-agent/1.2.0/install.sh \
| bash -s -- \
--collector https://collector.example.net \
--token AGENT_REG_TOKEN \
--group vm \
--icon server \
--link https://proxmox.example.net
Je te conseille toutefois que ce script ne télécharge pas directement un binaire non vérifié : il doit récupérer le manifest, télécharger l’artefact pour l’OS/arch courants, vérifier la signature Minisign, installer le binaire, créer l’unité systemd et démarrer le service. C’est plus sûr que le simple curl | bash aveugle, tout en restant simple à opérer. citeturn28view0turn17search0
Pour l’auto-découverte, je te recommande de faire de la self-registration d’abord, et du mDNS/DNS-SD seulement en option locale. Les RFC IETF disent clairement que mDNS fonctionne sur le local link en absence de DNS conventionnel, et que DNS-SD sert à découvrir les instances d’un service à partir de son type. Cela veut dire que mDNS est pratique sur un LAN plat, mais pas un mécanisme principal pour des segments séparés, des VLANs ou des routes plus complexes. En outre, les travaux IETF sur la privacy de DNS-SD rappellent que la découverte de services divulgue typiquement des noms d’hôtes et des paramètres réseau. En pratique : annonce mDNS facultative pour le confort local, enregistrement explicite au collector pour le vrai fonctionnement. citeturn14search3turn14search1turn14search7
Pour la détection des états système, le plus propre est de mélanger hooks systemd et écoute D-Bus. La documentation systemd indique que ExecStop= et ExecStopPost= s’exécutent lors d’une opération d’arrêt/restart du service, ce qui te donne un bon point de sortie “propre” pour publier un dernier événement. En parallèle, l’interface org.freedesktop.login1 documente les signaux PrepareForShutdown() et PrepareForSleep() envoyés juste avant et juste après extinction/suspend, avec un booléen indiquant l’entrée ou la sortie de l’état. C’est exactement ce qu’il te faut pour les états “veille”, “arrêt”, “actif”. citeturn11search0turn12search0
Enfin, pour la mise à jour par mini-modules, le collector peut exposer une notion de “catalogue de modules” par agent : par exemple smart, gpu-nvidia, gpu-amd, services-extra, netinfo-advanced. Le widget n’a pas besoin d’exécuter lui-même le code distant ; il peut afficher la commande exacte à lancer, ou bien l’agent peut poller un manifest signé et proposer/appliquer l’update localement selon une politique. Cette seconde approche est plus sûre et plus industrialisable si tu comptes dépasser quelques machines. citeturn28view0turn16search1turn17search0
Plan de réalisation recommandé
Je te recommande un MVP Linux-first en trois étapes. Étape une : collector Rust + UI iframe + agent Rust qui remonte en push l’enregistrement, le heartbeat, CPU, RAM, volumes, état de base, et un top 5 processus rafraîchi toutes les 10 à 15 secondes. Tu obtiens déjà les tuiles, l’état online/offline, les popups secondaires et la génération de commandes d’installation. Cette première étape s’appuie presque entièrement sur tokio, axum, sysinfo, systemd et la mécanique Glance iframe. citeturn20search2turn16search2turn22view0turn26view0turn34view0
Étape deux : enrichissement matériel. Tu ajoutes DMI/SMBIOS, cartes réseau, disques SMART, capteurs/temperatures disponibles, et éventuellement données GPU NVIDIA. Cette couche est surtout utile pour ton volet latéral “hardware, icon, services, lien, emplacement, parent”. C’est aussi l’étape où tu peux stabiliser ton modèle d’inventaire pour qu’il serve en commun ton premier widget “cartographie IP réseau” et ton deuxième widget “agents”. citeturn25view0turn9search12turn8search0turn10search0
Étape trois : exploitation avancée. Tu ajoutes auto-discovery optionnelle, agent modules, politiques d’update, AMD SMI si nécessaire, et éventuellement une petite vue Glance custom-api de synthèse pour les KPIs globaux. À ce stade, Glance reste le portail, mais la logique métier vit clairement dans ton service Rust, ce qui est précisément ce que l’architecture de Glance favorise déjà avec iframe, extension et custom-api. citeturn35view0turn31view0turn34view0
Limites et points ouverts
La principale limite technique est que ton besoin est beaucoup plus proche d’un mini-produit de fleet monitoring que d’un simple widget statique. Ce n’est pas un défaut de Glance ; c’est juste que Glance documente aujourd’hui un modèle de données principalement fetch-on-load + cache, avec des extensions HTML et des custom APIs orientées rendu, et non un framework de widgets riches temps réel comparable à une SPA supervisée par un bus d’événements. L’existence d’une PR SSE non mergée va dans ce sens : le besoin de live updates existe, mais il n’est pas encore un socle stable du projet. citeturn33view1turn33view0turn31view0turn35view0
Le deuxième point ouvert est le périmètre OS. La stack que je te propose est excellente pour un parc Linux / VMs / bare metal sous systemd, parce qu’elle repose sur smartctl, login1/systemd, SMBIOS/DMI, NVML/AMD SMI et sysinfo. Si tu veux couvrir sérieusement Windows, BSD ou macOS avec le même niveau de détails matériels et d’événements système, il faudra des providers spécifiques ou un niveau de service un peu moins riche. sysinfo est multi-OS, mais tout ce qui touche au shutdown propre, à SMART, à SMBIOS détaillé et à la télémétrie GPU dépend fortement de l’OS et du vendor. citeturn22view0turn9search12turn8search0turn12search0turn10search0turn30search6
Le troisième point ouvert est la taille de parc visée. Si tu parles de quelques dizaines de machines, SQLite en WAL avec un collector unique, un cache mémoire court et des agrégats récents est probablement suffisant. Si tu parles de centaines de nœuds, d’historiques longs ou de multiples utilisateurs simultanés du dashboard, je partirais plus vite sur PostgreSQL et un découplage plus net entre ingestion temps réel et UI. Et si tu restes sur SQLite, je te recommande explicitement de vérifier la version déployée au regard des correctifs WAL mentionnés par la documentation SQLite de mars 2026. citeturn27view0turn19search1
En synthèse finale : oui, tu peux ajouter ce widget avec du Rust ; non, je ne te recommande pas de vouloir l’ajouter “dans Glance” comme widget natif Rust ; oui, je te recommande fortement un service Rust externe affiché par iframe dans une page Glance, avec un agent Rust en push, un collector temps réel, un stockage léger, une distribution via Gitea, et une vérification de signature des binaires. C’est l’architecture la plus propre, la plus maintenable, et la plus fidèle à la manière dont Glance expose aujourd’hui ses points d’extension. citeturn36view0turn35view0turn34view0turn31view0turn33view1