Haute disponibilité MySQL en production : cluster InnoDB, réplication de groupe et opérateurs Kubernetes
Un guide de l'ingénieur de production sur InnoDB Cluster, Group Replication, MySQL Operator pour Kubernetes, Percona XtraDB Cluster, les stratégies multi-cloud et les déploiements nus k3s/Rancher avec stockage Longhorn
Exécuter MySQL en production est un territoire familier pour la plupart des équipes d'ingénierie. L’exécuter avec une véritable haute disponibilité – où une panne de nœud, une partition réseau ou l’obscurité d’une zone de disponibilité entière n’entraîne pas de temps d’arrêt ou de perte de données – nécessite une architecture délibérée. Ce guide passe en revue chaque couche de cette architecture : depuis les primitives de réplication au sein de MySQL lui-même, en passant par les opérateurs Kubernetes qui automatisent la gestion du cycle de vie, en passant par les options gérées et autogérées sur chaque principal fournisseur de cloud, et jusqu'aux clusters k3s nus gérés par Rancher.
À la fin de cet article, vous disposerez d'un modèle mental complet pour choisir et utiliser MySQL HA en production, ainsi que d'exemples de configuration concrets que vous pourrez adapter à votre propre environnement.
MySQL Architecture de cluster InnoDB
InnoDB Cluster est la solution haute disponibilité intégrée d'Oracle pour MySQL. Il combine trois composants : MySQL Group Replication pour la synchronisation des données, MySQL Shell pour l'administration du cluster et MySQL Router pour le routage transparent des connexions et le basculement automatique. Ensemble, ils forment un cluster d'auto-réparation qui peut tolérer les pannes de nœuds sans intervention manuelle.
L'architecture est élégante dans sa simplicité. Les applications se connectent au routeur MySQL, qui maintient la connaissance de la topologie du cluster en interrogeant le schéma de métadonnées du cluster InnoDB. Lorsque le serveur principal échoue, la réplication de groupe choisit un nouveau serveur principal parmi les secondaires restants et le routeur MySQL redirige automatiquement le trafic d'écriture vers le nouveau serveur principal, généralement en quelques secondes. Le trafic de lecture peut être réparti sur tous les secondaires pour une mise à l'échelle horizontale de la lecture.
Principes fondamentaux de la réplication de groupeLa réplication de groupeMySQL est la base du cluster InnoDB. Il utilise un protocole de consensus basé sur Paxos pour garantir que chaque transaction validée sur le nœud principal est répliquée sur une majorité de nœuds avant d'être reconnue. Cela fournit une réplication synchrone virtuelle— une garantie que les données validées existent sur au moins la majorité des membres du cluster au moment de la validation.
La réplication de groupefonctionne selon deux modes :
- Mode à primaire unique— Un nœud accepte les écritures (le nœud principal) ; tous les autres sont des secondaires en lecture seule. Il s'agit du mode recommandé et par défaut. Cela évite entièrement les conflits d’écriture car un seul nœud peut générer des transactions.
- Mode multi-primaire— Tous les nœuds acceptent les écritures simultanément. Cela offre un débit d'écriture plus élevé pour les charges de travail qui partitionnent proprement entre différentes tables ou espaces de clés, mais cela introduit la possibilité de conflits de certification lorsque des transactions simultanées modifient les mêmes lignes. Les transactions en conflit sont annulées sur un nœud. Utilisez plusieurs primaires uniquement lorsque votre application est conçue pour gérer les échecs de certification et la logique de nouvelle tentative.
Configuration du cluster InnoDB avec MySQL Shell
MySQL Shell fournit AdminAPI, un ensemble de fonctions qui automatisent l'ensemble du cycle de vie du cluster. Voici la séquence de configuration complète pour un cluster à 3 nœuds.
# Step 1: Prepare each MySQL instance (run on all 3 nodes)
# Ensure my.cnf has required settings
[mysqld]
server-id=1 # unique per node: 1, 2, 3
gtid_mode=ON
enforce_gtid_consistency=ON
binlog_checksum=NONE
binlog_transaction_dependency_tracking=WRITESET
transaction_write_set_extraction=XXHASH64
loose-group_replication_start_on_boot=OFF
plugin_load_add='group_replication.so'
plugin_load_add='mysql_clone.so'
report_host='mysql-node-1' # unique per node
# Step 2: Use MySQL Shell to configure and create the cluster
mysqlsh -- dba configure-instance root@mysql-node-1:3306 --clusterAdmin=gradmin --clusterAdminPassword='SecurePass123!'
mysqlsh -- dba configure-instance root@mysql-node-2:3306 --clusterAdmin=gradmin --clusterAdminPassword='SecurePass123!'
mysqlsh -- dba configure-instance root@mysql-node-3:3306 --clusterAdmin=gradmin --clusterAdminPassword='SecurePass123!'
# Step 3: Connect to the first node and create the cluster
mysqlsh gradmin@mysql-node-1:3306
# Inside MySQL Shell:
var cluster = dba.createCluster('productionCluster', {
memberWeight: 90,
exitStateAction: 'ABORT_SERVER',
consistency: 'BEFORE_ON_PRIMARY_FAILOVER',
expelTimeout: 10
})
# Step 4: Add remaining nodes
cluster.addInstance('gradmin@mysql-node-2:3306', {recoveryMethod: 'clone'})
cluster.addInstance('gradmin@mysql-node-3:3306', {recoveryMethod: 'clone'})
# Step 5: Verify cluster status
cluster.status()
L'optionrecoveryMethod: 'clone'utilise le plug-in de clonage MySQL pour effectuer une copie complète des données du membre principal vers le membre joignant, ce qui est beaucoup plus rapide que la récupération incrémentielle à partir de journaux binaires pour de grands ensembles de données.
MySQL
Le routeurMySQL est démarré sur le cluster et génère automatiquement son fichier de configuration.
# Bootstrap MySQL Router against the cluster
mysqlrouter --bootstrap gradmin@mysql-node-1:3306 \
--directory /opt/mysqlrouter \
--conf-use-sockets \
--user=mysqlrouter \
--name='production-router'
# Start MySQL Router
/opt/mysqlrouter/start.sh
# Default ports after bootstrap:
# 6446 — R/W (routes to primary)
# 6447 — R/O (round-robin across secondaries)
# 6448 — R/W (X Protocol)
# 6449 — R/O (X Protocol)
Les applicationsse connectent au port R/W du routeur pour les écritures et au port R/O pour les répliques en lecture. Lorsque le routeur principal tombe en panne, le routeur détecte le changement de topologie et réachemine en quelques secondes.
Déploiement MySQL multirégionPour la reprise après sinistre et les lectures à faible latence dans toutes les zones géographiques, MySQL peut être déployé dans plusieurs régions. InnoDB ClusterSet étend InnoDB Cluster pour prendre en charge la réplication asynchrone entre un cluster principal et un ou plusieurs clusters de répliques dans différentes régions.
InnoDB ClusterSet fonctionne avec un cluster principal qui gère toutes les écritures et un ou plusieurs clusters de répliques qui reçoivent les modifications de manière asynchrone. En cas de catastrophe, un cluster de réplique peut être promu au rang principal via MySQL Shell.
# Create a ClusterSet from an existing InnoDB Cluster
mysqlsh gradmin@mysql-node-1:3306
var cluster = dba.getCluster()
var cs = cluster.createClusterSet('globalSet')
# Create a replica cluster in eu-west region
cs.createReplicaCluster('gradmin@mysql-eu-node-1:3306', 'euWestCluster', {
recoveryMethod: 'clone'
})
var euCluster = cs.getCluster('euWestCluster')
euCluster.addInstance('gradmin@mysql-eu-node-2:3306', {recoveryMethod: 'clone'})
euCluster.addInstance('gradmin@mysql-eu-node-3:3306', {recoveryMethod: 'clone'})
# Emergency failover (when primary cluster is unreachable)
cs.forcePrimaryCluster('euWestCluster')
MySQL sur Kubernetes — Modèle d'opérateur
L'exécution de MySQL sur Kubernetes nécessite de résoudre plusieurs défis qui ne s'appliquent pas aux charges de travail sans état : identités réseau stables, stockage persistant, démarrage et arrêt ordonnés et vérifications de l'état prenant en compte le cluster. Les opérateurs Kubernetes codent ces connaissances opérationnelles spécifiques au domaine dans un contrôleur qui surveille les ressources personnalisées et rapproche l'état réel de MySQL avec l'état souhaité déclaré dans YAML.
Opérateur MySQL pour Kubernetes (Oracle)
L'opérateur MySQL d'Oracle pour Kubernetes déploie et gère les instances de cluster InnoDB de manière native sur Kubernetes. Il crée des StatefulSets pour les pods de serveur MySQL, des déploiements pour le routeur MySQL et gère le basculement, la mise à l'échelle, la sauvegarde et les modifications de configuration automatisés.
# Install the MySQL Operator via Helm
helm repo add mysql-operator https://mysql.github.io/mysql-operator/
helm repo update
helm install mysql-operator mysql-operator/mysql-operator \
--namespace mysql-operator \
--create-namespace \
--set image.pullPolicy=IfNotPresent
Une fois l'opérateur exécuté, déployez un cluster InnoDB en créant une ressource personnalisée.
apiVersion: v1
kind: Secret
metadata:
name: mysql-root-credentials
namespace: production
stringData:
rootUser: root
rootHost: '%'
rootPassword: 'ProductionSecurePass123!'
---
apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
name: production-mysql
namespace: production
spec:
secretName: mysql-root-credentials
instances: 3
tlsUseSelfSigned: true
router:
instances: 2
datadirVolumeClaimTemplate:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Gi
storageClassName: gp3-encrypted
mycnf: |
[mysqld]
innodb_buffer_pool_size=4G
innodb_log_file_size=1G
innodb_flush_log_at_trx_commit=1
sync_binlog=1
max_connections=500
innodb_io_capacity=2000
innodb_io_capacity_max=4000
innodb_read_io_threads=8
innodb_write_io_threads=8
performance_schema=ON
slow_query_log=ON
long_query_time=1
podSpec:
containers:
- name: mysql
resources:
requests:
cpu: "2"
memory: 8Gi
limits:
cpu: "4"
memory: 16Gi
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: component
operator: In
values: [mysqld]
topologyKey: topology.kubernetes.io/zone
La règle d'anti-affinité des pods garantit que les pods MySQL sont répartis sur les zones de disponibilité, offrant ainsi une tolérance aux pannes au niveau de la zone. Le blocmycnfvous permet d'injecter une configuration MySQL adaptée à la production directement via le CR.
Opérateur Percona pour MySQL (PXC)
Percona Operator pour MySQL déploie Percona XtraDB Cluster (PXC), une solution de réplication synchrone multi-primaire basée sur Galera. PXC diffère d'InnoDB Cluster en ce sens que chaque nœud peut accepter des écritures (véritable multi-primaire) et que la réplication est synchrone au niveau de la certification à l'aide du wsrep API.
# Install Percona Operator
helm repo add percona https://percona.github.io/percona-helm-charts/
helm repo update
helm install pxc-operator percona/pxc-operator \
--namespace pxc \
--create-namespace
# Percona XtraDB Cluster Custom Resource
apiVersion: pxc.percona.com/v1
kind: PerconaXtraDBCluster
metadata:
name: production-pxc
namespace: pxc
spec:
crVersion: '1.14.0'
secretsName: pxc-secrets
pxc:
size: 3
image: percona/percona-xtradb-cluster:8.0.35
resources:
requests:
memory: 8Gi
cpu: "2"
limits:
memory: 16Gi
cpu: "4"
volumeSpec:
persistentVolumeClaim:
storageClassName: gp3-encrypted
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 200Gi
affinity:
antiAffinityTopologyKey: topology.kubernetes.io/zone
configuration: |
[mysqld]
innodb_buffer_pool_size=4G
innodb_flush_log_at_trx_commit=1
wsrep_provider_options="gcache.size=2G; gcs.fc_limit=256"
wsrep_slave_threads=8
wsrep_certify_nonPK=1
wsrep_trx_fragment_size=10M
max_connections=500
haproxy:
enabled: true
size: 3
image: percona/haproxy:2.8.5
resources:
requests:
memory: 1Gi
cpu: 500m
proxysql:
enabled: false
backup:
image: percona/percona-xtradb-cluster-operator:1.14.0-pxc8.0-backup-pxb8.0.35
storages:
s3-backup:
type: s3
verifyTLS: true
s3:
bucket: production-mysql-backups
region: us-east-1
credentialsSecret: aws-s3-credentials
schedule:
- name: daily-full
schedule: "0 3 * * *"
keep: 7
storageName: s3-backup
- name: hourly-incremental
schedule: "0 * * * *"
keep: 24
storageName: s3-backup
L'opérateur Percona gère les sauvegardes automatisées vers S3, la récupération ponctuelle, les mises à niveau progressives et ProxySQL ou HAProxy pour le routage des connexions. La réplication basée sur Galera dans PXC fournit de véritables écritures multi-primaires synchrones : chaque transaction validée est garantie d'exister sur tous les nœuds.
Routage de connexion: routeur MySQL vs ProxySQL
Le routeur MySQL et le ProxySQL servent tous deux de proxy de base de données, mais ils ont des atouts différents.
Le routeur MySQLest spécialement conçu pour le cluster InnoDB. Il lit les métadonnées du cluster, suit les modifications de topologie et achemine les connexions vers le bon primaire ou secondaire. Sa configuration est minimale et s'intègre parfaitement à l'écosystème MySQL. L’inconvénient est le routage limité au niveau des requêtes : il fonctionne au niveau de la connexion et non au niveau de la requête.
ProxySQLest un proxy MySQL à usage général doté de fonctionnalités avancées : répartition lecture/écriture au niveau des requêtes, mise en cache des requêtes, multiplexage des connexions, réécriture des requêtes et règles de routage sophistiquées. Il excelle dans les environnements où vous avez besoin d’un contrôle précis sur la manière dont les requêtes sont distribuées.
# ProxySQL configuration for read/write splitting
# proxysql.cnf
mysql_servers:
(
{ hostgroup_id=10, hostname="mysql-primary", port=3306, max_connections=200, weight=1000 },
{ hostgroup_id=20, hostname="mysql-secondary-1", port=3306, max_connections=200, weight=500 },
{ hostgroup_id=20, hostname="mysql-secondary-2", port=3306, max_connections=200, weight=500 }
)
mysql_query_rules:
(
{ rule_id=1, active=1, match_digest="^SELECT .* FOR UPDATE$", destination_hostgroup=10, apply=1 },
{ rule_id=2, active=1, match_digest="^SELECT", destination_hostgroup=20, apply=1 },
{ rule_id=3, active=1, match_digest=".*", destination_hostgroup=10, apply=1 }
)
mysql_replication_hostgroups:
(
{ writer_hostgroup=10, reader_hostgroup=20, comment="InnoDB Cluster" }
)
Dans les environnements Kubernetes, ProxySQL peut s'exécuter en tant que conteneur side-car dans vos pods d'application ou en tant que déploiement dédié. L'exécuter en tant que side-car élimine les sauts de réseau mais augmente l'utilisation des ressources du pod ; un déploiement dédié est plus facile à gérer à grande échelle.
Comparaison des fournisseurs de cloud:
géré et autogéréAWS : RDS Multi-AZ vs Aurora vs autogéré sur EKS
Amazon RDS Multi-AZfournit un basculement automatique entre une instance principale et une instance de secours dans différentes zones de disponibilité. Le basculement prend généralement entre 60 et 120 secondes. Il utilise la réplication physique synchrone vers le serveur de secours. Des réplicas en lecture peuvent être ajoutés pour la mise à l'échelle en lecture, mais ils utilisent la réplication asynchrone. RDS gère les sauvegardes, les correctifs et la surveillance, mais limite votre contrôle sur la configuration et le choix de version de MySQL.
Amazon Aurora MySQLest une réécriture cloud native du moteur de stockage MySQL. Il sépare le calcul du stockage : la couche de stockage est un système distribué et tolérant aux pannes qui réplique les données de six manières sur trois zones de disponibilité. Aurora offre un basculement en moins de 10 secondes, jusqu'à 15 réplicas en lecture avec un délai de réplication minimal et une mise à l'échelle automatique du stockage jusqu'à 128 TiB. Aurora Serverless v2 ajoute une mise à l'échelle automatique du calcul pour les charges de travail imprévisibles. Le compromis est le coût (Aurora est 20 à 40 % plus cher que RDS) et une compatibilité réduite avec certaines fonctionnalités du MySQL.
autogéré sur EKSvous offre un contrôle total sur la version, la configuration et la topologie de réplication de MySQL. Utilisez-le lorsque vous avez besoin de fonctionnalités MySQL spécifiques non disponibles dans les services gérés, lorsque vous avez besoin d'une portabilité multi-cloud ou lorsque l'optimisation des coûts à grande échelle justifie les frais opérationnels.
# EKS StorageClass for MySQL with gp3 volumes
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp3-encrypted
provisioner: ebs.csi.aws.com
parameters:
type: gp3
iops: "6000"
throughput: "250"
encrypted: "true"
fsType: ext4
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain
allowVolumeExpansion: true
Azure : serveur flexible ou autogéré sur AKS
Base de données Azure pour serveur flexible MySQLoffre une haute disponibilité redondante entre zones avec basculement automatique, des réplicas en lecture dans la même région et jusqu'à 16 To de stockage. Il prend en charge des fenêtres de maintenance configurables, la possibilité d'arrêter/démarrer des instances (utile pour réaliser des économies sur les coûts de développement/test) et l'intégration avec Azure Private Link pour l'isolation du réseau. Le niveau Business Critical offre les meilleures performances avec le stockage SSD local.
autogéré sur AKSutilise des disques gérés Azure (Premium SSD v2 recommandé pour les charges de travail de base de données) avec l'opérateur MySQL ou l'opérateur Percona. AKS fournit la prise en charge des zones de disponibilité et le Azure CNI pour l'intégration VNET au niveau du pod.
# AKS StorageClass for MySQL
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: premium-ssd-zrs
provisioner: disk.csi.azure.com
parameters:
skuName: Premium_ZRS
cachingMode: None
DiskIOPSReadWrite: "5000"
DiskMBpsReadWrite: "200"
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain
allowVolumeExpansion: true
Google Cloud : Cloud SQL vs autogéré sur GKE
Google Cloud SQL pour MySQLpropose des instances régionales avec basculement automatique entre zones, réplicas en lecture (y compris entre régions) et sauvegardes automatisées avec récupération à un moment donné. Cloud SQL offre le plus haut niveau de commodité de gestion avec l'intégration à l'écosystème IAM, VPC et de surveillance de Google. Le niveau Enterprise Plus ajoute une maintenance avec des temps d'arrêt quasi nuls et un cache de données pour des performances de lecture améliorées.
autogéré sur GKEutilise un disque SSD persistant ou un hyperdisque pour le stockage. GKE Autopilot simplifie la gestion des nœuds et peut exécuter l'opérateur MySQL avec une surcharge opérationnelle minimale.
# GKE StorageClass for MySQL
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ssd-regional
provisioner: pd.csi.storage.gke.io
parameters:
type: pd-ssd
replication-type: regional-pd
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain
allowVolumeExpansion: true
métal nu : k3s + Rancher + Longhorn
Toutes les charges de travail n'ont pas leur place dans le cloud public. Pour les exigences de souveraineté des données, de conformité, d'optimisation des coûts ou de latence, les clusters Kubernetes nus exécutant k3s avec gestion Rancher et stockage Longhorn fournissent une plate-forme de niveau production pour MySQL HA.
k3s Installation et configuration
k3s est une distribution Kubernetes légère et certifiée, idéale pour le Edge et le métal nu. Il regroupe tous les composants du plan de contrôle dans un seul binaire et utilise SQLite ou etcd intégré pour le stockage d'état.
# Install k3s server (first control-plane node)
curl -sfL https://get.k3s.io | sh -s - server \
--cluster-init \
--tls-san 10.10.0.10 \
--disable traefik \
--disable servicelb \
--write-kubeconfig-mode 644
# Join additional server nodes
curl -sfL https://get.k3s.io | K3S_TOKEN=$(cat /var/lib/rancher/k3s/server/node-token) sh -s - server \
--server https://10.10.0.10:6443 \
--tls-san 10.10.0.10
# Join worker nodes
curl -sfL https://get.k3s.io | K3S_URL=https://10.10.0.10:6443 \
K3S_TOKEN=$(cat /var/lib/rancher/k3s/server/node-token) sh -s -
Rangement Longhorn pour MySQL
Longhorn est un système de stockage en bloc distribué cloud natif conçu pour Kubernetes. Il réplique les données sur les nœuds, fournit des instantanés et des sauvegardes et s'intègre nativement au pilote Kubernetes CSI. Pour MySQL, Longhorn fournit la couche de stockage persistante et répliquée que les fournisseurs de cloud gèrent avec leurs offres de disques gérés.
# Install Longhorn
helm repo add longhorn https://charts.longhorn.io
helm repo update
helm install longhorn longhorn/longhorn \
--namespace longhorn-system \
--create-namespace \
--set defaultSettings.defaultReplicaCount=3 \
--set defaultSettings.storageMinimalAvailablePercentage=15 \
--set defaultSettings.guaranteedInstanceManagerCPU=12
# StorageClass for MySQL on Longhorn
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: longhorn-mysql
provisioner: driver.longhorn.io
parameters:
numberOfReplicas: "3"
staleReplicaTimeout: "2880"
dataLocality: best-effort
diskSelector: ssd
fsType: ext4
reclaimPolicy: Retain
volumeBindingMode: Immediate
allowVolumeExpansion: true
MetalLB pour l'équilibrage de charge
Sur bare metal, il n'y a pas d'équilibreur de charge cloud. MetalLB comble cette lacune en attribuant des adresses IP externes aux services Kubernetes de type LoadBalancer en utilisant le mode Layer 2 (ARP) ou BGP.
# MetalLB IP Address Pool and L2 Advertisement
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: mysql-pool
namespace: metallb-system
spec:
addresses:
- 10.10.0.100-10.10.0.110
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: mysql-l2
namespace: metallb-system
spec:
ipAddressPools:
- mysql-pool
Stratégies de sauvegarde et de récupération duUn cluster haute disponibilité protège contre les pannes de nœuds. Les sauvegardes protègent contre la corruption des données, les suppressions accidentelles, les bogues d'application et les scénarios de reprise après sinistre dans lesquels l'intégralité du cluster est perdue. Vous avez besoin des deux.
Sauvegardes logiquesavec mysqldump
mysqldumpproduit des sauvegardes au format SQL portables et lisibles par l'homme. Pour les bases de données de moins de 50 Go, c’est l’option la plus simple. Pour les ensembles de données plus volumineux, le temps de verrouillage et d’exportation rend difficile une utilisation en production pendant les heures de bureau.
# Full logical backup with consistent snapshot
mysqldump --all-databases \
--single-transaction \
--routines \
--triggers \
--events \
--set-gtid-purged=ON \
--result-file=/backups/full-$(date +%Y%m%d-%H%M%S).sql
# Compressed backup piped to S3
mysqldump --all-databases --single-transaction | \
gzip | aws s3 cp - s3://mysql-backups/full-$(date +%Y%m%d).sql.gz
Sauvegardes physiquesavec Percona XtraBackup
Percona XtraBackup effectue des sauvegardes physiques à chaud et non bloquantes des données InnoDB. Il copie les fichiers de données InnoDB tout en suivant le journal redo, puis applique le journal redo pendant la phase de préparation pour produire une sauvegarde cohérente. Il est considérablement plus rapide que mysqldump pour les grands ensembles de données et prend en charge les sauvegardes incrémentielles.
# Full physical backup
xtrabackup --backup \
--target-dir=/backups/full \
--user=backup_user \
--password=SecureBackupPass \
--parallel=4 \
--compress \
--compress-threads=4
# Incremental backup based on previous full
xtrabackup --backup \
--target-dir=/backups/incr-$(date +%H) \
--incremental-basedir=/backups/full \
--user=backup_user \
--password=SecureBackupPass
# Prepare (apply redo log) and restore
xtrabackup --prepare --target-dir=/backups/full
xtrabackup --prepare --target-dir=/backups/full \
--incremental-dir=/backups/incr-01
# Copy back to data directory
xtrabackup --copy-back --target-dir=/backups/full --datadir=/var/lib/mysql
chown -R mysql:mysql /var/lib/mysql
Journal binaire(binlog) Récupération ponctuelle
Les journaux binairesenregistrent chaque transaction de modification de données. Combinés à une sauvegarde complète, ils permettent une restauration à un moment précis (PITR) : restauration à n'importe quel moment spécifique, pas seulement à l'heure de la dernière sauvegarde.
# Enable binlog retention (my.cnf)
[mysqld]
log_bin=mysql-bin
binlog_expire_logs_seconds=604800 # 7 days
binlog_format=ROW
gtid_mode=ON
enforce_gtid_consistency=ON
# Restore from backup then replay binlogs to a specific point
mysqlbinlog --start-datetime="2026-04-12 08:00:00" \
--stop-datetime="2026-04-12 09:30:00" \
mysql-bin.000042 mysql-bin.000043 | mysql -u root -p
# Or replay to a specific GTID position
mysqlbinlog --include-gtids="3c2d4f9e-d17e-ec59-9029-6aafa8accef8:1-5000" \
mysql-bin.000042 | mysql -u root -p
Kubernetes Sauvegarde CronJob
Dans Kubernetes, les sauvegardes doivent s'exécuter sous forme de CronJobs plutôt que de commandes ad hoc. Cela garantit que les sauvegardes sont automatisées, surveillées et peuvent être alertées en cas d'échec.
apiVersion: batch/v1
kind: CronJob
metadata:
name: mysql-backup
namespace: production
spec:
schedule: "0 2 * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 7
failedJobsHistoryLimit: 3
jobTemplate:
spec:
activeDeadlineSeconds: 7200
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: percona/percona-xtrabackup:8.0.35
command:
- /bin/sh
- -c
- |
BACKUP_DIR=/backups/$(date +%Y%m%d-%H%M%S)
mkdir -p $BACKUP_DIR
xtrabackup --backup \
--host=production-mysql.production.svc \
--user=backup_user \
--password=$BACKUP_PASSWORD \
--target-dir=$BACKUP_DIR \
--parallel=4 \
--compress
xtrabackup --prepare --target-dir=$BACKUP_DIR
# Upload to S3
aws s3 sync $BACKUP_DIR s3://mysql-backups/$(date +%Y%m%d)/
# Cleanup local
rm -rf $BACKUP_DIR
env:
- name: BACKUP_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-backup-credentials
key: password
volumeMounts:
- name: backup-scratch
mountPath: /backups
resources:
requests:
cpu: "1"
memory: 2Gi
limits:
cpu: "2"
memory: 4Gi
volumes:
- name: backup-scratch
emptyDir:
sizeLimit: 100Gi
Surveillanceavec surveillance et gestion Percona (PMM)
Percona Monitoring and Management (PMM) est une plateforme de surveillance open source spécialement conçue pour l'observabilité des bases de données. Alors que Prometheus et Grafana fournissent une surveillance générale de Kubernetes, PMM ajoute des informations spécifiques à MySQL : analyse de requêtes (QAN) qui identifie les requêtes lentes, tableaux de bord de retard de réplication, taux de réussite du pool de tampons InnoDB, conflits de verrouillage de table, etc.
# Deploy PMM Server on Kubernetes
apiVersion: apps/v1
kind: Deployment
metadata:
name: pmm-server
namespace: monitoring
spec:
replicas: 1
selector:
matchLabels:
app: pmm-server
template:
metadata:
labels:
app: pmm-server
spec:
containers:
- name: pmm-server
image: percona/pmm-server:2
ports:
- containerPort: 443
env:
- name: DISABLE_TELEMETRY
value: "1"
volumeMounts:
- name: pmm-data
mountPath: /srv
resources:
requests:
cpu: "1"
memory: 4Gi
limits:
cpu: "2"
memory: 8Gi
volumes:
- name: pmm-data
persistentVolumeClaim:
claimName: pmm-data-pvc
---
apiVersion: v1
kind: Service
metadata:
name: pmm-server
namespace: monitoring
spec:
type: ClusterIP
selector:
app: pmm-server
ports:
- port: 443
targetPort: 443
# Register MySQL instances with PMM Client (run on each MySQL pod or sidecar)
pmm-admin config --server-url=https://admin:password@pmm-server.monitoring:443 --server-insecure-tls
pmm-admin add mysql \
--username=pmm_monitor \
--password=MonitorPass123 \
--host=127.0.0.1 \
--port=3306 \
--query-source=perfschema \
--service-name=mysql-node-1
L'analyse des requêtes duPMM est particulièrement utile en production. Il capture chaque requête exécutée sur le serveur (via un schéma de performances ou un journal de requêtes lentes), les regroupe par empreinte digitale et affiche des mesures telles que la latence moyenne, les lignes examinées par rapport aux lignes envoyées et le temps de verrouillage. C'est ainsi que vous identifiez les requêtes qui dégradent les performances de votre application.
Réglage de la configuration de production
La configuration MySQL par défaut est adaptée à une petite charge de travail à usage général. Les environnements de production dotés de serveurs de bases de données dédiés nécessitent des paramètres très différents. Voici les paramètres critiques et comment les dimensionner.
Pool de tampons InnoDBLe pool de tampons InnoDB est l'endroit où MySQL met en cache les données de table et d'index en mémoire. Il s’agit du paramètre de configuration le plus impactant. Pour un serveur MySQL dédié, définissez-le sur 70 à 80 % de la RAM disponible.
[mysqld]
# Memory: 16Gi container limit -> allocate ~12Gi to buffer pool
innodb_buffer_pool_size = 12G
innodb_buffer_pool_instances = 8 # 1 per GB (up to 64)
innodb_buffer_pool_chunk_size = 1536M # pool_size / instances
Configuration du journal de rétablissement
Le journal redo absorbe les écritures avant qu'elles ne soient transférées dans les fichiers de données. Des journaux de rétablissement plus volumineux réduisent la fréquence des vidages de points de contrôle et améliorent le débit d'écriture. MySQL 8.0.30+ utiliseinnodb_redo_log_capacityau lieu de l'ancieninnodb_log_file_size.
[mysqld]
# MySQL 8.0.30+
innodb_redo_log_capacity = 4G
# MySQL 8.0.29 and earlier
# innodb_log_file_size = 2G
# innodb_log_files_in_group = 2
: compromis entre durabilité et performances
La combinaison deinnodb_flush_log_at_trx_commitetsync_binlogdétermine votre garantie de durabilité.
- Durabilité maximale(recommandé pour la production) :
innodb_flush_log_at_trx_commit=1+sync_binlog=1. Chaque transaction est vidée dans le journal redo et le journal binaire sur le disque avant d'être accusée de réception. Aucune perte de données en cas de crash. - équilibré :
innodb_flush_log_at_trx_commit=2+sync_binlog=1. Le journal redo est écrit dans le cache du système d'exploitation par validation, mais n'est vidé sur le disque qu'une fois par seconde. Jusqu'à 1 seconde de transactions peut être perdue en cas de panne du système d'exploitation (le crash du MySQL est toujours sûr). - Performances maximales(non recommandé pour la production) :
innodb_flush_log_at_trx_commit=0+sync_binlog=0. Les écritures sont regroupées et vidées périodiquement. Risque de perdre jusqu'à 1 seconde de transactions en cas de crash.
[mysqld]
# Production durability settings
innodb_flush_log_at_trx_commit = 1
sync_binlog = 1
innodb_doublewrite = ON
innodb_checksum_algorithm = crc32
Configuration des E/S
[mysqld]
# SSD-optimised I/O settings
innodb_io_capacity = 4000
innodb_io_capacity_max = 8000
innodb_read_io_threads = 8
innodb_write_io_threads = 8
innodb_flush_method = O_DIRECT
innodb_file_per_table = ON
innodb_open_files = 10000
Gestion des connexions et des threads
[mysqld]
max_connections = 500
max_connect_errors = 1000000
thread_cache_size = 64
table_open_cache = 4096
table_open_cache_instances = 16
back_log = 1500
# Thread pool (MySQL Enterprise or Percona)
thread_pool_size = 16
thread_pool_max_threads = 1000
Tables temporaires et tampons de tri
[mysqld]
tmp_table_size = 64M
max_heap_table_size = 64M
sort_buffer_size = 4M
join_buffer_size = 4M
read_rnd_buffer_size = 2M
Modèle de configuration de production complet[mysqld]
# Identity
server-id = 1
report_host = mysql-node-1
# GTID Replication
gtid_mode = ON
enforce_gtid_consistency = ON
binlog_format = ROW
log_bin = mysql-bin
binlog_expire_logs_seconds = 604800
log_slave_updates = ON
relay_log_recovery = ON
# InnoDB Engine
innodb_buffer_pool_size = 12G
innodb_buffer_pool_instances = 8
innodb_redo_log_capacity = 4G
innodb_flush_log_at_trx_commit = 1
sync_binlog = 1
innodb_doublewrite = ON
innodb_flush_method = O_DIRECT
innodb_io_capacity = 4000
innodb_io_capacity_max = 8000
innodb_read_io_threads = 8
innodb_write_io_threads = 8
innodb_file_per_table = ON
innodb_open_files = 10000
innodb_adaptive_hash_index = ON
innodb_change_buffering = all
# Connections
max_connections = 500
max_connect_errors = 1000000
thread_cache_size = 64
table_open_cache = 4096
table_open_cache_instances = 16
back_log = 1500
# Temp / Sort
tmp_table_size = 64M
max_heap_table_size = 64M
sort_buffer_size = 4M
join_buffer_size = 4M
# Monitoring
performance_schema = ON
slow_query_log = ON
long_query_time = 1
log_queries_not_using_indexes = ON
log_throttle_queries_not_using_indexes = 60
# Security
local_infile = OFF
skip_name_resolve = ON
default_authentication_plugin = caching_sha2_password
# Group Replication (InnoDB Cluster)
plugin_load_add = 'group_replication.so'
plugin_load_add = 'mysql_clone.so'
loose-group_replication_start_on_boot = OFF
loose-group_replication_bootstrap_group = OFF
loose-group_replication_recovery_use_ssl = ON
loose-group_replication_ssl_mode = REQUIRED
Tests de basculement et ingénierie du chaos
Un système à haute disponibilité qui n'a jamais été testé dans des conditions de défaillance n'est pas vraiment une haute disponibilité : c'est une hypothèse. Les tests de basculement doivent faire partie de votre cadence opérationnelle habituelle, et non quelque chose que vous découvrez qui fonctionne (ou ne fonctionne pas) lors d'un incident réel.
Test de basculement contrôlé
# Test 1: Graceful primary failover (InnoDB Cluster)
mysqlsh gradmin@mysql-node-1:3306
var cluster = dba.getCluster()
cluster.setPrimaryInstance('gradmin@mysql-node-2:3306')
# Verify: cluster.status() should show node-2 as PRIMARY
# Test 2: Simulate primary crash
# On the primary node:
kill -9 $(pidof mysqld)
# Monitor: watch the cluster elect a new primary
# Verify: applications reconnect via MySQL Router within seconds
# Test 3: Network partition simulation
# On the primary node, block Group Replication port:
iptables -A INPUT -p tcp --dport 33061 -j DROP
iptables -A OUTPUT -p tcp --dport 33061 -j DROP
# The isolated node should be expelled; cluster continues with remaining members
# Cleanup:
iptables -D INPUT -p tcp --dport 33061 -j DROP
iptables -D OUTPUT -p tcp --dport 33061 -j DROP
# Test 4: Kubernetes pod deletion
kubectl delete pod mysql-0 -n production --grace-period=0 --force
# The StatefulSet controller recreates the pod
# The operator rejoins it to the cluster
Ingénierie du chaos avec Litmus ou Chaos Mesh
Les outils d'ingénierie du chaos structuréinjectent systématiquement les pannes et mesurent le rayon de l'explosion. Chaos Mesh, un projet CNCF, s'intègre nativement à Kubernetes.
# Chaos Mesh: Kill a MySQL pod randomly
apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
name: mysql-pod-kill
namespace: production
spec:
action: pod-kill
mode: one
selector:
namespaces:
- production
labelSelectors:
app.kubernetes.io/component: mysqld
scheduler:
cron: "0 */4 * * *"
---
# Chaos Mesh: Network delay between MySQL pods
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
name: mysql-network-delay
namespace: production
spec:
action: delay
mode: one
selector:
namespaces:
- production
labelSelectors:
app.kubernetes.io/component: mysqld
delay:
latency: "200ms"
jitter: "50ms"
duration: "5m"
scheduler:
cron: "30 */6 * * *"
---
# Chaos Mesh: Disk I/O stress
apiVersion: chaos-mesh.org/v1alpha1
kind: IOChaos
metadata:
name: mysql-io-latency
namespace: production
spec:
action: latency
mode: one
selector:
namespaces:
- production
labelSelectors:
app.kubernetes.io/component: mysqld
volumePath: /var/lib/mysql
path: '*'
delay: '100ms'
percent: 50
duration: '5m'
Que mesurer lors des tests de basculement
- Objectif de temps de récupération (RTO)— Combien de temps s'écoule entre la détection d'une panne et la restauration du service ? InnoDB Cluster atteint généralement 5 à 30 secondes. Le cluster Percona XtraDB peut être plus rapide car il n'y a pas d'élection (tous les nœuds sont accessibles en écriture).
- Objectif de point de récupération (RPO)— Quelle quantité de données est perdue lors du basculement ? Avec la réplication synchrone (réplication de groupe en mode primaire unique, PXC), le RPO est nul pour les transactions validées. Avec la réplication asynchrone (inter-région ClusterSet), le RPO est égal au décalage de réplication.
- Taux d'erreur d'application— Combien de requêtes d'application échouent pendant la fenêtre de basculement ? Cela teste non seulement le basculement de la base de données, mais également la logique de nouvelle tentative de connexion de votre application et la vitesse de reroutage du routeur MySQL.
- Temps de vidange de la connexion— Combien de temps faut-il aux connexions existantes vers l'ancien primaire pour se vider et se reconnecter au nouveau primaire ?
: liste de contrôle en cas de défaillance du nœud principal
- Vérifiez que le cluster a élu un nouveau primaire :
cluster.status() - Confirmez que le routeur MySQL est acheminé vers le nouveau serveur principal : vérifiez les journaux du routeur et le nombre de connexions.
- Surveiller le délai de réplication sur les secondaires restants :
SELECT * FROM performance_schema.replication_group_member_stats - Si le nœud défaillant peut être récupéré, rejoignez-le :
cluster.rejoinInstance('gradmin@failed-node:3306') - Si le nœud défaillant ne peut pas être récupéré, supprimez-le et ajoutez-en un nouveau :
cluster.removeInstance('gradmin@failed-node:3306', {force: true}) - Vérifier l'état du cluster : tous les membres EN LIGNE, aucune erreur de réplication, planification de sauvegarde intacte
- Mettez à jour votre plan de capacité : fonctionner avec 2 nœuds signifie que vous avez une tolérance zéro aux pannes jusqu'à ce que le troisième soit restauré
Renforcement de la sécurité
Les déploiementsProduction MySQL doivent répondre à plusieurs problèmes de sécurité au-delà de l'authentification de base.
Chiffrement en transit et au repos
[mysqld]
# TLS for client connections
ssl_ca = /etc/mysql/certs/ca.pem
ssl_cert = /etc/mysql/certs/server-cert.pem
ssl_key = /etc/mysql/certs/server-key.pem
require_secure_transport = ON
tls_version = TLSv1.3
# Encryption at rest (InnoDB tablespace encryption)
early-plugin-load = keyring_file.so
keyring_file_data = /var/lib/mysql-keyring/keyring
innodb_undo_log_encrypt = ON
innodb_redo_log_encrypt = ON
default_table_encryption = ON
Kubernetes Secrets et secrets scellés
# Never store database credentials in plain YAML
# Use Sealed Secrets or an external secret manager
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: mysql-root-credentials
namespace: production
spec:
encryptedData:
rootUser: AgBy3i4OJSWK+PiTy...
rootPassword: AgCtr7pJ2XQWK+Pi...
template:
metadata:
name: mysql-root-credentials
namespace: production
type: Opaque
Journalisation d'audit
[mysqld]
# MySQL Enterprise Audit (or Percona Audit Log plugin)
plugin-load-add = audit_log.so
audit_log_format = JSON
audit_log_policy = LOGINS
audit_log_rotate_on_size = 100M
audit_log_rotations = 10
Matrice de décision: Choisir votre architecture MySQL HA
La bonne architecture dépend de vos besoins spécifiques. Voici un cadre de décision.
- Cloud unique, préférence gérée, compatible MySQL— Utilisez Aurora (AWS), Flexible Server Business Critical (Azure) ou Cloud SQL Enterprise Plus (GCP). Ceux-ci fournissent les frais généraux opérationnels les plus bas.
- Cloud unique, autogéré, nécessite un contrôle complet de MySQL— Utilisez MySQL Operator ou Percona Operator sur EKS/AKS/GKE avec des classes de stockage cloud natives.
- Multi-cloud ou cloud hybride— Utilisez Percona Operator avec PXC ou MySQL Operator avec InnoDB Cluster. La couche d'abstraction Kubernetes rend votre déploiement MySQL portable sur les cloud.
- Bare metal ou edge— Utilisez k3s avec Rancher, Longhorn Storage, MetalLB et MySQL Operator ou Percona Operator.
- Débit d'écriture maximal, plusieurs primaires requis— Utilisez le cluster Percona XtraDB (Galera) avec l'opérateur Percona. Tous les nœuds acceptent les écritures avec certification synchrone.
- Distribution mondiale avec reprise après sinistre— Utilisez InnoDB ClusterSet avec une réplication asynchrone entre les régions. Acceptez le compromis RPO de la réplication asynchrone pour les avantages de latence des écritures locales.
pour la production MySQL HA
Avant de déclarer votre déploiement MySQL HA prêt pour la production, validez chaque élément de cette liste de contrôle.
- Santé du cluster— Tous les membres signalent l'état EN LIGNE. La réplication de groupe ne montre aucune erreur.
- Sauvegardes automatisées— Sauvegardes CronJob ou gérées par l'opérateur exécutées dans les délais. Sauvegardes vérifiées par des tests de restauration périodiques.
- Surveillance— PMM ou Prometheus/Grafana collectant les métriques MySQL. Alertes configurées pour le décalage de réplication, la saturation des connexions, le taux de réussite du pool de mémoire tampon inférieur à 99 % et l'espace disque.
- Basculement testé— Basculement principal testé au cours des 30 derniers jours. RTO et RPO mesurés et dans les limites des objectifs SLA.
- Routage de connexion— Routeur MySQL ou ProxySQL vérifié et équilibré en charge. Les chaînes de connexion d’application pointent vers le routeur, et non vers des instances MySQL individuelles.
- Sécurité— TLS requis pour toutes les connexions. Chiffrement au repos activé. Informations d'identification stockées dans un gestionnaire de secrets. Journalisation d’audit active. Utilisateurs de base de données bénéficiant du moindre privilège pour chaque application.
- Limites de ressources— Demandes de ressources Kubernetes et limites définies de manière appropriée. PodDisruptionBudgets en place. L'anti-affinité des pods répartit les pods MySQL à travers les zones.
- Plan de capacité— Utilisation du stockage surveillée avec des alertes à 70 % et 85 %. Expansion du volume testée. Procédures de mise à l'échelle verticale et horizontale documentées.
- Runbooks— Procédures documentées pour le basculement principal, le remplacement de nœud, la restauration de sauvegarde, la mise à niveau de version et le mode lecture seule d'urgence.
- Tests de chaos— Expériences de chaos régulières programmées pour valider les hypothèses de résilience.
Conclusion
La haute disponibilité duMySQL en production n'est pas un choix technologique unique : il s'agit d'un système de décisions imbriquées qui couvrent la couche de réplication, la plate-forme d'orchestration, le sous-système de stockage, la pile de surveillance et les processus opérationnels qui les entourent. InnoDB Cluster avec réplication de groupe fournit la primitive HA fondamentale. Les opérateurs Kubernetes d'Oracle et Percona automatisent la gestion du cycle de vie qui autrement prendrait beaucoup de temps d'ingénierie. Contrôle des échanges de services gérés dans le cloud pour plus de commodité. Les déploiements sans système d'exploitation avec k3s, Rancher et Longhorn prouvent que vous n'avez pas besoin d'un fournisseur de cloud pour exécuter MySQL HA de qualité production.
L'idée la plus critique est que la haute disponibilité est une propriété de l'ensemble du système, et pas seulement de la base de données. Il comprend la manière dont votre application gère les échecs de connexion et les tentatives, la manière dont votre couche proxy détecte et contourne les nœuds défaillants, la manière dont votre surveillance alerte avant que les utilisateurs ne s'en aperçoivent, la manière dont votre stratégie de sauvegarde permet la récupération à partir de scénarios que la haute disponibilité ne peut gérer seule, et la manière dont votre équipe pratique les procédures de basculement afin qu'elles s'exécutent proprement sous le stress d'un incident réel.
Créez-le délibérément, testez-le régulièrement et traitez vos runbooks comme des documents vivants qui évoluent avec chaque incident et chaque expérience de chaos. C’est ainsi que MySQL gagne sa place en tant que plateforme de données fiable et hautement disponible en production.