Definir una lógica de copia de seguridad y restauración personalizada


Cuando habilitas el agente de Backup for GKE en tu clúster de Google Kubernetes Engine, Backup for GKE proporciona un CustomResourceDefinition que introduce un nuevo tipo de recurso de Kubernetes: el ProtectedApplication.

Para escribir un ProtectedApplication, se deben realizar tres actividades:

Los recursos de ProtectedApplication te ofrecen estas funciones cuando personalizas la lógica de copia de seguridad y restauración a nivel de aplicación:

  • Operaciones de copia de seguridad y restauración más precisas. Sin ProtectedApplications, el ámbito de tus copias de seguridad debe definirse a nivel de Namespace (seleccionando allNamespaces o selectedNamespaces). Se aplica una lógica similar a la restauración de recursos con espacio de nombres. Crear recursos ProtectedApplication te permite asignar un nombre a un subconjunto de los recursos de un Namespace. Después, puedes crear una copia de seguridad y restaurar ese subconjunto incluyendo selectedApplications en el ámbito de la copia de seguridad (y, de forma similar, para restaurar).

  • Orquestar los detalles de la copia de seguridad o del proceso de restauración, como:

    • Se omiten los volúmenes seleccionados durante la copia de seguridad.

    • Incorporar la topología de las aplicaciones a las copias de seguridad y la restauración (por ejemplo, crear una copia de seguridad de una sola instancia de una base de datos replicada y usarla para restaurar varias instancias).

    • Ejecutar enlaces definidos por el usuario antes y después de crear instantáneas de los volúmenes. Por ejemplo, se pueden usar para vaciar y silenciar una carga de trabajo antes de crear una instantánea y, después, reactivarla.

Puedes crear ProtectedApplication con kubectl, como otros recursos de Kubernetes. Son completamente opcionales. Si no hay recursos de ProtectedApplication, Backup for GKE crea copias de seguridad de volumen de todos los volúmenes incluidos en el ámbito de una copia de seguridad. Las copias de seguridad de volumen resultantes serán coherentes con los fallos: se capturarán todas las escrituras volcadas en el disco en un momento concreto (es decir, no habrá escrituras parciales). Sin embargo, es posible que algunas aplicaciones mantengan datos en la memoria que no se escriben en el disco, por lo que el hecho de que una aplicación pueda recuperarse correctamente de una copia de seguridad coherente en caso de fallo depende de la lógica de la aplicación.

Seleccionar recursos

El primer paso para crear el recurso ProtectedApplication es identificar los demás recursos del mismo Namespace que quieras incluir en la aplicación. Se trata del conjunto de recursos de los que se creará una copia de seguridad o que se restaurarán si proporcionas la opción de ámbito selectedApplications en tu configuración de BackupPlan.

Los recursos se identifican mediante un selector de etiquetas. Para ello, debe etiquetar todos sus recursos (mediante el campo metadata.label de cada recurso) con la misma etiqueta. Ten en cuenta que esto también se aplica a los recursos que crean automáticamente los controladores. Estos recursos creados automáticamente se etiquetan con la plantilla correspondiente. Ten en cuenta que es habitual reutilizar la misma etiqueta que ya estás usando para asociar los elementos Pods y PersistentVolumeClaims generados con su recurso principal.

Entre las consideraciones de uso se incluyen las siguientes:

  • Si quieres proteger los recursos que crean recursos secundarios, tanto los recursos principales (como StatefulSet, Deployment o DaemonSet) como los secundarios (como Pod o PersistentVolumeClaim) deben tener la etiqueta que se usa en el campo Selector del ProtectedApplication.
  • Si un operador crea automáticamente algunos de los recursos a los que hace referencia tu ProtectedApplication, también debes incluir los recursos personalizados del operador en tu selector ProtectedApplication. De esta forma, se evitará una condición de carrera en el momento de la restauración que puede producirse cuando el operador intenta crear un recurso mientras se restaura simultáneamente a partir de una copia de seguridad.

En el siguiente ejemplo se muestra cómo puedes aplicar la etiqueta app: nginx a los demás recursos, además de a 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:
      ...

Una vez que hayas aplicado la etiqueta seleccionada a todos los recursos de destino (y a las plantillas a partir de las que se generan recursos adicionales), podrás hacer referencia a esos recursos desde un ProtectedApplication. Por ejemplo:

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

Definir reglas de orquestación

Una vez que hayas identificado todos los recursos de tu ProtectedApplication, puedes definir reglas de orquestación detalladas para un subconjunto de estos recursos. Estas reglas solo se pueden aplicar a dos tipos de recursos: Deployments y StatefulSets, y se hace referencia a ellas en la sección components del ProtectedApplication.

Descripción general de los componentes

Para configurar un componente, debes hacer lo siguiente:

  • Seleccionar una estrategia fundamental para determinar cómo funcionarán las copias de seguridad y la restauración de este componente. Hay tres estrategias disponibles:

    • BackupAllRestoreAll: crea una copia de seguridad de los volúmenes asociados a todas las instancias del componente y restaura todos los volúmenes a partir de las copias de seguridad.

    • BackupOneRestoreAll: crea una copia de seguridad de los volúmenes de solo una instancia del componente y usa esas copias para restaurar todas las instancias.

    • DumpAndLoad: exporta datos de la aplicación a un solo volumen en el momento de la copia de seguridad e importa esos datos a la aplicación en el momento de la restauración.

  • Definir hooks de ejecución que se ejecuten durante la copia de seguridad (y posiblemente la restauración, según la estrategia). Un hook es un comando que se ejecuta en contenedores específicos.

  • Seleccionar un subconjunto de volúmenes para crear copias de seguridad.

Hooks de ejecución

Un hook es un comando shell que Copia de seguridad de GKE ejecuta en un contenedor en una fase concreta del proceso de copia de seguridad o restauración.

Hay cuatro tipos de ganchos:

  • pre hooks: estos comandos se ejecutan justo antes de crear copias de seguridad de los volúmenes y, por lo general, se espera que vacíen los datos de la memoria en el disco y, a continuación, silencien la aplicación para que no se escriban datos nuevos en el disco. Estos hooks se usan en las estrategias BackupAllRestoreAll y BackupOneRestoreAll.

  • post hooks: estos comandos se ejecutan durante el proceso de copia de seguridad del volumen justo después del paso de creación de instantáneas (SNAPSHOT) del proceso de copia de seguridad del volumen (antes del paso de subida [UPLOADING]). Por lo general, el paso de creación de la instantánea solo tarda unos segundos. Por lo general, se espera que reactiven la aplicación (es decir, que permitan que se lleven a cabo los procesos normales y las escrituras en disco). Estos ganchos se usan en las estrategias BackupAllRestoreAll, BackupOneRestoreAll y DumpAndLoad.

  • dump hooks: estos comandos se ejecutan antes de que se cree una copia de seguridad del volumen en la estrategia DumpAndLoad y, por lo general, se espera que exporten datos de la aplicación al volumen de copia de seguridad designado.

  • load hooks: estos comandos se ejecutan en el momento de la restauración después de que se restaure el volumen de la copia de seguridad en los casos de la estrategia DumpAndLoad. Por lo general, se espera que importen los datos del volumen de copia de seguridad a la aplicación.

Puedes proporcionar más de un hook para cada tipo y Backup for GKE los ejecutará en el orden en que los definas.

Los ganchos se definen en la sección de componentes de la especificación ProtectedApplication. Todas las definiciones de ganchos tienen los mismos campos disponibles:

  • name: nombre que asignas al hook.

  • container - (opcional) nombre del contenedor en el que se va a ejecutar el comando. Si no proporcionas el contenedor, Backup for GKE ejecutará el hook en el primer contenedor definido para los Pod(s) de destino.

  • command: es el comando real que se envía al contenedor, construido como un array de palabras. La primera palabra de la matriz es la ruta al comando y las palabras posteriores son los argumentos que se deben transmitir al comando.

  • timeoutSeconds: (opcional) tiempo antes de que se cancele la ejecución del hook. Si no lo indica, el valor predeterminado es de 30 segundos.

  • onError: (opcional) comportamiento que se adopta cuando falla el hook. Puede ser Ignore o Fail (valor predeterminado). Si le asignas el valor Fail, cuando falle un hook, también fallará la copia de seguridad del volumen. Si lo defines como Ignore, se ignorarán los errores de este hook.

Antes de aplicar los hooks de ProtectedApplication a tu aplicación, debes probar el comando con kubectl exec para asegurarte de que los hooks se comportan como esperas:

kubectl exec POD_NAME -- COMMAND

Haz los cambios siguientes:

  • POD_NAME: nombre del pod que contiene el recurso ProtectedApplication.
  • COMMAND: la matriz que contiene el comando que quieres ejecutar en el contenedor.

Seleccionar un subconjunto de volúmenes para crear una copia de seguridad

A veces, las aplicaciones escriben en volúmenes que no interesa restaurar (por ejemplo, determinados volúmenes de registro o de borrador). Puedes suprimir la copia de seguridad de estos volúmenes mediante un selector de volúmenes.

Para usar esta función, primero debes aplicar una etiqueta común a los recursos PersistentVolumeClaim de los volúmenes de los que quieras crear una copia de seguridad. También debes desactivar esta etiqueta en los recursos PersistentVolumeClaim de los volúmenes de los que no quieras crear copias de seguridad. A continuación, incluye una cláusula volumeSelector en la definición del componente de la siguiente manera:

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

Si proporciona un volumeSelector para un componente, solo se crearán copias de seguridad y se restaurarán los volúmenes cuyos recursos PersistentVolumeClaim tengan la etiqueta indicada. En el momento de la restauración, los demás volúmenes se aprovisionan como vacíos en lugar de restaurarse a partir de una copia de seguridad de volumen.

Estrategia: BackupAllRestoreAll

Esta es la estrategia más sencilla, ya que crea una copia de seguridad de todos los volúmenes del componente en el momento de la copia de seguridad y los restaura todos a partir de sus copias de seguridad de volumen en el momento de la restauración. Es la mejor opción cuando tu aplicación no tiene replicación entre Pods.

Esta estrategia admite los siguientes parámetros:

  • backupPreHooks: (opcional) una lista ordenada de hooks que se ejecutan justo antes de crear copias de seguridad de los volúmenes. Estos comandos se ejecutan en todos los Pods del componente.

  • backupPostHooks: (opcional) una lista ordenada de hooks que se ejecutan después de que las copias de seguridad de los volúmenes hayan alcanzado la fase UPLOADING. Estos comandos se ejecutan en todos los Pods del componente.

  • volumeSelector: lógica (opcional) para hacer coincidir un subconjunto de volúmenes con la copia de seguridad.

En este ejemplo se crea un recurso ProtectedApplication que detiene el sistema de archivos antes de crear una copia de seguridad del volumen de los registros y lo reactiva después de crear la copia de seguridad:

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-deployment"]
    strategy:
      type: BackupAllRestoreAll
      backupAllRestoreAll:
        backupPreHooks:
        - name: freeze
          container: nginx
          command:
          - bash
          - "-c"
          - |
            # Add application logic to flush data to disk before snapshot
            # and freeze the application from further changes.
            echo "Freezing the application"

            # Return 0 on successful freeze of application, and non-zero
            # for errors
            exit 0
        backupPostHooks:
        - name: unfreeze
          container: nginx
          command:
          - bash
          - "-c"
          - |
            # Add application logic to unfreeze the application.
            echo "Unfreezing the application"

            # Return 0 on successful freeze of application, and non-zero
            # for errors
            exit 0

Estrategia: BackupOneAndRestoreAll

Esta estrategia crea una copia de seguridad de un Pod seleccionado. Esta única copia es la fuente para restaurar todos los pods durante una restauración. Este método puede ayudar a reducir el coste de almacenamiento y el tiempo de creación de copias de seguridad. Esta estrategia funciona en una configuración de alta disponibilidad cuando un componente se implementa con un elemento primario PersistentVolumeClaim y varios secundarios PersistentVolumeClaims.

Esta estrategia admite los siguientes parámetros:

  • backupTargetName (obligatorio): especifica el Deployment o StatefulSet que quieres usar para crear la copia de seguridad de los datos. Se selecciona automáticamente el mejor Pod para hacer la copia de seguridad. En una configuración de alta disponibilidad, te recomendamos que lo definas en una de las réplicas de tu aplicación.

  • backupPreHooks: (opcional) una lista ordenada de hooks que se ejecutan justo antes de crear copias de seguridad de los volúmenes. Estos comandos solo se ejecutan en la copia de seguridad Pod seleccionada.

  • backupPostHooks: (opcional) una lista ordenada de hooks que se ejecutan después de que las copias de seguridad de los volúmenes hayan alcanzado la fase UPLOADING. Estos comandos solo se ejecutan en la copia de seguridad Pod seleccionada.

  • volumeSelector: lógica (opcional) para hacer coincidir un subconjunto de volúmenes con la copia de seguridad.

Si un componente se configura con varias implementaciones o StatefulSets, todos los recursos deben tener la misma estructura PersistentVolume, lo que significa que deben seguir estas reglas:

  • El número de PersistentVolumeClaims que usan todos los Deployments o StatefulSets debe ser el mismo.
  • El propósito de PersistentVolumeClaims en el mismo índice debe ser el mismo. En el caso de los StatefulSets, el índice se define en volumeClaimTemplate. En el caso de los despliegues, el índice se define en Volumes y se omiten los volúmenes no persistentes.
  • Si el componente de la aplicación consta de implementaciones, cada implementación debe tener exactamente una réplica.

Teniendo en cuenta estas consideraciones, se pueden seleccionar varios conjuntos de volúmenes para la copia de seguridad, pero solo se seleccionará un volumen de cada conjunto.

En este ejemplo, suponiendo una arquitectura de un StatefulSet principal y un StatefulSet secundario, se muestra una copia de seguridad de los volúmenes de un Pod en el StatefulSet secundario y, a continuación, una restauración de todos los demás volúmenes:

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: [...]

Estrategia: DumpAndLoad

Esta estrategia usa un volumen específico para los procesos de copia de seguridad y restauración, y requiere un PersistentVolumeClaim específico conectado a un componente que almacene datos de volcado.

Esta estrategia admite los siguientes parámetros:

  • dumpTarget (obligatorio): especifica el Deployment o StatefulSet que quieres usar para crear una copia de seguridad de los datos. Se selecciona automáticamente el mejor Pod para hacer la copia de seguridad. En una configuración de alta disponibilidad, te recomendamos que asignes a este campo una de las réplicas de tu aplicación.

  • loadTarget - (obligatorio) especifica qué Deployment o StatefulSet se debe usar para cargar los datos. Se selecciona automáticamente el mejor Pod para hacer la copia de seguridad. El destino de carga no tiene por qué ser el mismo que el destino de volcado.

  • dumpHooks: (obligatorio) lista ordenada de hooks que se ejecutan para rellenar el volumen de copia de seguridad dedicado. Estos comandos solo se ejecutan en el volcado seleccionado Pod.

  • backupPostHooks: (opcional) una lista ordenada de hooks que se ejecutan después de que las copias de seguridad de los volúmenes hayan alcanzado la fase UPLOADING. Estos comandos solo se ejecutan en el volcado seleccionado Pod.

  • loadHooks - (obligatorio) una lista ordenada de hooks que se ejecutan para cargar los datos del volumen restaurado después de que se inicie la aplicación. Estos comandos solo se ejecutan en la carga Pod seleccionada.

  • volumeSelector: lógica obligatoria para asociar un solo volumen a la copia de seguridad y la restauración (el volumen de "volcado"). Aunque solo debe coincidir con un volumen, se configura de la misma forma que el subconjunto de volúmenes que se va a crear como copia de seguridad que usan otras estrategias.

Si la aplicación consta de implementaciones, cada una de ellas debe tener exactamente una réplica.

En este ejemplo, se presupone una arquitectura de un StatefulSet principal y un StatefulSet secundario con PersistentVolumeClaims dedicados tanto al StatefulSet principal como al secundario. Se muestra una estrategia 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

Comprobar si un ProtectedApplication está listo para crear una copia de seguridad

Para comprobar si un ProtectedApplication está listo para una copia de seguridad, ejecuta el siguiente comando:

kubectl describe protectedapplication APPLICATION_NAME

Sustituye APPLICATION_NAME por el nombre de tu aplicación.

Si está lista, la descripción de la aplicación mostrará el estado Ready to backup como true, como en este ejemplo:

% 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>

Siguientes pasos