Volúmenes Persistentes en Kubernetes: Guía Completa de Producción
Los Volúmenes Persistentes (PV) y las Reclamaciones de Volúmenes Persistentes (PVC) proporcionan abstracción de almacenamiento en Kubernetes, permitiendo que las aplicaciones con estado persistan datos más allá de los ciclos de vida de los pods. Esta guía completa cubre conceptos de PV/PVC, clases de almacenamiento, aprovisionamiento dinámico y mejores prácticas de producción para persistencia de datos en Kubernetes.
Tabla de Contenidos
- Introducción
- Prerrequisitos
- Conceptos de Almacenamiento
- Volúmenes Persistentes
- Reclamaciones de Volúmenes Persistentes
- Clases de Almacenamiento
- Aprovisionamiento Dinámico
- Modos de Volumen y Acceso
- StatefulSets con Almacenamiento Persistente
- Patrones de Producción
- Respaldo y Recuperación ante Desastres
- Solución de Problemas
- Conclusión
Introducción
Los Volúmenes Persistentes de Kubernetes desacoplan el almacenamiento de los pods, proporcionando almacenamiento duradero que sobrevive a reinicios y reprogramaciones de pods. Comprender PV, PVC y StorageClasses es esencial para ejecutar aplicaciones con estado como bases de datos, colas de mensajes y sistemas de almacenamiento de archivos.
¿Por Qué Volúmenes Persistentes?
- Persistencia de Datos: Sobrevive reinicios y eliminaciones de pods
- Abstracción de Almacenamiento: Desacopla almacenamiento de especificaciones de pods
- Aprovisionamiento Dinámico: Creación automática de volúmenes
- Portabilidad: API de almacenamiento consistente entre proveedores
- Gestión del Ciclo de Vida: Ciclo de vida de almacenamiento independiente
Jerarquía de Volúmenes
StorageClass
↓
PersistentVolume (PV)
↓
PersistentVolumeClaim (PVC)
↓
Montaje de Volumen en Pod
Prerrequisitos
- Cluster de Kubernetes (1.19+)
- kubectl configurado
- Backend de almacenamiento (local, NFS, proveedor en la nube, etc.)
- Comprensión básica de Pods y Deployments
Verificar configuración:
kubectl version --client
kubectl get nodes
kubectl get storageclass
Conceptos de Almacenamiento
Tipos de Volúmenes
Volúmenes Efímeros:
emptyDir: Almacenamiento temporal, ciclo de vida del podconfigMap: Datos de configuraciónsecret: Datos sensibles
Volúmenes Persistentes:
hostPath: Sistema de archivos del nodo (solo desarrollo)nfs: Network File Systemcsi: Plugins de Container Storage Interface- Nube:
awsEBS,gcePersistentDisk,azureDisk
Estados del Ciclo de Vida
Estados de PV:
Available: Listo para reclamarBound: Reclamado por PVCReleased: PVC eliminado, datos retenidosFailed: Recuperación automática fallida
Estados de PVC:
Pending: Esperando vinculación con PVBound: Vinculado a PVLost: PV no disponible
Volúmenes Persistentes
Ejemplo Básico de PV
# pv-local.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
hostPath:
path: /mnt/data
# Crear PV
kubectl apply -f pv-local.yaml
# Verificar estado del PV
kubectl get pv
kubectl describe pv local-pv
Volumen Persistente NFS
# pv-nfs.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
spec:
capacity:
storage: 50Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
server: nfs-server.example.com
path: /exported/path
mountOptions:
- hard
- nfsvers=4.1
Ejemplos de PV de Proveedores en la Nube
AWS EBS
# pv-aws-ebs.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: aws-ebs-pv
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: aws-ebs
awsElasticBlockStore:
volumeID: vol-0123456789abcdef0
fsType: ext4
Google Persistent Disk
# pv-gce-pd.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: gce-pd-pv
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: gce-pd
gcePersistentDisk:
pdName: my-disk-name
fsType: ext4
Azure Disk
# pv-azure-disk.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: azure-disk-pv
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: azure-disk
azureDisk:
diskName: myAKSDisk
diskURI: /subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Compute/disks/myAKSDisk
kind: Managed
Políticas de Reclamación
spec:
persistentVolumeReclaimPolicy: Retain # Mantener datos después de eliminación de PVC
# O
persistentVolumeReclaimPolicy: Delete # Eliminar volumen después de eliminación de PVC
# O
persistentVolumeReclaimPolicy: Recycle # Obsoleto - limpieza básica (rm -rf)
Reclamaciones de Volúmenes Persistentes
PVC Básico
# pvc-basic.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: basic-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: standard
# Crear PVC
kubectl apply -f pvc-basic.yaml
# Verificar estado del PVC
kubectl get pvc
kubectl describe pvc basic-pvc
Usar PVC en Pod
# pod-with-pvc.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-with-storage
spec:
containers:
- name: app
image: nginx:alpine
volumeMounts:
- name: data
mountPath: /usr/share/nginx/html
volumes:
- name: data
persistentVolumeClaim:
claimName: basic-pvc
Usar PVC en Deployment
# deployment-with-pvc.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 1 # Nota: volúmenes RWO limitan a 1 réplica
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:alpine
volumeMounts:
- name: web-storage
mountPath: /usr/share/nginx/html
volumes:
- name: web-storage
persistentVolumeClaim:
claimName: web-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: web-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: standard
Selector para PV Específico
# pvc-with-selector.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: selective-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: "" # Vacío para vinculación manual
selector:
matchLabels:
environment: production
tier: database
Clases de Almacenamiento
Las StorageClasses permiten el aprovisionamiento dinámico de PersistentVolumes.
Ver Clases de Almacenamiento
# Listar clases de almacenamiento
kubectl get storageclass
kubectl get sc
# Describir clase de almacenamiento
kubectl describe sc standard
StorageClass Básica
# storageclass-basic.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-ssd
replication-type: regional-pd
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
reclaimPolicy: Delete
StorageClasses de Proveedores en la Nube
AWS EBS
# sc-aws-ebs.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: aws-ebs-gp3
provisioner: ebs.csi.aws.com
parameters:
type: gp3
iops: "3000"
throughput: "125"
encrypted: "true"
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
Google Cloud
# sc-gce-pd.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gce-ssd
provisioner: pd.csi.storage.gke.io
parameters:
type: pd-ssd
replication-type: regional-pd
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
Azure Disk
# sc-azure.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azure-premium
provisioner: disk.csi.azure.com
parameters:
skuName: Premium_LRS
kind: Managed
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
StorageClass Predeterminada
# Establecer clase de almacenamiento predeterminada
kubectl patch storageclass standard \
-p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
# Eliminar predeterminada
kubectl patch storageclass standard \
-p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
Aprovisionamiento Dinámico
El aprovisionamiento dinámico crea automáticamente PVs cuando se crean PVCs.
PVC con Aprovisionamiento Dinámico
# pvc-dynamic.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dynamic-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
storageClassName: fast-ssd # Usa aprovisionador de StorageClass
# Crear PVC
kubectl apply -f pvc-dynamic.yaml
# PV se crea automáticamente
kubectl get pv
kubectl get pvc dynamic-pvc
Expansión de Volúmenes
# sc-expandable.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: expandable-storage
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp3
allowVolumeExpansion: true # Habilitar expansión
# Expandir PVC
kubectl patch pvc dynamic-pvc -p '{"spec":{"resources":{"requests":{"storage":"30Gi"}}}}'
# Verificar estado de expansión
kubectl get pvc dynamic-pvc
kubectl describe pvc dynamic-pvc
Modos de Volumen y Acceso
Modos de Acceso
- ReadWriteOnce (RWO): Un solo nodo lectura-escritura
- ReadOnlyMany (ROX): Múltiples nodos solo lectura
- ReadWriteMany (RWX): Múltiples nodos lectura-escritura
- ReadWriteOncePod (RWOP): Un solo pod lectura-escritura (1.22+)
# Diferentes modos de acceso
spec:
accessModes:
- ReadWriteOnce # Almacenamiento de bloques (EBS, Azure Disk)
# O
- ReadWriteMany # Almacenamiento compartido (NFS, EFS, Azure Files)
# O
- ReadOnlyMany # Solo lectura compartido
Modos de Volumen
# Modo de bloque (dispositivo de bloque crudo)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: block-pvc
spec:
accessModes:
- ReadWriteOnce
volumeMode: Block # Modo de bloque
resources:
requests:
storage: 10Gi
storageClassName: fast-ssd
---
# Usar volumen de bloque en pod
apiVersion: v1
kind: Pod
metadata:
name: pod-with-block
spec:
containers:
- name: app
image: myapp:latest
volumeDevices: # volumeDevices en lugar de volumeMounts
- name: data
devicePath: /dev/xvda
volumes:
- name: data
persistentVolumeClaim:
claimName: block-pvc
StatefulSets con Almacenamiento Persistente
Los StatefulSets proporcionan identificadores de red únicos y estables y almacenamiento persistente.
StatefulSet con VolumeClaimTemplates
# statefulset-mysql.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
value: password
ports:
- containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
storageClassName: fast-ssd
---
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
clusterIP: None # Servicio sin cabeza
selector:
app: mysql
ports:
- port: 3306
StatefulSet de PostgreSQL
# statefulset-postgres.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15-alpine
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
ports:
- containerPort: 5432
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 50Gi
storageClassName: standard
Patrones de Producción
Múltiples Montajes de Volumen
# pod-multi-volumes.yaml
apiVersion: v1
kind: Pod
metadata:
name: multi-volume-pod
spec:
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: app-data
mountPath: /app/data
- name: logs
mountPath: /app/logs
- name: config
mountPath: /app/config
readOnly: true
volumes:
- name: app-data
persistentVolumeClaim:
claimName: app-data-pvc
- name: logs
persistentVolumeClaim:
claimName: logs-pvc
- name: config
configMap:
name: app-config
Init Container para Configuración de Datos
# pod-with-init.yaml
apiVersion: v1
kind: Pod
metadata:
name: app-with-init
spec:
initContainers:
- name: setup
image: busybox:latest
command: ['sh', '-c', 'echo "Configurando datos" > /data/setup.txt']
volumeMounts:
- name: data
mountPath: /data
containers:
- name: app
image: nginx:alpine
volumeMounts:
- name: data
mountPath: /usr/share/nginx/html
volumes:
- name: data
persistentVolumeClaim:
claimName: app-pvc
Límites de Recursos
# pvc-with-limits.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: limited-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
limits:
storage: 50Gi # Expansión máxima
storageClassName: expandable-storage
Respaldo y Recuperación ante Desastres
Instantáneas de Volúmenes
# volumesnapshot.yaml
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: data-snapshot
spec:
volumeSnapshotClassName: csi-snapclass
source:
persistentVolumeClaimName: app-data-pvc
# Crear instantánea
kubectl apply -f volumesnapshot.yaml
# Verificar instantánea
kubectl get volumesnapshot
kubectl describe volumesnapshot data-snapshot
Restaurar desde Instantánea
# pvc-from-snapshot.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: restored-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: standard
dataSource:
name: data-snapshot
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
Respaldo con Jobs
# backup-job.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: backup-job
spec:
schedule: "0 2 * * *" # Diariamente a las 2 AM
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: backup-tool:latest
volumeMounts:
- name: data
mountPath: /data
readOnly: true
- name: backup
mountPath: /backup
restartPolicy: OnFailure
volumes:
- name: data
persistentVolumeClaim:
claimName: app-data-pvc
- name: backup
persistentVolumeClaim:
claimName: backup-pvc
Solución de Problemas
Problemas Comunes
# PVC atascado en Pending
kubectl describe pvc <pvc-name>
# Verificar: StorageClass existe, recursos suficientes, afinidad de nodo
# Pod no puede montar volumen
kubectl describe pod <pod-name>
kubectl get events --sort-by='.lastTimestamp'
# Verificar: PVC está Bound, claimName correcto, modos de acceso coinciden
# Volumen no se expande
kubectl describe pvc <pvc-name>
# Verificar: allowVolumeExpansion=true, almacenamiento subyacente soporta expansión
# Verificar vinculación PV/PVC
kubectl get pv,pvc
kubectl describe pv <pv-name>
kubectl describe pvc <pvc-name>
Comandos de Depuración
# Verificar clase de almacenamiento
kubectl get sc
kubectl describe sc <sc-name>
# Verificar PV
kubectl get pv
kubectl describe pv <pv-name>
# Verificar PVC
kubectl get pvc -A
kubectl describe pvc <pvc-name>
# Verificar volúmenes de pod
kubectl describe pod <pod-name> | grep -A 5 Volumes
# Verificar almacenamiento del nodo
kubectl describe node <node-name> | grep -A 10 "Allocated resources"
# Eventos
kubectl get events --field-selector involvedObject.name=<pvc-name>
Limpiar Recursos Atascados
# Eliminar finalizadores de PVC (si está atascado eliminando)
kubectl patch pvc <pvc-name> -p '{"metadata":{"finalizers":null}}'
# Forzar eliminación de PV
kubectl delete pv <pv-name> --grace-period=0 --force
# Verificar volúmenes huérfanos
kubectl get pv | grep Released
Conclusión
Los Volúmenes Persistentes son esenciales para aplicaciones con estado en Kubernetes. Comprender PV, PVC, StorageClasses y aprovisionamiento dinámico permite una persistencia de datos confiable para cargas de trabajo de producción.
Conclusiones Clave
- PV: Recurso de almacenamiento físico en el cluster
- PVC: Solicitud de almacenamiento por pods
- StorageClass: Plantilla de aprovisionamiento dinámico
- Modos de Acceso: Controlan cómo se accede a los volúmenes
- StatefulSets: Despliegue ordenado con almacenamiento estable
- Respaldos: Instantáneas regulares y planes de recuperación ante desastres
Referencia Rápida
# PersistentVolume
kubectl get pv
kubectl describe pv <pv-name>
kubectl delete pv <pv-name>
# PersistentVolumeClaim
kubectl get pvc
kubectl describe pvc <pvc-name>
kubectl delete pvc <pvc-name>
# StorageClass
kubectl get sc
kubectl describe sc <sc-name>
# Instantáneas de Volumen
kubectl get volumesnapshot
kubectl describe volumesnapshot <name>
# Expandir PVC
kubectl patch pvc <name> -p '{"spec":{"resources":{"requests":{"storage":"50Gi"}}}}'
Lista de Verificación de Producción
- Elegir StorageClass apropiada para la carga de trabajo
- Establecer modos de acceso correctos (RWO, RWX, ROX)
- Configurar capacidad de expansión de volumen
- Implementar estrategia de respaldo (instantáneas, jobs)
- Establecer política de reclamación apropiada
- Monitorear uso y capacidad de volumen
- Probar procedimientos de restauración
- Documentar arquitectura de almacenamiento
- Implementar cuotas de recursos
- Planificar recuperación ante desastres
Próximos Pasos
- Planificar: Evaluar requisitos de almacenamiento
- Configurar: Establecer StorageClasses
- Desplegar: Implementar StatefulSets con almacenamiento
- Respaldar: Configurar estrategia de instantáneas
- Monitorear: Rastrear métricas de volumen
- Optimizar: Dimensionar correctamente asignaciones de almacenamiento
- Recuperación ante Desastres: Probar procedimientos de respaldo/restauración
¡Domina los Volúmenes Persistentes para ejecutar aplicaciones con estado de manera confiable en Kubernetes!


