Définir une logique personnalisée de sauvegarde et de restauration


Lorsque vous activez l'agent Sauvegarde pour GKE dans votre cluster Google Kubernetes Engine, Sauvegarde pour GKE fournit une ressource CustomResourceDefinition, qui introduit un nouveau type de ressource Kubernetes : le ProtectedApplication.

La composition d'un ProtectedApplication implique trois activités :

Les ressources ProtectedApplication apportent les fonctionnalités suivantes lorsque vous personnalisez la logique de sauvegarde et de restauration au niveau de l'application :

  • Opérations de sauvegarde et de restauration plus précises. Sans ProtectedApplications, le champ d'application de vos sauvegardes doit être défini au niveau de Namespace (en sélectionnant allNamespaces ou selectedNamespaces. La même logique s'applique à la restauration de ressources d'espace de noms. La création de ressources ProtectedApplication vous permet de fournir un nom à un sous-ensemble de ressources d'un Namespace. Vous pouvez ensuite sauvegarder et restaurer ce sous-ensemble en répertoriant selectedApplications dans votre champ d'application de sauvegarde (et également, pour la restore).

  • Orchestrer des détails précis du processus de sauvegarde ou de restauration, y compris :

    • Ignorer des volumes spécifiques lors de la sauvegarde.

    • Intégrer la topologie de l'application à la sauvegarde et à la restauration (pour par exemple ne sauvegarder qu'une seule instance d'une base de données répliquée et l'utiliser pour restaurer plusieurs instances).

    • L'exécution de hooks définis par l'utilisateur avant et après que les volumes ont été capturés. Ils peuvent être utilisés, par exemple, pour vider et suspendre une charge de travail avant de créer un instantané et la faire reprendre après.

La création de ressources ProtectedApplication s'effectue via kubectl, comme pour les autres ressources Kubernetes. Ces ressources sont totalement facultatives. En l'absence de ressources ProtectedApplication, Sauvegarde pour GKE crée des sauvegardes de volume pour tous les volumes couverts par une sauvegarde, et les sauvegardes de volume obtenues sont de type cohérent avec les plantages, c'est-à-dire que toutes les écritures supprimées du disque à quelque moment que ce soit sont capturées (c'est-à-dire aucune écriture partielle). Toutefois, certaines applications peuvent conserver en mémoire les données qui ne sont pas vidées sur le disque. Par conséquent, le fait de pouvoir récupérer une application après une sauvegarde cohérente avec le plantage dépend de la logique de l'application.

Sélectionner des ressources

La première étape de la création de votre ressource ProtectedApplication consiste à identifier les autres ressources du même Namespace que celui que vous souhaitez inclure dans l'application. Il s'agit de l'ensemble des ressources qui seront sauvegardées ou restaurées si vous spécifiez l'option selectedApplications dans votre configuration BackupPlan.

Les ressources sont identifiées à l'aide d'un sélecteur de libellés. Vous devez donc attribuer le même libellé à toutes vos ressources (à l'aide du champ metadata.label de chaque ressource). Notez que cela s'applique également aux ressources créées automatiquement par les contrôleurs. Ces ressources créées automatiquement se voient attribuer un libellé à l'aide du modèle correspondant. Notez qu'il est courant de réutiliser le même libellé que celui que vous utilisez déjà pour associer les éléments Pods et PersistentVolumeClaims générés à leur ressource parente. L'exemple suivant montre comment appliquer le libellé app: nginx aux autres ressources en plus de Deployment.

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-vars
  namespace: webserver
  labels:
    app: nginx
  data:
    ...
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nginx-logs
  namespace: webserver
  labels:
    app: nginx
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 50Mi
  storageClassName: standard-rwo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: webserver
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      volumes:
        - name: nginx-logs
          persistentVolumeClaim:
           claimName: nginx-logs
      containers:
      ...

Une fois que le libellé sélectionné est appliqué à toutes vos ressources cibles (et aux modèles à partir desquels les ressources supplémentaires sont générées), vous pouvez les référencer à partir d'une ProtectedApplication. Exemple :

kind: ProtectedApplication
apiVersion: gkebackup.gke.io/v1
metadata:
  name: nginx
  namespace: webserver
spec:
  resourceSelection:
    type: Selector
    selector:
      matchLabels:
        app: nginx
  ...

Définir des règles d'orchestration

Une fois que toutes les ressources de votre ProtectedApplication sont identifiées, vous pouvez choisir de définir des règles d'orchestration détaillées pour un sous-ensemble de ces ressources. Ces règles ne peuvent s'appliquer qu'à deux types de ressources : les Deployments et les StatefulSets. Elles sont référencées dans la section components de ProtectedApplication.

Présentation des composants

La configuration d'un composant implique les opérations suivantes :

  • Sélectionner une stratégie fondamentale, régissant les modalités de sauvegarde et de restauration de ce composant. Trois stratégies sont disponibles :

    • BackupAllRestoreAll : sauvegarde les volumes associés à toutes les instances du composant, et restaure tous ces volumes à partir des sauvegardes.

    • BackupOneRestoreAll : sauvegarde les volumes à partir d'une seule instance du composant, et utilise ces sauvegardes pour restaurer toutes les instances.

    • DumpAndLoad : exporte les données de l'application vers un seul volume au moment de la sauvegarde, et importe ces données dans l'application au moment de la restauration.

  • Définir les hooks d'exécution à exécuter pendant la sauvegarde (et éventuellement la restauration, selon la stratégie). Un hook est une commande exécutée dans des conteneurs spécifiques.

  • Sélectionner un sous-ensemble de volumes à sauvegarder.

Hooks d'exécution

Un hook est une commande shell exécutée par Sauvegarde pour GKE dans un conteneur à une phase particulière du processus de sauvegarde ou de restauration.

Il existe quatre types de hooks différents :

  • pre hooks : ces commandes sont exécutées juste avant la sauvegarde des volumes. Elles sont généralement censées vider toutes les données en mémoire sur le disque, puis suspendre l'application afin qu'aucune nouvelle écriture sur disque ne se produise. Ces hooks sont utilisés dans les stratégies BackupAllRestoreAll et BackupOneRestoreAll.

  • post hooks : ces commandes sont exécutées pendant le processus de sauvegarde de volume juste après l'étape de prise d'instantané du processus de sauvegarde de volume (avant l'étape d'importation). En règle générale, l'étape de prise d'instantané ne prend que quelques secondes. Elles sont généralement censées relancer l'application (c'est-à-dire autoriser le traitement normal et les écritures sur disque). Ces hooks sont utilisés dans les stratégies BackupAllRestoreAll, BackupOneRestoreAll et DumpAndLoad.

  • dump hooks : ces commandes sont exécutées avant que le volume ne soit sauvegardé dans la stratégie DumpAndLoad. Elles sont généralement censées exporter les données de l'application vers le volume de sauvegarde désigné.

  • load hooks : ces commandes sont exécutées au moment de la restauration, après que le volume de sauvegarde a été restauré dans le contexte de cas d'utilisation DumpAndLoad. Elles sont généralement censées importer les données du volume de sauvegarde dans l'application.

Vous pouvez fournir plusieurs hooks pour chaque type, et Sauvegarde pour GKE les exécutera dans l'ordre dans lequel vous les définissez.

Vous définissez des hooks dans la section de la spécification ProtectedApplication dédiée aux composants. Toutes les définitions de hook proposent les mêmes champs :

  • name : nom que vous attribuez au hook.

  • container : (facultatif) nom du conteneur dans lequel exécuter la commande. Si vous ne fournissez pas le conteneur, Sauvegarde pour GKE exécute le hook dans le premier conteneur défini pour le ou les Pods cibles.

  • command : commande effectivement envoyée au conteneur, construite sous la forme d'un tableau de mots. Le premier mot du tableau est le chemin d'accès à la commande ; les mots suivants correspondent aux arguments à transmettre à la commande.

  • timeoutSeconds (facultatif) : durée avant l'annulation de l'exécution du hook. Si vous ne fournissez pas cette valeur, elle sera définie par défaut sur 30 secondes.

  • onError (facultatif) : comportement adopté en cas d'échec du hook. Peut être défini sur Ignore ou Fail (valeur par défaut). Si cette valeur est définie sur Fail, la sauvegarde de volume échoue en cas d'échec d'un hook. Si vous définissez cette valeur sur Ignore, les échecs de ce hook sont ignorés.

Avant d'appliquer des hooks ProtectedApplication à votre application, vous devez tester la commande avec kubectl exec pour vous assurer que les hooks se comportent comme prévu :

kubectl exec POD_NAME -- COMMAND

Remplacez les éléments suivants :

  • POD_NAME : nom du pod qui contient la ressource ProtectedApplication.
  • COMMAND : tableau contenant la commande que vous souhaitez exécuter dans le conteneur, par exemple /sbin/fsfreeze, -f, /var/log/nginx.

Sélectionner un sous-ensemble de volumes à sauvegarder

Parfois, les applications écrivent dans des volumes qu'il n'est pas intéressant de restaurer (par exemple, certains volumes de journaux ou de travail). Vous pouvez supprimer la sauvegarde de ces volumes à l'aide d'un sélecteur de volume.

Pour utiliser cette fonctionnalité, vous devez d'abord appliquer un libellé commun aux volumes que vous souhaitez sauvegarder, puis laisser ce libellé désactivé pour les volumes que vous ne souhaitez pas sauvegarder. Vous devez ensuite inclure une clause volumeSelector dans la définition de votre composant, comme suit :

spec:
  ...
  components:
  ...
    strategy:
      ...
      volumeSelector:
        matchLabels:
          label_name: label_value

Si vous fournissez une clause volumeSelector pour un composant, seuls les volumes portant le libellé donné seront sauvegardés et restaurés. Au moment de la restauration, tous les autres volumes seront provisionnés vides, plutôt que d'être restaurés à partir d'une sauvegarde de volume.

Stratégie : BackupAllRestoreAll

Il s'agit de la stratégie la plus simple : elle sauvegarde tous les volumes du composant au moment de la sauvegarde, et restaure tous ces volumes à partir de leurs sauvegardes au moment de la restauration. Il s'agit du meilleur choix lorsque votre application ne nécessite pas de réplication entre les Pods.

Cette stratégie accepte les paramètres suivants :

  • backupPreHooks : (facultatif) liste numérotée de hooks exécutés juste avant la sauvegarde des volumes. Ces commandes sont exécutées sur tous les Pods du composant.

  • backupPostHooks : (facultatif) liste numérotée des hooks exécutés une fois que les sauvegardes de volume ont atteint la phase d'importation. Ces commandes sont exécutées sur tous les Pods du composant.

  • volumeSelector : (facultatif) logique permettant de faire correspondre un sous-ensemble de volumes à une sauvegarde.

L'exemple ci-dessous crée une ressource ProtectedApplication qui suspend le système de fichiers avant de sauvegarder le volume de journaux, puis annule la suspension après la sauvegarde :

kind: ProtectedApplication
apiVersion: gkebackup.gke.io/v1
metadata:
  name: nginx
  namespace: sales
spec:
  resourceSelection:
    type: Selector
    selector:
      matchLabels:
        app: nginx
  components:
  - name: nginx-app
    resourceKind: Deployment
    resourceNames: ["nginx"]
    strategy:
      type: BackupAllRestoreAll
      backupAllRestoreAll:
        backupPreHooks:
        - name: fsfreeze
          container: nginx
          command: [ /sbin/fsfreeze, -f, /var/log/nginx ]
        backupPostHooks:
        - name: fsunfreeze
          container: nginx
          command: [ /sbin/fsfreeze, -u, /var/log/nginx ]

Stratégie : BackupOneAndRestoreAll

Cette stratégie sauvegarde une copie d'un pod sélectionné. Cette copie unique sert de source pour la restauration de tous les pods lors d'une opération de restauration. Cette méthode permet de réduire les coûts de stockage et le temps de sauvegarde. Cette stratégie fonctionne dans une configuration à haute disponibilité lorsqu'un composant est déployé avec une PersistentVolumeClaim principale et plusieurs PersistentVolumeClaims secondaires.

Cette stratégie accepte les paramètres suivants :

  • backupTargetName (obligatoire) : spécifie l'objet Deployment ou StatefulSet que vous souhaitez utiliser pour sauvegarder les données. Le meilleur pod à sauvegarder est sélectionné automatiquement. Dans une configuration à haute disponibilité, nous vous recommandons de définir ce paramètre sur l'une des instances répliquées de votre application.

  • backupPreHooks : (facultatif) liste numérotée de hooks exécutés juste avant la sauvegarde des volumes. Ces commandes s'exécutent uniquement sur le Pod de sauvegarde sélectionné.

  • backupPostHooks : (facultatif) liste numérotée des hooks exécutés une fois que les sauvegardes de volume ont atteint la phase d'importation. Ces commandes s'exécutent uniquement sur le Pod de sauvegarde sélectionné.

  • volumeSelector : (facultatif) logique permettant de faire correspondre un sous-ensemble de volumes à une sauvegarde.

Si un composant est configuré avec plusieurs déploiements ou StatefulSets, toutes les ressources doivent avoir la même structure PersistentVolume, ce qui signifie qu'elles doivent respecter les règles suivantes :

  • Le nombre de PersistentVolumeClaims utilisées par tous les déploiements ou StatefulSets doit être identique.
  • L'objectif de PersistentVolumeClaims dans le même index doit être identique. Pour les StatefulSets, l'index est défini dans volumeClaimTemplate. Pour les déploiements, l'index est défini dans Volumes et tous les volumes non persistants sont ignorés.
  • Si le composant d'application se compose de déploiements, chaque déploiement doit posséder exactement une instance dupliquée.

Compte tenu de ces éléments, plusieurs ensembles de volume peuvent être sélectionnés pour la sauvegarde, mais un seul volume de chaque ensemble de volumes sera sélectionné.

Cet exemple, dans l'hypothèse d'une architecture comprenant un StatefulSet principal et un StatefulSet secondaire, indique une sauvegarde de volumes d'un pod dans un StatefulSet secondaire, puis une restauration pour tous les autres volumes :

kind: ProtectedApplication
apiVersion: gkebackup.gke.io/v1
metadata:
  name: mariadb
  namespace: mariadb
spec:
  resourceSelection:
    type: Selector
    selector:
      matchLabels:
        app: mariadb
  components:
  - name: mariadb
    resourceKind: StatefulSet
    resourceNames: ["mariadb-primary", "mariadb-secondary"]
    strategy:
      type: BackupOneRestoreAll
      backupOneRestoreAll:
        backupTargetName: mariadb-secondary
        backupPreHooks:
        - name: quiesce
          container: mariadb
          command: [...]
        backupPostHooks:
        - name: unquiesce
          container: mariadb
          command: [...]

Stratégie : DumpAndLoad

Cette stratégie utilise un volume dédié aux processus de sauvegarde et de restauration. Elle nécessite une PersistentVolumeClaim dédiée associée à un composant qui stocke les données de vidage.

Cette stratégie accepte les paramètres suivants :

  • dumpTarget (obligatoire) : spécifie l'objet Deployment ou StatefulSet que vous souhaitez utiliser pour sauvegarder les données. Le meilleur pod à sauvegarder est sélectionné automatiquement. Dans une configuration à haute disponibilité, nous vous recommandons de le définir sur l'une des instances dupliquées de votre application.

  • loadTarget (obligatoire) : spécifie l'objet Deployment ou StatefulSet à utiliser pour charger les données. Le meilleur pod à sauvegarder est sélectionné automatiquement. La cible de chargement ne doit pas nécessairement être identique à la cible de vidage.

  • dumpHooks : (obligatoire) liste numérotée de hooks exécutés pour remplir le volume de sauvegarde dédié. Ces commandes ne sont exécutées que sur le Pod de vidage sélectionné.

  • backupPostHooks : (facultatif) liste numérotée des hooks exécutés une fois que les sauvegardes de volume ont atteint la phase d'importation. Ces commandes ne sont exécutées que sur le Pod de vidage sélectionné.

  • loadHooks : (obligatoire) liste numérotée des hooks exécutés pour charger les données à partir du volume restauré, après le démarrage de l'application. Ces commandes ne sont exécutées que sur la charge Pod sélectionnée.

  • volumeSelector : (obligatoire) logique permettant de faire correspondre un seul volume à la sauvegarde et à la restauration (le volume de "vidage"). Bien que ce paramètre ne doive correspondre qu'à un seul volume, sa configuration est identique à la définition d'un sous-ensemble de volumes à sauvegarder, utilisé par d'autres stratégies.

Si l'application comprend des déploiements, chaque déploiement doit posséder exactement une instance dupliquée.

L'exemple ci-dessous, en prenant pour hypothèse une architecture comprenant un StatefulSet principal et un StatefulSet secondaire avec des PersistentVolumeClaims dédiées pour le StatefulSet principal et le StatefulSet secondaire, exploite une stratégie DumpAndLoad :

kind: ProtectedApplication
apiVersion: gkebackup.gke.io/v1
metadata:
  name: mariadb
  namespace: mariadb
spec:
  resourceSelection:
    type: Selector
    selector:
      matchLabels:
        app: mariadb
  components:
  - name: mariadb-dump
    resourceKind: StatefulSet
    resourceNames: ["mariadb-primary", "mariadb-secondary"]
    strategy:
      type: DumpAndLoad
      dumpAndLoad:
        loadTarget: mariadb-primary
        dumpTarget: mariadb-secondary
        dumpHooks:
        - name: db_dump
          container: mariadb
          command:
          - bash
          - "-c"
          - |
            mysqldump -u root --all-databases > /backup/mysql_backup.dump
        loadHooks:
        - name: db_load
          container: mariadb
          command:
          - bash
          - "-c"
          - |
            mysql -u root < /backup/mysql_backup.sql
        volumeSelector:
          matchLabels:
            gkebackup.gke.io/backup: dedicated-volume

Vérifier si un fichier ProtectedApplication est prêt pour la sauvegarde

Vous pouvez vérifier si une ProtectedApplication est prête pour une sauvegarde en exécutant la commande suivante :

kubectl describe protectedapplication APPLICATION_NAME

Remplacez APPLICATION_NAME par le nom de votre application.

Si elle est prête, la description de l'application affiche l'état Ready to backup sous la forme true, comme dans l'exemple suivant :

% kubectl describe protectedapplication nginx
Name:         nginx
Namespace:    default
API Version:  gkebackup.gke.io/v1
Kind:         ProtectedApplication
Metadata:
  UID:               90c04a86-9dcd-48f2-abbf-5d84f979b2c2
Spec:
  Components:
    Name:           nginx
    Resource Kind:  Deployment
    Resource Names:
      nginx
    Strategy:
      Backup All Restore All:
        Backup Pre Hooks:
          Command:
             /sbin/fsfreeze
             -f
             /var/log/nginx
          Container:         nginx
          Name:              freeze
        Backup Post Hooks:
          Command:
             /sbin/fsfreeze
             -u
             /var/log/nginx
          Container:         nginx
          Name:              unfreeze
      Type:                  BackupAllRestoreAll
  Resource Selection:
    Selector:
      Match Labels:
        app:        nginx
    Type:           Selector
 Status:
  Ready To Backup:  true 
Events:             <none>

Étape suivante