Go + systemd Zero-Downtime Deploy Kit

Publique um serviço Go em uma VPS sem perder requisições, usando só systemd, Caddy e bash. Sem pm2, sem containers.

Comprar na Polar: $29 Compra única · v0.1.0 · download imediato pela Polar

Capa de Go + systemd Zero-Downtime Deploy Kit
Saída de uma execução real dos testes, não uma montagem.

Para quem é

Você envia um binário Go para um servidor e o roda sob o systemd. Hoje o deploy é scp, systemctl restart e uma olhada rápida nos logs. Funciona até o dia em que um restart cai num minuto de pico e o Caddy responde 502 por alguns segundos, ou em que você sobe um build que inicia direito mas falha todas as requisições, e nada impede que ele receba o tráfego.

Este kit troca isso por um script de deploy que sobe a nova release ao lado da antiga, verifica a release antes de ela receber qualquer tráfego, faz a troca e volta sozinho se a nova release degradar. É o equivalente em Go e systemd do kit para Node e pm2 que já vendemos, para quem não usa pm2 e não quer usar.

O que você leva

  • deploy.sh: pastas de release com um symlink current atômico, deploy de um binário compilado no CI ou de um build feito no servidor, health checks (N respostas seguidas), troca, drain, rollback automático, deploy.sh rollback e um lock para que dois deploys não se sobreponham.
  • Units do systemd com hardening: Type=notify, parada graceful, política de restart, usuário sem privilégios e segredos em um arquivo de ambiente com modo 600 que o usuário do serviço não consegue ler.
  • Um instalador que prepara o usuário, os diretórios, as units e o arquivo de ambiente.
  • Um app de exemplo pequeno em Go (só biblioteca padrão), com graceful shutdown e um helper testado para socket activation e sd_notify.
  • Alertas de falha por Telegram ou webhook quando um deploy falha. Deploys bem-sucedidos ficam em silêncio.
  • Um harness de testes e a saída da última execução.

Duas formas de fazer a troca

Blue/green (o padrão). Duas instâncias, app@a e app@b, ficam atrás do Caddy. A nova release sobe na porta ociosa e precisa responder ao health check várias vezes seguidas antes de o Caddy ficar sabendo dela. Então o Caddy recebe um reload graceful, e a instância antiga faz drain e para. Se a nova release degradar depois da troca, o script devolve tudo ao estado anterior. O rollback é o mesmo movimento ao contrário, e com KEEP_PREVIOUS=1 a instância antiga continua aquecida, então o rollback vira só uma troca no Caddy.

Socket activation. O systemd mantém o socket de escuta aberto enquanto o serviço reinicia, então as conexões ficam na fila em vez de serem recusadas. A nova release roda antes como canary em outra porta, para que um build quebrado nunca chegue ao restart.

O blue/green é o padrão porque mantém a requisição mais lenta próxima da duração da própria requisição. O modo socket existe para quando você não consegue rodar duas instâncias: uma porta fixa que não dá para mover, ou um recurso que só um processo pode segurar. As medições abaixo cobrem os dois.

Medições

Ambiente: um container Debian 12 com systemd 252 como PID 1, Caddy 2.6.2, um app de exemplo que leva 1.5 s para subir, 20 requisições por segundo constantes pelo Caddy e três execuções por cenário. Uma requisição conta como falha em qualquer erro de conexão, timeout ou status diferente de 200.

CenárioEnviadasFalhas
systemctl restart simples, uma instância152 a 16233 a 42 (todas HTTP 502)
Idem, com lb_try_duration 5s no Caddy1620, mas a requisição mais lenta levou 2.3 s
Blue/green, deploy bom293 a 2950
Blue/green, release cujo /health retorna 500458 a 4600
Blue/green, release que cai ao iniciar1320
Blue/green, release que degrada depois da troca381 a 3830 (a release ruim respondeu 182 ou 183 requisições antes do rollback)
Blue/green, rollback293 a 2950
Blue/green, rollback com KEEP_PREVIOUS=1222 a 2230, o tráfego mudou após 0.1 s
Blue/green, deploy bom com requisições de 2 s em andamento2340, as requisições lentas terminaram, não foram cortadas
Modo socket, deploy bom268 a 2690
Modo socket, deploy bom com requisições de 2 s em andamento307 a 3100, requisição mais lenta de 5.65 s

Um deploy leva de 8 a 9 segundos no modo blue/green, e o tráfego está na nova release após cerca de 3.6 segundos. Esses são os números para este app e esta máquina.

Como o deploy acontece

O README percorre sete passos. Resumindo: pegar o lock, criar a pasta da release, subir a instância ociosa e esperar que ela avise que está pronta, fazer o health check direto nela, gravar o novo upstream e dar reload no Caddy, trocar o symlink, esperar, verificar de novo e só então parar a instância antiga. Cada passo tem um caminho de falha definido, e os que importam são testados: uma release não saudável, um crash ao iniciar e uma release que degrada depois da troca.

Requisitos

Linux com systemd, Caddy, curl e flock. Go e git só se você compila no servidor. Um serviço em um host.

Perguntas frequentes

Uso Node e pm2. Este kit é para mim?

Não, veja o VPS Deploy & Backup Kit. Ele faz o mesmo trabalho para essa stack.

Preciso do Caddy?

O blue/green precisa dele para a troca. Medimos os dois modos com o Caddy na frente.

Funciona no Ubuntu?

O Ubuntu também usa systemd e o kit só precisa de systemd e Caddy, então se encaixa. Foi testado no Debian 12.

O que acontece se eu rodar um segundo deploy enquanto outro está em andamento?

Ele sai na hora com código 75 e não manda alerta. Isso é o lock.

Licença

Uso nos seus próprios servidores. Não revenda os arquivos.

Changelog

  • 0.1.0: primeira release.

Ler mais

Quero este

Comprar na Polar: $29

Pagamento, impostos e notas fiscais ficam com a Polar, que atua como merchant of record. Você recebe o link de download assim que concluir a compra.