- Deptool est un outil de déploiement créé pour exploiter directement DNS et la configuration du serveur web, qui affiche d’abord un plan des changements puis, après confirmation, les applique aux hôtes cibles
- Il pré-rend toute la configuration du cluster, la gère avec Git, place sous
/var/lib/deptool des répertoires par commit sur chaque hôte, puis change le lien symbolique current pour basculer atomiquement de version
- Avant le déploiement, il prend un verrou sur chaque hôte, compare le commit connu localement à l’état réellement déployé et interrompt les plans obsolètes ; il ne continue que si les verrous de tous les hôtes affectés ont été obtenus
- Les services s’exécutent comme unités systemd ; en cas de changement de configuration, ils redémarrent et, si le démarrage échoue, le lien revient à la version précédente connue comme saine avant un nouveau redémarrage, ce qui permet un rollback automatique en quelques millisecondes
- L’exécution distante repose sur un agent statique utilisant SSH uniquement comme couche de transport, ce qui permet une installation automatique avec les seuls coreutils, même sur des environnements sans Python ni gestionnaire de paquets comme Flatcar Linux
Pourquoi j’ai créé Deptool
- Le projet est parti du déménagement du blog vers l’Europe, pour éviter la contradiction consistant à publier un texte sur la souveraineté numérique européenne sur un hébergement américain et des hyperscalers sous contrôle américain
- Comme le DNS dépendait aussi de Cloudflare, il est devenu nécessaire d’exploiter soi-même un serveur DNS
- Le serveur web existant faisait tourner Nginx et Lego pour le renouvellement des certificats sur une petite VM ; la configuration Nginx était générée avec Nix, puis copiée sur le serveur par un petit script Python qui redémarrait Nginx
- Exploiter un serveur DNS impliquait au minimum deux serveurs, davantage d’unités systemd, de fichiers de configuration et de zonefiles, ce qui dépassait les capacités du script existant
- Passer à NixOS était une option, mais l’auteur a préféré conserver l’approche actuelle — un OS de base minimal et des services exécutés dans un chroot en lecture seule avec uniquement les binaires nécessaires — et créer un nouvel outil de déploiement
À quoi ressemble l’utilisation de Deptool
- Deptool affiche d’abord le plan des changements de configuration du cluster, puis les applique aux hôtes cibles après confirmation
- L’exemple de mise à jour d’enregistrements DNS montre qu’après l’exécution de
deptool deploy, l’outil prévoit des changements du fichier de configuration nsd et le redémarrage de nsd.service sur s4.ruuda.nl et s5.ruuda.nl
- En cas d’échec du déploiement, un rollback automatique est appliqué ; dans l’exemple, après confirmation du déploiement sur 2 hôtes du cluster
prod, l’opération réussit en 0,99 seconde
- La sortie sépare l’hôte cible, les applications modifiées, les fichiers modifiés et les unités systemd à redémarrer, afin de permettre de vérifier ce qui sera réellement exécuté avant le déploiement
Les critères de l’outil de déploiement souhaité
-
Rapide
- Une mise à jour de configuration doit prendre moins d’une seconde ; même un ping transatlantique tourne autour de 100 ms, donc il n’y a pas de raison fondamentale pour que cela soit plus lent
-
Prévisible
- L’outil doit d’abord montrer ce qu’il va faire, puis exécuter exactement cela
- L’auteur voulait une séparation entre les étapes de plan et d’apply, comme avec OpenTofu
- Le check mode d’Ansible est jugé peu fiable, car des changements en cascade peuvent n’apparaître qu’après l’exécution d’étapes impératives, et l’état de l’hôte peut aussi changer entre la vérification et l’exécution réelle
-
Sûr
- Même si la configuration Nginx est cassée, l’outil doit éviter que le serveur web reste indisponible pendant plusieurs minutes, grâce à un rollback automatique en quelques millisecondes
-
Simple
- Le besoin se limite à copier des fichiers de configuration depuis un laptop vers un serveur et à redémarrer quelques unités systemd
- Il n’est pas nécessaire de résoudre tous les problèmes de déploiement ni de fournir du contrôle de flux ou l’exécution de code arbitraire
- Le traitement des templates de configuration peut être confié à un autre outil ; les problèmes liés aux templates YAML sont séparés dans generate et dans un outil distinct de génération de fichiers
-
Déclaratif
- Si un fichier ou une application est retiré de la configuration, il doit aussi être supprimé du serveur
- Il ne doit pas être nécessaire d’ajouter une étape de nettoyage explicite, ni de risquer un oubli provoquant de la dérive ou des fichiers résiduels
-
Sans configuration initiale
- L’outil doit pouvoir gérer un serveur immédiatement après son provisionnement
- Si l’installation manuelle d’un agent, d’un démon, de dépendances ou une procédure d’enregistrement de l’hôte est nécessaire, cela recrée le problème consistant à devoir automatiser cette procédure elle-même
Séparer la génération de configuration et le déploiement
- L’idée clé est de séparer la génération de configuration du déploiement
- Unsible, créé par David au travail, n’exécute pas un playbook Ansible étape par étape : il fabrique localement une archive tarball, l’envoie vers l’hôte puis y place les fichiers
- Les scripts de déploiement simples déjà utilisés construisaient aussi la configuration à l’extérieur, le script se limitant pratiquement à copier des fichiers
- On peut voir NixOS comme l’application de cette idée au système local ; ce que l’on peut apprendre de Nix, c’est de stocker les artefacts générés dans un emplacement où plusieurs versions peuvent coexister, et de limiter la partie impérative de l’administration système à une petite étape d’activation consistant à changer quelques liens symboliques
- Cette conception fonctionne bien à la fois pour la gestion de paquets et la configuration système
Comment fonctionne Deptool
-
Pré-rendu de toute la configuration du cluster
- Tous les fichiers de configuration du cluster sont générés à l’avance et stockés dans un répertoire sur disque
- L’arborescence fait deux niveaux de profondeur : au niveau supérieur, un répertoire par hôte cible ; en dessous, un répertoire par application
-
Stockage dans un dépôt Git
- Placer le répertoire de configuration dans un dépôt Git permet de comparer les différences entre versions et de voir ce qui a changé à l’échelle du cluster
- Un diffstat permet de savoir quels hôtes sont affectés et quelles applications ont changé, et il est possible de voir le diff exact de chaque fichier de configuration
-
Matérialisation des fichiers dans des répertoires isolés sur l’hôte
- Tous les fichiers sont placés sous
/var/lib/deptool afin d’éviter toute interférence avec d’autres éléments
- Un répertoire est créé au nom du commit à déployer, ce qui permet à plusieurs versions de coexister sur disque
- Le lien symbolique
current pointe vers la version déployée, ce qui permet un changement atomique de version
- Les fichiers supprimés ne sont pas matérialisés dans la version suivante, ce qui évite les fichiers résiduels
- Pour les applications qui exigent des fichiers à un emplacement précis, on peut créer aux endroits nécessaires du système de fichiers des liens symboliques pointant vers
/var/lib/deptool
- La création ou la suppression de liens symboliques n’est pas atomique, mais cela n’est nécessaire qu’au moment d’ajouter ou de retirer un lien, pas lors de la modification du contenu des fichiers
- Si un lien symbolique n’est plus présent dans une version déployée ultérieure, le diff permet ensuite de savoir qu’il faut le supprimer, ce qui évite les fichiers orphelins
-
Enregistrement de l’état de déploiement avec des refs de suivi distant
- Sur le laptop de l’opérateur, l’outil suit le commit déployé sur chaque hôte
- L’état de déploiement n’est pas une propriété du cluster entier, mais une propriété par hôte
- Si un changement n’affecte pas un hôte donné, il n’est pas nécessaire d’y déployer un nouveau commit
- Cette information permet de calculer hors ligne le diff du cluster ; ce diff devient alors le plan de déploiement, affichable en quelques millisecondes
-
Acquisition d’un verrou sur les hôtes cibles avant le déploiement
- L’outil se connecte en SSH et envoie une requête de verrouillage qui inclut le commit qu’il pense déployé sur cet hôte
- Si le verrou est obtenu, le plan était valide ; tant que ce verrou n’est pas libéré, aucun autre déploiement ne peut avoir lieu sur cet hôte, et le plan reste donc valide
- Le déploiement ne continue que si l’outil détient le verrou sur tous les hôtes affectés par le changement
- Si la ref est ancienne et qu’autre chose a déjà été déployé sur l’hôte, le plan devient obsolète et l’opération est interrompue
- Une fois la ref locale mise à jour, l’exécution suivante peut afficher le plan le plus récent
-
Redémarrage des unités systemd
- Tous les services s’exécutent comme unités systemd et démarrent rapidement ; en cas de doute, l’auteur préfère donc redémarrer
- Lorsqu’une configuration d’application change, les unités systemd affectées sont redémarrées
- Si le démarrage d’une unité échoue, le lien symbolique revient à la version précédente connue comme saine et le redémarrage est relancé, ce qui permet un rollback automatique en quelques millisecondes
Un modèle de concurrence optimiste
- Les déploiements Deptool reposent en partie sur une concurrence optimiste
- L’outil construit un plan en supposant qu’il connaît l’état actuel du cluster ; si cette hypothèse est fausse, il faut réessayer
- En l’absence de contention, cette approche est très rapide, ce qui correspond au cas d’une infrastructure personnelle gérée par une seule personne depuis le même laptop
- Dans un environnement où plusieurs personnes tentent en permanence de déployer, une seule réussira et les autres devront réessayer, ce qui peut fortement dégrader les performances
- Le modèle est comparable à
git push : il ne passe pas à l’échelle de centaines de personnes ou de milliers de serveurs, mais il suffit largement pour une infrastructure personnelle
- Créer son propre outil permet aussi de l’optimiser exactement pour son cas d’usage
Construction de l’agent
-
Flatcar Linux et contraintes des hôtes initiaux
- Les serveurs web tournent sur Flatcar Linux
- Flatcar Linux est un OS basé sur des images ; son userspace est très réduit et fournit coreutils et Bash, mais ni gestionnaire de paquets ni Python
- C’est bon pour réduire la surface d’attaque, mais peu pratique pour installer quoi que ce soit
- Si l’outil devait commencer par installer quelque chose pour fonctionner, cela recréerait un nouveau problème d’automatisation de cette installation
-
SSH utilisé uniquement comme couche de transport
- Comme les nouveaux hôtes doivent être gérés de l’extérieur, il est possible d’utiliser SSH et
sudo sans mot de passe
- Exécuter directement des commandes via SSH est lent à cause du handshake ; de plus, les arguments
argv ne traversent pas proprement la frontière SSH, ce qui oblige à gérer les problèmes de word splitting et d’escaping du shell-over-SSH
- Deptool exécute un seul programme sans argument, à un emplacement prévisible, et l’utilise comme agent
- L’agent lit les messages sur stdin et répond sur stdout
- SSH n’est utilisé que comme moyen de transport, à la manière d’un socket ; aucune entrée contrôlée par l’utilisateur n’entre dans une commande SSH ou shell, ce qui évite les problèmes d’escaping
-
Utilisation d’un binaire statique
- L’agent est compilé comme binaire statique
- Il ne suppose rien d’autre que le noyau, et n’a pas besoin d’un interpréteur devant parser plusieurs Mo de code avant de pouvoir faire quelque chose d’utile
- Même après avoir atténué ses pires défauts avec mitigate, Ansible envoie plusieurs Mo de modules Python à chaque connexion, alors que Flatcar n’a même pas Python
-
Binaire placé dans un chemin basé sur le commit
- Le binaire de l’agent est stocké dans un chemin contenant le commit avec lequel il a été construit
- Cela garantit que les deux extrémités de la connexion exécutent la même version et évite les problèmes de compatibilité de protocole
- Le chemin suit le format
/var/lib/deptool/bin/deptool-<version>-<commit>
-
Supposer d’abord que le binaire est déjà présent
- Comme le handshake SSH a un coût élevé, il n’est pas gaspillé dans des étapes de probing ou d’installation idempotente
- Le binaire de l’agent pèse environ 1,6 Mo : ce n’est pas assez gros pour rendre son transfert prohibitif, mais ce n’est pas gratuit non plus
- Les changements de configuration du cluster sont bien plus fréquents que les mises à jour de Deptool ; on suppose donc en général que le binaire existe déjà
-
Installer le binaire si son exécution échoue
- Si le démarrage du binaire échoue, l’installation est effectuée via une deuxième connexion SSH
- La commande exécutée est la suivante
uname -sm
&& sudo mkdir -p /var/lib/deptool/{bin,apps,store}
&& sudo dd status=none of=<remote_bin_path>
&& sudo chmod +x <remote_bin_path>
&& sudo sha256sum <remote_bin_path>
- L’outil lit d’abord une ligne sur stdout pour récupérer la sortie de
uname, ce qui lui permet d’identifier l’OS et l’architecture CPU et d’envoyer le binaire de l’agent correspondant à cette plateforme
- En écrivant le binaire sur stdin, le
dd distant l’enregistre sur disque
- Enfin, l’outil lit une ligne supplémentaire sur stdout afin de vérifier le
shasum calculé à distance et de valider la réussite du transfert
- Ce processus ne dépend que de programmes coreutils standardisés
- Une nouvelle tentative d’exécution de l’agent devrait alors réussir, et l’agent supprime les anciennes versions pour éviter que le disque ne se remplisse
Effets et coûts de l’approche par agent
- Elle permet d’exécuter un agent sur l’hôte distant et de communiquer avec lui
- Elle autorise une installation automatique sur l’hôte distant sans autre prérequis que coreutils
- Les deux côtés exécutent la même version, ce qui garantit structurellement la compatibilité du protocole
- Les entrées contrôlées par l’utilisateur ne transitent que par un socket basé sur SSH et n’entrent jamais dans des commandes SSH ou shell, évitant ainsi les problèmes d’escaping et les limites de longueur
- Dans le cas général, un seul handshake SSH suffit, ce qui maintient une faible latence
- Dans les cas plus rares, comme un déploiement sur une nouvelle machine ou après une mise à jour de l’outil, il faut deux connexions supplémentaires et un transfert ponctuel de 1,6 Mo
- Avec
ControlMaster, la plupart des surcoûts des connexions suivantes peuvent être évités, ce qui ramène le coût total à quelques secondes
- Dans ce cas, on n’est plus sous la seconde, mais cela reste meilleur qu’Ansible selon l’auteur
- Dans un cycle où l’on déploie une configuration, on la modifie légèrement puis on redéploie, SSH peut garder la connexion sous-jacente, ce qui donne au déploiement une impression d’instantanéité
Résultats d’usage et publication
- Deptool est utilisé depuis un mois pour gérer une infrastructure personnelle
- Le fait de voir immédiatement un plan exact avant toute connexion et de disposer d’un rollback automatique est appréciable, mais le changement le plus marquant reste le déploiement en moins d’une seconde
- Quand une bonne méthode de déploiement prend plusieurs minutes, on est tenté d’éditer directement les fichiers sur le serveur pour raccourcir la boucle de feedback ; avec Deptool, modifier localement puis déployer est plus rapide que se connecter en SSH au serveur pour y ouvrir un éditeur
- La méthode la moins contraignante devient donc la bonne méthode, et toutes les modifications appliquées sont enregistrées dans l’historique Git
- Même si quelque chose casse, Deptool effectue le rollback avant même que l’auteur ait le temps de constater la panne
- Deptool a été créé pour résoudre exactement un problème personnel, et le fait qu’il ne cherche pas à résoudre tous les problèmes de déploiement de tout le monde est précisément ce qui le rend pertinent pour ce cas d’usage
- Il peut être particulièrement utile sur des systèmes d’exploitation basés sur des images ; il est publié sur Codeberg et GitHub, avec un manuel détaillé
1 commentaires
Avis sur Lobste.rs
Je suis vraiment ravi qu’il soit explicitement indiqué qu’aucun texte généré par LLM n’a été mis là-dedans : not putting LLM-generated text anywhere near this
L’outil en lui-même semble aussi bien peaufiné et bien conçu, mais je pense que je vais continuer à utiliser NixOS pour le moment
Je compte clairement l’essayer. Ça ressemble à une version plus aboutie du système maison que j’ai créé pour déployer des services basés sur systemd
Le tutoriel a l’air bien, mais je me demande quelle est la meilleure façon de gérer l’état local. Par exemple, je n’ai pas trouvé dans la doc où stocker la base de données sqlite de l’application
Je me demande aussi s’il existe un moyen d’envoyer le binaire de l’application sur le serveur pour qu’il soit utilisé par l’unité systemd. Sinon, j’aimerais savoir comment le déploiement des binaires est géré
/var/lib/<yourapp>Si vous exécutez l’application comme une unité systemd, vous pouvez utiliser
StateDirectory=pour que systemd crée le répertoire avec le bon propriétaireLes applications que j’exploite sont construites en petites images EROFS avec ce script basé sur Nix, et ce script inclut aussi la fonctionnalité pour pousser l’image vers le serveur. Avant, c’était une étape séparée, mais maintenant j’ai regroupé build et push en une seule étape, et tout est placé dans un répertoire unique pour permettre la coexistence de plusieurs versions
Le résultat du build inclut aussi un JSON contenant les chemins des fichiers, que j’importe dans la configuration du cluster, puis que je rends en unités systemd avant de les déployer avec Deptool. En d’autres termes, un outil s’occupe du déploiement des images, et Deptool s’occupe de l’activation
Si vous utilisez des conteneurs, vous les poussez généralement dans un registre, et le serveur n’a qu’un fichier de configuration indiquant quoi récupérer, donc cette partie peut être gérée uniquement avec Deptool
Une autre approche assez bonne consiste à utiliser des bootable containers
Ce qui manque encore, c’est quelque chose qui exécute réellement
bootc update --applysur l’hôte approprié. Il existe un mécanisme de mise à jour automatique, mais il n’est pas orchestré, ce qui n’est pas souhaitable dans un clusterPour l’instant je le fais à la main, mais comme il suffit au final d’exécuter une seule commande bootc, ça semble facile à transformer plus tard en script
Chaque fois qu’un nouvel outil de déploiement sort, j’ai tendance à être un peu sceptique, mais celui-ci semble bien conçu et bien peaufiné
Le fait d’utiliser directement la commande
sshme paraît aussi être le bon choix. On sait que lesshde l’utilisateur fonctionne réellement, et il se peut même qu’il utilise une configuration très particulière ou un binaire ssh patchéLes outils qui essaient de réimplémenter ssh eux-mêmes via une bibliothèque externe risquent fort de gêner certains utilisateurs
J’aimerais en savoir plus sur comment et pourquoi EROFS est utilisé
Flatcar n’a pas de gestionnaire de paquets, donc il faut bien faire monter soi-même le logiciel et ses dépendances d’une façon ou d’une autre, et une image de système de fichiers autonome est l’une de ces façons
Avec une image OCI, un outil séparé comme Podman ou Docker doit extraire le tar quelque part et monter une pile d’overlays, alors qu’avec une image de système de fichiers, on peut l’exécuter directement depuis l’unité systemd via
RootImage=Je construis les images avec Nix, donc elles sont vraiment minimales. Il n’y a que le binaire Nginx, LibreSSL, libc et quelques bibliothèques partagées, même pas Bash
Cela fait partie d’une défense en profondeur. Même si Nginx avait une vulnérabilité d’exécution de code à distance, un attaquant se retrouverait dans un espace de noms de système de fichiers avec très peu de matière pour construire l’exploit suivant, et l’ensemble du système de fichiers est en lecture seule. Pas simplement parce qu’il est monté en lecture seule, mais parce qu’EROFS ne permet tout simplement pas l’écriture
Avant, j’utilisais Squashfs et ça marchait bien, mais ce système de fichiers a été conçu à l’époque des live CD. EROFS fait des compromis plus adaptés aux systèmes actuels, même si, honnêtement, je ne pense pas qu’il y aurait une différence mesurable pour mon usage
Les images sont plus petites, mais c’est surtout parce que j’utilise des paramètres de compression différents. En théorie, EROFS est aussi mieux adapté au content-defined chunking si l’on veut réutiliser des données entre des images de versions différentes, mais je ne l’utilise pas encore réellement pour le transfert des images
Justement, je discutais avec un ami d’une stratégie de déploiement simple quand cet article est sorti, et c’est assez proche de la conclusion à laquelle nous arrivions
En revanche, je me demande comment la gestion des secrets fonctionne dans cette configuration
Le titre dit « Prompting the deployment tool I wish I had », mais
https://codeberg.org/ruuda/deptool/…
D’une certaine manière, c’est impressionnant que des nombres à virgule flottante aient réussi à convaincre Rust de s’utiliser lui-même
Pour le dire positivement, Rust est un langage « discipliné », avec des conventions fortes et un écosystème d’outils solide. Les deux aident les LLM
Curieusement, les LLM ont tendance à produire des programmes plus courts en Rust que dans certains autres langages, du moins si on les guide un minimum. Comme j’ai de toute façon l’intention de tout lire et de tout retoucher, plus c’est court, mieux c’est pour moi
Je me demande comment les secrets sont gérés avec ça. S’il y a un workflow privilégié, et s’ils sont mis dans l’image EROFS ou injectés via systemd
Ce répertoire est monté en lecture-écriture pour l’unité Lego, et en lecture seule pour l’unité Nginx