Declaração do Problema
Precisa de proteger os seus sistemas contra a perda de dados, minimizar o tempo de indisponibilidade durante desastres e garantir a continuidade do negócio com procedimentos de recuperação testados.
Objectivos de Recuperação
| Métrica | Definição | Metas Típicas |
|---|
| RPO (Recovery Point Objective) | Perda máxima de dados aceitável | 0 - 24 horas |
| RTO (Recovery Time Objective) | Tempo máximo de indisponibilidade aceitável | Minutos - horas |
Arquitectura de Backup
┌─────────────────────────────────────────────────────────────────────────┐
│ Production Environment │
├────────────────┬────────────────┬────────────────┬─────────────────────┤
│ Kubernetes │ Database │ Object Store │ Configuration │
│ Workloads │ (Primary) │ (S3/MinIO) │ (GitOps Repo) │
└───────┬────────┴───────┬────────┴───────┬────────┴─────────┬───────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌───────────────────────────────────────────────────────────────────────┐
│ Backup Layer │
├────────────────┬────────────────┬────────────────┬────────────────────┤
│ Velero │ pg_dump / │ S3 Cross- │ Git Mirror │
│ Snapshots │ mysqldump │ Region │ │
└───────┬────────┴───────┬────────┴───────┬────────┴────────┬───────────┘
│ │ │ │
└────────────────┴────────────────┴──────────────────┘
│
┌───────────▼───────────┐
│ Offsite Backup │
│ Storage (S3/GCS) │
│ Different Region │
└───────────────────────┘
1. Backup de Kubernetes com Velero
Instalar o Velero
# Instalar a CLI do Velero
wget https://github.com/vmware-tanzu/velero/releases/download/v1.12.0/velero-v1.12.0-linux-amd64.tar.gz
tar -xvf velero-v1.12.0-linux-amd64.tar.gz
mv velero-v1.12.0-linux-amd64/velero /usr/local/bin/
# Instalar o Velero no cluster
velero install \
--provider aws \
--plugins velero/velero-plugin-for-aws:v1.8.0 \
--bucket velero-backups \
--secret-file ./credentials-velero \
--backup-location-config region=us-west-2,s3ForcePathStyle="true",s3Url=https://s3.us-west-2.amazonaws.com \
--snapshot-location-config region=us-west-2
credentials-velero
[default]
aws_access_key_id=YOUR_ACCESS_KEY
aws_secret_access_key=YOUR_SECRET_KEY
Agendamentos de Backup
apiVersion: velero.io/v1
kind: Schedule
metadata:
name: daily-backup
namespace: velero
spec:
schedule: "0 2 * * *" # Diariamente às 2:00
template:
includedNamespaces:
- production
- staging
excludedResources:
- events
- pods
storageLocation: default
ttl: 720h # Retenção de 30 dias
snapshotVolumes: true
volumeSnapshotLocations:
- default
---
apiVersion: velero.io/v1
kind: Schedule
metadata:
name: hourly-backup
namespace: velero
spec:
schedule: "0 * * * *" # A cada hora
template:
includedNamespaces:
- production
includedResources:
- configmaps
- secrets
- deployments
- services
- ingresses
ttl: 168h # Retenção de 7 dias
snapshotVolumes: false
Backup Manual
# Criar backup de todos os recursos de produção
velero backup create production-backup-$(date +%Y%m%d) \
--include-namespaces production \
--snapshot-volumes
# Criar backup antes de alterações importantes
velero backup create pre-migration-backup \
--include-namespaces production \
--wait
Restaurar a partir de Backup
# Listar backups
velero backup get
# Restaurar para o mesmo cluster
velero restore create --from-backup production-backup-20240115 \
--include-namespaces production
# Restaurar para um namespace diferente (para testes)
velero restore create --from-backup production-backup-20240115 \
--namespace-mappings production:restore-test
2. Estratégias de Backup de Bases de Dados
Arquivo Contínuo de PostgreSQL (WAL)
# postgresql.conf
archive_mode = on
archive_command = 'aws s3 cp %p s3://db-backups/wal/%f'
wal_level = replica
# Script de backup base
##!/bin/bash
BACKUP_NAME="base-$(date +%Y%m%d_%H%M%S)"
pg_basebackup -D /tmp/$BACKUP_NAME -Ft -z -Xs -P
aws s3 cp /tmp/$BACKUP_NAME.tar.gz s3://db-backups/base/$BACKUP_NAME.tar.gz
rm -rf /tmp/$BACKUP_NAME*
Backup de PostgreSQL com pgBackRest
# /etc/pgbackrest/pgbackrest.conf
[global]
repo1-path=/backup/pgbackrest
repo1-retention-full=2
repo1-retention-diff=14
repo1-cipher-type=aes-256-cbc
repo1-cipher-pass=your-encryption-key
[main]
pg1-path=/var/lib/pgsql/data
# Backup completo (semanal)
pgbackrest --stanza=main backup --type=full
# Backup diferencial (diário)
pgbackrest --stanza=main backup --type=diff
# Recuperação point-in-time
pgbackrest --stanza=main restore \
--type=time \
--target="2024-01-15 10:30:00"
Backup de MySQL/Percona com Percona XtraBackup
# Backup completo
xtrabackup --backup --target-dir=/backup/full/$(date +%Y%m%d)
# Backup incremental
xtrabackup --backup --target-dir=/backup/incr/$(date +%Y%m%d) \
--incremental-basedir=/backup/full/20240115
# Preparar para o restauro
xtrabackup --prepare --target-dir=/backup/full/20240115
xtrabackup --prepare --target-dir=/backup/full/20240115 \
--incremental-dir=/backup/incr/20240116
# Restauro
systemctl stop mysql
rm -rf /var/lib/mysql/*
xtrabackup --copy-back --target-dir=/backup/full/20240115
chown -R mysql:mysql /var/lib/mysql
systemctl start mysql
Kubernetes CronJob para Backup de Base de Dados
apiVersion: batch/v1
kind: CronJob
metadata:
name: database-backup
namespace: production
spec:
schedule: "0 3 * * *"
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: postgres:15
command:
- /bin/bash
- -c
- |
set -e
BACKUP_NAME="backup-$(date +%Y%m%d-%H%M%S).sql.gz"
pg_dump -h $DB_HOST -U $DB_USER $DB_NAME | gzip > /tmp/$BACKUP_NAME
aws s3 cp /tmp/$BACKUP_NAME s3://db-backups/postgres/$BACKUP_NAME
# Limpar backups antigos (manter os últimos 30)
aws s3 ls s3://db-backups/postgres/ | sort -r | tail -n +31 | \
awk '{print $4}' | xargs -I {} aws s3 rm s3://db-backups/postgres/{}
env:
- name: DB_HOST
value: postgres-service
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_NAME
value: production
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: s3-credentials
key: access-key
- name: AWS_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: s3-credentials
key: secret-key
3. Recuperação de Desastres Multi-Região
Configuração Activo-Passivo
# Região primária
apiVersion: v1
kind: Service
metadata:
name: app-primary
annotations:
external-dns.alpha.kubernetes.io/hostname: app.example.com
external-dns.alpha.kubernetes.io/ttl: "60"
spec:
type: LoadBalancer
selector:
app: myapp
---
# Região secundária (standby)
apiVersion: v1
kind: Service
metadata:
name: app-secondary
annotations:
# Registo DNS activado apenas durante o failover
external-dns.alpha.kubernetes.io/hostname: app-dr.example.com
spec:
type: LoadBalancer
selector:
app: myapp
Replicação de Base de Dados entre Regiões
# Réplica cross-region do CloudNativePG
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: postgres-replica
namespace: production
spec:
instances: 2
replica:
enabled: true
source: postgres-primary
externalClusters:
- name: postgres-primary
connectionParameters:
host: postgres-primary.us-west-2.rds.amazonaws.com
user: replicator
password:
name: replication-credentials
key: password
4. Verificação e Testes de Backup
Testes Automatizados de Restauro
apiVersion: batch/v1
kind: CronJob
metadata:
name: backup-verification
spec:
schedule: "0 6 * * 0" # Semanalmente ao domingo
jobTemplate:
spec:
template:
spec:
restartPolicy: Never
containers:
- name: verify
image: backup-verifier:latest
command:
- /bin/bash
- -c
- |
set -e
# Obter o backup mais recente
LATEST=$(aws s3 ls s3://db-backups/postgres/ | sort -r | head -1 | awk '{print $4}')
# Descarregar e restaurar para a base de dados de teste
aws s3 cp s3://db-backups/postgres/$LATEST /tmp/backup.sql.gz
gunzip /tmp/backup.sql.gz
# Criar base de dados de teste
psql -h $TEST_DB_HOST -U $DB_USER -c "DROP DATABASE IF EXISTS backup_test"
psql -h $TEST_DB_HOST -U $DB_USER -c "CREATE DATABASE backup_test"
psql -h $TEST_DB_HOST -U $DB_USER -d backup_test -f /tmp/backup.sql
# Executar queries de verificação
USERS=$(psql -h $TEST_DB_HOST -U $DB_USER -d backup_test -t -c "SELECT COUNT(*) FROM users")
ORDERS=$(psql -h $TEST_DB_HOST -U $DB_USER -d backup_test -t -c "SELECT COUNT(*) FROM orders")
# Verificar a integridade dos dados
if [ "$USERS" -gt 0 ] && [ "$ORDERS" -gt 0 ]; then
echo "Backup verification PASSED"
curl -X POST $SLACK_WEBHOOK -d '{"text":"✅ Weekly backup verification passed"}'
else
echo "Backup verification FAILED"
curl -X POST $SLACK_WEBHOOK -d '{"text":"❌ Weekly backup verification FAILED"}'
exit 1
fi
# Limpeza
psql -h $TEST_DB_HOST -U $DB_USER -c "DROP DATABASE backup_test"
5. Runbook de Recuperação de Desastres
Checklist de Activação de DR
## Disaster Recovery Activation
### Pre-Activation (Assess)
- [ ] Confirm primary site is unavailable
- [ ] Estimate time to recover primary
- [ ] Get management approval for failover
- [ ] Notify stakeholders
### Activation (Execute)
1. [ ] Verify DR site health
```bash
kubectl get nodes
kubectl get pods -A
```
2. [ ] Activate database replica
```bash
# Promote PostgreSQL replica
kubectl exec -it postgres-0 -- pg_ctl promote
```
3. [ ] Update DNS records
```bash
# Point traffic to DR site
aws route53 change-resource-record-sets ...
```
4. [ ] Verify application connectivity
```bash
curl -I https://app.example.com/health
```
5. [ ] Monitor for errors
```bash
kubectl logs -f deployment/myapp
```
### Post-Activation (Verify)
- [ ] Verify all services operational
- [ ] Check database consistency
- [ ] Monitor error rates
- [ ] Communicate status to stakeholders
- [ ] Document timeline and actions taken
### Failback (Return to Primary)
- [ ] Restore primary site
- [ ] Sync data from DR to primary
- [ ] Test primary site
- [ ] Switch traffic back
- [ ] Deactivate DR site
6. Encriptação e Segurança de Backups
Encriptar Backups em Repouso
# Utilizar GPG para encriptação de backups
pg_dump mydb | gpg --encrypt --recipient [email protected] > backup.sql.gpg
# Utilizar OpenSSL
pg_dump mydb | openssl enc -aes-256-cbc -salt -pass file:/path/to/keyfile > backup.sql.enc
# Desencriptar
openssl enc -d -aes-256-cbc -pass file:/path/to/keyfile < backup.sql.enc | psql mydb
Política de Bucket S3
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EnforceEncryption",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::db-backups/*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "aws:kms"
}
}
},
{
"Sid": "RestrictAccess",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::db-backups",
"arn:aws:s3:::db-backups/*"
],
"Condition": {
"NotIpAddress": {
"aws:SourceIp": ["10.0.0.0/8"]
}
}
}
]
}
A Mentalidade de DR de um Senior
O Problema do etcd: Backups do Estado do Cluster
O estado do seu cluster Kubernetes vive no etcd. Se o etcd falhar e não tiver um backup, terá de reconstruir todo o cluster a partir do zero.
Backups de etcd Encriptados:
# Criar backup encriptado
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-$(date +%Y%m%d).db \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt \
--key=/etc/kubernetes/pki/etcd/healthcheck-client.key
# Encriptar e carregar
gpg --encrypt --recipient [email protected] /backup/etcd-*.db
aws s3 cp /backup/etcd-*.db.gpg s3://cluster-backups/etcd/
Agende isto, no mínimo, diariamente. Um backup de etcd desactualizado é melhor do que não ter backup.
Filosofia de Testes: Backups Não Testados Não Valem Nada
Regra Senior: um backup que não foi testado não é um backup — é uma esperança.
O que Testar: 1. Consegue descarregar o backup? (Rede, permissões, chaves de encriptação) 2. Consegue restaurar o backup? (Formato, corrupção, completude) 3. Os dados estão correctos? (Contagens de linhas, checksums, testes da aplicação) 4. Quanto tempo demora o restauro? (Cumpre o seu RTO?)
Agende testes automatizados de restauro semanalmente. Se um teste falhar, alguém deve ser notificado via pager.
O Framework de Decisão de Failover
O failover é caro e arriscado. Utilize este framework:
| Pergunta | Se Sim | Se Não |
|---|
| O site primário está completamente indisponível? | Continuar para a próxima | Aguardar e monitorizar |
| A recuperação vai demorar mais do que o RTO? | Continuar para a próxima | Aguardar e recuperar |
| Os dados no site de DR estão actuais (dentro do RPO)? | Avançar com o failover | Avaliar o risco de perda de dados |
| As partes interessadas aprovam a perda de dados? | Executar o failover | Aguardar ou encontrar alternativas |
Nunca faça failover sem aprovação explícita. A pessoa que decide deve compreender as implicações da perda de dados.
Failback: A Metade Esquecida
Planear o failover sem planear o failback deixa-o preso em modo de DR indefinidamente.
Considerações de Failback:
- Como sincroniza os dados de volta para o site primário?
- Como verifica que o primário está saudável antes de mudar?
- Como minimiza o tempo de indisponibilidade da segunda mudança?
- E se o primário falhar novamente durante o failback?
Documente e teste os procedimentos de failback em conjunto com o failover.
Checklist de DR
Estratégia de Backup
- [ ] RPO e RTO definidos e documentados
- [ ] Todos os dados críticos identificados
- [ ] Agendamentos de backup automatizados configurados
- [ ] Backups armazenados offsite/entre regiões
- [ ] Encriptação de backups activada
- [ ] Políticas de retenção de backups definidas
- [ ] Backups de etcd agendados para clusters Kubernetes
Capacidade de Recuperação
- [ ] Site de DR provisionado e testado
- [ ] Replicação de base de dados configurada
- [ ] Failover de DNS configurado
- [ ] Runbooks de recuperação documentados
- [ ] Recuperação testada trimestralmente
- [ ] Procedimentos de failback documentados
Verificação
- [ ] Verificação automatizada de backups (semanal)
- [ ] Testes regulares de restauro
- [ ] Exercícios de DR realizados
- [ ] Tempo de recuperação medido face ao RTO
- [ ] Integridade dos dados verificada após o restauro
Artigos Relacionados na Wiki