- Deptool es una herramienta de despliegue creada para operar directamente la configuración de DNS y servidores web; primero muestra un plan de cambios y, tras confirmarlo, lo aplica en los hosts objetivo
- Renderiza de antemano la configuración completa del clúster, la gestiona con Git y coloca directorios por commit bajo
/var/lib/deptool en cada host; luego cambia el enlace simbólico current para cambiar de versión de forma atómica
- Antes de desplegar, toma un lock en cada host y compara el commit que conoce localmente con el estado real desplegado para abortar planes obsoletos; solo continúa si logra asegurar los locks de todos los hosts afectados
- Los servicios se ejecutan como unidades de systemd y se reinician cuando cambia la configuración; si el arranque falla, restaura el enlace a la versión previa conocida como buena y reinicia de nuevo para hacer rollback automático en milisegundos
- La ejecución remota usa SSH solo como capa de transporte con un agente estático, por lo que puede instalarse automáticamente usando solo coreutils incluso en entornos sin Python ni gestor de paquetes, como Flatcar Linux
El trasfondo de crear Deptool
- Surgió al mover el blog a Europa para evitar la contradicción de publicar textos sobre soberanía digital europea sobre hosting estadounidense y hyperscalers bajo control de EE. UU.
- Como también dependía de Cloudflare para DNS, surgió la necesidad de operar un servidor DNS propio
- El servidor web existente ejecutaba Nginx y Lego para renovar certificados en una VM pequeña, y la configuración de Nginx se generaba con Nix y luego se copiaba al servidor con un pequeño script en Python que reiniciaba Nginx
- Para operar un servidor DNS hacían falta al menos dos servidores, más unidades de systemd, archivos de configuración y zonefiles, así que el script existente ya no alcanzaba
- Existía la opción de migrar a NixOS, pero se decidió mantener el enfoque actual de un sistema base mínimo con servicios ejecutándose en un chroot de solo lectura que contiene solo los binarios necesarios, y crear una nueva herramienta de despliegue
Cómo se usa Deptool
- Deptool primero muestra un plan de cambios de configuración del clúster y, tras recibir confirmación, lo aplica en los hosts objetivo
- En el ejemplo de actualización de registros DNS, al ejecutar
deptool deploy muestra como plan cambios en los archivos de configuración de nsd y el reinicio de nsd.service en s4.ruuda.nl y s5.ruuda.nl
- Si el despliegue falla, se aplica rollback automático; en el ejemplo, tras confirmar si debía aplicarse a 2 hosts del clúster
prod, termina con éxito en 0.99 segundos
- La salida separa host objetivo, aplicación modificada, archivos cambiados y unidades de systemd a reiniciar, para poder verificar antes del despliegue qué se va a hacer realmente
Condiciones para la herramienta de despliegue deseada
-
Rápida
- Las actualizaciones de configuración deben tomar menos de 1 segundo, y si incluso un ping transatlántico ronda los 100 ms, no hay una razón esencial para que tenga que ser más lento
-
Predecible
- La herramienta debe mostrar primero qué va a hacer y luego ejecutar exactamente eso
- Se busca un enfoque con etapas separadas de plan y apply, como en OpenTofu
- El check mode de Ansible se considera poco confiable porque los cambios encadenados pueden hacerse visibles solo después de ejecutar pasos imperativos, y además no evita que el estado del host cambie entre la verificación y la ejecución real
-
Segura
- Incluso si la configuración de Nginx queda rota, la herramienta debe hacer rollback automático en milisegundos para evitar que el servidor web quede caído durante minutos
-
Simple
- Lo único necesario es copiar archivos de configuración desde la laptop al servidor y reiniciar algunas unidades de systemd
- No hace falta resolver todos los problemas de despliegue ni ofrecer flujo de control o ejecución de código arbitrario
- El procesamiento de plantillas de archivos de configuración puede hacerse con otra herramienta, y las objeciones a las plantillas YAML se separan en generate y en una herramienta aparte para generar archivos
-
Debe ser declarativa
- Si se elimina un archivo o aplicación de la configuración, también debe eliminarse del servidor
- No debería hacer falta agregar un paso explícito de limpieza, ni depender de recordarlo para evitar drift o archivos sobrantes
-
Sin configuración inicial
- Debe poder gestionar el servidor inmediatamente después de aprovisionarlo
- Si hay que instalar manualmente un agente, demonio o dependencias, o registrar el host mediante algún procedimiento, entonces aparece el problema de tener que automatizar también ese procedimiento
Separar la generación de configuración y el despliegue
- La idea central es separar la generación de configuración del despliegue
- En el trabajo, Unsible, creado por David, no ejecuta un playbook de Ansible paso a paso, sino que crea un tarball localmente y lo envía al host para colocar los archivos
- El script de despliegue simple que ya existía también construía la configuración fuera del servidor y el script cumplía un papel cercano al de copiar archivos
- NixOS también puede verse como una aplicación local de esta idea, y lo que puede aprenderse de Nix es guardar los artefactos generados en ubicaciones donde varias versiones puedan coexistir y limitar la parte imperativa de la administración del sistema a una pequeña etapa de activación que cambia algunos enlaces simbólicos
- Este diseño encaja bien tanto para gestión de paquetes como para configuración del sistema
Cómo funciona Deptool
-
Renderiza por adelantado la configuración completa del clúster
- Genera por adelantado los archivos de configuración de todo el clúster y los guarda en un directorio en disco
- El árbol de directorios tiene dos niveles de profundidad: arriba hay un directorio por host objetivo, y debajo un directorio por aplicación
-
Lo pone en un repositorio Git
- Al poner el directorio de configuración en un repositorio Git, se pueden comparar diferencias entre versiones y ver qué cambió en todo el clúster
- Con diffstat se puede ver qué hosts fueron afectados y qué apps cambiaron, además del diff exacto de cada archivo de configuración
-
Materializa los archivos en directorios aislados del host
- Todos los archivos se colocan bajo
/var/lib/deptool para no interferir con otros componentes
- Como se crea un directorio con el nombre del commit a desplegar, varias versiones pueden coexistir en disco
- El enlace simbólico
current apunta a la versión desplegada, lo que permite cambiar la versión de forma atómica
- Los archivos eliminados no se materializan en la siguiente versión, así que no quedan archivos residuales
- Para aplicaciones que requieren archivos en ubicaciones específicas, pueden crearse enlaces simbólicos desde los lugares necesarios del sistema de archivos hacia
/var/lib/deptool
- La creación o eliminación de enlaces simbólicos no es atómica, pero solo hace falta al agregar o quitar enlaces, no al modificar el contenido de los archivos
- Si en una versión posterior ese enlace simbólico ya no está incluido, el diff permite saber que debe eliminarse, así que no quedan archivos sobrantes
-
Registra el estado del despliegue con refs remotas de seguimiento
- Desde la laptop del operador se rastrea qué commit está desplegado en cada host
- El estado del despliegue no es una propiedad del clúster completo, sino una propiedad por host
- Si un cambio no afecta a cierto host, no hace falta desplegar un commit nuevo en ese host
- Con esta información puede calcularse offline el diff del clúster completo, y ese diff se convierte en el plan de despliegue, que puede mostrarse en milisegundos
-
Adquiere locks en los hosts objetivo antes de desplegar
- Se conecta por SSH y envía una solicitud de lock que incluye el commit que cree que está desplegado en ese host
- Si obtiene el lock, significa que el plan era válido y que, hasta liberar el lock, ningún otro despliegue puede avanzar en ese host, por lo que el plan sigue siendo válido
- El despliegue solo avanza si tiene los locks de todos los hosts afectados por el cambio
- Si la ref estaba desactualizada y en realidad se desplegó otra cosa en el host, el plan está stale y se aborta
- Luego, al actualizar la ref local, en la siguiente ejecución ya se verá el plan más reciente
-
Reinicia las unidades de systemd
- Todos los servicios se ejecutan como unidades de systemd y arrancan rápido, así que cuando hay dudas se prefiere reiniciar
- Cuando cambia la configuración de una aplicación, se reinician las unidades de systemd afectadas
- Si el arranque de una unidad falla, se restaura el enlace simbólico a la versión previa conocida como buena y se reinicia otra vez, permitiendo rollback automático en milisegundos
Modelo de concurrencia optimista
- Los despliegues de Deptool tienen un componente de concurrencia optimista
- Se planifica asumiendo que se conoce el estado actual del clúster, y si esa suposición es incorrecta, hay que reintentar
- Cuando no hay contención, es muy rápido, y ese es el caso típico de una infraestructura personal donde una sola persona despliega desde la misma laptop
- En un entorno donde varias personas intentan desplegar constantemente, solo una tendrá éxito y el resto deberá reintentar, lo que puede degradar mucho el rendimiento
- Este modelo se parece a
git push: no escala a cientos de personas o miles de servidores, pero es suficiente para infraestructura personal
- Al crear tu propia herramienta puedes optimizarla exactamente para tu caso de uso
Construcción del agente
-
Flatcar Linux y restricciones del host inicial
- El servidor web corre en Flatcar Linux
- Flatcar Linux es un sistema operativo basado en imágenes y con un userspace muy pequeño; trae coreutils y Bash, pero no gestor de paquetes ni Python
- Eso es bueno para reducir la superficie de ataque, pero inconveniente a la hora de instalar algo
- Si la herramienta requiere instalar algo antes de poder funcionar, aparece un nuevo problema: automatizar también ese proceso de instalación
-
SSH se usa solo como capa de transporte
- Como los hosts nuevos deben gestionarse desde fuera, pueden usarse SSH y
sudo sin contraseña
- Ejecutar comandos directamente por SSH no solo hace lento el handshake, sino que además
argv no cruza de forma segura el límite de SSH, y hay que lidiar con word splitting y problemas de escaping de shell-over-SSH
- Deptool ejecuta un único programa sin argumentos en una ubicación predecible y usa ese programa como agente
- El agente lee mensajes desde stdin y responde por stdout
- SSH se usa solo como medio de transporte, como si fuera un socket, y como la entrada controlada por el usuario no entra en SSH ni en comandos de shell, se evitan los problemas de escaping
-
Usa un binario estático
- El agente se compila como binario estático
- No asume nada aparte del kernel, y tampoco requiere un intérprete que tenga que parsear varios MB de código antes de hacer algo útil
- Incluso después de mitigar sus peores defectos, Ansible sigue transfiriendo varios MB de módulos Python en cada conexión, y Flatcar ni siquiera tiene Python
-
Coloca el binario en una ruta basada en commit
- El binario del agente se guarda en una ruta que incluye el commit con el que fue construido
- Así se garantiza que ambos extremos de la conexión ejecuten la misma versión y no haya problemas de compatibilidad de protocolo
- La ruta tiene el formato
/var/lib/deptool/bin/deptool-<version>-<commit>
-
Primero asume que el binario ya existe
- Como el handshake de SSH es costoso, no se desperdicia en sondeos ni en pasos de instalación idempotente
- El binario del agente pesa alrededor de 1.6 MB: no es tan grande como para prohibir su transferencia, pero tampoco es gratis
- Los cambios en la configuración del clúster ocurren mucho más seguido que las actualizaciones de Deptool, así que normalmente se asume que el binario ya está presente
-
Instala el binario si falla la ejecución
- Si falla el arranque del binario, la instalación se hace con una segunda conexión SSH
- El comando de ejecución es el siguiente
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>
- Primero se lee una línea de stdout para obtener la salida de
uname, y con eso se verifica el sistema operativo y la arquitectura de CPU para enviar el binario del agente correspondiente a esa plataforma
- El binario se escribe en stdin y el
dd remoto lo guarda en disco
- Al final se lee una línea adicional de stdout para verificar el shasum calculado remotamente y confirmar que la transferencia fue exitosa
- Este proceso depende solo de programas estandarizados de coreutils
- Luego se vuelve a intentar ejecutar el agente y debería funcionar; además, el agente limpia versiones antiguas para evitar que el disco se llene
Efectos y costo del enfoque con agente
- Se obtiene una forma de ejecutar y comunicarse con un agente en el host remoto
- La instalación automática es posible sin requerimientos adicionales en el host remoto aparte de coreutils
- Como ambos lados ejecutan la misma versión, la compatibilidad del protocolo queda garantizada estructuralmente
- La entrada controlada por el usuario solo se transmite por un socket basado en SSH y no entra en SSH ni en comandos de shell, evitando problemas de escaping y límites de longitud
- En el caso habitual solo hace falta un handshake de SSH, así que la latencia es baja
- En casos poco frecuentes, como desplegar en una máquina nueva o después de actualizar la herramienta, hacen falta 2 conexiones extra y una transferencia única de 1.6 MB
- Con
ControlMaster se puede omitir la mayor parte del overhead de las conexiones posteriores, así que el costo total queda en unos pocos segundos
- En ese escenario ya no se logra un despliegue en menos de 1 segundo, pero aun así se considera mejor que Ansible
- En el flujo de desplegar configuración, hacer un pequeño ajuste y volver a desplegar, SSH puede mantener viva la conexión base, por lo que el despliegue se siente inmediato
Resultados de uso y publicación
- Deptool se ha usado durante el último mes para gestionar infraestructura personal
- Poder ver al instante el plan exacto antes de conectarse y contar con rollback automático está muy bien, pero el cambio más importante es el despliegue en menos de 1 segundo
- Si una forma correcta de desplegar tarda minutos, dan ganas de editar archivos directamente en el servidor para acortar el ciclo de retroalimentación; con Deptool, en cambio, modificar localmente y desplegar es más rápido que conectarse por SSH al servidor y abrir un editor
- La forma con menos fricción se vuelve la forma correcta, y todos los cambios aplicados quedan registrados en el historial de Git
- Si algo se rompe, Deptool hace rollback antes incluso de que uno alcance a darse cuenta de que se rompió
- Deptool fue creado para resolver exactamente un problema personal, y el hecho de no intentar solucionar todos los problemas de despliegue de todo el mundo es precisamente lo que lo hace destacar en ese caso de uso
- Puede ser especialmente útil en sistemas operativos basados en imágenes; está publicado en Codeberg y GitHub, y también ofrece un manual detallado
1 comentarios
Comentarios en Lobste.rs
Me alegró mucho que dejara claro que no metió texto generado por LLM en este proyecto: not putting LLM-generated text anywhere near this
La herramienta en sí también se ve bien pulida y con buen diseño, pero por ahora creo que seguiré usando NixOS
Definitivamente pienso probarlo. Se ve como una versión más pulida del sistema que hice para desplegar servicios basados en systemd
Viendo el tutorial, se ve bien, pero me pregunto cuál sería la mejor forma de manejar el estado local. Por ejemplo, no encontré en la documentación dónde debería guardarse la base de datos sqlite de una app
También me pregunto si hay alguna forma de transferir el binario de la app al servidor para usarlo desde la unidad de systemd. Si no la hay, me gustaría saber cómo se maneja el despliegue de binarios
/var/lib/<yourapp>Si ejecutas la aplicación como una unidad de systemd, puedes usar
StateDirectory=para que systemd cree el directorio con la propiedad de usuario correctaLas aplicaciones que opero las construyo como pequeñas imágenes EROFS con este script basado en Nix, y ese script también incluye la capacidad de subir la imagen al servidor. Antes era un paso separado, pero ahora uní compilación y envío en una sola etapa, y todo va a un directorio único para que puedan coexistir varias versiones
El resultado de la compilación también incluye un JSON con rutas de archivos, que luego importo a la configuración del clúster, renderizo como unidades de systemd y despliego con Deptool. O sea, una herramienta se encarga del despliegue de imágenes y Deptool se encarga de la activación
Si usas contenedores, normalmente haces push a un registro, y en el servidor solo tienes un archivo de configuración que indica qué traer, así que esa parte sí se puede manejar solo con Deptool
Otro enfoque que también está bastante bien es usar bootable containers
Lo que todavía me falta es algo que realmente ejecute
bootc update --applyen el host adecuado. Hay mecanismos de actualización automática, pero no están orquestados, así que no es la forma que uno quiere en un clústerPor ahora lo hago manualmente, pero al final solo hay que ejecutar un comando de bootc, así que después parece fácil convertirlo en un script
Cada vez que aparece una nueva herramienta de despliegue la veo con algo de escepticismo, pero esta sí parece bien diseñada y bien pulida
Que use directamente el comando
sshtambién me parece la decisión correcta. Sabes que esesshque tiene el usuario realmente funciona, y hasta podría estar usando una configuración muy particular o un binario de ssh parchadoLas herramientas que intentan implementar ssh por su cuenta mediante librerías externas probablemente terminan estorbando a algunos usuarios
Me gustaría saber más sobre cómo y por qué usa EROFS
Flatcar no tiene gestor de paquetes, así que de alguna forma tienes que subir tú mismo el software y sus dependencias, y una imagen de sistema de archivos autocontenida es una manera de hacerlo
Con una imagen OCI, alguna herramienta aparte como Podman o Docker tiene que descomprimir el tar en algún lado y armar una pila de montajes overlay, pero si ya tienes una imagen de sistema de archivos, puedes ejecutarla directamente desde una unidad de systemd con
RootImage=Compilo las imágenes con Nix para que incluyan de verdad solo lo mínimo. Solo están el binario de Nginx, LibreSSL, libc y algunas bibliotecas compartidas; ni siquiera hay Bash
Esto es parte de una defensa en profundidad. Aunque Nginx tuviera una vulnerabilidad de ejecución remota de código, el atacante estaría corriendo dentro de un espacio de nombres del sistema de archivos con muy poco material para construir el siguiente exploit, y además todo el sistema de archivos es de solo lectura. No solo porque esté montado como solo lectura, sino porque EROFS simplemente no admite escritura
Antes usaba Squashfs y funcionaba bien, pero ese sistema de archivos fue diseñado pensando en la época de los live CD. EROFS toma decisiones de compromiso más adecuadas para sistemas actuales, aunque sinceramente no creo que en mi caso de uso haya una diferencia medible
Las imágenes sí son más pequeñas, pero eso es porque uso otra configuración de compresión. En teoría EROFS también se presta mejor al chunking definido por contenido cuando quieres reutilizar datos entre imágenes de distintas versiones, pero todavía no lo estoy usando de verdad para transferir imágenes
Justo estaba hablando con un amigo sobre estrategias simples de despliegue cuando salió este post, y se acerca bastante a las conclusiones a las que estábamos llegando
Pero me pregunto cómo manejan aquí la gestión de secretos
Dice “Prompting the deployment tool I wish I had”, pero
https://codeberg.org/ruuda/deptool/…
En cierto sentido, sí es impresionante que un montón de números de punto flotante hayan logrado convencerlo de usar Rust
Si lo quieres poner de forma amable, Rust es un lenguaje “disciplinado”, con convenciones fuertes y un ecosistema de herramientas sólido. Ambas cosas ayudan a los LLM
Curiosamente, los LLM tienden a generar programas más cortos en Rust que en algunos otros lenguajes, al menos si les das un poco de guía. Como de todos modos pienso leer y retocar todo el código, para mí es mejor que sea más corto
Me pregunto cómo manejas los secretos con esto. Quisiera saber si tienes un flujo de trabajo preferido, o si los metes dentro de la imagen EROFS o los inyectas con systemd
Ese directorio se monta como lectura-escritura en la unidad de Lego, y como solo lectura en la unidad de Nginx