Respaldo y restauración de aplicaciones en Kubernetes
Este artículo es la tercer parte de nuestra serie sobre la protección de datos en Kubernetes:
1. Protección de Datos en Kubernetes con Kasten K10
2. Instalación y configuración de Kasten K10: Paso a Paso
3. Respaldo y restauración de aplicaciones en Kubernetes
4. Migrar una aplicación entre clusters de kubernetes con Kasten K10
En esta publicación, vamos a guiarte para desplegar una aplicación en Kubernetes, esta escrita en PHP y que usa MySQL para persistir datos. Posteriormente realizaremos su respaldo con Kasten K10 y veremos como restaurar este respaldo. Recuerda que estamos usando un ambiente de kubernetes de prueba con minikube, este lo instalamos en el artículo anterior. De cualquier forma puedes usar cualquier cluster de Kubernetes.
Todo el código que necesitas para desplegar la aplicación está disponible en este repositorio de GitHub.
Desplegar la base de datos MySQL
Vamos a desplegar una instancia de MySQL versión 8.0 usando la imagen oficial en dockerhub.
Primero, vamos a crear un namespace dedicado a los recursos de nuestra aplicación:
kubectl create ns php-app
Ahora, aplica los manifests que están en el repositorio para crear los recursos siguientes:
Crea los objetos de PV/PVC:
kubectl apply -f https://raw.githubusercontent.com/thesentinella/php-mysql-k8s/main/app/mysql/pv.yml
Crea el despliegue:
kubectl apply -f https://raw.githubusercontent.com/thesentinella/php-mysql-k8s/main/app/mysql/deployment.yml
Verifica el despliegue:
kubectl describe deployment mysql -n php-app
La salida debe ser similar a esta:
Name: mysql
Namespace: default
CreationTimestamp: Tue, 11 Jun 2024 16:39:30 -0600
Labels: <none>
Annotations: deployment.kubernetes.io/revision: 2
Selector: app=mysql
Replicas: 1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType: Recreate
MinReadySeconds: 0
Pod Template:
Labels: app=mysql
Containers:
mysql:
Image: mysql:8.0
Port: 3306/TCP
Host Port: 0/TCP
Environment:
MYSQL_USER: userapp
MYSQL_DATABASE: myapp
MYSQL_PASSWORD: s0m3p4ss
MYSQL_ROOT_PASSWORD: s0m3p4ss4r00t
Mounts:
/var/lib/mysql from mysql-persistent-storage (rw)
Volumes:
mysql-persistent-storage:
Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
ClaimName: mysql-pv-claim
ReadOnly: false
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: mysql-65469c767d (0/0 replicas created)
NewReplicaSet: mysql-5fdc4d7b9c (1/1 replicas created)
Events: <none>
Lista los pods:
kubectl get pods -l app=mysql -n php-app
NAME READY STATUS RESTARTS AGE
mysql-5fdc4d7b9c-zwt8v 1/1 Running 1 (3h36m ago) 4h17m
Verifica el volumen:
kubectl describe pvc mysql-pv-claim -n php-app
Name: mysql-pv-claim
Namespace: default
StorageClass: standard
Status: Bound
Volume: pvc-f8cc973e-a4a8-48c6-a5c4-c68c8a0acf81
Labels: <none>
Annotations: pv.kubernetes.io/bind-completed: yes
pv.kubernetes.io/bound-by-controller: yes
volume.beta.kubernetes.io/storage-provisioner: k8s.io/minikube-hostpath
volume.kubernetes.io/storage-provisioner: k8s.io/minikube-hostpath
Finalizers: [kubernetes.io/pvc-protection]
Capacity: 20Gi
Access Modes: RWO
VolumeMode: Filesystem
Used By: mysql-5fdc4d7b9c-zwt8v
Events: <none>
Accede a la instancia de MySQL:
kubectl run -n php-app -it --rm --image=mysql:8.0 --restart=Never mysql-client -- mysql -h mysql -u userapp -ps0m3p4ss
¿Qué hace este comando?
Cuando ejecutas este comando, hace lo siguiente:
- Crea un Pod Temporal: Lanza un pod temporal usando la imagen de Docker MySQL 8.0.
- Conecta al Servidor MySQL: Dentro de este pod, ejecuta el cliente MySQL e intenta conectarse a un servidor MySQL que se ejecuta en el clúster de Kubernetes. Utiliza el nombre del servicio MySQL (mysql) para resolver el host de la base de datos y las credenciales proporcionadas (userapp / s0m3p4ss).
- Sesión Interactiva: La opción -it te permite interactuar con el servidor MySQL a través de la línea de comandos.
- Limpieza: Después de que sales del cliente MySQL, el pod se elimina automáticamente debido a la opción –rm.
La salida debe ser similar a esta:
If you don't see a command prompt, try pressing enter.
mysql>
Dentro de esta consola puedes ejecutar comandos de MySQL, por ejemplo listar las bases de datos:
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| myapp |
| performance_schema |
+--------------------+
3 rows in set (0.01 sec)
Con esto terminamos de desplegar nuestra base de datos y está lista para recibir peticiones de nuestra aplicación.
Desplegar la aplicación de PHP
La aplicación de PHP se puede desplegar con un simple comando, solo falta aplicar el manifiesto:
kubectl apply -f https://raw.githubusercontent.com/thesentinella/php-mysql-k8s/main/app/php/deployment.yml
Este despliegue hace referencia a la imagen galvaradosentinella/php-app, esta es una imagen que se construye con el archivo Dockerfile que está en el repositorio que estamos usando para este ejercicio.
FROM php:apache
RUN docker-php-ext-install mysqli
# Copy the PHP application code to the Apache document root
COPY index.php /var/www/html/
# Expose port 80
EXPOSE 80
En el repositorio también se incluye el archivo index.php que tiene el código de la aplicación. Esta aplicación es una lista de To-Do muy sencilla que muestra una lista de pendientes y permite agregar más. Estos datos se almacenan en la base de datos de MySQL que desplegamos antes. La aplicación de PHP tiene como variables de ambiente los valores de conexión a MySQL definidos en su despleigue:
env:
- name: MYSQL_HOST
value: mysql
- name: MYSQL_USER
value: userapp
- name: MYSQL_PASSWORD
value: s0m3p4ss
- name: MYSQL_DATABASE
value: myapp
Puedes construir tu mismo la imagen y usar otro repositorio o incluso puedes modificar el código PHP.
Después de desplegar la aplicación, encontrarás un servicio NodePort disponible. Obtenemos la ruta para consumir el servicio con el siguiente comando minikube service php-app --url -n php-app
Una vez que accedes puedes ver la aplicación e interactuar con ella. Yo he agregado 3 pendientes a mi lista:
![](https://www.sentinel.la/wp-content/uploads/2024/06/image-13-1024x271.png)
Ya que tenemos una aplicación que persiste datos, estamos listos para respaldarla con Kasten K10.
Respaldo con Kasten K10
Vamos a respaldar la aplicación, podemos ver que Kasten ya hizo el discovery de la aplicación y aparece listada en el dashboard como “Unmanaged”:
![](https://www.sentinel.la/wp-content/uploads/2024/06/image-1-1024x299.png)
Lo más simple de configurar es un snapshot, vamos al menú de tres puntos que aparece en la aplicación y seleccionamos la opción:
![](https://www.sentinel.la/wp-content/uploads/2024/06/image-2-1024x476.png)
Podemos omitir los parametros por ahora y simplemente crear el snapshot como se muestra a continuación:
![](https://www.sentinel.la/wp-content/uploads/2024/06/image-3.png)
De regreso en el dashboard, debemos ver el progeso del snapshot y una vez finalizado se ve así:
![](https://www.sentinel.la/wp-content/uploads/2024/06/image-14-1024x164.png)
Al hacer click en este snapshot podemos ver que se respaldaron los siguientes objetos de kubernetes:
- Namespaces: php-app
- Services: php-app/mysql, php-app/php-app
- ServiceAccounts: php-app/default
- StorageClasses: storage.k8s.io/csi-hostpath-sc
- Deployments: php-app/mysql, php-app/php-app
- PersistentVolumeClaims: php-app/mysql-pv-claim
Ademas, se respaldo el contenido del volumen asociado, que cuenta con la información de la base de datos.
Restauración
Una vez que tenemos un spanshot, emularemos el caso de un incidente eliminando todos los elementos en el namespace php-app y los restauraremos con kasten.
Eliminaremos el PVC de MySQLcon el siguiente comando:
kubectl patch pvc mysql-pv-claim -n php-app -p '{"metadata":{"finalizers":null}}'
kubectl delete pvc mysql-pv-claim --grace-period=0 --force -n php-app
En este momento hemos perdido el volumen de los datos, confirmamos actualizando la página:
![](https://www.sentinel.la/wp-content/uploads/2024/06/image-10-1024x240.png)
El nuevo pod que se crea, no es capaz de montar el volumen por que el PVC no existe.
kubectl get all -n php-app
NAME READY STATUS RESTARTS AGE
pod/mysql-5fdc4d7b9c-8dtrd 0/1 Terminating 0 4m51s
pod/mysql-5fdc4d7b9c-fjwcv 0/1 Pending 0 14s
pod/php-app-5849f8b4d6-5jbk9 1/1 Running 0 5m34s
Vemos que se queda en estado de Pending, al describir el pod vemos claramente el error:
kubectl describe pod/mysql-5fdc4d7b9c-fjwcv -n php-app
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 2m14s default-scheduler 0/1 nodes are available: persistentvolumeclaim "mysql-pv-claim" not found. preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling.
Warning FailedScheduling 2m12s default-scheduler 0/1 nodes are available: persistentvolumeclaim "mysql-pv-claim" not found. preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling.
Para restaurar vamos al dashboard de Kasten y vemos que nuestra aplicación tiene un respaldo, elegimos la opción restore:
![](https://www.sentinel.la/wp-content/uploads/2024/06/image-6-1024x440.png)
Seleccionamos el punto de restauración que tenemos:
![](https://www.sentinel.la/wp-content/uploads/2024/06/image-7.png)
Se nos muestra un resumen de los elementos a restaurar incluidos el volumen de datos en sí y los objetos de Kubernetes, confirmamos y restauramos:
![](https://www.sentinel.la/wp-content/uploads/2024/06/image-8.png)
Aparece un último cuadro de diálogo y volvemos a confirmar:
![](https://www.sentinel.la/wp-content/uploads/2024/06/image-9.png)
Una vez restaurado, vemos en el dashboard lo siguiente:
![](https://www.sentinel.la/wp-content/uploads/2024/07/image-1024x131.png)
Comprobamos que la aplicación se restauró:
![](https://www.sentinel.la/wp-content/uploads/2024/06/image-13-1024x271.png)
Y tenemos de vuelta nuestros datos,
En el último articulo de la serie veremos como poder migrar la aplicación por completo a otro cluster de Kubernetes, esta vez desplegado en AWS. De tal forma que podremos conocer las 3 características fundamentales de Kasten: Respaldos, DRP y Movilidad de aplicaciones.
Instalación y configuración de Kasten K10: Paso a Paso - Sentinella
14/08/2024[…] Este artículo es la tercer parte de nuestra serie sobre la protección de datos en Kubernetes:1. Protección de Datos en Kubernetes con Kasten K102. Instalación y configuración de Kasten K10: Paso a Paso3. Respaldo y restauración de aplicaciones en Kubernetes […]
Protección de Datos en Kubernetes con Kasten K10 - Sentinella
14/08/2024[…] Este artículo es la segunda parte de nuestra serie sobre la protección de datos en Kubernetes:1. Protección de Datos en Kubernetes con Kasten K102. Instalación y configuración de Kasten K10: Paso a Paso3. Respaldo y restauración de aplicaciones en Kubernetes […]
Migrar una aplicación entre clusters de kubernetes con Kasten K10 - Sentinella
15/08/2024[…] de Datos en Kubernetes con Kasten K102. Instalación y configuración de Kasten K10: Paso a Paso3. Respaldo y restauración de aplicaciones en Kubernetes4. Migrar una aplicación entre clusters de kubernetes con Kasten […]