Stratégies de sauvegarde de base de données de production qui fonctionnent réellement : MySQL et PostgreSQL
Stratégies de sauvegarde et de restauration éprouvées pour MySQL et PostgreSQL en production
La perte de données de production est le genre d'incident qui met fin à des carrières, ferme des entreprises et fait la une de Hacker News pour toutes les mauvaises raisons. Pourtant, la plupart des équipes traitent les sauvegardes de bases de données après coup – une tâche cron mise en place il y a deux ans et que personne n’a vérifiée depuis. Cet article présente des stratégies de sauvegarde éprouvées pour MySQL et PostgreSQL qui ont fait leurs preuves dans des environnements de production allant des clusters k3s à nœud unique aux déploiements cloud multirégionaux traitant quotidiennement des millions de transactions.
Nous couvrirons l'ensemble du spectre : méthodes de sauvegarde logiques et physiques, récupération à un moment précis, planification automatisée, cibles de stockage dans le cloud, chiffrement, vérification, approches natives Kubernetes et planification de reprise après sinistre qui relie le tout. Chaque recommandation est accompagnée d'une configuration concrète et d'un code que vous pouvez adapter à votre environnement.
Comprendre les types de sauvegarde
Avant de plonger dans des outils spécifiques, vous avez besoin d'un modèle mental clair des quatre stratégies de sauvegarde fondamentales et de la manière dont elles interagissent avec les objectifs de récupération. Chaque stratégie établit un compromis différent entre la vitesse de sauvegarde, la consommation de stockage et le temps de récupération.
Une sauvegarde complètecapture l'intégralité de la base de données à un moment donné. C’est le plus simple à raisonner et le plus rapide à restaurer, mais aussi le plus coûteux en stockage et le plus lent à créer. Une sauvegarde incrémentiellecapture uniquement les données modifiées depuis la dernière sauvegarde, quel que soit son type. Il est rapide à créer et à compacter le stockage, mais la restauration nécessite de rejouer la chaîne complète : la dernière sauvegarde complète et chaque sauvegarde incrémentielle ultérieure. Une sauvegarde différentiellecapture tout ce qui a changé depuis la dernière sauvegarde complète. Il occupe un juste milieu – plus grand qu’un incrémentiel mais plus simple à restaurer car vous n’avez besoin que du dernier complet plus le dernier différentiel. Enfin, l'archivage continu(archivage WAL dans PostgreSQL, streaming de journaux binaires dans MySQL) capture chaque transaction individuelle au fur et à mesure qu'elle se produit, permettant une récupération à tout moment entre les sauvegardes.
La stratégie optimale pour la plupart des systèmes de production est une combinaison : sauvegardes complètes hebdomadaires, incrémentielles ou différentielles quotidiennes et archivage continu des WAL/binlog. Cela vous offre à la fois une récupération rapide à partir de sauvegardes complètes récentes et la possibilité de restaurer à tout moment lorsque vous en avez besoin.
MySQL Méthodes de sauvegarde
MySQL propose plusieurs outils de sauvegarde, chacun adapté à différentes tailles de bases de données et exigences de récupération. Le bon choix dépend de votre volume de données, de la fenêtre de sauvegarde acceptable et des objectifs RTO/RPO.
mysqldump — La sauvegarde logique universelle
mysqldumpproduit des instructions SQL qui recréent le schéma et les données. Il fonctionne sur toutes les versions et moteurs de stockage de MySQL, ce qui en fait la solution de repli universelle. Cependant, il verrouille les tables pendant le vidage (sauf si vous utilisez--single-transactionavec InnoDB) et la vitesse de restauration se dégrade considérablement pour les bases de données au-delà de 50 à 100 Go, car il relit les instructions INSERT individuelles.
# 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 — Sauvegarde logique parallèle
mysqlpumpaméliore lemysqldumpavec le dumping de table parallèle et la compression intégrée. Cela peut réduire considérablement le temps de sauvegarde des bases de données comportant de nombreuses tables indépendantes.
# 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 — Sauvegardes physiques pour InnoDB
Pour les bases de données de plus de 50 Go, les sauvegardes logiques deviennent peu pratiques : la fenêtre de sauvegarde et le temps de restauration augmentent de manière linéaire avec la taille des données. Percona XtraBackup effectue des copies au niveau physique des fichiers de données InnoDB sans verrouiller la base de données, ce qui le rend adapté aux déploiements de plusieurs téraoctets.
# 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 mysqldJournal binaire MySQL — Récupération ponctuelle
Les journaux binairesMySQL enregistrent chaque instruction de modification de données ou changement de ligne. Lorsqu'ils sont combinés à une sauvegarde complète ou physique, ils permettent une récupération à tout moment après la sauvegarde.
# 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éthodes de sauvegarde
L'écosystème de sauvegarde du PostgreSQL est sans doute plus riche que celui du MySQL, avec des outils propriétaires et un ensemble mature de solutions communautaires spécialement conçues pour les environnements de production à grande échelle.
pg_dump et pg_dumpall — Sauvegardes logiques
Commemysqldump,pg_dumpproduit des sauvegardes logiques. Le format personnalisé (-Fc) est le format par défaut recommandé car il prend en charge la restauration parallèle, la restauration sélective de tables et la compression intégrée.
# 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 — Fondation de sauvegarde physique
pg_basebackupprend une copie physique de l'intégralité du répertoire de données PostgreSQL. Il constitue la base de la configuration de la récupération autonome et de la réplication en streaming. Combiné à l'archivage WAL, il permet une récupération à un moment précis.
# 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 — Gestion des sauvegardes de niveau entreprise
pgBackRest est la référence en matière de gestion des sauvegardes PostgreSQL. Il prend en charge les sauvegardes complètes, incrémentielles et différentielles, la sauvegarde et la restauration parallèles, le chiffrement, les cibles multi-dépôts (disque local, S3, GCS, Azure Blob) et l'archivage WAL automatisé, le tout via une configuration unique et cohérente.
# /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 — Archivage WAL léger vers le stockage cloud
WAL-G est une alternative plus simple à pgBackRest qui se concentre sur le streaming de segments WAL et les sauvegardes de base vers le stockage d'objets cloud. Il est populaire dans les environnements conteneurisés où vous souhaitez un seul binaire avec une configuration minimale.
# 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 — Serveur de sauvegarde centralisé
Barman (Backup and Recovery Manager) est conçu pour les environnements dans lesquels un serveur de sauvegarde dédié gère les sauvegardes de plusieurs instances PostgreSQL. Il prend en charge les protocoles de réplication rsync/SSH et streaming pour le transport de sauvegarde.
# /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/mainRécupération ponctuelle (PITR)
La récupération ponctuelle est la fonctionnalité la plus importante de votre boîte à outils de sauvegarde. Il vous permet de restaurer une base de données dans l'état exact dans lequel elle se trouvait à un moment donné, pas seulement au moment où les sauvegardes ont été effectuées, mais à chaque seconde entre les sauvegardes. Ceci est essentiel pour récupérer après une suppression accidentelle de données, des bogues d’application qui corrompent les données et des incidents de sécurité pour lesquels vous devez identifier le moment exact de la compromission.
PITR pour PostgreSQL
PostgreSQL PITR fonctionne en restaurant une sauvegarde de base et en rejouant les segments WAL jusqu'à l'horodatage cible. Avec pgBackRest, l'ensemble du processus est une seule commande.
# 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 pour MySQL
MySQL PITR combine une restauration physique XtraBackup avec une relecture des journaux binaires.
# 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 rootArchitecture de sauvegarde multi-cloudLes stratégies de sauvegarde de productiondoivent tenir compte de la défaillance d’un seul fournisseur de cloud ou d’une seule région. Le stockage des sauvegardes exclusivement dans le même compte cloud et la même région que votre base de données de production signifie qu'une seule compromission de compte, un problème de facturation ou une panne régionale peut supprimer simultanément vos données et vos sauvegardes. Une architecture robuste envoie des sauvegardes à au moins deux cibles de stockage indépendantes avec réplication entre régions.
Configuration du stockage cloudavec politiques de cycle de vie
# 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}}
]
}
JSONCryptage et sécurité des sauvegardes
Les sauvegardesconstituent une cible de grande valeur pour les attaquants. Une sauvegarde non cryptée volée donne à un attaquant une copie complète de vos données de production sans avoir besoin de violer vos systèmes en cours d'exécution. Chaque sauvegarde, en transit et au repos, doit être chiffrée.
Chiffrement en transitsignifie utiliser TLS pour toutes les connexions entre le serveur de base de données et la destination de sauvegarde. PgBackRest et XtraBackup diffusent tous deux sur des canaux cryptés lorsqu'ils sont configurés avec TLS. Lors de l'expédition vers S3, Azure Blob ou GCS, les SDK clients utilisent HTTPS par défaut.
Chiffrement au reposcomporte deux couches. Le chiffrement côté serveur (SSE) chiffre les données une fois qu'elles arrivent à la cible de stockage : S3 SSE-KMS, Azure Storage Service Encryption ou chiffrement par défaut GCS. Le chiffrement côté client chiffre les données avant qu'elles ne quittent le serveur de base de données, garantissant ainsi que le fournisseur de cloud ne voit jamais les données en texte brut. pgBackRest et WAL-G prennent tous deux en charge le chiffrement côté client de manière native.
# 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)Stockez les clés de chiffrement séparément des sauvegardes elles-mêmes. Un gestionnaire de secrets comme HashiCorp Vault, AWS Secrets Manager ou Azure Key Vault est l'approche recommandée. Si vous stockez la clé avec la sauvegarde, un attaquant qui accède à votre stockage de sauvegarde dispose de tout ce dont il a besoin.
Planification automatisée des sauvegardes
Les sauvegardes manuellesne sont pas des sauvegardes, ce sont des aspirations. Les planifications de sauvegarde doivent être automatisées, surveillées et alerter en cas d'échec.
Système Cron pour Bare Metal et 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 Travaux Cron
Dans les environnements Kubernetes, les CronJobs remplacent le cron du système. Ils offrent une logique de nouvelle tentative intégrée, un contrôle de concurrence et une intégration avec RBAC et la gestion des secrets du cluster.
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-La sauvegarde native s'approche du
L'exécution de bases de données dans Kubernetes introduit une nouvelle dimension dans la stratégie de sauvegarde. Outre les sauvegardes de base de données au niveau de l'application, vous devez envisager la sauvegarde au niveau du cluster (etcd), les instantanés de volumes persistants et les sauvegardes gérées par l'opérateur.
Velero pour la sauvegarde au niveau du cluster
Velero sauvegarde les ressources Kubernetes (déploiements, services, cartes de configuration, secrets) et les volumes persistants. Il ne remplace pas les sauvegardes de bases de données au niveau des applications : il capture un instantané des PV cohérent en cas de panne, qui peut ne pas être cohérent sur le plan transactionnel pour les bases de données. Utilisez Velero pour la récupération de cluster et les outils au niveau de l'application (pgBackRest, XtraBackup) pour la récupération de base de données.
# 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 databaseLonghorn Instantanés sur k3s
Les déploiementsk3s utilisent généralement Longhorn comme fournisseur de stockage CSI. Longhorn fournit des instantanés au niveau du volume et la possibilité de répliquer des instantanés vers un stockage d'objets compatible S3. Pour les bases de données, combinez des instantanés Longhorn avec des sauvegardes au niveau des applications pour une défense en profondeur.
# 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-0Sauvegardes gérées par l'opérateur
Les opérateurs de bases de donnéescomme CloudNativePG (pour PostgreSQL) et Percona Operator (pour MySQL) intègrent la gestion des sauvegardes directement dans le cycle de vie de la base de données. L'opérateur gère automatiquement la planification, l'archivage des WAL et la conservation via des ressources personnalisées.
# 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-pgStratégies spécifiques au fournisseur de cloud
AWS — RDS et Aurora
AWS RDS fournit des instantanés quotidiens automatisés avec une conservation configurable (jusqu'à 35 jours) et une sauvegarde continue via l'archivage du journal des transactions pour une récupération à un moment précis. Aurora ajoute Backtrack, qui peut rembobiner le cluster à n'importe quel point de la fenêtre de retour en arrière sans nécessiter de restauration : il inverse simplement l'état interne de la base de données.
# 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 — Serveur flexible
La base de donnéesAzure pour serveur flexible PostgreSQL et MySQL comprend des sauvegardes automatiques avec un stockage localement redondant ou géoredondant. La sauvegarde géoredondante permet une restauration inter-régions pour la reprise après sinistre.
# 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 — Cloud SQL
Cloud SQL fournit des sauvegardes automatisées et une récupération ponctuelle prêtes à l'emploi. Pour les bases de données autogérées sur GCE ou GKE, GCS avec classes de stockage Nearline et Coldline fournit un stockage de sauvegarde à long terme rentable.
# 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 \
--offloadVérification des sauvegardes et tests de restauration
Une sauvegarde qui n'a jamais été testée n'est pas une sauvegarde, c'est un espoir. Les tests de restauration automatisés doivent être exécutés selon un calendrier régulier, idéalement une fois par semaine, dans un environnement isolé. Le test doit valider non seulement que la restauration s'effectue sans erreur, mais aussi que les données restaurées sont cohérentes et que l'application peut se connecter et les interroger.
#!/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"Planification de reprise après sinistre du: RTO et RPO
Chaque stratégie de sauvegarde doit être conçue autour de deux mesures :Objectif de temps de récupération (RTO)– combien de temps vous pouvez vous permettre d'être en panne – etObjectif de point de récupération (RPO)– quantité de données que vous pouvez vous permettre de perdre. Ces chiffres déterminent chaque décision concernant la fréquence, la méthode et l’architecture des sauvegardes.
| Scénario | RTO | RPO | Stratégie |
|---|---|---|---|
| Paiement du commerce électronique | < 5 min | 0 (aucune perte de données) | Réplication synchrone + archivage WAL continu + basculement en veille chaude |
| Application SaaS | < 30 minutes | < 1 min | Réplication streaming + archivage continu WAL-G + basculement automatisé |
| Outils internes | < 4 heures | < 1 heure | Différentiel journalier + incrémental horaire + archivage WAL |
| Analyses/entrepôt de données | < 24 heures | < 24 heures | Sauvegarde complète quotidienne sur le stockage cloud |
| Développement/mise en scène | < 48 heures | < 1 semaine | Sauvegarde complète hebdomadaire |
Pour un RPO nul, vous avez besoin d'une réplication synchrone vers au moins un serveur de secours. Cela ajoute de la latence à chaque transaction d'écriture mais garantit qu'aucune transaction validée n'est perdue. La plupart des systèmes de production acceptent un RPO (secondes de perte potentielle) proche de zéro en utilisant une réplication en streaming asynchrone avec un archivage continu des WAL/binlog, ce qui évite la pénalité de latence en écriture.
Modèle de runbook de récupération après sinistre# 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 scheduledSurveillance des sauvegardes et alertes
Les systèmes de sauvegardeéchouent silencieusement. La base de données continue de fonctionner, l'application continue de gérer le trafic et personne ne remarque que les sauvegardes ont cessé de fonctionner il y a trois semaines, jusqu'à ce qu'ils en aient besoin. Une surveillance proactive est essentielle.
# 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)Erreurs courantes et anti-modèles
Après avoir géré les sauvegardes de bases de données dans des dizaines d'environnements de production, ce sont les erreurs qui causent le plus de dégâts.
1. Sauvegarde sur le même disque que la base de données.Si le disque tombe en panne, vous perdez à la fois la base de données et la sauvegarde. Écrivez toujours les sauvegardes sur une cible de stockage distincte, idéalement hors hôte et hors région.
2. Ne testez jamais les restaurations.Une sauvegarde qui ne peut pas être restaurée n'est pas une sauvegarde. Planifiez des tests de restauration automatisés chaque semaine et demandez aux ingénieurs d'effectuer des exercices de restauration manuelle tous les trimestres.
3. S'appuyer uniquement sur la réplication comme sauvegarde. La réplicationn'est pas une sauvegarde. UnDROP TABLEsur le serveur principal est instantanément répliqué sur toutes les répliques. La réplication protège contre les pannes matérielles et non contre les erreurs logiques.
4. Ne pas surveiller les tâches de sauvegarde. Les tâches Cronéchouent silencieusement. Les CronJobs Kubernetes sont suspendus. Les informations d'identification S3 expirent. Chaque tâche de sauvegarde doit signaler le succès ou l'échec à un système de surveillance et alerter si la sauvegarde réussie la plus récente est plus ancienne que votre RPO.
5. Stockage des clés de cryptage avec les sauvegardes.Si un attaquant accède à votre stockage de sauvegarde, il ne devrait pas également disposer de la clé de déchiffrement. Stockez les clés dans un gestionnaire de secrets dédié.
6. Aucune politique de rétention.Sans politiques de cycle de vie, le stockage de sauvegarde croît sans limite. Définissez des fenêtres de conservation claires : 7 jours pour les segments WAL, 30 jours pour les sauvegardes quotidiennes, 12 mois pour les sauvegardes mensuelles et automatisez leur suppression.
7. Ignorer l'impact sur les performances de sauvegarde.L'exécution d'une sauvegarde complète sur une base de données principale pendant les heures de pointe dégrade les performances de l'application. Planifiez des sauvegardes pendant les fenêtres à faible trafic ou sauvegardez à partir d'une réplique dédiée.
8. Utilisation demysqldumppour les grandes bases de données sans--single-transaction.Sans cet indicateur,mysqldumpverrouille les tables, bloquant les écritures pendant la durée du dump. Pour les bases de données volumineuses, cela peut signifier des minutes ou des heures d’indisponibilité.
9. Oubli de sauvegarder la configuration de la base de données.La restauration des données ne représente que la moitié de la bataille. Si vous perdezpostgresql.conf,pg_hba.conf,my.cnf, les paramètres de réplication et les autorisations utilisateur, vous ne pouvez pas remettre la base de données dans un état utilisable. Incluez les fichiers de configuration dans votre processus de sauvegarde.
10. Ne pas documenter la procédure de restauration.Lors d'une panne, la personne qui restaure la base de données peut ne pas être celle qui a configuré les sauvegardes. Un runbook écrit et testé est essentiel.
Optimisation des coûts pour le stockage de sauvegarde
Les coûts de stockage de sauvegarde dupeuvent augmenter rapidement, en particulier avec des sauvegardes complètes fréquentes de bases de données volumineuses. Ces stratégies permettent de maîtriser les coûts sans compromettre la recouvrabilité.
Utiliser des sauvegardes incrémentielles/différentielles.Une sauvegarde complète hebdomadaire plus différentielle quotidienne utilise une fraction du stockage par rapport aux sauvegardes complètes quotidiennes. La capacité de restauration delta de pgBackRest signifie que les sauvegardes incrémentielles sont restaurées presque aussi rapidement que les sauvegardes complètes.
Activer la compression.Les algorithmes de compression modernes comme zstd offrent d'excellents taux de compression (5:1 à 10:1 pour les données de base de données typiques) avec une surcharge CPU minimale. pgBackRest et XtraBackup prennent en charge zstd de manière native.
Implémentez la hiérarchisation du stockage.Déplacez les sauvegardes vers des niveaux de stockage de moins en moins chers à mesure qu'elles vieillissent. Les politiques de cycle de vie présentées précédemment (S3 Standard → IA → Glacier, GCS Standard → Nearline → Coldline) peuvent réduire les coûts de stockage à long terme de 70 à 90 %.
Dédupliquer si possible.pgBackRest utilise la déduplication au niveau des blocs dans son référentiel, stockant uniquement les blocs modifiés dans les sauvegardes. Cela réduit considérablement le stockage des bases de données où la majorité des données sont statiques.
Fenêtres de rétention de bonne taille.De nombreuses équipes conservent par défaut les sauvegardes pour toujours, par prudence. Analysez vos modèles de récupération réels et vos exigences de conformité, puis définissez une rétention adaptée. Une exigence de conservation de 7 ans permet d'utiliser un stockage d'archives approfondi à moins de 1 $/To/mois sur la plupart des fournisseurs de cloud.
# 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 ARassembler le tout : une architecture de sauvegarde complète
Voici l'architecture de sauvegarde recommandée pour un environnement de production exécutant à la fois MySQL et PostgreSQL, déployée sur Kubernetes avec reprise après sinistre multi-cloud.
Pour PostgreSQL :
- pgBackRest comme outil de sauvegarde principal avec deux référentiels (S3 + Azure Blob)
- Archivage WAL continu dans les deux référentiels
- Sauvegarde complète hebdomadaire + différentiel quotidien (via K8s CronJob)
- Instantanés de volume Longhorn toutes les 4 heures pour une restauration rapide
- Vérification automatisée de la restauration tous les mercredis
- Surveillance Prometheus avec alertes sur l'âge des sauvegardes, les pannes et le stockage
Pour MySQL :
- Percona XtraBackup pour les sauvegardes physiques diffusées sur S3
- Envoi continu de journaux binaires vers un stockage séparé
- Hebdomadaire complet + incrémentiel quotidien (via K8s CronJob)
mysqldumpquotidien des schémas critiques pour les sauvegardes logiques portables- Instantanés de volume Longhorn toutes les 4 heures
- Vérification automatisée de la restauration tous les jeudis
Pour le cluster Kubernetes :
- Sauvegarde quotidienne Velero des espaces de noms de base de données avec instantanés PV
- RKE2/k3s etcd instantanés toutes les 6 heures vers le stockage hors cluster Dépôt
- GitOps comme source de vérité pour tous les manifestes
Pour la reprise après sinistre :
- Réplication inter-région
- S3 vers la région DR
- Azure GRS pour copies de sauvegarde géoredondantes
- Exercice DR mensuel : reconstruction complète du cluster à partir des sauvegardes dans la région DR
- Runbook documenté avec arbre de décision pour chaque scénario de défaillance
Conclusion
Les sauvegardes de bases de donnéesconstituent la dernière ligne de défense entre votre entreprise et une perte de données catastrophique. Les stratégies décrites dans cet article — sauvegardes physiques et logiques, archivage continu des WAL et des binlogs, stockage multi-cloud avec politiques de cycle de vie, chiffrement à chaque couche, planification automatisée via Kubernetes CronJobs et vérification systématique des restaurations — représentent l'état actuel de l'art pour les environnements de production MySQL et PostgreSQL.
Le point à retenir le plus important est le suivant : une stratégie de sauvegarde pourest aussi efficace que son dernier test de restauration réussi. Chaque outil, script et modèle d'architecture présenté dans cet article existe dans un seul but : garantir que, lorsque le pire se produit, vous puissiez récupérer vos données, respecter vos engagements RTO et RPO et maintenir votre entreprise en activité. Créez votre système de sauvegarde, automatisez-le, surveillez-le, testez-le, puis testez-le à nouveau. Votre futur moi vous remerciera.