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 symlinkcurrentatô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 rollbacke 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ário | Enviadas | Falhas |
|---|---|---|
systemctl restart simples, uma instância | 152 a 162 | 33 a 42 (todas HTTP 502) |
Idem, com lb_try_duration 5s no Caddy | 162 | 0, mas a requisição mais lenta levou 2.3 s |
| Blue/green, deploy bom | 293 a 295 | 0 |
Blue/green, release cujo /health retorna 500 | 458 a 460 | 0 |
| Blue/green, release que cai ao iniciar | 132 | 0 |
| Blue/green, release que degrada depois da troca | 381 a 383 | 0 (a release ruim respondeu 182 ou 183 requisições antes do rollback) |
| Blue/green, rollback | 293 a 295 | 0 |
Blue/green, rollback com KEEP_PREVIOUS=1 | 222 a 223 | 0, o tráfego mudou após 0.1 s |
| Blue/green, deploy bom com requisições de 2 s em andamento | 234 | 0, as requisições lentas terminaram, não foram cortadas |
| Modo socket, deploy bom | 268 a 269 | 0 |
| Modo socket, deploy bom com requisições de 2 s em andamento | 307 a 310 | 0, 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
- What a plain systemctl restart costs a Go service under load (em inglês)
- systemd socket activation: no dropped requests, but a cost (em inglês)
- Roll back a bad deploy in a minute with release folders (em inglês)
- Database migrations that survive a zero-downtime deploy (em inglês)
- Deploy health checks that catch bad releases (em inglês)
- Caddy blue/green config for a one-server app, line by line (em inglês)
Quero este
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.