105 lines
4.6 KiB
YAML
105 lines
4.6 KiB
YAML
# Backup diario do Postgres do tenant `plataforma` (pg_dump cifrado com age, ver P2.4)
|
|
apiVersion: v1
|
|
kind: PersistentVolumeClaim
|
|
metadata:
|
|
name: pg-backups
|
|
namespace: plataforma-prod
|
|
spec:
|
|
accessModes: [ReadWriteOnce]
|
|
storageClassName: local-path
|
|
resources:
|
|
requests:
|
|
storage: 5Gi
|
|
---
|
|
# ConfigMap com o DESTINATARIO age (chave PUBLICA).
|
|
#
|
|
# Publica pode ser versionada: ela so cifra. Quem tem a publica NAO abre o
|
|
# backup. A privada e gerada por scripts/backup/gerar_chave_backup.sh na maquina
|
|
# de quem restaura, e NUNCA chega neste repositorio nem no VPS.
|
|
#
|
|
# O valor abaixo e um marcador: substitua pelo destinatario real antes de
|
|
# aplicar. Com o marcador, o backup_tenant.sh recusa rodar em vez de gravar em
|
|
# claro — e o comportamento certo.
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: pg-backup-chave
|
|
namespace: plataforma-prod
|
|
data:
|
|
# Chave PUBLICA do age. Versionar isto e seguro e proposital: ela so serve
|
|
# para CIFRAR. Abrir um backup exige a privada, que por desenho nao esta
|
|
# neste repositorio nem no servidor — se estivesse no VPS, quem invadisse a
|
|
# maquina levaria a chave junto com o arquivo cifrado, e o backup cifrado
|
|
# nao teria servido para nada.
|
|
#
|
|
# Gerada em 2026-09-17 por `scripts/backup/gerar_chave_backup.sh`, na maquina
|
|
# do operador. ANOTE A DATA: rotacionar exige manter a chave antiga enquanto
|
|
# houver backup cifrado com ela dentro da retencao de 7 dias.
|
|
destinatario: "age1g37gc3gvq0zn7802pqh5dd0m3pkvc4z3g2hqk5w3dgvnaz3hwsrq7ps04y"
|
|
---
|
|
apiVersion: batch/v1
|
|
kind: CronJob
|
|
metadata:
|
|
name: pg-backup
|
|
namespace: plataforma-prod
|
|
spec:
|
|
schedule: "0 2 * * *" # diariamente 02:00
|
|
successfulJobsHistoryLimit: 3
|
|
failedJobsHistoryLimit: 3
|
|
concurrencyPolicy: Forbid
|
|
jobTemplate:
|
|
spec:
|
|
template:
|
|
spec:
|
|
restartPolicy: OnFailure
|
|
containers:
|
|
# Imagem propria: postgres:16 + age. O dump precisa ser cifrado
|
|
# ANTES de sair do processo, entao as duas ferramentas tem de estar
|
|
# no mesmo container. Construida de scripts/backup/Containerfile.
|
|
- name: pg-backup
|
|
image: docker.io/library/atm-pg-backup:1.2
|
|
env:
|
|
- name: PGPASSWORD
|
|
valueFrom:
|
|
secretKeyRef: { name: db-credentials, key: password }
|
|
- name: CHAVE_PUBLICA
|
|
valueFrom:
|
|
configMapKeyRef: { name: pg-backup-chave, key: destinatario }
|
|
# ── QUANDO ESTE TENANT MIGRAR PARA O BANCO COMPARTILHADO (P4.7)
|
|
#
|
|
# Estas tres linhas mudam JUNTO com a migracao, e nao antes:
|
|
#
|
|
# PGHOST postgres -> pg-compartilhado
|
|
# PGUSER atm -> r_<tenant>
|
|
# DATABASES athleticmap ... -> athleticmap_<tenant>
|
|
#
|
|
# O `keycloak` sai da lista se o Keycloak tambem for compartilhado
|
|
# (P4.8, card 156) — ate la ele continua no banco do tenant.
|
|
#
|
|
# E `PGHOST` aponta para o POSTGRES, e nao para o PgBouncer: o
|
|
# `pg_dump` abre uma transacao unica e longa, que e exatamente o
|
|
# que transaction pooling nao serve. Pelo bouncer, um dump grande
|
|
# seguraria uma conexao do pool pelo tempo inteiro do backup.
|
|
- { name: PGHOST, value: "postgres" }
|
|
- { name: PGUSER, value: "atm" }
|
|
- { name: DATABASES, value: "athleticmap keycloak" }
|
|
- { name: DESTINO, value: "/backups" }
|
|
- { name: RETENCAO_DIAS, value: "7" }
|
|
# O envio para fora. O script se cala quando o Secret nao existe, e
|
|
# falha ALTO quando existe e nao funciona — sao coisas diferentes,
|
|
# e um CronJob que falha toda madrugada por falta de credencial
|
|
# ensina todo mundo a ignorar o unico alerta que nao se pode ignorar.
|
|
- { name: COMANDO_ENVIO, value: "enviar_offsite.sh {}" }
|
|
# `optional: true`: enquanto o Secret `backup-offsite` nao existir, o
|
|
# CronJob roda igual e guarda local. No dia em que ele existir, o
|
|
# envio liga sozinho — sem tocar neste arquivo.
|
|
envFrom:
|
|
- secretRef: { name: backup-offsite, optional: true }
|
|
command: ["backup_tenant.sh"]
|
|
volumeMounts:
|
|
- { name: backups, mountPath: /backups }
|
|
volumes:
|
|
- name: backups
|
|
persistentVolumeClaim:
|
|
claimName: pg-backups
|