diff --git a/tenants/plataforma/51-backup-compartilhado.yaml b/tenants/plataforma/51-backup-compartilhado.yaml new file mode 100644 index 0000000..1218299 --- /dev/null +++ b/tenants/plataforma/51-backup-compartilhado.yaml @@ -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_` 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_` 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