Estrategias de copia de seguridad de bases de datos de producción que realmente funcionan: MySQL y PostgreSQL
Estrategias comprobadas de copia de seguridad y recuperación para MySQL y PostgreSQL en producción
La pérdida de datos de producción es el tipo de incidente que acaba con carreras, cierra empresas y aparece en la portada de Hacker News por todas las razones equivocadas. Sin embargo, la mayoría de los equipos tratan las copias de seguridad de las bases de datos como una ocurrencia tardía: un trabajo cron que alguien creó hace dos años y que nadie ha verificado desde entonces. Este artículo presenta estrategias de respaldo probadas en batalla para MySQL y PostgreSQL que han sido probadas en entornos de producción que van desde clústeres k3s de un solo nodo hasta implementaciones de nube multirregionales que procesan millones de transacciones diariamente.
Cubriremos todo el espectro: métodos de respaldo físico y lógico, recuperación en un momento dado, programación automatizada, objetivos de almacenamiento en la nube, cifrado, verificación, enfoques nativos de Kubernetes y la planificación de recuperación ante desastres que lo une todo. Cada recomendación viene con una configuración y un código concretos que puede adaptar a su entorno.
Descripción de los tipos de copia de seguridad
Antes de sumergirse en herramientas específicas, necesita un modelo mental claro de las cuatro estrategias de respaldo fundamentales y cómo interactúan con los objetivos de recuperación. Cada estrategia establece un compromiso diferente entre la velocidad de respaldo, el consumo de almacenamiento y el tiempo de recuperación.
Una copia de seguridad completacaptura toda la base de datos en un único momento. Es el más sencillo de entender y el más rápido de restaurar, pero también el más caro en almacenamiento y el más lento de crear. Una copia de seguridad incrementalcaptura solo los datos que han cambiado desde la última copia de seguridad de cualquier tipo. Es rápido de crear y compactar en el almacenamiento, pero la restauración requiere reproducir la cadena completa: la última copia de seguridad completa más cada incremental posterior. Una copia de seguridad diferencialcaptura todo lo que ha cambiado desde la última copia de seguridad completa. Ocupa un término medio: más grande que un incremental pero más sencillo de restaurar porque solo necesita el último completo más el último diferencial. Finalmente, el archivo continuo(archivo WAL en PostgreSQL, transmisión de registros binarios en MySQL) captura cada transacción individual a medida que ocurre, lo que permite la recuperación en un momento dado en cualquier momento entre copias de seguridad.
La estrategia óptima para la mayoría de los sistemas de producción es una combinación: copias de seguridad completas semanales, incrementales o diferenciales diarios y archivado WAL/binlog continuo. Esto le brinda una recuperación rápida de las copias de seguridad completas recientes y la capacidad de recuperarse en cualquier momento cuando lo necesite.
MySQL Métodos de copia de seguridad
MySQL ofrece varias herramientas de respaldo, cada una adaptada a diferentes tamaños de bases de datos y requisitos de recuperación. La elección correcta depende de su volumen de datos, ventana de respaldo aceptable y objetivos de RTO/RPO.
mysqldump: la copia de seguridad lógica universal
mysqldumpproduce declaraciones SQL que recrean el esquema y los datos. Funciona en todas las versiones y motores de almacenamiento de MySQL, lo que lo convierte en el recurso universal. Sin embargo, bloquea las tablas durante el volcado (a menos que se use--single-transactioncon InnoDB) y la velocidad de restauración se degrada significativamente para bases de datos de más de 50 a 100 GB porque reproduce declaraciones INSERT individuales.
# Full logical backup with consistent snapshot for InnoDB
mysqldump --single-transaction --routines --triggers --events \
--set-gtid-purged=ON --all-databases \
| gzip > /backups/mysql-full-$(date +%Y%m%d-%H%M%S).sql.gz
# Single database backup with compression
mysqldump --single-transaction --routines --triggers \
--databases production_db \
| pigz -p4 > /backups/production_db-$(date +%Y%m%d).sql.gz
# Schema-only backup for migration planning
mysqldump --no-data --routines --triggers --events \
--all-databases > /backups/schema-only-$(date +%Y%m%d).sqlmysqlpump — Copia de seguridad lógica paralela
mysqlpumpmejora amysqldumpcon volcado de tablas paralelo y compresión incorporada. Puede reducir significativamente el tiempo de copia de seguridad de bases de datos con muchas tablas independientes.
# Parallel logical backup with 4 threads and zstd compression
mysqlpump --default-parallelism=4 --compress-output=ZSTD \
--include-databases=production_db,analytics_db \
--set-gtid-purged=ON \
> /backups/mysql-pump-$(date +%Y%m%d).sql.zstPercona XtraBackup — Copias de seguridad físicas para InnoDB
Para bases de datos de más de 50 GB, las copias de seguridad lógicas se vuelven poco prácticas: tanto la ventana de copia de seguridad como el tiempo de restauración crecen linealmente con el tamaño de los datos. Percona XtraBackup toma copias de nivel físico de archivos de datos InnoDB sin bloquear la base de datos, lo que lo hace adecuado para implementaciones de varios terabytes.
# Full physical backup with streaming to compressed archive
xtrabackup --backup --target-dir=/backups/full-$(date +%Y%m%d) \
--user=backup_user --password=secure_pass \
--parallel=4 --compress --compress-threads=4
# Incremental backup based on last full
xtrabackup --backup --target-dir=/backups/incr-$(date +%Y%m%d) \
--incremental-basedir=/backups/full-20260412 \
--user=backup_user --password=secure_pass \
--parallel=4
# Stream full backup directly to S3 via xbstream
xtrabackup --backup --stream=xbstream --compress \
--user=backup_user --password=secure_pass | \
aws s3 cp - s3://db-backups/mysql/full-$(date +%Y%m%d).xbstream
# Prepare for restore (apply redo log)
xtrabackup --prepare --target-dir=/backups/full-20260412
# Prepare incremental on top of full
xtrabackup --prepare --apply-log-only --target-dir=/backups/full-20260412
xtrabackup --prepare --target-dir=/backups/full-20260412 \
--incremental-dir=/backups/incr-20260412
# Restore
systemctl stop mysqld
xtrabackup --copy-back --target-dir=/backups/full-20260412
chown -R mysql:mysql /var/lib/mysql
systemctl start mysqldRegistro binario MySQL: recuperación en un momento dado
Los registros binarios MySQL registran cada declaración de modificación de datos o cambio de fila. Cuando se combinan con una copia de seguridad completa o física, permiten la recuperación en un momento dado en cualquier momento después de que se realizó la copia de seguridad.
# Enable binary logging in my.cnf
[mysqld]
server-id = 1
log-bin = /var/log/mysql/mysql-bin
binlog_format = ROW
binlog_expire_logs_seconds = 604800 # 7 days
sync_binlog = 1
gtid_mode = ON
enforce_gtid_consistency = ON
# Flush and archive binary logs
mysqladmin flush-logs
mysqlbinlog --read-from-remote-server --host=db-primary \
--raw --stop-never --result-file=/backup/binlogs/ \
mysql-bin.000042
# Point-in-time recovery: replay binlog up to specific timestamp
mysqlbinlog --stop-datetime="2026-04-12 14:30:00" \
/backup/binlogs/mysql-bin.000042 \
/backup/binlogs/mysql-bin.000043 | mysql -u rootPostgreSQL Métodos de copia de seguridad
El ecosistema de respaldo de PostgreSQL es posiblemente más rico que el de MySQL, con herramientas propias y un conjunto maduro de soluciones comunitarias diseñadas específicamente para entornos de producción a gran escala.
pg_dump y pg_dumpall — Copias de seguridad lógicas
Al igual quemysqldump,pg_dumpproduce copias de seguridad lógicas. El formato personalizado (-Fc) es el predeterminado recomendado porque admite restauración paralela, restauración selectiva de tablas y compresión integrada.
# Custom format with parallel dump (4 worker jobs)
pg_dump -Fc -j4 -f /backups/production-$(date +%Y%m%d).dump production_db
# Directory format for maximum parallelism on large databases
pg_dump -Fd -j8 -f /backups/production-$(date +%Y%m%d)/ production_db
# All databases including globals (roles, tablespaces)
pg_dumpall > /backups/pg-all-$(date +%Y%m%d).sql
# Parallel restore from custom format
pg_restore -j4 -d production_db_restored /backups/production-20260412.dump
# Selective restore: single table
pg_restore -j4 -d production_db -t orders /backups/production-20260412.dumppg_basebackup — Base de respaldo físico
pg_basebackuptoma una copia física de todo el directorio de datos PostgreSQL. Es la base para la configuración de replicación de transmisión y recuperación independiente. Combinado con el archivo WAL, permite la recuperación en un momento dado.
# Physical backup with WAL files included
pg_basebackup -D /backups/base-$(date +%Y%m%d) \
-Ft -z -Xs -P -c fast \
-U replication_user -h db-primary
# Stream backup directly to a tar archive with checksums
pg_basebackup -D - -Ft -Xs -c fast \
-U replication_user -h db-primary | \
gzip > /backups/pg-base-$(date +%Y%m%d).tar.gzpgBackRest: gestión de copias de seguridad de nivel empresarial
pgBackRest es el estándar de oro para la gestión de copias de seguridad de PostgreSQL. Admite copias de seguridad completas, incrementales y diferenciales, copia de seguridad y restauración paralelas, cifrado, destinos de múltiples repositorios (disco local, S3, GCS, Azure Blob) y archivado WAL automatizado, todo a través de una configuración única y cohesiva.
# /etc/pgbackrest/pgbackrest.conf
[global]
repo1-type=s3
repo1-s3-bucket=pg-backups-production
repo1-s3-endpoint=s3.eu-west-1.amazonaws.com
repo1-s3-region=eu-west-1
repo1-s3-key=AKIAIOSFODNN7EXAMPLE
repo1-s3-key-secret=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
repo1-path=/pgbackrest
repo1-retention-full=4
repo1-retention-diff=14
repo1-cipher-type=aes-256-cbc
repo1-cipher-pass=a_very_secure_encryption_passphrase
# Second repository for geographic redundancy
repo2-type=azure
repo2-azure-container=pg-backups-dr
repo2-azure-account=prodbackupstorage
repo2-azure-key=base64encodedkeyhere
repo2-path=/pgbackrest
repo2-retention-full=2
process-max=4
compress-type=zst
compress-level=6
log-level-console=info
log-level-file=detail
start-fast=y
[production]
pg1-path=/var/lib/postgresql/16/main
pg1-port=5432
# PostgreSQL WAL archive configuration (postgresql.conf)
# archive_mode = on
# archive_command = 'pgbackrest --stanza=production archive-push %p'
# Create the stanza
pgbackrest --stanza=production stanza-create
# Verify the stanza configuration
pgbackrest --stanza=production check
# Full backup
pgbackrest --stanza=production --type=full backup
# Differential backup
pgbackrest --stanza=production --type=diff backup
# Incremental backup
pgbackrest --stanza=production --type=incr backup
# List backups
pgbackrest --stanza=production infoWAL-G: Archivado WAL ligero en almacenamiento en la nube
WAL-G es una alternativa más sencilla a pgBackRest que se centra en la transmisión de segmentos WAL y copias de seguridad básicas al almacenamiento de objetos en la nube. Es popular en entornos en contenedores donde desea un único binario con una configuración mínima.
# Environment variables for WAL-G with S3
export WALG_S3_PREFIX=s3://pg-wal-archive/production
export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
export AWS_REGION=eu-west-1
export WALG_COMPRESSION_METHOD=lz4
export PGHOST=/var/run/postgresql
# Configure WAL archiving in postgresql.conf
# archive_mode = on
# archive_command = 'wal-g wal-push %p'
# restore_command = 'wal-g wal-fetch %f %p'
# Take a base backup
wal-g backup-push /var/lib/postgresql/16/main
# List backups
wal-g backup-list
# Restore from latest backup
wal-g backup-fetch /var/lib/postgresql/16/main LATEST
# Delete old backups (retain last 4)
wal-g delete retain FULL 4 --confirmBarman: servidor de respaldo centralizado
Barman (Administrador de copias de seguridad y recuperación) está diseñado para entornos donde un servidor de copias de seguridad dedicado gestiona copias de seguridad para múltiples instancias PostgreSQL. Admite protocolos de replicación de streaming y rsync/SSH para el transporte de copias de seguridad.
# /etc/barman.d/production.conf
[production]
description = "Production PostgreSQL 16"
ssh_command = ssh postgres@db-primary
conninfo = host=db-primary user=barman dbname=postgres
streaming_conninfo = host=db-primary user=streaming_barman
backup_method = postgres
streaming_archiver = on
slot_name = barman
retention_policy = RECOVERY WINDOW OF 14 DAYS
# Create replication slot and start streaming
barman receive-wal --create-slot production
barman switch-wal --force --archive production
# Take a backup
barman backup production
# List backups
barman list-backup production
# Restore to point in time
barman recover --target-time "2026-04-12 14:30:00" \
production 20260412T120000 /var/lib/postgresql/16/mainRecuperación de un punto en el tiempo (PITR)
La recuperación en un momento dado es la capacidad más importante de su kit de herramientas de respaldo. Le permite restaurar una base de datos al estado exacto en el que se encontraba en cualquier momento específico, no sólo cuando se realizaron las copias de seguridad, sino en cualquier segundo entre copias de seguridad. Esto es fundamental para recuperarse de una eliminación accidental de datos, errores de aplicaciones que corrompen los datos e incidentes de seguridad en los que es necesario identificar el momento exacto del compromiso.
PITR para PostgreSQL
PostgreSQL PITR funciona restaurando una copia de seguridad base y reproduciendo segmentos WAL hasta la marca de tiempo de destino. Con pgBackRest, todo el proceso es un solo comando.
# Restore to specific point in time with pgBackRest
pgbackrest --stanza=production \
--type=time --target="2026-04-12 16:42:30" \
--target-action=promote \
restore
# Manual PITR using pg_basebackup + WAL archive
# 1. Stop PostgreSQL
systemctl stop postgresql
# 2. Clear the data directory and restore base backup
rm -rf /var/lib/postgresql/16/main/*
tar xzf /backups/pg-base-20260412.tar.gz -C /var/lib/postgresql/16/main/
# 3. Create recovery signal and configure restore
cat > /var/lib/postgresql/16/main/postgresql.auto.conf << 'CONF'
restore_command = 'cp /backup/wal_archive/%f %p'
recovery_target_time = '2026-04-12 16:42:30'
recovery_target_action = 'promote'
CONF
touch /var/lib/postgresql/16/main/recovery.signal
# 4. Start PostgreSQL — it will replay WAL and stop at target
systemctl start postgresqlPITR para MySQL
MySQL PITR combina una restauración física XtraBackup con reproducción de registros binarios.
# 1. Restore the XtraBackup
systemctl stop mysqld
rm -rf /var/lib/mysql/*
xtrabackup --prepare --target-dir=/backups/full-20260412
xtrabackup --copy-back --target-dir=/backups/full-20260412
chown -R mysql:mysql /var/lib/mysql
systemctl start mysqld
# 2. Identify the binlog position from XtraBackup metadata
cat /backups/full-20260412/xtrabackup_binlog_info
# Output: mysql-bin.000042 154 3E11FA47-1111-1111-1111-AAAAAAAAAAAA:1-1234
# 3. Replay binlogs up to target time
mysqlbinlog --start-position=154 \
--stop-datetime="2026-04-12 16:42:30" \
/backup/binlogs/mysql-bin.000042 \
/backup/binlogs/mysql-bin.000043 | mysql -u rootArquitectura de copia de seguridad multinube
Las estrategias de respaldo de producción deben tener en cuenta las fallas de cualquier proveedor o región de nube. Almacenar copias de seguridad exclusivamente en la misma cuenta de nube y región que su base de datos de producción significa que una sola cuenta comprometida, un problema de facturación o una interrupción regional pueden eliminar tanto sus datos como sus copias de seguridad simultáneamente. Una arquitectura sólida envía copias de seguridad a al menos dos destinos de almacenamiento independientes con replicación entre regiones.
Configuración de almacenamiento en la nube con políticas de ciclo de vida
# AWS S3 lifecycle policy (Terraform)
resource "aws_s3_bucket_lifecycle_configuration" "backup_lifecycle" {
bucket = aws_s3_bucket.db_backups.id
rule {
id = "backup-tiering"
status = "Enabled"
transition {
days = 30
storage_class = "STANDARD_IA"
}
transition {
days = 90
storage_class = "GLACIER"
}
transition {
days = 365
storage_class = "DEEP_ARCHIVE"
}
expiration {
days = 2555 # 7 years for compliance
}
}
rule {
id = "wal-segments"
status = "Enabled"
filter {
prefix = "wal-archive/"
}
expiration {
days = 30
}
}
}
# Enable cross-region replication
resource "aws_s3_bucket_replication_configuration" "backup_replication" {
bucket = aws_s3_bucket.db_backups.id
role = aws_iam_role.replication.arn
rule {
id = "cross-region-dr"
status = "Enabled"
destination {
bucket = aws_s3_bucket.db_backups_dr.arn
storage_class = "STANDARD_IA"
encryption_configuration {
replica_kms_key_id = aws_kms_key.dr_key.arn
}
}
source_selection_criteria {
sse_kms_encrypted_objects {
status = "Enabled"
}
}
}
}# Azure Blob immutability policy (Azure CLI)
az storage container immutability-policy create \
--account-name prodbackupstorage \
--container-name pg-backups \
--period 365 \
--allow-protected-append-writes true
# GCS lifecycle with nearline transition
gsutil lifecycle set /dev/stdin gs://pg-backups-production << 'JSON'
{
"rule": [
{"action": {"type": "SetStorageClass", "storageClass": "NEARLINE"}, "condition": {"age": 30}},
{"action": {"type": "SetStorageClass", "storageClass": "COLDLINE"}, "condition": {"age": 90}},
{"action": {"type": "Delete"}, "condition": {"age": 2555}}
]
}
JSONCopia de seguridad, cifrado y seguridad
Las copias de seguridadson un objetivo de gran valor para los atacantes. Una copia de seguridad robada y sin cifrar le brinda al atacante una copia completa de sus datos de producción sin necesidad de vulnerar sus sistemas en ejecución. Cada copia de seguridad, en tránsito y en reposo, debe estar cifrada.
Cifrado en tránsitosignifica usar TLS para todas las conexiones entre el servidor de la base de datos y el destino de la copia de seguridad. Tanto pgBackRest como XtraBackup transmiten a través de canales cifrados cuando se configuran con TLS. Cuando se envía a S3, Azure Blob o GCS, los SDK del cliente usan HTTPS de forma predeterminada.
Cifrado en reposotiene dos capas. El cifrado del lado del servidor (SSE) cifra los datos después de que llegan al destino de almacenamiento: S3 SSE-KMS, Azure Storage Service Encryption o cifrado predeterminado de GCS. El cifrado del lado del cliente cifra los datos antes de que abandonen el servidor de la base de datos, lo que garantiza que el proveedor de la nube nunca vea datos en texto sin formato. pgBackRest y WAL-G admiten el cifrado del lado del cliente de forma nativa.
# pgBackRest client-side encryption config
repo1-cipher-type=aes-256-cbc
repo1-cipher-pass=long_random_passphrase_stored_in_vault
# WAL-G client-side encryption
export WALG_LIBSODIUM_KEY=$(cat /etc/wal-g/encryption.key)
# Or GPG-based
export WALG_GPG_KEY_ID=backup@example.com
# MySQL XtraBackup with encryption
xtrabackup --backup --encrypt=AES256 \
--encrypt-key-file=/etc/mysql/backup-encryption.key \
--target-dir=/backups/full-encrypted-$(date +%Y%m%d)Almacene las claves de cifrado por separado de las propias copias de seguridad. El enfoque recomendado es un administrador de secretos como HashiCorp Vault, AWS Secrets Manager o Azure Key Vault. Si almacena la clave junto con la copia de seguridad, un atacante que obtenga acceso a su almacenamiento de copia de seguridad tendrá todo lo que necesita.
Programación de copia de seguridad automatizada
Las copias de seguridad manuales no son copias de seguridad, son aspiraciones. Los programas de respaldo deben automatizarse, monitorearse y alertar en caso de falla.
Cron del sistema para Bare Metal y VM
# /etc/cron.d/database-backups
# PostgreSQL — pgBackRest
# Full backup every Sunday at 01:00
0 1 * * 0 postgres pgbackrest --stanza=production --type=full backup 2>&1 | logger -t pgbackrest-full
# Differential backup every day at 01:00 (except Sunday)
0 1 * * 1-6 postgres pgbackrest --stanza=production --type=diff backup 2>&1 | logger -t pgbackrest-diff
# MySQL — XtraBackup
# Full backup every Sunday at 02:00
0 2 * * 0 root /usr/local/bin/mysql-backup.sh full 2>&1 | logger -t xtrabackup-full
# Incremental backup every day at 02:00 (except Sunday)
0 2 * * 1-6 root /usr/local/bin/mysql-backup.sh incremental 2>&1 | logger -t xtrabackup-incr
# Backup verification — restore test every Wednesday at 04:00
0 4 * * 3 root /usr/local/bin/verify-backup.sh 2>&1 | logger -t backup-verifyKubernetes Trabajos cronológicos
En entornos Kubernetes, CronJobs reemplaza el cron del sistema. Ofrecen lógica de reintento integrada, control de concurrencia e integración con RBAC del clúster y gestión de secretos.
apiVersion: batch/v1
kind: CronJob
metadata:
name: pg-backup-full
namespace: database
spec:
schedule: "0 1 * * 0" # Sunday 01:00 UTC
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 4
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 2
activeDeadlineSeconds: 7200 # 2 hour timeout
template:
spec:
serviceAccountName: db-backup
restartPolicy: OnFailure
containers:
- name: pgbackrest
image: pgbackrest/pgbackrest:2.50
command:
- /bin/bash
- -c
- |
pgbackrest --stanza=production --type=full backup
RESULT=$?
if [ $RESULT -eq 0 ]; then
curl -s -X POST "$SLACK_WEBHOOK" \
-d '{"text":"PostgreSQL full backup completed successfully"}'
else
curl -s -X POST "$SLACK_WEBHOOK" \
-d '{"text":"ALERT: PostgreSQL full backup FAILED"}'
fi
exit $RESULT
envFrom:
- secretRef:
name: pgbackrest-credentials
- secretRef:
name: slack-webhook
resources:
requests:
memory: 512Mi
cpu: 500m
limits:
memory: 2Gi
cpu: "2"
volumeMounts:
- name: pgbackrest-config
mountPath: /etc/pgbackrest
volumes:
- name: pgbackrest-config
configMap:
name: pgbackrest-conf
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: pg-backup-diff
namespace: database
spec:
schedule: "0 1 * * 1-6" # Mon-Sat 01:00 UTC
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 7
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 2
activeDeadlineSeconds: 3600
template:
spec:
serviceAccountName: db-backup
restartPolicy: OnFailure
containers:
- name: pgbackrest
image: pgbackrest/pgbackrest:2.50
command:
- /bin/bash
- -c
- |
pgbackrest --stanza=production --type=diff backup
envFrom:
- secretRef:
name: pgbackrest-credentials
resources:
requests:
memory: 256Mi
cpu: 250m
limits:
memory: 1Gi
cpu: "1"
volumeMounts:
- name: pgbackrest-config
mountPath: /etc/pgbackrest
volumes:
- name: pgbackrest-config
configMap:
name: pgbackrest-conf
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: mysql-backup-full
namespace: database
spec:
schedule: "0 2 * * 0"
concurrencyPolicy: Forbid
jobTemplate:
spec:
backoffLimit: 2
activeDeadlineSeconds: 7200
template:
spec:
serviceAccountName: db-backup
restartPolicy: OnFailure
containers:
- name: xtrabackup
image: percona/percona-xtrabackup:8.0
command:
- /bin/bash
- -c
- |
xtrabackup --backup --stream=xbstream --compress \
--user=$MYSQL_BACKUP_USER \
--password=$MYSQL_BACKUP_PASS \
--host=mysql-primary.database.svc | \
aws s3 cp - s3://$S3_BUCKET/mysql/full-$(date +%Y%m%d).xbstream
envFrom:
- secretRef:
name: mysql-backup-credentials
- secretRef:
name: aws-credentials
resources:
requests:
memory: 512Mi
cpu: 500m
limits:
memory: 2Gi
cpu: "2"Kubernetes: enfoques de copia de seguridad nativa
La ejecución de bases de datos en Kubernetes introduce una nueva dimensión a la estrategia de respaldo. Además de las copias de seguridad de bases de datos a nivel de aplicación, debe considerar la copia de seguridad a nivel de clúster (etcd), las instantáneas de volumen persistentes y las copias de seguridad administradas por el operador.
Velero para copia de seguridad a nivel de clúster
Velero realiza una copia de seguridad de los recursos Kubernetes (implementaciones, servicios, mapas de configuración, secretos) y volúmenes persistentes. No reemplaza las copias de seguridad de bases de datos a nivel de aplicación: captura una instantánea de los PV que no son consistentes con fallas, lo que puede no ser consistente desde el punto de vista transaccional para las bases de datos. Utilice Velero para la recuperación de clústeres y herramientas a nivel de aplicación (pgBackRest, XtraBackup) para la recuperación de bases de datos.
# Install Velero with S3 backend
velero install \
--provider aws \
--bucket velero-backups \
--secret-file ./credentials-velero \
--backup-location-config region=eu-west-1 \
--snapshot-location-config region=eu-west-1 \
--use-volume-snapshots=true \
--plugins velero/velero-plugin-for-aws:v1.9.0
# Create a scheduled backup of the database namespace
velero schedule create db-namespace-backup \
--schedule="0 3 * * *" \
--include-namespaces database \
--ttl 720h \
--storage-location default \
--volume-snapshot-locations default
# On-demand backup before maintenance
velero backup create pre-maintenance-$(date +%Y%m%d) \
--include-namespaces database,monitoring \
--wait
# Restore a namespace from backup
velero restore create --from-backup pre-maintenance-20260412 \
--include-namespaces databaseInstantáneas de Longhornen k3s
Las implementaciones dek3s suelen utilizar Longhorn como proveedor de almacenamiento CSI. Longhorn proporciona instantáneas a nivel de volumen y la capacidad de replicar instantáneas en un almacenamiento de objetos compatible con S3. Para las bases de datos, combine instantáneas de Longhorn con copias de seguridad a nivel de aplicación para una defensa en profundidad.
# Longhorn recurring snapshot job via CRD
apiVersion: longhorn.io/v1beta2
kind: RecurringJob
metadata:
name: pg-data-snapshot
namespace: longhorn-system
spec:
cron: "0 */4 * * *" # every 4 hours
task: snapshot
retain: 6
concurrency: 1
groups:
- pg-data
labels:
app: postgresql
---
# Longhorn recurring backup to S3
apiVersion: longhorn.io/v1beta2
kind: RecurringJob
metadata:
name: pg-data-s3-backup
namespace: longhorn-system
spec:
cron: "0 2 * * *" # daily at 02:00
task: backup
retain: 14
concurrency: 1
groups:
- pg-data
labels:
app: postgresql
---
# VolumeSnapshot using Longhorn CSI
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: pg-data-snap-$(date +%Y%m%d)
namespace: database
spec:
volumeSnapshotClassName: longhorn-snapshot-vsc
source:
persistentVolumeClaimName: pg-data-postgresql-0Copias de seguridad administradas por el operador
Los operadores de bases de datos como CloudNativePG (para PostgreSQL) y Percona Operador (para MySQL) integran la gestión de copias de seguridad directamente en el ciclo de vida de la base de datos. El operador maneja la programación, el archivado WAL y la retención automáticamente a través de recursos personalizados.
# CloudNativePG cluster with integrated backup
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: production-pg
namespace: database
spec:
instances: 3
storage:
size: 100Gi
storageClass: longhorn
backup:
barmanObjectStore:
destinationPath: s3://pg-backups/cnpg/
endpointURL: https://s3.eu-west-1.amazonaws.com
s3Credentials:
accessKeyId:
name: aws-creds
key: ACCESS_KEY_ID
secretAccessKey:
name: aws-creds
key: SECRET_ACCESS_KEY
wal:
compression: gzip
maxParallel: 4
data:
compression: gzip
retentionPolicy: "30d"
---
apiVersion: postgresql.cnpg.io/v1
kind: ScheduledBackup
metadata:
name: production-pg-weekly
namespace: database
spec:
schedule: "0 1 * * 0"
backupOwnerReference: self
cluster:
name: production-pgEstrategias específicas del proveedor de nube
AWS — RDS y Aurora
AWS RDS proporciona instantáneas diarias automatizadas con retención configurable (hasta 35 días) y copia de seguridad continua mediante el archivo de registros de transacciones para una recuperación en un momento dado. Aurora agrega Backtrack, que puede rebobinar el clúster a cualquier punto dentro de la ventana de retroceso sin requerir una restauración; simplemente invierte el estado interno de la base de datos.
# Enable automated backups with maximum retention (Terraform)
resource "aws_db_instance" "production" {
identifier = "production-pg"
engine = "postgres"
engine_version = "16.2"
instance_class = "db.r6g.xlarge"
allocated_storage = 500
backup_retention_period = 35 # maximum
backup_window = "03:00-04:00"
copy_tags_to_snapshot = true
deletion_protection = true
storage_encrypted = true
kms_key_id = aws_kms_key.rds.arn
# Enable PITR
enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"]
}
# Cross-region automated backup replication
resource "aws_db_instance_automated_backups_replication" "dr" {
source_db_instance_arn = aws_db_instance.production.arn
kms_key_id = aws_kms_key.dr_rds.arn
retention_period = 14
}
# Aurora Backtrack (MySQL-compatible Aurora only)
resource "aws_rds_cluster" "aurora_production" {
cluster_identifier = "aurora-production"
engine = "aurora-mysql"
engine_version = "8.0.mysql_aurora.3.05.2"
backtrack_window = 86400 # 24 hours of backtrack
backup_retention_period = 35
preferred_backup_window = "03:00-04:00"
storage_encrypted = true
}
# Manual snapshot with cross-region copy
aws rds create-db-snapshot \
--db-instance-identifier production-pg \
--db-snapshot-identifier pre-migration-$(date +%Y%m%d)
aws rds copy-db-snapshot \
--source-db-snapshot-identifier arn:aws:rds:eu-west-1:123456789:snapshot:pre-migration-20260412 \
--target-db-snapshot-identifier pre-migration-20260412-dr \
--source-region eu-west-1 \
--region us-east-1 \
--kms-key-id arn:aws:kms:us-east-1:123456789:key/dr-key-idAzure — Servidor flexible
La base de datos Azure para el servidor flexible PostgreSQL y MySQL incluye copias de seguridad automáticas con almacenamiento con redundancia local o geográfica. La copia de seguridad con redundancia geográfica permite la restauración entre regiones para la recuperación ante desastres.
# Azure Flexible Server with geo-redundant backup (Terraform)
resource "azurerm_postgresql_flexible_server" "production" {
name = "production-pg"
location = "westeurope"
resource_group_name = azurerm_resource_group.db.name
sku_name = "GP_Standard_D4s_v3"
version = "16"
storage_mb = 524288 # 512 GB
backup_retention_days = 35
geo_redundant_backup_enabled = true
authentication {
active_directory_auth_enabled = true
password_auth_enabled = false
}
}
# Azure Blob immutability for self-managed backups
resource "azurerm_storage_management_policy" "backup_lifecycle" {
storage_account_id = azurerm_storage_account.backups.id
rule {
name = "backup-tiering"
enabled = true
filters {
blob_types = ["blockBlob"]
prefix_match = ["pg-backups/"]
}
actions {
base_blob {
tier_to_cool_after_days_since_modification_greater_than = 30
tier_to_archive_after_days_since_modification_greater_than = 90
delete_after_days_since_modification_greater_than = 2555
}
}
}
}GCP — Nube SQL
Cloud SQL proporciona copias de seguridad automatizadas y recuperación en un momento dado listas para usar. Para bases de datos autoadministradas en GCE o GKE, GCS con clases de almacenamiento nearline y coldline proporciona almacenamiento de respaldo rentable a largo plazo.
# Cloud SQL with automated backups and PITR (Terraform)
resource "google_sql_database_instance" "production" {
name = "production-pg"
database_version = "POSTGRES_16"
region = "europe-west1"
settings {
tier = "db-custom-4-16384"
backup_configuration {
enabled = true
start_time = "03:00"
point_in_time_recovery_enabled = true
transaction_log_retention_days = 7
backup_retention_settings {
retained_backups = 30
retention_unit = "COUNT"
}
}
ip_configuration {
ssl_mode = "ENCRYPTED_ONLY"
}
}
}
# Export to GCS for long-term retention
gcloud sql export sql production-pg \
gs://pg-backups-longterm/export-$(date +%Y%m%d).sql.gz \
--database=production_db \
--offloadVerificación de copia de seguridad y prueba de restauración
Una copia de seguridad que nunca ha sido probada no es una copia de seguridad, es una esperanza. Las pruebas de restauración automatizadas deben ejecutarse según un cronograma regular, idealmente semanalmente, en un entorno aislado. La prueba debe validar no solo que la restauración se complete sin errores, sino que los datos restaurados sean consistentes y la aplicación pueda conectarse y consultarlos.
#!/bin/bash
# verify-backup.sh — Automated backup verification script
set -euo pipefail
RESTORE_DIR="/tmp/backup-verify-$(date +%Y%m%d-%H%M%S)"
LOG_FILE="/var/log/backup-verify.log"
SLACK_WEBHOOK="${SLACK_WEBHOOK_URL}"
DB_TYPE="${1:-postgresql}" # postgresql or mysql
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"; }
alert() {
log "ALERT: $*"
curl -s -X POST "$SLACK_WEBHOOK" \
-H 'Content-Type: application/json' \
-d "{\"text\":\"BACKUP VERIFY FAILED: $*\"}"
}
cleanup() {
log "Cleaning up $RESTORE_DIR"
rm -rf "$RESTORE_DIR"
if [ "$DB_TYPE" = "postgresql" ]; then
pg_ctlcluster 16 verify stop 2>/dev/null || true
else
mysqladmin -S /tmp/mysql-verify.sock shutdown 2>/dev/null || true
fi
}
trap cleanup EXIT
mkdir -p "$RESTORE_DIR"
if [ "$DB_TYPE" = "postgresql" ]; then
log "Starting PostgreSQL backup verification"
# Restore latest pgBackRest backup to temporary directory
pgbackrest --stanza=production \
--pg1-path="$RESTORE_DIR/pgdata" \
--type=immediate \
--target-action=promote \
restore 2>&1 | tee -a "$LOG_FILE"
if [ ${PIPESTATUS[0]} -ne 0 ]; then
alert "pgBackRest restore failed"
exit 1
fi
# Start PostgreSQL on a different port
pg_ctlcluster 16 verify start -- \
-D "$RESTORE_DIR/pgdata" \
-o "-p 5433" \
-o "-c listen_addresses=127.0.0.1"
sleep 5
# Verify data integrity
TABLES=$(psql -p 5433 -d production_db -t -c \
"SELECT count(*) FROM information_schema.tables WHERE table_schema='public';")
log "Verified $TABLES tables exist"
ROW_CHECK=$(psql -p 5433 -d production_db -t -c \
"SELECT count(*) FROM orders WHERE created_at > now() - interval '7 days';")
log "Recent orders count: $ROW_CHECK"
if [ "$ROW_CHECK" -lt 1 ]; then
alert "PostgreSQL restore has no recent data — possible stale backup"
exit 1
fi
# Run pg_amcheck for corruption detection (PostgreSQL 14+)
pg_amcheck -p 5433 -d production_db --heapallindexed 2>&1 | tee -a "$LOG_FILE"
log "PostgreSQL backup verification PASSED"
else
log "Starting MySQL backup verification"
# Prepare and restore latest XtraBackup
LATEST_FULL=$(ls -td /backups/full-* | head -1)
cp -r "$LATEST_FULL" "$RESTORE_DIR/mysql-data"
xtrabackup --prepare --target-dir="$RESTORE_DIR/mysql-data"
# Start MySQL on a different socket and port
mysqld --datadir="$RESTORE_DIR/mysql-data" \
--socket=/tmp/mysql-verify.sock \
--port=3307 \
--skip-networking=0 \
--bind-address=127.0.0.1 &
sleep 10
# Verify data
TABLES=$(mysql -S /tmp/mysql-verify.sock -e \
"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='production_db';" -sN)
log "Verified $TABLES tables exist"
mysqlcheck -S /tmp/mysql-verify.sock --all-databases --check 2>&1 | tee -a "$LOG_FILE"
log "MySQL backup verification PASSED"
fi
curl -s -X POST "$SLACK_WEBHOOK" \
-H 'Content-Type: application/json' \
-d "{\"text\":\"Backup verification PASSED for $DB_TYPE at $(date)\"}"
log "Verification complete"Planificación de recuperación ante desastres: RTO y RPO
Cada estrategia de respaldo debe diseñarse en torno a dos métricas:Objetivo de tiempo de recuperación (RTO)(cuánto tiempo puede permitirse estar inactivo) yObjetivo de punto de recuperación (RPO): cuántos datos puede permitirse perder. Estos números impulsan todas las decisiones sobre la frecuencia, el método y la arquitectura de las copias de seguridad.
| Escenario | RTO | RPO | Estrategia |
|---|---|---|---|
| Pago de comercio electrónico | < 5 min | 0 (pérdida de datos cero) | Replicación síncrona + archivado WAL continuo + conmutación por error en espera activa |
| Aplicación SaaS | < 30 minutos | < 1 min | Replicación de streaming + archivado continuo WAL-G + conmutación por error automatizada |
| Herramientas internas | < 4 horas | < 1 hora | Diferencial diario + incremental por hora + archivo WAL |
| Análisis/almacén de datos | < 24 horas | < 24 horas | Copia de seguridad completa diaria en almacenamiento en la nube |
| Desarrollo/puesta en escena | < 48 horas | < 1 semana | Copia de seguridad completa semanal |
Para un RPO cero, necesita replicación sincrónica al menos en un modo de espera. Esto agrega latencia a cada transacción de escritura pero garantiza que no se pierda ninguna transacción confirmada. La mayoría de los sistemas de producción aceptan RPO (segundos de pérdida potencial) casi nulo mediante el uso de replicación de transmisión asincrónica con archivado WAL/binlog continuo, lo que evita la penalización de latencia de escritura.
Plantilla de runbook de recuperación ante desastres# DR Runbook: Database Recovery
## Severity Levels
- P1: Complete data loss / corruption — all hands, CEO notified
- P2: Partial data loss / single region down — on-call team + escalation
- P3: Replica failure / backup failure — on-call investigation
## Recovery Procedures
### Scenario A: Primary DB failure, replicas healthy
1. Promote replica to primary (automated via Patroni / orchestrator)
2. Verify application connectivity
3. Re-establish replication from new primary
4. Investigate root cause
### Scenario B: Complete cluster failure, backups intact
1. Provision new database infrastructure
2. Restore latest full backup
3. Apply WAL/binlog to reach latest consistent point
4. Verify data integrity with checksums
5. Update DNS / connection strings
6. Resume application traffic
7. Re-establish backup schedule immediately
### Scenario C: Data corruption (bad migration / SQL injection)
1. Identify exact timestamp of corruption
2. Restore to point-in-time just before corruption
3. Export affected tables from restored copy
4. Merge clean data into production
5. OR: full PITR restore if corruption is widespread
## Contacts
- DBA on-call: [PagerDuty rotation]
- Infrastructure: [PagerDuty rotation]
- VP Engineering: [phone number]
## Validation Checklist
- [ ] Application health checks pass
- [ ] Row counts match expected ranges
- [ ] Recent transactions are present
- [ ] Replication re-established
- [ ] Backup schedule resumed
- [ ] Post-incident review scheduledMonitoreo y alerta de respaldo
Los sistemas de respaldo fallan silenciosamente. La base de datos sigue funcionando, la aplicación sigue atendiendo tráfico y nadie se da cuenta de que las copias de seguridad dejaron de funcionar hace tres semanas, hasta que necesitan una. El seguimiento proactivo es esencial.
# Prometheus alerting rules for backup monitoring
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: backup-alerts
namespace: monitoring
spec:
groups:
- name: database-backups
rules:
- alert: BackupTooOld
expr: |
(time() - backup_last_successful_timestamp_seconds) > 90000
for: 10m
labels:
severity: critical
annotations:
summary: "Database backup is older than 25 hours"
description: "Last successful backup for {{ $labels.database }} was {{ $value | humanizeDuration }} ago"
- alert: BackupJobFailed
expr: |
kube_job_status_failed{job_name=~".*backup.*"} > 0
for: 5m
labels:
severity: critical
annotations:
summary: "Backup CronJob failed: {{ $labels.job_name }}"
- alert: WALArchivingLagging
expr: |
pg_stat_archiver_failed_count > 0
for: 5m
labels:
severity: warning
annotations:
summary: "PostgreSQL WAL archiving has failures"
- alert: BackupStorageQuotaNearing
expr: |
(backup_storage_used_bytes / backup_storage_quota_bytes) > 0.85
for: 30m
labels:
severity: warning
annotations:
summary: "Backup storage at {{ $value | humanizePercentage }} capacity"
- alert: BinlogSpaceCritical
expr: |
mysql_binlog_size_bytes > 53687091200
for: 10m
labels:
severity: warning
annotations:
summary: "MySQL binlog space exceeds 50GB — check archiving"# Custom Prometheus exporter for pgBackRest metrics
#!/usr/bin/env python3
"""pgBackRest Prometheus exporter — exposes backup age and size metrics."""
import json
import subprocess
import time
from prometheus_client import start_http_server, Gauge
BACKUP_AGE = Gauge('pgbackrest_last_backup_age_seconds', 'Seconds since last backup', ['stanza', 'type'])
BACKUP_SIZE = Gauge('pgbackrest_last_backup_size_bytes', 'Size of last backup', ['stanza', 'type'])
BACKUP_REPO_SIZE = Gauge('pgbackrest_repo_size_bytes', 'Total repository size', ['stanza'])
def collect():
result = subprocess.run(
['pgbackrest', '--output=json', 'info'],
capture_output=True, text=True
)
info = json.loads(result.stdout)
for stanza_info in info:
stanza = stanza_info['name']
for backup in stanza_info.get('backup', []):
backup_type = backup['type']
stop_time = backup['timestamp']['stop']
age = time.time() - stop_time
BACKUP_AGE.labels(stanza=stanza, type=backup_type).set(age)
size = backup['info']['size']
BACKUP_SIZE.labels(stanza=stanza, type=backup_type).set(size)
if __name__ == '__main__':
start_http_server(9854)
while True:
collect()
time.sleep(300)Errores comunes y antipatrones
Después de administrar copias de seguridad de bases de datos en docenas de entornos de producción, estos son los errores que causan el mayor daño.
1. Realizar una copia de seguridad en el mismo disco que la base de datos.Si el disco falla, se pierde tanto la base de datos como la copia de seguridad. Escriba siempre las copias de seguridad en un destino de almacenamiento independiente, idealmente fuera del host y fuera de la región.
2. Nunca probar restauraciones.Una copia de seguridad que no se puede restaurar no es una copia de seguridad. Programe pruebas de restauración automatizadas semanalmente y haga que los ingenieros realicen simulacros de restauración manuales trimestralmente.
3. Depender únicamente de la replicación como respaldo.La replicación no es una copia de seguridad. UnDROP TABLEen el principal se replica instantáneamente en todas las réplicas. La replicación protege contra fallas de hardware, no contra errores lógicos.
4. No monitorear trabajos de respaldo.Los trabajos cron fallan silenciosamente. Kubernetes CronJobs se suspenden. Las credenciales de S3 caducan. Cada trabajo de respaldo debe informar el éxito o el fracaso a un sistema de monitoreo y alertar si el respaldo exitoso más reciente es anterior a su RPO.
5. Almacenamiento de claves de cifrado junto con copias de seguridad.Si un atacante obtiene acceso a su almacenamiento de respaldo, no debería tener también la clave de descifrado. Almacene las claves en un administrador de secretos dedicado.
6. Sin política de retención.Sin políticas de ciclo de vida, el almacenamiento de respaldo crece sin límites. Defina ventanas de retención claras: 7 días para segmentos WAL, 30 días para copias de seguridad diarias, 12 meses para copias de seguridad mensuales y automatice su eliminación.
7. Ignorar el impacto en el rendimiento de la copia de seguridad.La ejecución de una copia de seguridad completa en una base de datos principal durante las horas pico degrada el rendimiento de la aplicación. Programe copias de seguridad durante períodos de poco tráfico o realice una copia de seguridad desde una réplica dedicada.
8. Uso demysqldumppara bases de datos grandes sin--single-transaction.Sin este indicador,mysqldumpbloquea las tablas y bloquea las escrituras mientras dure el volcado. Para bases de datos grandes, esto puede significar minutos u horas de inactividad.
9. Olvidar hacer una copia de seguridad de la configuración de la base de datos.Restaurar datos es sólo la mitad de la batalla. Si pierdepostgresql.conf,pg_hba.conf,my.cnf, la configuración de replicación y las concesiones de usuario, no podrá devolver la base de datos a un estado utilizable. Incluya archivos de configuración en su proceso de copia de seguridad.
10. No documentar el procedimiento de restauración.Durante una interrupción, la persona que restaura la base de datos puede no ser la persona que configuró las copias de seguridad. Un runbook escrito y probado es fundamental.
Optimización de costos para almacenamiento de respaldo
Los costos de almacenamiento de respaldo depueden crecer rápidamente, especialmente con respaldos completos frecuentes de grandes bases de datos. Estas estrategias mantienen los costos bajo control sin comprometer la recuperabilidad.
Utilice copias de seguridad incrementales/diferenciales.Un diferencial completo semanal más un diferencial diario utiliza una fracción del almacenamiento en comparación con los respaldos completos diarios. La capacidad de restauración delta de pgBackRest significa que las copias de seguridad incrementales se restauran casi tan rápido como las copias de seguridad completas.
Habilitar la compresión.Los algoritmos de compresión modernos como zstd ofrecen excelentes relaciones de compresión (5:1 a 10:1 para datos de bases de datos típicos) con una sobrecarga mínima de CPU. Tanto pgBackRest como XtraBackup admiten zstd de forma nativa.
Implementar almacenamiento en niveles.Mueva las copias de seguridad a niveles de almacenamiento progresivamente más baratos a medida que envejecen. Las políticas de ciclo de vida mostradas anteriormente (Estándar S3 → IA → Glacier, Estándar GCS → Nearline → Coldline) pueden reducir los costos de almacenamiento a largo plazo entre un 70 % y un 90 %.
Deduplicar siempre que sea posible.pgBackRest utiliza la deduplicación a nivel de bloque en su repositorio, almacenando solo los bloques modificados en las copias de seguridad. Esto reduce drásticamente el almacenamiento de bases de datos donde la mayoría de los datos son estáticos.
Ventanas de retención del tamaño adecuado.Muchos equipos mantienen las copias de seguridad para siempre por precaución. Analice sus patrones de recuperación reales y requisitos de cumplimiento, luego establezca la retención que coincida. Un requisito de retención de 7 años puede utilizar almacenamiento de archivos profundo a menos de $1/TB/mes en la mayoría de los proveedores de nube.
# Cost comparison: Full vs Incremental backup storage
# Assuming 500 GB database, 5% daily change rate, 30-day retention
# Strategy A: Daily full backups
# 500 GB × 30 days = 15,000 GB = 15 TB
# S3 Standard: 15 TB × $0.023/GB = $345/month
# Strategy B: Weekly full + daily incremental
# 4 full × 500 GB = 2,000 GB
# 26 incremental × 25 GB = 650 GB
# Total: 2,650 GB ≈ 2.6 TB
# S3 Standard: 2.6 TB × $0.023/GB = $59.80/month
# Strategy C: Strategy B + lifecycle tiering
# Current week: 525 GB Standard = $12.08
# Weeks 2-4: 2,125 GB Standard-IA = $26.56
# Total: $38.64/month
# Savings: Strategy C is 89% cheaper than Strategy Aponiéndolo todo junto: una arquitectura de respaldo completa
Esta es la arquitectura de respaldo recomendada para un entorno de producción que ejecuta MySQL y PostgreSQL, implementada en Kubernetes con recuperación ante desastres de múltiples nubes.
Para PostgreSQL:
- pgBackRest como herramienta de respaldo principal con dos repositorios (S3 + Azure Blob)
- Archivado WAL continuo en ambos repositorios
- Copia de seguridad completa semanal + diferencial diario (a través de K8s CronJob)
- Instantáneas del volumen Longhorn cada 4 horas para una reversión rápida
- Verificación de restauración automatizada todos los miércoles Monitoreo
- Prometheus con alertas sobre antigüedad, falla y almacenamiento de la copia de seguridad
Para MySQL:
- Percona XtraBackup para copias de seguridad físicas transmitidas a S3
- Envío continuo de registros binarios a almacenamiento separado
- Semanal completo + incremental diario (a través de K8s CronJob)
- Diario
mysqldumpde esquemas críticos para copias de seguridad lógicas portátiles - Instantáneas de volumen de Longhorn cada 4 horas
- Verificación de restauración automatizada todos los jueves
Para el clúster Kubernetes:
- Copia de seguridad diaria de Velero de espacios de nombres de bases de datos con instantáneas de PV
- RKE2/k3s instantáneas etcd cada 6 horas en almacenamiento fuera del clúster Repositorio
- GitOps como fuente de verdad para todos los manifiestos
Para recuperación ante desastres:
- Replicación entre regiones de S3 a la región DR
- Azure GRS para copias de seguridad con redundancia geográfica
- Exploración de DR mensual: reconstrucción completa del clúster a partir de copias de seguridad en la región de DR
- Runbook documentado con árbol de decisión para cada escenario de falla
Conclusión
Las copias de seguridad de bases de datos son la última línea de defensa entre su negocio y una pérdida catastrófica de datos. Las estrategias descritas en este artículo (copias de seguridad físicas y lógicas, archivado continuo de WAL y binlog, almacenamiento en múltiples nubes con políticas de ciclo de vida, cifrado en cada capa, programación automatizada a través de Kubernetes CronJobs y verificación de restauración sistemática) representan el estado actual del arte para los entornos de producción MySQL y PostgreSQL.
La conclusión más importante es la siguiente:una estrategia de copia de seguridad es tan buena como su última prueba de restauración exitosa. Cada herramienta, script y patrón de arquitectura de este artículo existe para cumplir un propósito: garantizar que, cuando suceda lo peor, pueda recuperar sus datos, cumplir con sus compromisos de RTO y RPO y mantener su negocio en funcionamiento. Cree su sistema de respaldo, automatícelo, monitorícelo, pruébelo y luego pruébelo nuevamente. Tu yo futuro te lo agradecerá.