tutoriales.com

Gestión de Estado Persistente en Kubernetes: Almacenamiento Duradero con PVC y StorageClasses 💾

Este tutorial explora cómo Kubernetes maneja el almacenamiento persistente, un aspecto crucial para aplicaciones con estado. Aprenderás a configurar PersistentVolumes (PV), PersistentVolumeClaims (PVC) y StorageClasses para garantizar que tus datos sobrevivan al ciclo de vida de los pods.

Intermedio20 min de lectura7 views
Reportar error

Las aplicaciones modernas, especialmente aquellas que gestionan datos (bases de datos, colas de mensajes, sistemas de ficheros), requieren que sus datos persistan más allá de la vida útil de un pod. En Kubernetes, los pods son efímeros y pueden reiniciarse, reprogramarse o eliminarse en cualquier momento, lo que conlleva la pérdida de cualquier dato almacenado directamente en su sistema de archivos local. Aquí es donde entra en juego el concepto de almacenamiento persistente.

Este tutorial te guiará a través de los componentes clave de la gestión de almacenamiento en Kubernetes: PersistentVolumes (PV), PersistentVolumeClaims (PVC) y StorageClasses. Veremos cómo configurarlos y usarlos para asegurar que tus aplicaciones tengan acceso a almacenamiento duradero y fiable.

📝 ¿Por qué necesitamos Almacenamiento Persistente en Kubernetes?

Imagina que tienes una base de datos PostgreSQL ejecutándose en un pod de Kubernetes. Si ese pod falla o necesita ser actualizado, Kubernetes lo terminará y creará uno nuevo. Si los datos de la base de datos se almacenaran dentro del pod, se perderían para siempre. Esto es inaceptable para la mayoría de las aplicaciones con estado.

El almacenamiento persistente resuelve este problema desacoplando el almacenamiento de los pods. Permite que los datos residan en un volumen externo al pod, de modo que, incluso si el pod se elimina, los datos permanecen intactos y pueden ser montados por un nuevo pod.

🔥 Importante: Los contenedores por defecto usan un sistema de archivos *efímero*. Cualquier dato escrito en él se pierde cuando el contenedor se detiene o se elimina. Para datos críticos, siempre se debe usar almacenamiento persistente.

📖 Conceptos Clave del Almacenamiento en Kubernetes

Antes de sumergirnos en la práctica, es fundamental entender los tres pilares del almacenamiento persistente en Kubernetes:

  1. PersistentVolume (PV): Un recurso de almacenamiento en el clúster que ha sido aprovisionado por un administrador o dinámicamente. Piensa en un PV como un volumen de almacenamiento físico (o su equivalente en la nube, como un disco de AWS EBS, un disco de Google Cloud Persistent Disk, o un volumen NFS). No está acoplado a ningún pod específico.
  2. PersistentVolumeClaim (PVC): Una solicitud de almacenamiento por parte de un usuario. Un PVC es como una solicitud de un usuario para una cierta cantidad y tipo de almacenamiento. Un pod puede usar un PVC como volumen, y el PVC vincula al pod con un PV disponible.
  3. StorageClass: Proporciona una forma de describir las "clases" de almacenamiento disponibles en el clúster. Permite el aprovisionamiento dinámico de PVs. En lugar de que un administrador cree PVs manualmente, una StorageClass puede aprovisionar automáticamente un PV cuando un PVC lo solicita. Esto es clave en entornos de nube.

Diagrama Conceptual: PV, PVC y Pod

Pod (Aplicación) PVC PersistentVolume Claim PV Persistent Volume Storage Externo (NFS, Cloud...) Solicita Vincula

🛠️ Configurando PersistentVolumes (PV) y PersistentVolumeClaims (PVC)

Vamos a empezar con un ejemplo práctico de cómo crear y usar PVs y PVCs. Para este tutorial, asumiremos un aprovisionamiento estático de PVs para ilustrar los conceptos básicos. Más adelante, abordaremos las StorageClasses para el aprovisionamiento dinámico.

Paso 1: Crear un PersistentVolume (PV) 📝

Primero, necesitamos un PV que represente el almacenamiento físico disponible. En un entorno de producción, esto podría ser un volumen NFS, iSCSI, o un disco de nube. Para nuestro ejemplo, usaremos un hostPath que apunta a un directorio en el nodo. Esto es útil para fines de desarrollo y pruebas, pero no recomendado para producción.

Crea un archivo pv-hostpath.yaml:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-pv
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"

Analicemos este manifiesto:

  • metadata.name: Nombre del PersistentVolume.
  • labels.type: Una etiqueta para identificar el tipo de PV (útil para filtrado).
  • spec.storageClassName: Un nombre para la clase de almacenamiento. Lo configuramos como manual porque no estamos usando una StorageClass real para el aprovisionamiento dinámico en este ejemplo. Este nombre debe coincidir con el storageClassName del PVC.
  • spec.capacity.storage: Define la capacidad del volumen. Aquí, 1 Gigabyte.
  • spec.accessModes: Describe cómo el volumen puede ser montado por los pods. Los modos comunes son:
    • ReadWriteOnce (RWO): El volumen puede ser montado como lectura/escritura por un único nodo.
    • ReadOnlyMany (ROX): El volumen puede ser montado como solo lectura por muchos nodos.
    • ReadWriteMany (RWX): El volumen puede ser montado como lectura/escritura por muchos nodos (no todos los tipos de volumen lo soportan).
  • spec.hostPath.path: Para hostPath, especifica la ruta en el sistema de archivos del nodo. Asegúrate de que este directorio exista en el nodo donde se ejecutará el pod.

Crea el PV:

kubectl apply -f pv-hostpath.yaml

Verifica el estado del PV:

kubectl get pv

Deberías ver algo como esto:

NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
my-pv     1Gi        RWO            Retain           Available           manual                  5s

El estado Available indica que el PV está listo para ser reclamado por un PVC.

Paso 2: Crear un PersistentVolumeClaim (PVC) ✨

Ahora, un pod necesita acceso a este almacenamiento. Para ello, crearemos un PVC que solicitará el PV.

Crea un archivo pvc-claim.yaml:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 500Mi

Desglose del manifiesto:

  • metadata.name: Nombre del PersistentVolumeClaim.
  • spec.storageClassName: Debe coincidir con el storageClassName del PV (manual en nuestro caso). Esto ayuda a Kubernetes a emparejar el PVC con el PV correcto.
  • spec.accessModes: Los modos de acceso solicitados deben ser compatibles con los modos de acceso del PV. Aquí, ReadWriteOnce.
  • spec.resources.requests.storage: La cantidad de almacenamiento solicitada. Nuestro PV tiene 1Gi, y el PVC pide 500Mi, lo cual es compatible.

Crea el PVC:

kubectl apply -f pvc-claim.yaml

Verifica el estado del PVC y el PV:

kubectl get pvc
kubectl get pv

Ahora deberías ver que el PVC está Bound y el PV también:

NAME      STATUS   VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
my-pvc    Bound    my-pv     1Gi        RWO            manual         10s
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM             STORAGECLASS   REASON   AGE
my-pv     1Gi        RWO            Retain           Bound    default/my-pvc    manual                  1m

¡Felicidades! Has vinculado un PVC con un PV. Esto significa que el almacenamiento solicitado ha sido aprovisionado y está listo para ser usado.

Paso 3: Usar el PVC en un Pod 🚀

Finalmente, vamos a crear un pod que monte este PVC. Usaremos un pod simple que escribirá un archivo en el volumen.

Crea un archivo pod-with-pvc.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: my-app-with-pvc
spec:
  containers:
    - name: my-container
      image: busybox
      command: ["sh", "-c", "while true; do echo $(date -u) > /mnt/data/timestamp.txt; sleep 5; done"]
      volumeMounts:
        - name: persistent-storage
          mountPath: /mnt/data
  volumes:
    - name: persistent-storage
      persistentVolumeClaim:
        claimName: my-pvc

Explicación del manifiesto del pod:

  • spec.containers.volumeMounts: Define dónde se montará el volumen dentro del contenedor. El mountPath es /mnt/data.
  • spec.volumes: Aquí es donde se referencia el PVC. persistentVolumeClaim.claimName debe coincidir con el nombre de tu PVC (my-pvc).

Crea el pod:

kubectl apply -f pod-with-pvc.yaml

Verifica que el pod esté en ejecución:

kubectl get pod my-app-with-pvc

Accede al pod para verificar que los datos se están escribiendo:

kubectl exec -it my-app-with-pvc -- cat /mnt/data/timestamp.txt

Deberías ver una marca de tiempo que se actualiza cada 5 segundos. Intenta borrar el pod y recrearlo. ¡Los datos persistirán!

kubectl delete pod my-app-with-pvc
# Espera a que se elimine completamente
kubectl apply -f pod-with-pvc.yaml
# Una vez en ejecución, verifica de nuevo:
kubectl exec -it my-app-with-pvc -- cat /mnt/data/timestamp.txt

Verás que el timestamp.txt continúa desde donde lo dejó, demostrando la persistencia de los datos.


☁️ StorageClasses: Aprovisionamiento Dinámico en la Nube

El aprovisionamiento estático de PVs, como el que hicimos, es útil para entender los conceptos, pero en entornos de nube y producción, el aprovisionamiento dinámico con StorageClass es la norma.

Una StorageClass abstrae los detalles del aprovisionamiento del almacenamiento subyacente. Cuando un PVC solicita una StorageClass específica, Kubernetes (a través de un aprovisionador) crea automáticamente un PV que cumple con los requisitos del PVC.

Ventajas de StorageClass:

  • Automatización: No es necesario que los administradores aprovisionen PVs manualmente.
  • Flexibilidad: Los usuarios pueden solicitar diferentes tipos de almacenamiento (SSD, HDD, de alto rendimiento, de bajo coste) simplemente especificando la StorageClass adecuada en su PVC.
  • Portabilidad: Los manifiestos de PVC pueden ser más genéricos, ya que no necesitan especificar detalles de infraestructura específicos.

Ejemplo de una StorageClass (AWS EBS) ☁️

Aquí tienes un ejemplo de una StorageClass para AWS EBS. Cada proveedor de nube (GCP, Azure) o solución on-premise (Ceph, Portworx) tiene sus propios aprovisionadores y parámetros.

Crea un archivo sc-aws-ebs.yaml (esto requiere un clúster de Kubernetes en AWS con el aprovisionador EBS configurado):

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gp2-storage
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp2
  fsType: ext4
reclaimPolicy: Delete
volumeBindingMode: Immediate

Desglose:

  • provisioner: Especifica qué aprovisionador se encargará de crear el volumen. Para AWS EBS, es kubernetes.io/aws-ebs.
  • parameters: Opciones específicas del aprovisionador. type: gp2 indica un volumen GP2 SSD en AWS. fsType: ext4 define el sistema de archivos.
  • reclaimPolicy: Define qué sucede con el PV subyacente y los datos cuando se elimina el PVC. Las opciones son Retain (retener el volumen) o Delete (eliminar el volumen). Para aprovisionamiento dinámico, Delete es común para evitar residuos, pero Retain se usa para volúmenes muy críticos.
  • volumeBindingMode: Determina cuándo se vincula un PV al PVC. Immediate significa en cuanto se crea el PVC. WaitForFirstConsumer espera hasta que un pod que usa el PVC es programado para el binding, lo que puede ser útil para la topología de la región/zona.

Crea la StorageClass:

kubectl apply -f sc-aws-ebs.yaml

Uso de StorageClass con un PVC Dinámico 🎯

Ahora, un PVC puede solicitar esta StorageClass sin necesidad de un PV preexistente.

Crea un archivo pvc-dynamic.yaml:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-dynamic-pvc
spec:
  storageClassName: gp2-storage
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi

Crea el PVC:

kubectl apply -f pvc-dynamic.yaml

Cuando crees este PVC, Kubernetes interactuará con el aprovisionador de AWS EBS para crear un volumen de EBS de 2Gi con las características gp2, y luego creará un PV automáticamente para vincularlo a my-dynamic-pvc.

Verifica el estado del PVC y los nuevos PVs generados:

kubectl get pvc my-dynamic-pvc
kubectl get pv

Deberías ver que my-dynamic-pvc está Bound a un PV generado dinámicamente.

Limpiando Recursos de Aprovisionamiento Dinámico 🧹

Si la reclaimPolicy de tu StorageClass es Delete, al eliminar el PVC también se eliminará el PV y el volumen subyacente en la nube. Esto es muy conveniente pero ten cuidado con los datos importantes.

kubectl delete pvc my-dynamic-pvc
# El PV asociado se eliminará automáticamente

🔄 Reclaim Policy: ¿Qué pasa con los datos?

La reclaimPolicy de un PersistentVolume (PV) determina qué ocurre con el volumen subyacente y los datos cuando el PVC que lo reclama se elimina.

Hay tres políticas:

  • Retain: El PV y el volumen subyacente persisten después de que el PVC se elimina. Esto requiere que un administrador elimine manualmente el PV y el volumen subyacente si ya no son necesarios. Es útil para preservar datos importantes.
  • Delete: El PV y el volumen subyacente se eliminan automáticamente cuando el PVC se elimina. Esta es la política por defecto para muchos aprovisionadores dinámicos y es ideal para volúmenes temporales o que no contienen datos críticos.
  • Recycle (Obsoleto): Esta política intentaba borrar los datos del volumen antes de ponerlo Available de nuevo. Ha sido deprecada y reemplazada por aprovisionadores dinámicos que gestionan el ciclo de vida completo.

💡 Ejemplos de Uso Avanzado y Consideraciones

1. Modos de Acceso (Access Modes) 📖

Es importante entender bien los modos de acceso:

Modo de AccesoDescripciónSoporte por tipo de volumen
---------
ReadWriteOnceEl volumen puede ser montado como lectura/escritura por un único nodo.Soportado por la mayoría de los volúmenes (EBS, GPD, HostPath, etc.)
ReadOnlyManyEl volumen puede ser montado como solo lectura por muchos nodos.Soportado por muchos volúmenes (NFS, CephFS, Cinder, GlusterFS, etc.)
---------
ReadWriteManyEl volumen puede ser montado como lectura/escritura por muchos nodos.Solo soportado por algunos sistemas de archivos de red (NFS, CephFS, GlusterFS, Azure File, etc.). Los sistemas de archivos de bloque (EBS, GPD) generalmente no lo soportan ya que están diseñados para montarse en un solo nodo.

2. Snapshots de Volumen (Volume Snapshots) 📸

Kubernetes también soporta la creación de snapshots de volúmenes persistentes. Esto te permite crear copias puntuales de tus datos, que son útiles para copias de seguridad, restauraciones o clonación de entornos.

Para usar snapshots, necesitas instalar el controlador de snapshots en tu clúster y un CSI driver (Container Storage Interface) que soporte esta funcionalidad para tu almacenamiento subyacente.

Ejemplo de un VolumeSnapshotClass (similar a StorageClass para snapshots):

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
  name: csi-aws-ebs-snapclass
  labels:
    app: ebs-csi-driver
driver: ebs.csi.aws.com
deletionPolicy: Delete

Ejemplo de un VolumeSnapshot:

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: new-snapshot-demo
spec:
  volumeSnapshotClassName: csi-aws-ebs-snapclass
  source:
    persistentVolumeClaimName: my-dynamic-pvc

3. Escalabilidad y Rendimiento 📈

  • Rendimiento: La elección de la StorageClass (por ejemplo, SSD vs. HDD, IOPS provisionadas) impactará directamente el rendimiento de tu aplicación. Monitorea el rendimiento del disco para tus aplicaciones con estado.
  • Volúmenes Compartidos: Para aplicaciones que requieren que varios pods accedan al mismo volumen de lectura/escritura (por ejemplo, sistemas de gestión de contenido), necesitarás un sistema de archivos distribuido que soporte ReadWriteMany, como NFS o CephFS.
💡 Consejo: Considera el uso de operadores de base de datos (por ejemplo, para PostgreSQL, MySQL) que automatizan la gestión de la persistencia, copias de seguridad y restauración, y la alta disponibilidad para bases de datos en Kubernetes.

Limpieza de Recursos 🗑️

Para limpiar los recursos que creamos en el aprovisionamiento estático:

  1. Eliminar el Pod:
kubectl delete -f pod-with-pvc.yaml
  1. Eliminar el PVC:
kubectl delete -f pvc-claim.yaml
  1. Eliminar el PV:
kubectl delete -f pv-hostpath.yaml

Para el aprovisionamiento dinámico (si lo probaste):

  1. Eliminar el Pod (si creaste uno usándolo):
# kubectl delete -f pod-with-dynamic-pvc.yaml
  1. Eliminar el PVC (esto también eliminará el PV y el volumen en la nube si reclaimPolicy es Delete):
kubectl delete -f pvc-dynamic.yaml
  1. Eliminar la StorageClass:
kubectl delete -f sc-aws-ebs.yaml

Conclusión ✅

La gestión del estado persistente es un pilar fundamental para ejecutar aplicaciones robustas y con datos en Kubernetes. A través de PersistentVolumes (PV), PersistentVolumeClaims (PVC) y StorageClasses, Kubernetes proporciona un marco flexible y potente para desacoplar el almacenamiento de la computación, asegurando que tus datos estén seguros y disponibles incluso si los pods subyacentes cambian o se reinician.

Dominar estos conceptos te permitirá desplegar y gestionar con confianza bases de datos, sistemas de archivos y otras aplicaciones con estado crítico en tus clústeres de Kubernetes, aprovechando al máximo la elasticidad y resiliencia de la plataforma.

Tutoriales relacionados

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!