Chapitre 06

Sauvegardes et reprise

Une sauvegarde qu'on n'a jamais restaurée n'est pas une sauvegarde, c'est une rumeur. Ce chapitre installe deux filets, puis les met à l'épreuve.

Niveau avancé  ·  Lecture 15 min  ·  Base Talos 1.14, Kubernetes 1.36

Renseignez votre domaine et vos adresses : les commandes et les fichiers de la page s'adaptent. Les valeurs restent dans votre navigateur et ne nous sont jamais transmises.

01Deux filets, deux usages

CoucheOutilFréquenceRétentionSert à
Objets KubernetesVeleroChaque nuit30 joursRestaurer une application ou un namespace
Volumes CephVelero, snapshot CSI puis copie KopiaAvec les objets30 joursRécupérer les données d'une application
etcdtalos-backup, chiffré ageToutes les 6 heures14 joursReconstruire un cluster perdu
VM et configurationOpenTofu et GitÀ chaque changementHistorique GitRecréer l'infrastructure

Velero répond à « j'ai supprimé mon application » ; etcd répond à « le cluster n'existe plus ». Les deux partent vers un stockage S3 situé hors du cluster Proxmox : une sauvegarde rangée dans le Ceph qu'elle protège ne protège pas grand-chose. Notre page stratégie de sauvegarde revient sur ce principe.

02Un S3 hors du cluster

Le stockage est un serveur de sauvegarde séparé, avec un pool ZFS. Nous y avons installé Garage, un serveur S3 léger écrit en Rust : un binaire, un service systemd, un dataset ZFS compressé avec un quota. Caddy le publie en HTTPS et filtre les adresses : seules l'IP de sortie des clusters et le bastion passent.

Extrait du Caddyfile (généré par Ansible)
s3.k8s.exemple.fr, etcd-demo.s3.k8s.exemple.fr, etcd-mgmt.s3.k8s.exemple.fr {
        @autorise remote_ip 203.0.113.35 198.51.100.10
        handle @autorise {
                reverse_proxy 127.0.0.1:3900
        }
        respond 403
}
Un bucket et une clé par usage
garage bucket create velero-demo
garage bucket create etcd-demo
garage key create k8s-demo
garage bucket allow --read --write velero-demo --key k8s-demo
garage bucket allow --read --write etcd-demo --key k8s-demo

Chaque cluster a sa clé, qui n'ouvre que ses deux buckets : un cluster compromis ne peut pas effacer les sauvegardes de l'autre. Une règle de cycle de vie supprime les snapshots etcd de plus de 14 jours.

Les clés vont dans SOPS, pas à l'écran

garage key create affiche la clé secrète. Nous l'avons appris de la façon habituelle : une clé apparue en clair dans un terminal, donc dans un historique, est une clé à renouveler. Le playbook capture désormais la sortie et l'écrit directement dans SOPS.

Adressage par chemin ou par sous-domaine

La version de talos-backup utilisée ignore l'option qui force l'adressage des buckets par chemin et appelle etcd-demo.s3.k8s.exemple.fr. Garage sait servir ce mode (option root_domain), Caddy reçoit les noms des buckets concernés, et un enregistrement DNS générique couvre le tout.

03Velero : objets et volumes

Velero sauvegarde chaque nuit les objets Kubernetes. Pour les volumes, il demande un instantané CSI à ceph-csi, puis son data mover copie le contenu de l'instantané vers S3 avec Kopia : la sauvegarde ne dépend plus du Ceph d'origine.

Extrait de k8s/velero/values-demo.yaml
configuration:
  features: EnableCSI
  defaultSnapshotMoveData: true
  backupStorageLocation:
    - name: default
      provider: aws
      bucket: velero-demo
      default: true
      config:
        region: garage
        s3ForcePathStyle: "true"
        s3Url: https://s3.k8s.exemple.fr
        checksumAlgorithm: ""
snapshotsEnabled: false        # pas de VolumeSnapshotLocation : on passe par CSI
deployNodeAgent: true          # copie le contenu des snapshots vers S3 (Kopia)

schedules:
  quotidienne:
    schedule: "CRON_TZ=Europe/Paris 30 0 * * *"
    template:
      ttl: 720h
      snapshotMoveData: true

La ligne checksumAlgorithm: "" n'est pas une coquetterie : les versions récentes du SDK AWS ajoutent par défaut une somme de contrôle que les S3 compatibles n'acceptent pas tous. La clé S3 arrive dans un secret créé depuis SOPS, jamais dans les valeurs Helm.

04talos-backup : etcd chiffré

etcd est la mémoire du cluster : objets, secrets, état de tout. Le perdre, c'est l'amnésie totale. talos-backup demande un instantané à l'API Talos, le chiffre avec une clé publique age et l'envoie dans S3. La clé privée correspondante est dans SOPS, et nulle part ailleurs sur la plateforme.

Extrait de k8s/talos-backup/cronjob.yaml
# Un compte qui ne peut faire qu'une chose : sauvegarder etcd
apiVersion: talos.dev/v1alpha1
kind: ServiceAccount
metadata:
  name: talos-backup-secrets
  namespace: system-upgrade
spec:
  roles: [os:etcd:backup]
---
apiVersion: batch/v1
kind: CronJob
metadata:
  name: talos-backup
  namespace: system-upgrade
spec:
  schedule: "15 */6 * * *"
  timeZone: Europe/Paris
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      backoffLimit: 2
      template:
        spec:
          restartPolicy: Never           # garde le pod en échec, et ses journaux
          containers:
            - name: talos-backup
              image: ghcr.io/siderolabs/talos-backup:v0.1.0-beta.3
              envFrom:
                # clés S3, bucket, nom du cluster, clé publique age
                - secretRef: { name: talos-backup-s3 }
              env:
                - { name: CUSTOM_S3_ENDPOINT, value: "https://s3.k8s.exemple.fr" }

Le rôle os:etcd:backup doit être autorisé côté Talos, dans le document KubeTalosAPIAccessConfig vu au chapitre 02, pour le namespace concerné.

Une version bêta, avec ses surprises

La version de talos-backup disponible à la rédaction ignore plusieurs options documentées : l'adressage par chemin, la compression, et l'un des deux noms de variable de la clé age. Nous fournissons la clé sous les deux noms, servons les buckets en sous-domaine, et acceptons des snapshots non compressés (quelques dizaines de Mo). Autre leçon : avec restartPolicy: OnFailure, un job en échec supprime ses pods, et l'on enquête sans journaux.

05Restaurer avec Velero

Le test que nous faisons, et que nous vous conseillons de refaire régulièrement : restaurer un namespace dans un autre namespace, pour ne rien écraser.

Restauration de test
velero backup get
velero restore create test-restauration \
  --from-backup velero-quotidienne-20261028003000 \
  --include-namespaces boutique \
  --namespace-mappings boutique:boutique-restore \
  --wait
velero restore describe test-restauration
kubectl -n boutique-restore get pods,pvc

Les volumes sont recréés dans Ceph puis remplis depuis S3. Pour remplacer la production, on supprime le namespace d'origine et l'on restaure sans --namespace-mappings.

Les bases de données

Une copie de volume prise à chaud est cohérente « comme après une coupure de courant ». Une base de données s'en remet en général, mais « en général » n'est pas une politique de sauvegarde : pour une vraie base, ajoutez des hooks Velero qui figent les écritures, ou la sauvegarde native de la base vers S3.

06Reconstruire depuis etcd

Le scénario du pire : le cluster a disparu, VM comprises. La procédure tient en quatre étapes, et nous la tenons pour écrite tant qu'elle n'a pas été chronométrée lors d'un exercice de reprise.

  1. Récupérer et déchiffrer le dernier snapshot.
  2. Recréer les VM avec OpenTofu, sans laisser Talos amorcer un etcd vide.
  3. Amorcer le premier control plane depuis le snapshot ; les autres le rejoignent.
  4. Relancer le bootstrap du cluster pour réaligner le socle, puis restaurer les volumes avec Velero si besoin.
Étapes 1 et 3
SECRETS=secrets/secrets.sops.yaml
OBJ=$(rclone lsf -R --files-only s3:etcd-demo | tail -1)
rclone cat "s3:etcd-demo/$OBJ" \
  | age -d -i <(sops -d --extract '["etcd_backup"]["age_private"]' $SECRETS) \
  > db.snapshot

talosctl -n 10.20.1.11 bootstrap --recover-from=./db.snapshot

# le snapshot contient en clair tous les secrets du cluster
shred -u db.snapshot

La moitié du travail a déjà été vérifiée : nous déchiffrons régulièrement un snapshot et comptons les clés qu'il contient. Une archive qui ne se déchiffre pas, mieux vaut l'apprendre un mardi après-midi qu'un dimanche à 3 heures.

07Liste de contrôle

PointVérificationFait
S3Hors du cluster Proxmox, filtré par adresse, une clé par cluster☐
SecretsClés S3 et clé privée age dans SOPS, copie de la clé age hors du bastion☐
Velerovelero backup get : sauvegardes récentes, état Completed☐
VolumesData mover actif, VolumeSnapshotClass ceph-rbd présente☐
etcdJobs talos-backup Complete, objets récents dans le bucket☐
TestUne restauration Velero dans un namespace de test, chaque trimestre☐
TestUn snapshot etcd déchiffré, chaque mois☐
RepriseExercice complet de reconstruction depuis etcd, chronométré☐
AlertesUn échec de sauvegarde réveille quelqu'un (page observabilité, à venir)☐

Ce chapitre clôt la mise en place. Il reste deux briques, que nous traiterons dans une page à part : l'observabilité, pour qu'un échec de nuit ne passe plus inaperçu, et la sécurité des accès, avec authentification unique et double facteur. La plateforme est déjà prête à accueillir des développeurs : leur guide les attend.

Une plateforme Kubernetes, sans y laisser vos nuits

Nous concevons, montons et exploitons des plateformes Kubernetes sur Proxmox VE pour des équipes qui veulent se consacrer à leur code : mises à jour nocturnes, sauvegardes testées, déploiement en GitOps. Reprise d'un cluster existant, plateforme clé en main ou astreinte : nous intervenons au forfait comme au long cours.