1 pontos por GN⁺ 1 시간 전 | 1 comentários | Compartilhar no WhatsApp
  • Deptool é uma ferramenta de deploy criada para operar diretamente DNS e configurações de servidor web, mostrando primeiro o plano de mudanças e, após confirmação, aplicando-o aos hosts de destino
  • Ela renderiza antecipadamente toda a configuração do cluster, gerencia isso com Git e, em cada host, mantém diretórios por commit em /var/lib/deptool, trocando o link simbólico current para alternar a versão de forma atômica
  • Antes do deploy, obtém um lock em cada host, compara o commit que o ambiente local acredita conhecer com o estado realmente implantado e aborta planos obsoletos; só prossegue quando consegue os locks de todos os hosts afetados
  • Os serviços rodam como unidades systemd e são reiniciados quando a configuração muda; se a inicialização falhar, o link volta para a versão anterior conhecida como boa e reinicia de novo, realizando rollback automático em milissegundos
  • A execução remota usa SSH apenas como camada de transporte, em um modelo de agente estático, permitindo instalação automática só com coreutils mesmo em ambientes como Flatcar Linux, que não têm Python nem gerenciador de pacotes

O contexto por trás da criação do Deptool

  • O projeto começou ao migrar o blog para a Europa, para evitar a contradição de publicar textos sobre soberania digital europeia em hospedagem americana e sobre hiperescaladores sob controle dos EUA
  • Como o DNS também dependia do Cloudflare, surgiu a necessidade de operar um servidor DNS próprio
  • O servidor web existente rodava Nginx e Lego para renovação de certificados em uma VM pequena, com a configuração do Nginx gerada por Nix e copiada para o servidor por um pequeno script em Python que então reiniciava o Nginx
  • Para operar um servidor DNS, passariam a ser necessários pelo menos dois servidores, mais unidades systemd, arquivos de configuração e zonefiles, tornando o script anterior insuficiente
  • Havia a opção de migrar para NixOS, mas a decisão foi manter a abordagem atual — um sistema operacional base mínimo e serviços rodando em chroot somente leitura com apenas os binários necessários — e criar uma nova ferramenta de deploy

Como o Deptool é usado

  • Deptool primeiro mostra o plano de mudanças na configuração do cluster e, após confirmação, aplica-o aos hosts de destino
  • No exemplo de atualização de registros DNS, ao executar deptool deploy, ele mostra um plano com alteração dos arquivos de configuração do nsd e reinício de nsd.service em s4.ruuda.nl e s5.ruuda.nl
  • Em caso de falha no deploy, o rollback automático é aplicado; no exemplo, após confirmar a aplicação em 2 hosts do cluster prod, tudo é concluído com sucesso em 0,99 segundo
  • A saída separa host de destino, aplicação alterada, arquivos modificados e unidades systemd a reiniciar, permitindo verificar o que será realmente feito antes do deploy

Requisitos da ferramenta de deploy desejada

  • Rápida

    • Atualizações de configuração devem levar menos de 1 segundo; como até um ping transatlântico fica na casa dos 100 ms, não haveria motivo intrínseco para ser muito mais lento
  • Previsível

    • A ferramenta deve mostrar primeiro o que vai fazer e então executar exatamente aquilo
    • A ideia é separar as etapas de plano (plan) e aplicação (apply), como no OpenTofu
    • O check mode do Ansible é visto como pouco confiável, porque mudanças encadeadas podem aparecer só depois da execução de etapas imperativas, e ele também não impede que o estado do host mude entre a verificação e a execução real
  • Segura

    • Mesmo que a configuração do Nginx quebre, a ferramenta deve fazer rollback automático em milissegundos para evitar que o servidor web fique fora do ar por minutos
  • Simples

    • O necessário é apenas copiar arquivos de configuração do notebook para o servidor e reiniciar algumas unidades systemd
    • Não há necessidade de resolver todos os problemas de deploy nem de oferecer fluxo de controle ou execução arbitrária de código
    • O processamento de templates de configuração pode ser feito por outra ferramenta; a crítica aos templates YAML é separada em generate e na ferramenta separada de geração de arquivos
  • Declarativa

    • Se um arquivo ou aplicação for removido da configuração, ele também deve ser removido do servidor
    • Não deve ser necessário adicionar uma etapa explícita de limpeza, nem correr o risco de esquecê-la e gerar drift ou arquivos sobrando
  • Sem configuração inicial

    • Deve ser possível gerenciar o servidor imediatamente após o provisionamento
    • Se for preciso instalar manualmente agentes, daemons ou dependências, ou registrar o host, isso cria o problema de ter que automatizar esse procedimento também

Separação entre geração de configuração e deploy

  • A ideia central é separar geração de configuração de deploy
  • No trabalho do autor, o Unsible, criado por David, não executa playbooks do Ansible passo a passo; em vez disso, gera localmente um tarball e o envia ao host para posicionar os arquivos
  • O script de deploy simples já seguia essa lógica, com a configuração sendo gerada fora dele e o script atuando quase como uma cópia de arquivos
  • O NixOS também pode ser visto como uma aplicação dessa ideia ao sistema local; uma lição do Nix é armazenar artefatos gerados em locais onde múltiplas versões podem coexistir e limitar a parte imperativa da administração de sistemas a uma pequena etapa de ativação que troca alguns links simbólicos
  • Esse desenho funciona bem tanto para gerenciamento de pacotes quanto para configuração de sistema

Como o Deptool funciona

  • Renderiza antecipadamente toda a configuração do cluster

    • Os arquivos de configuração de todo o cluster são gerados previamente e salvos em um diretório no disco
    • A árvore de diretórios tem profundidade de dois níveis: no topo ficam os diretórios por host de destino e, abaixo deles, os diretórios por aplicação
  • Coloca isso em um repositório Git

    • Ao colocar o diretório de configuração em um repositório Git, torna-se possível comparar diferenças entre versões e ver o que mudou em todo o cluster
    • Com um diffstat, dá para saber quais hosts e apps foram afetados, além de ver o diff exato de cada arquivo de configuração
  • Materializa os arquivos em diretórios isolados no host

    • Todos os arquivos ficam sob /var/lib/deptool, para não interferirem com outros elementos
    • Como é criado um diretório com o nome do commit a ser implantado, várias versões podem coexistir no disco
    • O link simbólico current aponta para a versão implantada, permitindo trocar de versão de forma atômica
    • Arquivos excluídos não são materializados na próxima versão, então não ficam sobras
    • Para aplicações que exigem arquivos em locais específicos, é possível criar links simbólicos dos caminhos necessários no sistema de arquivos para /var/lib/deptool
    • A criação e remoção de links simbólicos não é atômica, mas isso só é necessário ao adicionar ou remover links, e não ao modificar o conteúdo dos arquivos
    • Se um link simbólico não estiver incluído numa versão posterior, o diff indicará que ele deve ser removido, evitando arquivos remanescentes
  • Registra o estado do deploy com refs remotas de rastreamento

    • O notebook do operador acompanha qual commit foi implantado em cada host
    • O estado do deploy não é uma propriedade do cluster inteiro, e sim uma propriedade por host
    • Se uma mudança não afeta determinado host, não é necessário implantar um novo commit nele
    • Com essa informação, o diff do cluster pode ser calculado offline, e esse diff se torna o plano de deploy, podendo ser mostrado em milissegundos
  • Obtém locks nos hosts de destino antes do deploy

    • A ferramenta se conecta por SSH e envia uma solicitação de lock, incluindo o commit que acredita estar implantado naquele host
    • Se o lock for obtido, o plano era válido e continuará válido até a liberação do lock, pois nenhum outro deploy poderá ocorrer naquele host nesse intervalo
    • O deploy só prossegue quando os locks de todos os hosts afetados pela mudança foram obtidos
    • Se a ref estiver desatualizada e algo mais tiver sido implantado no host, o plano é considerado obsoleto e abortado
    • Depois, basta atualizar a ref local para ver o plano mais recente na próxima execução
  • Reinicia unidades systemd

    • Todos os serviços rodam como unidades systemd e iniciam rapidamente, então, em caso de dúvida, a escolha é reiniciar
    • Quando a configuração de uma aplicação muda, as unidades systemd afetadas são reiniciadas
    • Se a inicialização da unidade falhar, o link simbólico volta para a versão anterior conhecida como boa e a unidade é reiniciada novamente, permitindo rollback automático em milissegundos

Modelo de concorrência otimista

  • Os deploys do Deptool têm um componente de concorrência otimista
  • O plano é feito assumindo que o estado atual do cluster é conhecido; se essa suposição estiver errada, é preciso tentar de novo
  • Sem contenção, isso é muito rápido, o que corresponde ao caso de uma pessoa fazendo deploy da própria infraestrutura com o mesmo notebook
  • Em ambientes onde várias pessoas tentam fazer deploy continuamente, apenas uma terá sucesso e as demais precisarão tentar de novo, o que pode degradar bastante o desempenho
  • O modelo é comparável a git push; ele não escala para centenas de pessoas ou milhares de servidores, mas é suficiente para infraestrutura pessoal
  • Criar a própria ferramenta permite otimizá-la exatamente para o caso de uso desejado

Construção do agente

  • Flatcar Linux e as restrições iniciais do host

    • O servidor web roda em Flatcar Linux
    • Flatcar Linux é um sistema operacional baseado em imagem, com userspace muito pequeno; há coreutils e Bash, mas não há gerenciador de pacotes nem Python
    • Isso é bom para reduzir a superfície de ataque, mas ruim para instalar qualquer coisa
    • Se a ferramenta precisar que algo seja instalado antes de funcionar, surge um novo problema: automatizar esse processo de instalação
  • Uso do SSH apenas como camada de transporte

    • Como novos hosts precisam ser gerenciados a partir de fora, é possível usar SSH e sudo sem senha
    • Executar comandos diretamente por SSH é lento por causa do handshake e também é inseguro para passar argv pela fronteira do SSH; além disso, seria necessário lidar com problemas de word splitting e escaping em shell-over-SSH
    • O Deptool executa um único programa sem argumentos em um local previsível e usa esse programa como agente
    • O agente lê mensagens da stdin e responde pela stdout
    • O SSH é usado apenas como meio de transporte, como se fosse um socket; como entradas controladas pelo usuário não entram em comandos SSH ou shell, os problemas de escaping são evitados
  • Uso de binário estático

    • O agente é compilado como binário estático
    • Assim, ele não depende de nada além do kernel, nem exige um interpretador que precise analisar vários megabytes de código antes de fazer algo útil
    • Mesmo após mitigar seus piores problemas, o Ansible ainda transfere vários megabytes de módulos Python a cada conexão, e o Flatcar nem sequer tem Python
  • Coloca o binário em um caminho baseado no commit

    • O binário do agente é armazenado em um caminho que inclui o commit com que foi compilado
    • Isso garante que as duas pontas da conexão executem a mesma versão, evitando problemas de compatibilidade de protocolo
    • O caminho segue o formato /var/lib/deptool/bin/deptool-<version>-<commit>
  • Assume primeiro que o binário já existe

    • Como o handshake SSH é caro, a ferramenta não desperdiça isso com sondagens ou etapas de instalação idempotentes
    • O binário do agente tem cerca de 1,6 MB: não é grande o suficiente para proibir a transferência, mas também não é gratuito
    • Mudanças na configuração do cluster acontecem com muito mais frequência do que atualizações do próprio Deptool, então normalmente o binário já estará presente
  • Instala o binário quando a execução falha

    • Se a inicialização do binário falhar, a instalação é feita por uma segunda conexão SSH
    • O comando de execução é o seguinte
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>
  • Primeiro, lê-se uma linha da stdout para obter a saída de uname; com isso, é possível identificar o sistema operacional e a arquitetura da CPU e enviar o binário do agente para a plataforma correspondente
  • Ao escrever o binário na stdin, o dd remoto grava o conteúdo no disco
  • Por fim, lê-se mais uma linha da stdout para verificar o shasum calculado no host remoto e validar se a transferência foi bem-sucedida
  • Esse processo depende apenas de programas padronizados do coreutils
  • Depois disso, uma nova tentativa de executar o agente deve funcionar, e o próprio agente limpa versões antigas para evitar que o disco encha

Efeitos e custos da abordagem com agente

  • O resultado é uma forma de executar e se comunicar com um agente no host remoto
  • A instalação automática funciona sem exigir nada além de coreutils no host remoto
  • Como ambos os lados executam a mesma versão, a compatibilidade de protocolo fica garantida estruturalmente
  • Dados controlados pelo usuário trafegam apenas por um socket baseado em SSH e não entram em comandos SSH ou shell, evitando problemas de escaping e limites de tamanho
  • No caso comum, é necessário apenas um handshake SSH, o que mantém a latência baixa
  • Em casos menos comuns, como deploy em máquina nova ou após atualizar a ferramenta, são necessárias mais 2 conexões e uma transferência única de 1,6 MB
  • Com ControlMaster, é possível evitar a maior parte da sobrecarga das conexões seguintes, então o custo total fica na casa de poucos segundos
  • Nesses casos, não se trata mais de um deploy abaixo de 1 segundo, mas ainda assim é melhor do que Ansible
  • No fluxo de implantar configuração, fazer um pequeno ajuste e implantar de novo, o SSH pode manter a conexão-base aberta, fazendo o deploy parecer imediato

Resultados de uso e publicação

  • O Deptool vem sendo usado há um mês para gerenciar infraestrutura pessoal
  • Ver o plano exato instantaneamente antes de conectar e contar com rollback automático já é ótimo, mas a maior mudança foi o deploy em menos de 1 segundo
  • Quando a forma correta de fazer deploy leva vários minutos, surge a tentação de editar arquivos diretamente no servidor para encurtar o ciclo de feedback; com o Deptool, porém, modificar localmente e fazer deploy é mais rápido do que entrar por SSH e abrir um editor no servidor
  • A forma com menos atrito se torna a forma correta, e todas as alterações aplicadas ficam registradas no histórico do Git
  • Mesmo que algo quebre, o Deptool faz rollback antes mesmo de a pessoa perceber que quebrou
  • O Deptool foi criado para resolver com precisão um problema pessoal, e o fato de não tentar resolver todos os problemas de deploy de todo mundo é justamente o que o destaca nesse caso de uso
  • Ele pode ser especialmente útil em sistemas operacionais baseados em imagem; está disponível no Codeberg e no GitHub, com um manual detalhado

1 comentários

 
GN⁺ 1 시간 전
Comentários no Lobste.rs
  • Fiquei muito feliz que o projeto deixou explícito que não colocou texto gerado por LLM em lugar nenhum: not putting LLM-generated text anywhere near this
    A ferramenta em si também parece bem refinada e com um bom design, mas por enquanto acho que vou continuar usando NixOS

  • Com certeza pretendo experimentar. Parece uma versão mais polida do sistema que eu mesmo fiz para implantar serviços baseados em systemd
    Pelo tutorial, parece bom, mas fiquei curioso sobre qual seria a melhor forma de lidar com estado local. Por exemplo, não consegui encontrar na documentação onde deveria ficar o banco de dados sqlite do app
    Também queria saber se existe alguma forma de enviar o binário da aplicação para o servidor e fazê-lo ser usado pela unidade systemd. Se não houver, queria entender como a distribuição de binários é tratada

    • No lado do servidor, normalmente você pode colocar onde já colocaria mesmo, e o local padrão é /var/lib/<yourapp>
      Se a aplicação roda como unidade systemd, você pode usar StateDirectory= para deixar o systemd criar o diretório com a propriedade correta do usuário
    • O Deptool por si só não transfere binários
      Os aplicativos que eu opero eu compilo como pequenas imagens EROFS com este script baseado em Nix, e esse script também inclui a capacidade de enviar a imagem para o servidor. Antes era uma etapa separada, mas agora juntei build e push em uma só, e ela vai para um diretório exclusivo para permitir que várias versões coexistam
      O resultado do build também inclui um JSON com caminhos de arquivos, que eu importo para a configuração do cluster, renderizo em unidades systemd e então implanto com o Deptool. Ou seja, uma ferramenta cuida da distribuição da imagem, e o Deptool cuida da ativação
      Se você usa contêineres, normalmente faz push para um registro, e no servidor só existe um arquivo de configuração dizendo o que buscar, então essa parte pode ser gerenciada apenas com o Deptool
  • Outra abordagem é usar bootable containers, o que também parece bem interessante
    O que ainda faz falta é algo que execute de fato bootc update --apply no host apropriado. Existem mecanismos de atualização automática, mas não são orquestrados, então não é o comportamento desejado em um cluster
    Por enquanto faço isso manualmente, mas no fim das contas o que precisa ser executado é só um comando bootc, então parece fácil transformar isso em script depois

  • Sempre fico um pouco cético quando aparece mais uma nova ferramenta de deploy, mas esta parece bem projetada e refinada
    Usar diretamente o comando ssh também parece a escolha certa. Você sabe que esse ssh que o usuário já tem realmente funciona, e ele pode estar usando uma configuração muito específica ou até um binário ssh com patch
    Ferramentas que tentam implementar ssh por conta própria em uma biblioteca externa provavelmente vão atrapalhar alguns usuários

  • Queria entender melhor como e por que você usa EROFS

    • Eu uso para implantar Nginx e alguns aplicativos no Flatcar, e é quase igual à forma como as pessoas usam imagens OCI para esse propósito
      O Flatcar não tem gerenciador de pacotes, então você precisa dar algum jeito de colocar o software e as dependências lá, e uma imagem de sistema de arquivos autocontida é uma dessas formas
      Imagens OCI exigem que alguma outra ferramenta, como Podman ou Docker, extraia o tar em algum lugar e monte uma pilha de overlay mounts, mas se já for uma imagem de sistema de arquivos, dá para executá-la diretamente em uma unidade systemd com RootImage=
      Eu construo as imagens com Nix para que contenham apenas o mínimo necessário. Só o binário do Nginx, LibreSSL, libc e algumas bibliotecas compartilhadas, sem nem Bash
      Isso faz parte de uma defesa em profundidade. Mesmo que o Nginx tenha uma vulnerabilidade de execução remota de código, ele roda dentro de um namespace de sistema de arquivos com pouquíssimo material para o invasor montar o próximo estágio do exploit, e o sistema de arquivos inteiro é somente leitura. Não apenas porque foi montado como somente leitura, mas porque no EROFS simplesmente não existe escrita
      Antes eu usava Squashfs e funcionava bem, mas esse sistema de arquivos foi projetado pensando na era dos live CDs. O EROFS faz concessões mais adequadas aos sistemas atuais, embora, honestamente, eu ache que no meu caso de uso não haja diferença mensurável
      As imagens ficam menores, mas isso acontece porque a configuração de compressão é diferente. Em teoria, o EROFS também é mais adequado para chunking definido por conteúdo quando você quer reutilizar dados entre imagens de versões diferentes, mas eu ainda não uso isso de fato na transferência das imagens
  • Esse post apareceu justamente quando eu estava discutindo com um amigo estratégias simples de deploy, e ficou bem próximo da conclusão a que estávamos chegando
    Só fiquei curioso sobre como funciona o gerenciamento de segredos nessa configuração

  • O título diz “Prompting the deployment tool I wish I had”, mas
    https://codeberg.org/ruuda/deptool/…
    Em certo sentido, é impressionante que os números de ponto flutuante tenham conseguido convencer o autor a usar Rust

    • Se eu fosse gerar código com LLM, em muitos aspectos eu até preferiria Rust
      Colocando de forma positiva, Rust é uma linguagem “disciplinada”, com convenções fortes e um ecossistema de ferramentas sólido. As duas coisas ajudam LLMs
      Curiosamente, as LLMs tendem a gerar programas mais curtos em Rust do que em algumas outras linguagens, pelo menos com um pouco de direcionamento. Como de qualquer forma eu pretendo ler e revisar todo o código, para mim é melhor que seja mais curto
  • Queria saber como vocês lidam com segredos nisso. Existe algum fluxo de trabalho preferido? Eles vão dentro da imagem EROFS ou são injetados pelo systemd?

    • No momento, os únicos segredos são certificados TLS, e eles só precisam existir em um único servidor, onde o Lego os coloca diretamente
      Esse diretório é montado como leitura e escrita para a unidade do Lego, e como somente leitura para a unidade do Nginx