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.
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.
📖 Conceptos Clave del Almacenamiento en Kubernetes
Antes de sumergirnos en la práctica, es fundamental entender los tres pilares del almacenamiento persistente en Kubernetes:
- 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.
- 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.
- 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
🛠️ 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 comomanualporque no estamos usando una StorageClass real para el aprovisionamiento dinámico en este ejemplo. Este nombre debe coincidir con elstorageClassNamedel 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: ParahostPath, 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 elstorageClassNamedel PV (manualen 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. ElmountPathes/mnt/data.spec.volumes: Aquí es donde se referencia el PVC.persistentVolumeClaim.claimNamedebe 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
StorageClassadecuada 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, eskubernetes.io/aws-ebs.parameters: Opciones específicas del aprovisionador.type: gp2indica un volumen GP2 SSD en AWS.fsType: ext4define el sistema de archivos.reclaimPolicy: Define qué sucede con el PV subyacente y los datos cuando se elimina el PVC. Las opciones sonRetain(retener el volumen) oDelete(eliminar el volumen). Para aprovisionamiento dinámico,Deletees común para evitar residuos, peroRetainse usa para volúmenes muy críticos.volumeBindingMode: Determina cuándo se vincula un PV al PVC.Immediatesignifica en cuanto se crea el PVC.WaitForFirstConsumerespera 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 ponerloAvailablede 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 Acceso | Descripción | Soporte por tipo de volumen |
|---|---|---|
| --- | --- | --- |
ReadWriteOnce | El volumen puede ser montado como lectura/escritura por un único nodo. | Soportado por la mayoría de los volúmenes (EBS, GPD, HostPath, etc.) |
ReadOnlyMany | El volumen puede ser montado como solo lectura por muchos nodos. | Soportado por muchos volúmenes (NFS, CephFS, Cinder, GlusterFS, etc.) |
| --- | --- | --- |
ReadWriteMany | El 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.
Limpieza de Recursos 🗑️
Para limpiar los recursos que creamos en el aprovisionamiento estático:
- Eliminar el Pod:
kubectl delete -f pod-with-pvc.yaml
- Eliminar el PVC:
kubectl delete -f pvc-claim.yaml
- Eliminar el PV:
kubectl delete -f pv-hostpath.yaml
Para el aprovisionamiento dinámico (si lo probaste):
- Eliminar el Pod (si creaste uno usándolo):
# kubectl delete -f pod-with-dynamic-pvc.yaml
- 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
- 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
- Despliegues Azules/Verdes en Kubernetes: Estrategias de Actualización sin Interrupciones 🔄intermediate20 min
- Escalado Automático en Kubernetes: Optimizando Recursos con HPA y VPA 🚀intermediate15 min
- Asegurando la Comunicación en Kubernetes: Implementando mTLS con Istio y Cert-Manager 🛡️intermediate25 min
- Optimización de Recursos en Kubernetes: Limitando y Solicitando CPU/Memoria para tus Contenedores 🚀intermediate15 min
- Observabilidad Integral en Kubernetes: Monitorización, Logs y Tracing con Prometheus, Grafana y Jaeger 📊intermediate20 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!