Velero: sauvegardez et restaurez (proprement) vos applications Kubernetes

Rémi Verchère @ Accenture

Avant-propos

  • Je ne suis pas expert backup, je ne suis là que par pur 🎷 altruisme 👐
  • Pas de live coding, que du REX 🦖

Au sommaire

  • Présentation de l'outil
  • Utilisation en prod, gestion des données, etc..
  • Exemple de migration
  • Des fails, parce que vous aimez ça

Qui suis-je ?

👋 Rémi Verchère

💻 Cloud Native Infrastructure Consultant

Backup, Kubernetes ?

Backup & Kubernetes

Pourquoi ?

  • Conteneurs Stateless
  • Manifest YAML facilement redéployables
  • CI/CD
  • GitOps
  • Boucle de réconciliation
  • kubectl apply || crictl run et c'est réglé ^^
  • Plein d'autres raisons approximatives...

Backup & Kubernetes

Parce que, des fois, dans la vraie vie...

  • Conteneurs Statefull
  • Migration & Protection des données
  • Reprise après sinistre
  • Ressources en prod pas "exactement" les mêmes
  • L'erreur est humaine 👾
$ kubectl delete namespace la-prod

Backup & Kubernetes

Velero - Généralités

Un peu d'histoire

  • Heptio Ark, "open-sourcé" mi 2017
  • Société rachetée par VMware fin 2018
  • Intégré à l'offre Tanzu (TMC)
  • Actuellement en version 1.11.0 (2023-04-20)
$ git log --reverse
commit 2fe501f527a88ea292ca3dde80992ec60b388dda
Author: Andy Goldstein <andy.goldstein@gmail.com>
Date:   Wed Aug 2 13:27:17 2017 -0400

Velero - Généralités

Fonctionnalités principales

  • Sauvegarde des ressources d'un cluster et les restaure en cas de perte.
  • Migration, Réplication des ressources d'un cluster à un autre.
  • Custom Ressources qui définissent quoi sauvegarder, comment et quand (selector, schedule)

Composants

  • Serveur (Controller) qui tourne sur votre cluster
  • Client en ligne de commande qui s'exécute localement

Velero - Architecture

Velero - Installation

Cluster: Chart Helm

$ helm repo add vmware-tanzu https://vmware-tanzu.github.io/helm-charts && helm repo update
$ helm install velero vmware-tanzu/velero --create-namespace -n velero
Release "velero" has been installed. Happy Helming!

Poste client: CLI

$ asdf plugin add velero && asdf install velero 1.11.0 && asdf global velero 1.11.0
$ velero version
Client:
        Version: v1.11.0
        Git commit: 0da2baa908c88ec3c45da15001f6a4b0bda64ae2
Server:
        Version: v1.11.0
Aurélie Vache - Understanding Kubernetes in a visual way
Aurélie Vache - Understanding Kubernetes in a visual way

Mise en situation : la prod

Il était une fois la prod "de confiance"

Contexte 🇫🇷

  • Plusieurs clusters Kubernetes managés OVHcloud
    • Storage object Openstack Swift ou Standard S3
    • Storage block Cinder
  • Applications hétérogènes
  • Outils de gestion d'infra majoritairement Open Source
  • Besoin de Backup :
    • Sur stockage S3, loin
    • Réplication des applications sur cluster de test
    • Possibilité de PRA sur un autre cloud provider

Exemple d'application

Nextcloud

  • Chart Helm
  • 1 déploiement de l'application
  • 1 base Mariadb
  • 1 cache Redis (master / replicas)
  • Ingress, Cronjobs, etc.

https://github.com/nextcloud/helm

Exemple d'application

Nextcloud

$ kubectl get deployment,statefulset,cronjob,service,ingress
NAME                        READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nextcloud   1/1     1            1           28d

NAME                                        READY   AGE
statefulset.apps/nextcloud-mariadb          1/1     28d
statefulset.apps/nextcloud-redis-master     1/1     28d
statefulset.apps/nextcloud-redis-replicas   1/1     28d

NAME                           SCHEDULE       SUSPEND   ACTIVE   LAST SCHEDULE   AGE
cronjob.batch/nextcloud-cron   */15 * * * *   False     0        62s             28d

NAME                               TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
service/nextcloud                  ClusterIP   10.3.124.75    <none>        8080/TCP   28d
service/nextcloud-mariadb          ClusterIP   10.3.145.33    <none>        3306/TCP   28d
service/nextcloud-redis-headless   ClusterIP   None           <none>        6379/TCP   28d
service/nextcloud-redis-master     ClusterIP   10.3.207.110   <none>        6379/TCP   28d
service/nextcloud-redis-replicas   ClusterIP   10.3.249.214   <none>        6379/TCP   28d

NAME                                  CLASS    HOSTS                     ADDRESS                            PORTS     AGE
ingress.networking.k8s.io/nextcloud   <none>   nextcloud.demo.vrchr.fr   ip-135-125-84-204.gra.lb.ovh.net   80, 443   28d

Backup & Restore, l'aventure...

Velero - Première fois

1er backup

$ velero backup create nextcloud-backup-1 --include-namespaces nextcloud

$ velero backup describe nextcloud-backup-1
Name:         nextcloud-backup-1
Namespace:    velero
[...]
Phase:  Completed

Errors:    0
Warnings:  0
[...]
Total items to be backed up:  200
Items backed up:              200
[...]

Velero - Première fois

1er restore

$ velero restore create nextcloud-restore-1 --from-backup nextcloud-backup-1

$ velero restore nextcloud-restore-1
Name:         nextcloud-restore-1
Namespace:    velero
Labels:       <none>
Annotations:  <none>

Phase:                       Completed
Total items to be restored:  200
Items restored:              200

Velero - Première fois

1er fail

Objets kubernetes sauvés, données sacrifiées !

center

Velero Snapshots, Backups, Restore

Volumes Persistants

Velero - Sauvegarde des données

Objects Kubernetes

Les objets kubernetes sont exportés sur bucket S3

Données persistantes : 2x2 possibilités

  1. Snapshots des Persistent Volumes (PV)
    • via API du Cloud Provider
    • via CSI (>= version 1.4). ⚠️ Compatibilité Cloud Provider (Cinder: ✅)
  1. Export des volumes montés par les pods : File System Backup FSB (Swift: ✅)
    • via Restic
    • via Kopia (>= version 1.10)

Velero - Snapshot PV via CSI

 center

Velero - Snapshot PV via CSI

Activation avec chart Helm

Sous réserve que votre CSI le supporte : --features=EnableCSI

# $ helm get values velero
snapshotsEnabled: true
[...]
configuration:
  [...]
  features: EnableCSI
[...]
initContainers:
  - name: velero-plugin-for-csi
    image: velero/velero-plugin-for-csi:v0.4.0
    volumeMounts:
      - mountPath: /target
        name: plugins

Velero - Snapshot PV via CSI

J'ai testé pour vous 😭

$ velero backup get                                       
NAME                            STATUS            ERRORS   WARNINGS   CREATED                          EXPIRES   STORAGE LOCATION   SELECTOR
daily-s3-20220920000000         Failed            0        0          2022-09-20 02:00:00 +0200 CEST   1d        velero-gra9        <none>
daily-s3-20220916000004         Completed         0        0          2022-09-16 02:00:04 +0200 CEST   1d        velero-gra9        <none>
daily-s3-20220915000003         Completed         0        0          2022-09-15 02:00:03 +0200 CEST   5h        velero-gra9        <none>
daily-snapshot-20220920010027   Failed            0        0          2022-09-20 03:00:27 +0200 CEST   1d        velero-gra9        <none>
daily-snapshot-20220915010003   Completed         0        0          2022-09-15 03:00:03 +0200 CEST   6h        velero-gra9        <none>
$ kubectl logs -f velero-5b86bc8db-5p8md
time="2022-10-03T19:32:22Z" level=info msg="Waiting for volumesnapshotcontents
snapcontent-bc5660db-b0a4-4cc5-98cb-a071d5473dcb to have snapshot handle. Retrying in 5s" [...]
time="2022-10-03T19:32:27Z" level=info msg="Waiting for volumesnapshotcontents
snapcontent-bc5660db-b0a4-4cc5-98cb-a071d5473dcb to have snapshot handle. Retrying in 5s" [...]

Velero - Snapshot PV via CSI

J'ai insisté, avec les équipes du CSP 💪

$ velero backup get                                       
NAME                            STATUS      ERRORS   WARNINGS   CREATED                          EXPIRES   STORAGE LOCATION   SELECTOR
daily-s3-20221004182135         Completed   0        0          2022-10-04 20:21:35 +0200 CEST   1d        velero-gra9        <none>
daily-s3-20221004181435         Completed   0        0          2022-10-04 20:14:35 +0200 CEST   1d        velero-gra9        <none>
[...]
daily-snapshot-20221004190535   Completed   0        0          2022-10-04 21:05:35 +0200 CEST   1d        velero-gra9        <none>
daily-snapshot-20221004190035   Completed   0        0          2022-10-04 21:00:35 +0200 CEST   1d        velero-gra9        <none>
daily-snapshot-20221004185535   Completed   0        0          2022-10-04 20:55:35 +0200 CEST   1d        velero-gra9        <none>
daily-snapshot-20221004185035   Completed   0        0          2022-10-04 20:50:35 +0200 CEST   1d        velero-gra9        <none>
[...]

⚠️ Aux limites configurées côté storage provider

⚠️ N'est pas considéré comme stockage pérenne

Velero - Backup via File System Backup (FSB)

 center

Velero - Backup via FSB (Restic)

Activation avec chart Helm

# $ helm get values velero
deployNodeAgent: true
# Deploiement des Daemonsets sur les nodes
[...]
configuration:
  [...]
  uploaderType: restic # par défaut pour le moment
  defaultVolumesToFsBackup: true
[...]
initContainers:
  # Deja défini pour l'export des objets k8s
  - name: velero-plugin-for-aws
    image: velero/velero-plugin-for-aws:v1.6.0
    volumeMounts:
      - mountPath: /target
        name: plugins

Velero - Backup via FSB (Restic)

2 modes de backup

  1. Opt-in: rien par défaut, on sélectionne ce qu'on veut sauvegarder
podAnnotations:
  ## Velero annotations
  backup.velero.io/backup-volumes-includes: nextcloud-main
  1. Opt-out: tout par défaut, on sélectionne ce qu'on veut exclure
    Par défaut depuis Velero 1.5
podAnnotations:
  ## Velero annotations
  backup.velero.io/backup-volumes-excludes: redis-data

Velero - Restore

  • Process de restore "presque" l'inverse du backup, mais en plus :

    • Validation des ressources seront compatibles

    • Ordre de restauration des ressources

    • Possibilité de changer de namespace --namespace-mappings

    • Possibilité de changer de storage classvelero.io/change-storage-class

    • Mode "incrémental" --existing-resource-policy

Données (PV/PVC)

  • Si Snapshot : Restore du volume, puis remapping / reference

  • Si FSB: Création d'un nouveau volume, réhydratation des données

Velero - Restore avec FSB (Restic)

center

Snapshots ou FSB ?

Velero - Snapshots ou FSB ?

Snapshot FSB
Non atomique Non atomique
Même média Média différent
Plus rapide Plus lent
Proche Distant (⚠️ coût transferts)
Non chiffré (⚠️ Storage Class) Chiffré
Même Cloud Provider (⚠️ Zone) Cloud Provider Agnostic

Velero - Snapshots ou FSB ?

Rappels de bonnes pratiques

center

voir même 3-2-1-1

Velero - Snapshots ou FSB ?

Les 2 ! Mais...

  • Bien configurer l'opt-in / opt-out côté FSB (defaultVolumesToFsBackup)

  • Bien configurer includes / excludes des volumes

  • Bien configurer l'utilisation de snapshots (--snapshot-volumes)

⚠️ Certaines combinaisons ne fonctionnent pas ensemble

  • Si opt-in && includes : pas de snapshots des volumes
time="2022-10-02T00:01:04Z" level=info msg="Skipping snapshot of persistent volume \
because volume is being backed up with restic."

Velero - Snapshots ou FSB ?

Mes recommendations

  • opt-out par défaut

  • 1 schedule classique: backup S3, avec excludes pour certains volumes, sans snapshots (snapshotVolumes: false)

  • 1 schedule snapshots: defaultVolumesToFsBackup: false (opt-in)

  • Dans le doute : FSB

1 point sur les hooks...

Velero - Hooks

  • Pre & Post Hooks

  • Pod Annotation ou Backup Spec

    • pre.hook.backup.velero.io/container

    • pre.hook.backup.velero.io/command

    • pre.hook.backup.velero.io/on-error

    • pre.hook.backup.velero.io/timeout

⚠️ commande doit être disponible, sinon utilisation de sidecar container

Et 1 autre sur les Bases de Données !

Velero - BDD & Atomicité

  1. Utilisation des hooks avec fsfreeze, sqldump
    ⚠️ blocage de la BDD le temps du backup

  2. Utilisation de BDD réplica en read-only
    Hook de backup sur le réplica, à l'ancienne

  1. En dehors du cluster Kubernetes

  2. Utilisation de BDD managée 🙆

  • Blueprints de backups ? Voir Kanister

🌩️ Cas de migration cloud 🌩️

Velero - Cas d'une migration cloud

center

Velero - Cas d'une migration cloud

  • Quelques points de vigilance

    • Bascule DNS (CQFD)

    • Version de Kubernetes & API différentes : "API Group Versions Feature"

    • Certaines ressources non nécessaires : "--exclude-resources"

    $ velero restore --exclude-resources CustomResourceDefinition,CertificateRequest,Order
    
    • Storage différent : "Changing PV/PVC Storage Class"
    kind: ConfigMap
    metadata:
      labels:
        velero.io/change-storage-class: RestoreItemAction
    data:
      <old-storage-class>: <new-storage-class>
    

Velero - Cas d'une migration cloud

  • Temps de restauration assez long (si plusieurs centaines de Go)
    Comment optimiser ce temps de "bascule" ?
    • Flag --existing-resource-policy pour restore "incrémental" ?

center

Velero - Cas d'une migration cloud

  • Temps de restauration assez long (si plusieurs centaines de Go)
    Comment optimiser ce temps de "bascule" ?
    • Réponse: rsync !

center

OpenShift MTC

center

OpenShift MTC

The Migration Toolkit for Containers (MTC) enables you to migrate stateful application workloads between OpenShift Container Platform 4 clusters at the granularity of a namespace.

The file system copy method uses Restic for indirect migration or Rsync for direct volume migration.

https://docs.openshift.com/container-platform/4.11/migration_toolkit_for_containers/about-mtc.html

✨ Quelques fails ✨

Velero - Backup qui s'est mal passé

  1. Storage Location
    Oubli de préciser, Velero ne sait pas où sauvegarder
spec:
  storageLocation: ovh-velero-storage-location
$ kubectl logs -f velero-7dcfbb4b6b-k8fl9
time="2023-01-31T03:09:07Z" level=warning msg="There is no existing backup storage location set as default.
Please see `velero backup-location-h` for options." controller=backup-storage-location
logSource="pkg/controller/backup_storage_location_controller.go:173"
  1. Timeout
    Sur gros volumes, Restic part en timeout
configuration:
  resticTimeout: 2h

Velero - Backup qui s'est mal passé

  1. Objets S3
    Backup trop gros (beaucoup de namespaces)

center

Velero - Backup qui s'est mal passé

  1. Restic OOMKilled !
    Augmenter les requests & limits (grosse amélioration depuis Restic 0.14.0)
    GOGC ! (https://tip.golang.org/doc/gc-guide)
    ⚠️ Ressources Daemonset
configuration:
  extraEnvVars:
    GOGC: 10
resources: # Velero
  limits:
    cpu: null
    memory: 2Gi # <--- ⚠ Restic Prune !
restic:
  resources:
    limits:
      cpu: "2"
      memory: 4Gi

Velero - Backup qui s'est mal passé

  1. Restic Locked backend
    Gestion multi clusters : attention aux storage location RW
stderr=unable to create lock in backend: repository is already locked exclusively by PID 11108
on velero-76cfbd7858-5fr8t by nonroot (UID 65532, GID 65532)
lock was created at 2022-09-30 04:00:05 (1m5.593684965s ago)
storage ID 44a72b6f the `unlock` command can be used to remove stale locks
exit status 1" error.file="/go/src/github.com/vmware-tanzu/velero/pkg/restic/backupper.go:184"
error.function="github.com/vmware-tanzu/velero/pkg/restic.(*backupper).BackupPodVolumes"
logSource="pkg/backup/backup.go:417"
name=nextcloud-mariadb-0
$ kubectl patch backupstoragelocation <STORAGE LOCATION NAME> \
    --namespace velero \
    --type merge \
    --patch '{"spec":{"accessMode":"ReadOnly"}}'

Velero - Backup qui s'est mal passé

  1. Restic, NFS & ReadWriteMany

5.1. Backup Serveur NFS

5.2. Backup PV Pod 1

5.3. Backup PV Pod 2

5.4. Backup PV Pod N

 

msg="Pod volume uses a persistent volume claim which has already been backed up \
with restic from another pod, skipping."

Velero - Restore qui s'est mal passé

  1. Restic Annotations
    Oubli d'annoter 1 volume lors du backup 🤭
podAnnotations:
  backup.velero.io/backup-volumes-includes:
  1. Selector
    Tout n'est pas pris en compte
$ kubectl get all -l app=mariadb-database
NAME                      READY   STATUS    RESTARTS   AGE
pod/mariadb-database-0   1/1     Running   0          93d

Velero - Restore qui s'est mal passé

  1. Base de données corrompue !
    Pas d'atomicité : Utilisation des pre-hooks & post-hooks
    Sur Backup, mais aussi sur Restore
metadata:
      annotations:
        backup.velero.io/backup-volumes: data
        pre.hook.backup.velero.io/command: '["/bin/bash", "-c",
          "mkdir -p /bitnami/mariadb/backups \
          && mysqldump -u $MARIADB_USER -p$MARIADB_PASSWORD \
          $MARIADB_DATABASE > /bitnami/mariadb/backups/nextcloud.dump"]'

Faut-il aussi pouvoir récupérer les dumps (tar)...

$ kubectl cp nextcloud-mariadb-0:~/nextcloud.dump ./nextcloud.dump                                                         
command terminated with exit code 126

Velero - Restore qui s'est mal passé

  1. Statefulset disparu ?!
    Bug avec velero.io/change-storage-class
$ velero restore create nextcloud-migration-22091501 --from-backup nextcloud-22091501
$ velero restore describe nextcloud-migration-22091501
[...]
Phase: PartiallyFailed (run 'velero restore logs nextcloud-migration-22091501' for more information)
Total items to be restored:  105
Items restored:              105

Started:    2022-09-15 21:02:46 +0200 CEST
Completed:  2022-09-15 22:13:04 +0200 CEST

[...]

Errors:
  Namespaces:
    nextcloud:  error preparing statefulsets.apps/nextcloud/nextcloud-mariadb: rpc error: code = Aborted desc = plugin panicked: \
                runtime error: invalid memory address or nil pointer dereference
                error preparing statefulsets.apps/nextcloud/nextcloud-redis-master: rpc error: code = Aborted desc = plugin panicked: \
                runtime error: invalid memory address or nil pointer dereference
[...]

Velero, encore plus !

Monitoring

metrics:
  serviceMonitor:
    enabled: true
# HELP velero_backup_attempt_total Total number of attempted backups
# HELP velero_backup_deletion_success_total Total number of successful backup deletions
# HELP velero_backup_duration_seconds Time taken to complete backup, in seconds
# HELP velero_backup_failure_total Total number of failed backups
# HELP velero_backup_items_errors Total number of errors encountered during backup
# HELP velero_backup_validation_failure_total Total number of validation failed backups
# HELP velero_csi_snapshot_attempt_total Total number of CSI attempted volume snapshots
successful volume snapshots
# HELP velero_restore_attempt_total Total number of attempted restores
# HELP velero_restore_total Current number of existent restores
# HELP velero_restore_validation_failed_total Total number of failed restores failing validations
# HELP velero_volume_snapshot_attempt_total Total number of attempted volume snapshots

Monitoring

center

Alerting !

center

Backup Policies

Kyverno

center

https://kyverno.io/policies/?policytypes=Velero

Derniers conseils

  • Lisez la doc Velero !
  • Vérifiez l'état de vos backups
  • Validez vos restaurations régulièrement
  • On n'est pas obligé de tout mettre dans Kubernetes, cf services managés 🙃
  • En vrai, ça marche plutôt pas mal, faut-il bien configurer l'outil.

Ils utilisent Velero

  • Openshift "OADP" & "MTC"
  • VMware "TMC"
  • Accenture 😬
  • Vous ? Parlons-en !

Merci !

Rémi Verchère @ Accenture

Mutation Wehbook

Notes: Je hais les backups, mais je hais encore plus perdre des données. Depuis quelques temps j'administre des cluster k8s & applications qui tournent dessus. On m'a présenté Velero comme l'outil magique, mais j'y crois pas trop ;) J'ai pas trouvé bcp de ressources FR sur le sujet, d'où ce talk pour partager avec vous mes galères !

Pas de sauvegarde d'ETCD / Control planes, on reste sur l'applicatif

1. The Velero client makes a call to the Kubernetes API server to create a Backup object. 2. The BackupController notices the new Backup object and performs validation. 3. The BackupController begins the backup process. It collects the data to back up by querying the API server for resources. 4. The BackupController makes a call to the object storage service – for example, AWS S3 – to upload the backup file.

CSI: Container Storage Interface

⚠️ `fsfreeze` & montages NFS

# Velero - Cas d'une migration cloud - Installation de Velero sur les 2 clusters. - Configuration des mêmes Storage Location (Read Only) - Backup Velero Cluster A, sur Bucket S3 avec Restic - Restore sur Cluster B, depuis même Bucket S3 avec Restic - Facile ! Ou pas...

Utilisation de nfs-ganesha-server-and-external-provisioner