A plataforma
15 · Backup e restauração
Todo dado tem backup. Toda restauração é testada. Um backup que nunca foi restaurado é uma suposição.
O mecanismo
Regras
| Regra | Onde está decidida |
|---|---|
Todo armazenamento com dados de produção tem um accessory de backup no deploy.yml da aplicação dona | Onde está decidida: toolbox ADR-0013, ORG-0022 |
| Agenda diária; retenção 7 dias / 4 semanas / 6 meses | Onde está decidida: idem |
| A ferramenta de backup usa a mesma versão maior do servidor (um dump de cliente mais novo pode trazer comandos que o servidor recusa) | Onde está decidida: ORG-0022 |
A restauração apaga e recria o banco antes de carregar o dump (um dump sem --clean falha por cima do banco existente; com --clean, objetos mais novos sobrevivem e o banco fica misto) | Onde está decidida: ORG-0022 |
A restauração é um script versionado por armazenamento (eng/backup/restore-<armazenamento>.sh; o do banco é o restore-db.sh) que confere o arquivo, exige --confirmar e falha cedo | Onde está decidida: ORG-0022 |
| A restauração é testada fora da produção antes de cada subida de versão maior do banco e periodicamente | Onde está decidida: ORG-0022 |
| O roteiro de restauração (parar as apps → restaurar → subir a dona do schema → subir as demais) está no guia | Onde está decidida: ORG-0022 |
O que fica em lacuna
{{LACUNA:persistencia.backup}} — a ferramenta de cada armazenamento, que depende do que foi
escolhido. Para PostgreSQL, o padrão da Hibex é prodrigestivill/postgres-backup-local com tag
da linha do servidor.
{{LACUNA:persistencia.sem-backup}} — os armazenamentos que ficam sem backup por só guardarem
dados em trânsito (uma fila, um cache), cada um com o motivo, ou "nenhum".
O roteiro de restauração
Riscos aceitos e explícitos
Cada stack repete na sua ADR o que aceita, para não esquecer:
- os dumps ficam na mesma VPS: perder a VPS leva os dumps junto (cópia externa é
decisão futura do
toolbox); - quem tem SSH no host lê os dumps, que contêm dados pessoais;
- um dump de um schema mais novo que a imagem da aplicação faz a migração recusar o boot: a restauração pede a mesma versão da aplicação, ou uma mais nova.
Além do banco
Cada armazenamento com dados de produção tem o seu backup e o seu script de restauração: um banco de documentos, um armazenamento de objetos com os anexos. Um armazenamento que só guarda dados em trânsito, como uma fila cujas mensagens nascem de outro armazenamento, pode ficar sem backup, desde que a ADR-0010 o diga, com o motivo. A ADR que introduz um armazenamento novo traz o backup dele junto: é condição de aceite.
Armazenamento sem imagem de backup pronta
Para o PostgreSQL há uma imagem que faz tudo (o dump, a agenda e a retenção). Para os outros, como um banco de documentos ou um armazenamento de objetos, o padrão é um script da stack num accessory:
| Peça | Como é |
|---|---|
| O accessory | Como é: <armazenamento>_backup, no deploy.yml da dona do schema, na imagem do próprio armazenamento (a ferramenta de dump na mesma versão do servidor) ou na imagem oficial da ferramenta de cópia |
| O script | Como é: eng/backup/backup-<armazenamento>.sh, versionado e montado por files: (um kamal accessory reboot sobe a versão nova, porque o kamal deploy não a reenvia) |
| A agenda | Como é: o próprio script, num laço: um backup por dia na hora BACKUP_HORA (UTC); --uma-vez faz um agora e sai, para o primeiro dia, os testes e a emergência |
| A retenção | Como é: 7 diários, 4 semanais e 6 mensais, pela data no nome do arquivo; o primeiro backup da semana e o do mês vão também para semanal/ e mensal/. Só um backup bem-sucedido apaga os antigos |
| O acesso | Como é: um usuário só de leitura (ou uma chave que só lê o bucket), com o segredo no Bitwarden |
| Os logs | Como é: uma linha JSON por backup, como as das aplicações: o Alloy as leva ao Loki, e uma linha ERROR é o alerta |
| A prova | Como é: um teste de integração roda o script contra o armazenamento em container, com --uma-vez, e restaura o resultado com o restore-<armazenamento>.sh |
Um armazenamento de objetos que precisa de versões leva dois passos: um espelho diário do bucket e as versões do espelho numa ferramenta com retenção. Cada passo fica num accessory, e eles compartilham o volume do espelho.