Sauvegarde et restauration de ClickHouse
Fonctionnement de la sauvegarde ClickHouse
Un sidecar de sauvegarde s'exécute aux côtés de ClickHouse, surveillant en continu les changements et prenant automatiquement des sauvegardes complètes et incrémentielles planifiées : il n'y a pas de tâche cron distincte à configurer.
Les sauvegardes sont :
- Cohérentes au niveau applicatif : prises via le mécanisme natif
FREEZEde ClickHouse (liens physiques zéro-copie), et non un instantané brut de volume, de sorte qu'une sauvegarde ne capture jamais une table en cours d'écriture. - Écrites dans votre object storage : à la fois les métadonnées/manifestes et les blobs de données sous-jacents sont copiés dans votre bucket configuré, sous le préfixe de chemin que vous configurez ci-dessous.
- Dédupliquées et conscientes des chaînes : les sauvegardes incrémentielles ne stockent que ce qui a changé ; les politiques de rétention suppriment des chaînes de sauvegarde entières plutôt que de laisser des incréments orphelins.
FREEZE fonctionne en créant des liens physiques vers les données de table en direct, et non des copies. Les permissions, la propriété et les attributs d'un lien physique sont une propriété des données sous-jacentes elles-mêmes : ils sont partagés entre chaque lien physique qui pointe vers elles. Modifier les permissions sur un fichier situé sous le chemin de sauvegarde les modifie aussi sur les données en direct que ClickHouse lit et écrit activement, ce qui peut entraîner une corruption des données. Ne touchez jamais manuellement aux permissions à cet endroit ; laissez ClickHouse et le sidecar de sauvegarde gérer ce chemin exclusivement.
Configuration du sidecar de sauvegarde
Un sidecar de sauvegarde est configuré par défaut sur le pod ClickHouse, via clickhouse-server.sidecars : le mécanisme du chart pour ajouter un conteneur supplémentaire aux côtés des volumes que ClickHouse lui-même utilise. Il exécute clickhouse-backup en mode server --watch, qui auto-planifie les sauvegardes complètes et incrémentielles en interne ; il n'y a pas de tâche cron distincte, et rien d'autre à déployer.
Destination de l'object storage
La destination de sauvegarde n'a aucune valeur par défaut : vous devez la configurer avant que les sauvegardes puissent s'exécuter. Cela se divise en deux parties, toutes deux spécifiques au fournisseur : les paramètres non sensibles (bucket/conteneur, région, chemin) vont sous clickhouse.backupConfig (voir Paramètres ajustables ci-dessous pour le reste de cette map), tandis que les identifiants (lorsqu'une clé statique est requise) sont transmis via un secret Kubernetes créé séparément, jamais en ligne dans votre fichier de valeurs (le même schéma décrit dans Prérequis). Le bucket cible est défini indépendamment du propre bucket de données de ClickHouse (voir Backends d'object storage) : utilisez un bucket réellement distinct, pas seulement un préfixe différent dans le même (voir le conseil dans cette section pour comprendre pourquoi). Choisissez votre fournisseur ci-dessous.
- AWS S3
- Azure Blob Storage
- GCS
- MinIO
clickhouse:
backupConfig:
REMOTE_STORAGE: 's3'
S3_BUCKET: '<bucket-name>'
S3_REGION: '<region>'
S3_PATH: 'backups/metadata'
S3_OBJECT_DISK_PATH: 'backups/objects'
Bucket cible : S3_BUCKET. Aucune entrée d'identifiant nécessaire : avec IRSA (voir Backends d'object storage), le token du ServiceAccount est automatiquement monté dans chaque conteneur du pod, y compris ce sidecar.
Avec Microsoft Entra Workload ID configuré pour le disque de stockage (voir Backends d'object storage), le sidecar en hérite gratuitement : le mutating webhook d'AKS injecte par défaut les variables d'environnement de token fédéré et le volume de token projeté dans chaque conteneur du pod, et pas seulement dans celui de ClickHouse : aucune configuration distincte nécessaire. Ajoutez simplement :
clickhouse:
backupConfig:
REMOTE_STORAGE: 'azblob'
AZBLOB_ACCOUNT_NAME: '<storage-account-name>'
AZBLOB_CONTAINER: '<container-name>'
AZBLOB_PATH: 'backups/metadata'
AZBLOB_OBJECT_DISK_PATH: 'backups/objects'
AZBLOB_USE_MANAGED_IDENTITY: 'true'
Bucket cible : AZBLOB_ACCOUNT_NAME + AZBLOB_CONTAINER. Aucune entrée d'identifiant nécessaire. AZBLOB_USE_MANAGED_IDENTITY est pris en charge par clickhouse-backup depuis la v2.7.1 : notre version épinglée 2.8.0 est qualifiée. Assurez-vous que le rôle RBAC accordé à l'identité managée (voir Backends d'object storage) couvre la cible de sauvegarde du conteneur, si elle est différente de celle de ClickHouse.
clickhouse:
backupConfig:
REMOTE_STORAGE: 'gcs'
GCS_BUCKET: '<bucket-name>'
GCS_PATH: 'backups/metadata'
GCS_OBJECT_DISK_PATH: 'backups/objects'
Le client GCS du sidecar de sauvegarde n'accepte pas les clés HMAC (contrairement au propre disque de ClickHouse, voir Backends d'object storage) : il a besoin de sa propre clé JSON native de compte de service, transmise en tant que secret Kubernetes plutôt que définie en ligne dans les valeurs, le même schéma décrit dans Prérequis : créez-la directement, ou synchronisez-la depuis votre propre magasin de secrets via un SecretStore/ExternalSecret de l'External Secrets Operator ou un Vault Secrets Operator.
Quelle que soit la façon dont vous le remplissez, le secret doit être nommé clickhouse-backup-secret et contenir une clé GCS_CREDENTIALS_JSON avec le contenu du fichier de clé JSON. Si vous le créez directement :
kubectl create secret generic clickhouse-backup-secret \
--namespace <namespace> \
--from-file=GCS_CREDENTIALS_JSON=<path-to-service-account-key.json>
Bucket cible : GCS_BUCKET. Nécessite l'identifiant ci-dessus.
Contrairement au propre disque de ClickHouse (structurellement bloqué de Workload Identity, voir Backends d'object storage), le client GCS de clickhouse-backup utilise l'API native de GCS et prend en charge GCS_SA_EMAIL : usurpation d'un compte de service GCP via GKE Workload Identity Federation, sans clé JSON.
Ce chemin provient de la propre documentation de clickhouse-backup (pris en charge depuis la v2.6.36 ; notre version épinglée 2.8.0 est qualifiée) et de la documentation Workload Identity Federation de GCP, mais n'a pas été testé de bout en bout sur un vrai cluster GKE. Validez-le vous-même avant de vous y fier en production.
Cela nécessite une configuration Workload Identity Federation spécifique au sidecar de sauvegarde (distincte du propre disque de ClickHouse, qui a de toute façon toujours besoin de la paire de clés HMAC statique) :
- Workload Identity Federation activé sur votre cluster GKE.
- Un ServiceAccount Kubernetes dédié, annoté avec l'e-mail d'un compte de service GCP.
- Une liaison de politique IAM autorisant ce ServiceAccount à usurper le compte de service GCP.
- Un rôle de stockage accordé au compte de service GCP, limité au bucket.
Activer Workload Identity Federation (s'il n'est pas déjà activé sur le cluster) :
gcloud container clusters update <cluster-name> \
--zone <zone> \
--workload-pool=<project-id>.svc.id.goog
Créer le compte de service GCP :
gcloud iam service-accounts create clickhouse-backup --project=<project-id>
Configuration du chart : annotez le ServiceAccount ClickHouse pour qu'il puisse usurper le compte de service GCP :
clickhouse-server:
serviceAccount:
annotations:
iam.gke.io/gcp-service-account: clickhouse-backup@<project-id>.iam.gserviceaccount.com
Liaison de politique IAM : autorisez le ServiceAccount Kubernetes à usurper le compte de service GCP (remplacez <namespace>) :
gcloud iam service-accounts add-iam-policy-binding \
clickhouse-backup@<project-id>.iam.gserviceaccount.com \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:<project-id>.svc.id.goog[<namespace>/clickhouse]"
Rôle de stockage : accordez au compte de service GCP l'accès au bucket :
gcloud storage buckets add-iam-policy-binding gs://<bucket-name> \
--member=serviceAccount:clickhouse-backup@<project-id>.iam.gserviceaccount.com \
--role=roles/storage.objectAdmin
Une fois cela en place :
clickhouse:
backupConfig:
REMOTE_STORAGE: 'gcs'
GCS_BUCKET: '<bucket-name>'
GCS_PATH: 'backups/metadata'
GCS_OBJECT_DISK_PATH: 'backups/objects'
GCS_SA_EMAIL: 'clickhouse-backup@<project-id>.iam.gserviceaccount.com'
Bucket cible : GCS_BUCKET. Aucune clé JSON à créer ou à stocker.
clickhouse-backup communique avec MinIO via son type de stockage distant s3 également (comme AWS S3), simplement pointé vers votre endpoint MinIO avec un adressage de type chemin et une paire statique clé d'accès/clé secrète, puisque MinIO n'a pas d'identité équivalente à IAM avec laquelle se fédérer :
clickhouse:
backupConfig:
REMOTE_STORAGE: 's3'
S3_BUCKET: '<bucket-name>'
S3_REGION: 'us-east-1' # required by the S3 client even though MinIO ignores it
S3_ENDPOINT: 'http://<minio-host>:<minio-port>'
S3_PATH: 'backups/metadata'
S3_OBJECT_DISK_PATH: 'backups/objects'
S3_FORCE_PATH_STYLE: 'true'
S3_DISABLE_SSL: 'true' # omit and use an https:// endpoint above instead if MinIO terminates TLS
Les identifiants sont transmis en tant que secret Kubernetes, jamais en ligne dans votre fichier de valeurs (le même schéma décrit dans Prérequis). Quelle que soit la façon dont vous le remplissez, le secret doit être nommé clickhouse-backup-secret et contenir les clés S3_ACCESS_KEY et S3_SECRET_KEY :
kubectl create secret generic clickhouse-backup-secret \
--namespace <namespace> \
--from-literal=S3_ACCESS_KEY=<minio-access-key> \
--from-literal=S3_SECRET_KEY=<minio-secret-key>
Bucket cible : S3_BUCKET. Nécessite l'identifiant ci-dessus.
Politiques de cycle de vie du bucket
Les règles de cycle de vie et les protections de bucket pour les deux buckets sont documentées ensemble dans Politiques de cycle de vie du bucket : voir la sous-section Backup bucket pour celui-ci (versionnage, Object Lock, réplication, mises en garde sur la couche d'archivage) et la sous-section ClickHouse data bucket pour le propre bucket de données de ClickHouse. Les deux ont besoin de règles différentes, parfois opposées.
Paramètres ajustables
Au-delà de la destination de l'object storage ci-dessus, une poignée d'autres paramètres du sidecar sont exposés sous la même map clickhouse.backupConfig, avec des valeurs par défaut raisonnables prêtes à l'emploi : ne remplacez que ceux que vous devez modifier, sans redéclarer toute la spécification du sidecar :
clickhouse:
backupConfig:
BACKUPS_TO_KEEP_REMOTE: '30' # only overriding retention here
| Paramètre | Objectif | Valeur par défaut |
|---|---|---|
WATCH_SCHEDULES | Planning des sauvegardes complètes/incrémentielles piloté par cron, voir Comprendre le planning de sauvegarde ci-dessous. | name=ch-bkp,full=0 2 * * 0,increment=0 2 * * *,full_type=rebase,delete_previous_cycle=false |
BACKUPS_TO_KEEP_REMOTE | Nombre de sauvegardes individuelles à conserver dans l'object storage : chaque sauvegarde complète et chaque incrément compte séparément, voir Comprendre le planning de sauvegarde ci-dessous. | 7 |
REBASE_BEFORE_REMOVE_OLD_REMOTE | Sans cela, BACKUPS_TO_KEEP_REMOTE n'est pas une limite stricte : avec les chaînes incrémentielles, clickhouse-backup ne supprimera pas une chaîne dont un incrément plus récent dépend encore, de sorte que le nombre conservé peut dépasser la limite. Cela rebase le plus ancien incrément encore dans la fenêtre de rétention sur une sauvegarde complète autonome, le rendant indépendant de la chaîne plus ancienne qui le précède, laquelle peut alors être supprimée, maintenant une rétention stricte. | true |
UPLOAD_CONCURRENCY | Tables téléversées en parallèle pendant une sauvegarde, et (même valeur) fichiers par table téléversés en parallèle. | 2 |
DOWNLOAD_CONCURRENCY | Identique à UPLOAD_CONCURRENCY, pour la restauration. | 2 |
S3_CONCURRENCY | Chunks parallèles pour le transfert multipart S3 d'un seul fichier, un niveau plus granulaire que UPLOAD_CONCURRENCY/DOWNLOAD_CONCURRENCY. | 2 |
REBASE_CONCURRENCY | Tables traitées en parallèle pendant une opération de rebase (voir full_type=rebase ci-dessous). | 2 |
LOG_LEVEL | Verbosité des logs du sidecar. | info |
ALLOW_EMPTY_BACKUPS | Sans cela, une nouvelle installation sans table encore présente est traitée comme une erreur fatale, entraînant un crash-loop du sidecar. | true |
Les paramètres de concurrence prennent par défaut une valeur fixe et prudente plutôt que l'auto-détection propre à clickhouse-backup, qui se dimensionne sur le nombre total de CPU du nœud plutôt que sur la limite CPU réelle de ce conteneur : augmentez-les si vous avez donné plus de CPU au sidecar et souhaitez des sauvegardes et restaurations plus rapides.
Quelques autres paramètres sont déjà configurés par le chart avec des valeurs par défaut raisonnables et ne sont pas exposés pour être remplacés ; voir la propre référence de configuration de clickhouse-backup pour la liste exhaustive des paramètres disponibles.
Comprendre le planning de sauvegarde
WATCH_SCHEDULES pilote la cadence de sauvegarde avec des expressions cron. Un planning a la forme :
name=<name>,full=<cron>[,increment=<cron>][,full_type=create|rebase][,delete_previous_cycle=true|false]
| Clé | Requis | Signification |
|---|---|---|
name | Oui | Préfixe des noms de sauvegarde de cette chaîne. Vous permet d'exécuter plusieurs plannings indépendants côte à côte, séparés par ;. |
full | Oui | Expression cron pour la sauvegarde complète. |
increment | Non | Expression cron pour la sauvegarde incrémentielle. Omettez pour des sauvegardes complètes uniquement. |
full_type | Non (create) | create re-téléverse toutes les données à chaque sauvegarde complète. rebase effectue à la place une copie côté serveur de la chaîne précédente (pas de re-téléversement, bien plus rapide), mais seulement une fois qu'une sauvegarde précédente existe déjà ; le tout premier cycle revient toujours à create. |
delete_previous_cycle | Non (false) | true supprime chaque sauvegarde plus ancienne de cette chaîne dès qu'une nouvelle sauvegarde complète réussit, ne conservant que le cycle actuel. false laisse les cycles plus anciens en place, élagués uniquement par BACKUPS_TO_KEEP_REMOTE ci-dessus. |
Les expressions cron acceptent la syntaxe standard à 5 champs (minute, heure, jour, mois, jour de la semaine), un champ optionnel de secondes en tête, et des descripteurs de style @every/@daily.
La valeur par défaut du chart :
clickhouse:
backupConfig:
WATCH_SCHEDULES: 'name=ch-bkp,full=0 2 * * 0,increment=0 2 * * *,full_type=rebase,delete_previous_cycle=false'
Cela exécute une sauvegarde complète chaque dimanche à 2h du matin (via rebase une fois qu'une précédente existe, donc pas de re-téléversement) et une sauvegarde incrémentielle chaque jour à 2h du matin. Le dimanche, les expressions cron full et increment se déclenchent au même tick : la sauvegarde complète est toujours prioritaire, donc aucun incrément distinct ne s'exécute ce jour-là.
Fenêtre de rétention : avec delete_previous_cycle: false, BACKUPS_TO_KEEP_REMOTE conserve les N sauvegardes les plus récentes, comptant chaque sauvegarde complète et chaque incrément comme sa propre entrée. À 7 entrées par semaine (1 complète + 6 incréments), BACKUPS_TO_KEEP_REMOTE: '7' conserve environ 1 semaine d'historique.
Fraîcheur des sauvegardes
La fraîcheur des sauvegardes est exposée dans le produit à l'aide des propres tables système de ClickHouse. Considérez une sauvegarde comme défaillante si aucune sauvegarde complète valide n'existe dans votre objectif de point de reprise, ou si aucune nouvelle sauvegarde n'a été prise récemment.
La boucle de surveillance du sidecar réessaie une sauvegarde échouée au prochain tick planifié au lieu de s'arrêter : un échec persistant (mauvais identifiants, un bucket inaccessible, un disque plein) enregistre une erreur à chaque tentative mais ne fait jamais planter le conteneur. Le statut du pod et le nombre de redémarrages restent au vert tout du long. Voir Surveillance et alertes ClickHouse pour mettre en place un signal externe qui, lui, détecte cela.
Restauration
La restauration des données ClickHouse est une opération manuelle et délibérée : elle n'est pas déclenchée automatiquement. clickhouse-backup restaure sur place, sur la même instance ClickHouse aux côtés de laquelle le sidecar s'exécute : il a besoin d'un accès direct au système de fichiers pour attacher les parties de données de la sauvegarde, la même exigence qui le maintient déployé en tant que sidecar en premier lieu (voir Fonctionnement de la sauvegarde ClickHouse ci-dessus). Il n'existe pas de flux de restauration vers une instance vide séparée sans mettre en place un second déploiement ClickHouse complet.
Procédure de haut niveau :
- Faites d'abord un instantané de l'état actuel :
clickhouse-backup create_remotesur la ou les tables affectées, même si ces données sont déjà dégradées. Vous obtenez un repli distinct de votre chaîne de sauvegarde habituelle si la restauration elle-même tourne mal. - Identifiez la sauvegarde à partir de laquelle restaurer : listez les sauvegardes distantes, vérifiez qu'elle n'est pas signalée comme cassée, et que sa chaîne de sauvegarde est intacte.
- Vérifiez la compatibilité des versions avant de vous engager dans une restauration complète :
clickhouse-backup download --schema <backup_name>récupère uniquement le schéma et les métadonnées (léger, sans données de table), afin que vous puissiez inspecterclickhouse_versiondans lemetadata.jsonrésultant par rapport à la version de ClickHouse actuellement en cours d'exécution. - Arrêtez les écritures sur la ou les tables affectées : mettez en pause ou réduisez l'échelle des producteurs qui écrivent dedans.
- Restaurez d'abord sous un nom différent (
--restore-database-mapping/--restore-table-mapping), jamais directement dans la table en direct. - Vérifiez la copie restaurée (nombre de lignes, contrôle ponctuel de valeurs connues) avant de toucher à la production.
- Basculez : une fois la copie mappée validée, restaurez à nouveau directement sur le nom de la table en direct avec
--rm(restore/restore_remote --rm <backup_name>) :clickhouse-backupsupprime le schéma existant et réattache depuis la sauvegarde.
Pour simplement tester qu'une sauvegarde fonctionne (et non répondre à un incident réel), restaurez plutôt dans une table ou un nom de base de données jetable, afin qu'un exercice ne touche jamais aux données de production.
Si vous réessayez une restauration en utilisant le même nom de sauvegarde après une tentative échouée ou interrompue, l'outil de restauration peut reprendre depuis un état local périmé et ne restaurer que le schéma (DDL), sautant silencieusement les données réelles, sans erreur. Si une restauration semble vide ou incomplète, effacez l'état local de l'outil avant de réessayer, ou utilisez une cible distincte à chaque fois.
Portée de la reprise après sinistre
Certaines fonctionnalités qui utilisent ClickHouse joignent également ces données avec des informations stockées dans PostgreSQL (par exemple, un inventaire des machines qui appartiennent à votre organisation). Restaurer ClickHouse seul, dans un environnement dont le PostgreSQL diffère, produit des résultats incohérents dans le produit : les données ClickHouse et le registre PostgreSQL sont en désaccord l'un avec l'autre.
Une reprise après sinistre complète pour ces fonctionnalités nécessite de restaurer à la fois ClickHouse et PostgreSQL à un point cohérent dans le temps. Planifiez votre stratégie de sauvegarde PostgreSQL (voir Sauvegarde et restauration) et votre stratégie de sauvegarde ClickHouse ensemble, et testez une restauration combinée dans le cadre de vos exercices de reprise après sinistre.
Objectifs de reprise
Nous ne publions pas actuellement d'objectif de point de reprise (RPO) ou d'objectif de temps de reprise (RTO) fixe pour ClickHouse : ceux-ci dépendent de votre cadence de sauvegarde et de votre volume de données. Nous recommandons d'exécuter un exercice de restauration sur votre propre volume de données pour mesurer votre RTO réel, et de définir votre cadence de sauvegarde (fréquence des sauvegardes complètes et incrémentielles) pour correspondre à la fenêtre de perte de données que vous êtes prêt à accepter.