Documentação/ Implantação/ Blue-Green e Rollbacks

Blue-Green e Rollbacks

Como o sh0 substitui uma versão em serviço sem interrompê-la, o que o histórico de implantações registra e o que o botão de reversão realmente faz na versão 1.10.2.

Como uma implantação substitui a versão em serviço

Cada implantação inicia a nova versão ao lado da que já está no ar e só move o tráfego quando o novo contêiner prova que responde. Nenhuma configuração é necessária. Para um aplicativo implantado a partir do Git, a sequência é:

  1. Build da imagem a partir do repositório clonado (os hooks pre_build e post_build rodam antes e depois).
  2. Início do novo contêiner enquanto o antigo continua atendendo. Os dois montam os mesmos volumes durante a sobreposição.
  3. Verificação de saúde do novo contêiner (detalhes abaixo).
  4. Hooks pre_deploy e, em seguida, réplicas adicionais se o aplicativo tiver mais de uma.
  5. Troca de rota: o proxy reverso Caddy passa a apontar para o novo contêiner.
  6. Parada do contêiner antigo: ele recebe um sinal de parada, tem 30 segundos para encerrar e depois é removido.
  7. Hooks post_deploy e, por fim, uma verificação de que o novo contêiner continua rodando e não está reiniciando em loop.

Aplicativos implantados por envio de arquivo, imagem Docker ou Dockerfile colado seguem a mesma ordem, sem as etapas de hooks. Se alguma etapa falhar antes da troca de rota, o novo contêiner é removido e a versão anterior continua atendendo; o log informa: A failed deploy leaves nothing running.

Verificação de saúde

O sh0 espera até 300 segundos para que o novo contêiner seja considerado saudável:

  • A imagem declara um HEALTHCHECK: o sh0 espera o Docker declará-lo saudável e também verifica se a porta responde de fora do contêiner.
  • Sem HEALTHCHECK: o sh0 abre uma conexão TCP com a porta do aplicativo. Nenhum caminho HTTP é solicitado.
  • Worker sem porta: o contêiner precisa ficar em execução por 15 segundos sem reiniciar.

Um reinício em loop ou um encerramento por falta de memória faz a implantação falhar na hora, sem esperar os 300 segundos.

O que “zero interrupção” não cobre

  • As conexões ainda abertas no contêiner antigo quando ele para (WebSocket, downloads longos, respostas em streaming) são cortadas após o período de carência de 30 segundos. Elas não são transferidas para o novo contêiner.
  • Durante a sobreposição, dois contêineres do mesmo aplicativo rodam sobre os mesmos volumes. Um aplicativo que não suporta rodar duas vezes ao mesmo tempo (arquivo SQLite com bloqueio exclusivo, tarefa que deve ser única) precisa tolerar esses segundos.
  • As migrações de banco de dados disparadas por um hook pre_deploy são aplicadas enquanto a versão antiga ainda atende o tráfego. Escreva-as de modo que o código antigo continue funcionando com o novo esquema.

Histórico de implantações

A aba Implantações de cada aplicativo lista as implantações, da mais recente para a mais antiga, com o gatilho (upload, cli-push, cli, rollback…), o commit nos aplicativos Git, a duração e o log de build completo. A mesma lista está disponível em GET /api/v1/apps/<app-id>/deployments.

Uma implantação bem-sucedida mantém o status running mesmo depois de substituída por outra mais recente: a versão no ar é a entrada running mais recente, não a única.

Reversão

Na aba Implantações, cada implantação com status running tem um botão Reverter para esta implantação, que pede confirmação (Confirmar reversão). O equivalente na API é POST /api/v1/deployments/<deployment-id>/rollback; exige o papel de desenvolvedor no aplicativo e responde 202 com a nova implantação enfileirada. A versão 1.10.2 da CLI não tem comando de reversão.

O que uma reversão implanta

Warning
Desde a versão 1.10.3, uma reversão executa a imagem registrada na implantação escolhida: nada é clonado nem construído. As versões 1.10.2 e anteriores reimplantam a versão atual — atualize antes de contar com uma reversão para desfazer uma publicação ruim.

Observado na versão 1.10.3 com uma aplicação implantada por envio de arquivo: versão v1, depois v2, depois reversão para a implantação v1. A implantação de reversão leva a imagem de v1, seu log informa que nada foi construído e a aplicação responde v1:

Terminal
$ curl -s https://panel.example.com/api/v1/apps/<app-id>/deployments \
    -H "Authorization: Bearer $SH0_TOKEN" \
  | jq -c '.data[] | {id: .id[0:8], status, triggered_by, commit_msg, image_id: .image_id[7:19]}'
{"id":"c97f6268","status":"running","triggered_by":"rollback","commit_msg":"Rollback to deployment 0c5abcdc","image_id":"44da57c73080"}
{"id":"a36c7932","status":"running","triggered_by":"cli-push","commit_msg":"Push update","image_id":"916d35592fa5"}
{"id":"0c5abcdc","status":"running","triggered_by":"upload","commit_msg":"File upload","image_id":"44da57c73080"}

$ curl -s https://my-site.example.com
version v1

Uma reversão é recusada com 400 quando a implantação escolhida não teve sucesso, e com 409 quando sua imagem não pode mais ser executada: uma imagem construída pelo sh0 e depois removida pela limpeza descrita abaixo, ou, para uma aplicação implantada a partir de uma imagem Docker, uma tag móvel como nginx:latest que a implantação ativa também usa. Uma aplicação configurada com várias réplicas volta com um único contêiner.

Uma reversão restaura a imagem, não a configuração: variáveis de ambiente, volumes e domínios continuam os de hoje. Para voltar a uma versão cuja imagem não existe mais, implante-a novamente: envie o arquivo anterior com sh0 push, ou reverta o commit e envie-o em uma aplicação Git.

Retenção de imagens

A cada 6 horas, o sh0 apaga as imagens antigas dos aplicativos e mantém as 3 mais recentes por aplicativo. Os dois valores são opções de sh0 serve, não configurações do painel: --cleanup-interval-hours (0 desativa a limpeza) e --cleanup-keep-images.