Velero realiza copias de seguridad (puntuales o recurrentes) para recuperarnos con rapidez de un desastre en el clúster de Kubernetes.

En la entrada anterior Velero - Backup y Disaster Recovery para Kubernetes (II) Crear copia de seguridad desplegamos una aplicación de prueba en el clúster (dos réplicas de Nginx en el Namespace nginx-example). Simulamos la pérdida “accidental” del Namespace nginx-example para ver cómo recuperarnos usando la copia creada por Velero.

Pérdida “accidental” de un Namespace

Eliminamos el Namespace nginx-example para simular un desastre:

$ kubectl delete ns nginx-example
namespace "nginx-example" deleted

Consultar las copias de seguridad disponibles

Si hemos programado un schedule, disponemos de múltiples copias de seguridad.

Consultamos las copias disponibles:

---
kind: Job
apiVersion:  batch/v1
metadata:
  labels:
      app: backup-nginx
  namespace: velero-cli
  generateName: velero-restore-get-
spec:
  template:
    metadata:
      labels:
        app: backup-nginx
    spec:
      serviceAccount: velerocli
      restartPolicy: Never
      containers:
        - name: velero-restore-get
          image: docker.io/xaviaznar/velero-cli:1.5.3
          imagePullPolicy: IfNotPresent
          command: ["/bin/sh"]
          args: ["-c", "velero restore get"]

Lo que devuelve, en los logs:

NAME STATUS ERRORS WARNINGS CREATED EXPIRES STORAGE LOCATION SELECTOR
nginx-backup Completed 0 0 2021-04-04 18:12:00 +0000 UTC 26d default <none>
nginx-backup-2021-04-04 Completed 0 0 2021-04-04 18:47:20 +0000 UTC 26d default <none>
nginx-example-backups-20210404203557 Completed 0 0 2021-04-04 20:35:57 +0000 UTC 27d default <none>
nginx-example-backups-20210404203057 Completed 0 0 2021-04-04 20:30:57 +0000 UTC 27d default <none>
nginx-example-backups-20210404202557 Completed 0 0 2021-04-04 20:25:57 +0000 UTC 27d default <none>
nginx-example-backups-20210404202057 Completed 0 0 2021-04-04 20:20:57 +0000 UTC 27d default <none>
nginx-example-backups-20210404201557 Completed 0 0 2021-04-04 20:15:57 +0000 UTC 27d default <none>
nginx-example-backups-20210404201057 Completed 0 0 2021-04-04 20:10:57 +0000 UTC 27d default <none>
nginx-example-backups-20210404200830 Completed 0 0 2021-04-04 20:08:30 +0000 UTC 27d default <none>
nginx-example-backups-20210404193557 Completed 0 0 2021-04-04 19:35:57 +0000 UTC 27d default <none>
nginx-example-backups-20210404193057 Completed 0 0 2021-04-04 19:30:57 +0000 UTC 27d default <none>
nginx-example-backups-20210404192557 Completed 0 0 2021-04-04 19:25:57 +0000 UTC 27d default <none>
nginx-example-backups-20210404192057 Completed 0 0 2021-04-04 19:20:57 +0000 UTC 27d default <none>
nginx-example-backups-20210404191557 Completed 0 0 2021-04-04 19:15:57 +0000 UTC 26d default <none>
nginx-example-backups-20210404191057 Completed 0 0 2021-04-04 19:10:57 +0000 UTC 26d default <none>
nginx-example-backups-20210404190557 Completed 0 0 2021-04-04 19:05:57 +0000 UTC 26d default <none>
nginx-example-backups-20210404190013 Completed 0 0 2021-04-04 19:00:13 +0000 UTC 26d default <none>

En general recuperamos a partir de la última copia de seguridad realizada, pero podemos seleccionar la copia específica desde la que restaurar. Si lo indicamos, también podemos realizar la recuperación en un Namespace diferente al de la copia original.

Restauramos a partir de la última copia realizada:

Definimos el nombre de la copia específica de la que queremos recuperar en una variable de entorno: $BACKUP2RESTORE

---
kind: Job
apiVersion:  batch/v1
metadata:
  labels:
      app: backup-nginx
  namespace: velero-cli
  generateName: velero-restore-create-
spec:
  template:
    metadata:
      labels:
        app: backup-nginx
    spec:
      serviceAccount: velerocli
      restartPolicy: Never
      containers:
        - name: velero-restore-create
          image: docker.io/xaviaznar/velero-cli:1.5.3
          imagePullPolicy: IfNotPresent
          env:
            - name: BACKUP2RESTORE
              value: "nginx-example-backups-20210404203557"
          command: ["/bin/sh"]
          args: ["-c", "velero restore create --from-backup $BACKUP2RESTORE"]

Consultando los logs del Job:

Restore request "nginx-example-backups-20210404203557-20210407190834" submitted successfully.
Run `velero restore describe nginx-example-backups-20210404203557-20210407190834` or `velero restore logs nginx-example-backups-20210404203557-20210407190834` for more details.

Al cabo de un instante, cuando ha finalizado la restauración a partir de la copia de seguridad, se han creado todos los recursos eliminados y la aplicación vuelve a estar disponible:

$ curl http://nginx.k3s.lab:31142
...
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
...

Tanto el Namespace como todos los recursos desplegados por la aplicación vuelven a estar disponibles (incluídos los creados automáticamente por Kubernetes, como configmap/kube-root-ca.crt, secret/default-token-fj9gv, etc…):

$ kubectl get all,secret,cm,sa -n nginx-example
NAME                                   READY   STATUS    RESTARTS   AGE
pod/nginx-deployment-57d5dcb68-jc7jx   1/1     Running   0          24m
pod/nginx-deployment-57d5dcb68-jbbjj   1/1     Running   0          24m

NAME               TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
service/my-nginx   NodePort   10.43.207.52   <none>        80:31142/TCP   24m

NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx-deployment   2/2     2            2           24m

NAME                                         DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-deployment-57d5dcb68   2         2         2       24m

NAME                         TYPE                                  DATA   AGE
secret/default-token-fj9gv   kubernetes.io/service-account-token   3      24m

NAME                         DATA   AGE
configmap/kube-root-ca.crt   1      24m

NAME                     SECRETS   AGE
serviceaccount/default   1         24m

Si lo deseamos, podemos consultar los logs del Job de restauración para obtener el detalle de todas las acciones realizadas para recuperar la copia:

---
kind: Job
apiVersion:  batch/v1
metadata:
  labels:
      app: backup-nginx
  namespace: velero-cli
  generateName: velero-restore-get-
spec:
  template:
    metadata:
      labels:
        app: backup-nginx
    spec:
      serviceAccount: velerocli
      restartPolicy: Never
      containers:
        - name: velero-restore-get
          image: docker.io/xaviaznar/velero-cli:1.5.3
          imagePullPolicy: IfNotPresent
          env:
            - name: RESTORE_NAME
              value "nginx-example-backups-20210404203557-20210407190834"
          command: ["/bin/sh"]
          args: ["-c", "velero restore logs $RESTORE_NAME"]
time="2021-04-07T19:08:34Z" level=info msg="starting restore" logSource="pkg/controller/restore_controller.go:467" restore=velero/nginx-example-backups-20210404203557-20210407190834
...
time="2021-04-07T19:08:35Z" level=info msg="Attempting to restore Endpoints: my-nginx" logSource="pkg/restore/restore.go:1107" restore=velero/nginx-example-backups-20210404203557-20210407190834
time="2021-04-07T19:08:35Z" level=info msg="Restoring resource 'endpointslices.discovery.k8s.io' into namespace 'nginx-example'" logSource="pkg/restore/restore.go:724" restore=velero/nginx-example-backups-20210404203557-20210407190834
time="2021-04-07T19:08:35Z" level=info msg="Getting client for discovery.k8s.io/v1beta1, Kind=EndpointSlice" logSource="pkg/restore/restore.go:768" restore=velero/nginx-example-backups-20210404203557-20210407190834
time="2021-04-07T19:08:35Z" level=info msg="Attempting to restore EndpointSlice: my-nginx-r8ggk" logSource="pkg/restore/restore.go:1107" restore=velero/nginx-example-backups-20210404203557-20210407190834
time="2021-04-07T19:08:35Z" level=info msg="Restoring resource 'services' into namespace 'nginx-example'" logSource="pkg/restore/restore.go:724" restore=velero/nginx-example-backups-20210404203557-20210407190834
time="2021-04-07T19:08:35Z" level=info msg="Getting client for /v1, Kind=Service" logSource="pkg/restore/restore.go:768" restore=velero/nginx-example-backups-20210404203557-20210407190834
time="2021-04-07T19:08:35Z" level=info msg="Executing item action for services" logSource="pkg/restore/restore.go:1002" restore=velero/nginx-example-backups-20210404203557-20210407190834
time="2021-04-07T19:08:35Z" level=info msg="Attempting to restore Service: my-nginx" logSource="pkg/restore/restore.go:1107" restore=velero/nginx-example-backups-20210404203557-20210407190834
time="2021-04-07T19:08:35Z" level=info msg="Waiting for all restic restores to complete" logSource="pkg/restore/restore.go:488" restore=velero/nginx-example-backups-20210404203557-20210407190834
time="2021-04-07T19:08:35Z" level=info msg="Done waiting for all restic restores to complete" logSource="pkg/restore/restore.go:504" restore=velero/nginx-example-backups-20210404203557-20210407190834
time="2021-04-07T19:08:35Z" level=info msg="Waiting for all post-restore-exec hooks to complete" logSource="pkg/restore/restore.go:508" restore=velero/nginx-example-backups-20210404203557-20210407190834
time="2021-04-07T19:08:35Z" level=info msg="Done waiting for all post-restore exec hooks to complete" logSource="pkg/restore/restore.go:516" restore=velero/nginx-example-backups-20210404203557-20210407190834
time="2021-04-07T19:08:35Z" level=info msg="restore completed" logSource="pkg/controller/restore_controller.go:482" restore=velero/nginx-example-backups-20210404203557-20210407190834

Conclusión

Velero es una de esas herramientas que hace una cosa y la hace bien; a través del uso de unos pocos Custom Resources, Velero realiza copias de seguridad de manera sencilla y a la vez, flexible, permitiendo incluir o excluir elementos durante el backup o los restores.

La posibilidad de programar las copias de seguridad como una tarea usando un Job permite integrar esta la configuracón de los backups en el proceso de despliegue de cualquier aplicación en un entorno productivo.

Para las tareas de consulta de las copias disponibles lanzar un Job puede parece un poco overkill, pero permte tener un “registro” de cualquier acción realizada en el clúster y se puede integrar en la metodología “GitOps”.

Una mejora a lo expuesto en estas entradas sobre Velero sería ajustar los permisos de la ServiceAccount velerocli usada por la herramienta de línea de comandos velero en los ejemplos para que únicamente permita realizar acciones sobre los Custom Resources definidos por Velero. La documentación oficial también describe cómo ajustar los permisos de Velero (la parte “servidor”) para que no sea necesario proporcionar permisos de cluster-admin: Run Velero more securely with restrictive RBAC settings.

El único inconveniente, por ponerle alguna pega a Velero, es que no es posible prescindir de la herramienta de comandos velero para crear o configurar los Custom Resources desplegados, como se indica en API types:

Here’s a list the API types that have some functionality that you can only configure via json/yaml vs the velero cli (hooks)

Pese a este inconveniente menor, la funcionalidad y la simplicidad con lo que Velero permite realizar copias de seguridad y restauraiones del clúster convierte a Velero en una herramienta imprescindible.