feat(plataforma): backup do Postgres compartilhado (P6.3)

This commit is contained in:
deploy
2026-09-19 16:47:52 +00:00
parent 139a20148f
commit 5c16257363
@@ -0,0 +1,89 @@
# =============================================================================
# P6.3 — backup do Postgres COMPARTILHADO (P4.7), onde nasce todo tenant novo.
#
# O `50-backup.yaml` copia o Postgres PROPRIO da plataforma (`athleticmap` e
# `keycloak`). O `pg-compartilhado` — um database `athleticmap_<tenant>` por
# cliente, criado pelo provisionamento da fase 5 — nao tinha backup nenhum:
# todo cliente que assinasse pela landing page teria os dados num unico volume
# `local-path`, sem copia.
#
# `DATABASES=auto:athleticmap_` descobre os bancos NA HORA de cada rodada: um
# tenant criado hoje entra no backup de amanha sem ninguem editar esta lista.
# Provado por `scripts/backup/verificar_backup_compartilhado.sh` (inclusive que
# `athleticmapx` nao entra — o `_` do prefixo e literal).
#
# ── A RETENCAO E A ELIMINACAO (art. 16 da LGPD) ──────────────────────────────
#
# RETENCAO_DIAS=7, igual ao 50-backup. E isto que faz o direito de eliminacao
# alcancar o backup: um titular eliminado hoje some do ultimo backup que o
# continha em no maximo 7 dias, quando o arquivo expira — sem ninguem abrir e
# editar backup cifrado. Ver docs/p63-politica-de-retencao.md.
#
# ── ANTES DE APLICAR ─────────────────────────────────────────────────────────
#
# 1. A imagem `atm-pg-backup:1.1` tem o `backup_tenant.sh` com `auto:` e ainda
# NAO FOI CONSTRUIDA. Construir de scripts/backup/Containerfile e importar no
# k3s, como a 1.0. Com a 1.0, o CronJob falharia: ela nao entende `auto:`.
# 2. Zero tenants no compartilhado faz o job terminar com ERRO, de proposito
# (prefixo errado tambem daria zero, e "sucesso sem copiar nada" e pior que
# falha). Num cluster sem nenhum tenant compartilhado ainda, nao aplique.
# =============================================================================
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pg-backups-compartilhado
namespace: plataforma-prod
spec:
accessModes: [ReadWriteOnce]
storageClassName: local-path
resources:
requests:
# 7 dias de dumps comprimidos. Um tenant consolidado tem poucos MB; 10 Gi
# cobre dezenas de clientes com folga. Revisar quando passar de 50.
storage: 10Gi
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: pg-backup-compartilhado
namespace: plataforma-prod
spec:
# 02:30: meia hora depois do backup do Postgres proprio, para os dois dumps nao
# disputarem disco e CPU do mesmo no.
schedule: "30 2 * * *"
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: pg-backup
image: docker.io/library/atm-pg-backup:1.1
env:
# O admin do compartilhado: e o unico papel que le TODOS os
# databases. O `r_<tenant>` de cada cliente so ve o proprio.
- name: PGPASSWORD
valueFrom:
secretKeyRef: { name: pg-compartilhado-admin, key: password }
- name: CHAVE_PUBLICA
valueFrom:
configMapKeyRef: { name: pg-backup-chave, key: destinatario }
# Direto no Postgres, e nao no PgBouncer: ver o 50-backup.
- { name: PGHOST, value: "pg-compartilhado" }
- { name: PGUSER, value: "postgres" }
- { name: DATABASES, value: "auto:athleticmap_" }
- { name: DESTINO, value: "/backups" }
- { name: RETENCAO_DIAS, value: "7" }
# Vazio pelo mesmo motivo do 50-backup: falta credencial de
# armazenamento externo. O script avisa no fim de cada rodada.
- { name: COMANDO_ENVIO, value: "" }
command: ["backup_tenant.sh"]
volumeMounts:
- { name: backups, mountPath: /backups }
volumes:
- name: backups
persistentVolumeClaim:
claimName: pg-backups-compartilhado