Saltar al contenido principal

Almacenamiento en Kubernetes

Introducción

En esta lección entenderás cómo Kubernetes abstrae el almacenamiento y cómo elegir la opción adecuada según el tamaño del clúster y tus SLOs. Verás en profundidad la diferencia entre aprovisionamiento estático y dinámico, el ciclo de vida de PV/PVC/StorageClass y cómo se relaciona con backends: almacenamiento nativo de nube, Longhorn (entornos pequeños), Rook‑Ceph (entornos medianos), además de NFS y almacenamiento local.

Objetivos:

  • Comprender PV, PVC, StorageClass y CSI a nivel conceptual y operativo.
  • Elegir entre aprovisionamiento estático vs dinámico con criterio.
  • Aplicar patrones de topología, seguridad, snapshots, clones y expansión.

Conceptos Fundamentales

Componentes clave

  • Volumes: Montajes en un Pod. Pueden ser efímeros (p.ej., emptyDir, configMap, secret) o persistentes vía PVC.
  • PersistentVolume (PV): Recurso del clúster que representa capacidad real (bloque o fichero) ya aprovisionada o creada por un driver CSI.
  • PersistentVolumeClaim (PVC): Solicitud de almacenamiento por parte de una aplicación (tamaño, modos de acceso, clase deseada).
  • StorageClass (SC): Plantilla que describe cómo aprovisionar PV de forma dinámica (driver CSI, parámetros, topología).
  • CSI (Container Storage Interface): Estándar que permite a proveedores de almacenamiento integrarse con Kubernetes.

Modos y parámetros esenciales

  • accessModes:
    • RWO (ReadWriteOnce): Lectura/escritura por un único nodo a la vez. Ideal para bases de datos.
    • RWX (ReadWriteMany): Varios nodos pueden leer/escribir. Ideal para contenido compartido.
    • ROX (ReadOnlyMany): Múltiples nodos en solo lectura.
  • volumeMode: Filesystem (lo más común) o Block (bloque crudo para motores de BD/alto rendimiento).
  • reclaimPolicy: Delete o Retain (evita borrados accidentales). Recycle está obsoleto.
  • volumeBindingMode (en SC):
    • Immediate: el volumen se crea al crear el PVC.
    • WaitForFirstConsumer: se retrasa hasta conocer el Pod consumidor y su topología (recomendado).
  • allowedTopologies / nodeAffinity: restringe dónde puede ubicarse un volumen (zonas, nodos).

Ciclo de vida y flujo de aprovisionamiento

  1. El usuario crea un PVC describiendo tamaño y, opcionalmente, storageClassName.
  2. Si el PVC referencia una StorageClass, el controlador CSI la usa para aprovisionamiento dinámico y crea un PV a medida.
  3. Si no hay StorageClass (o se indica storageClassName: "") Kubernetes busca PV estáticos preexistentes que cumplan el PVC (tamaño, accessModes, selector/labels).
  4. El PVC se vincula (binding) a un PV compatible. Con WaitForFirstConsumer, la creación/binding se alinea con el scheduling del Pod respetando la topología.
  5. El Pod monta el volumen. Al borrar el PVC, la política de reclaimPolicy decide si se borra el backend (Delete) o se retiene (Retain).

Idea clave: En dinámico, “pides y se crea”. En estático, “usas lo que ya existe”.

Aprovisionamiento estático vs dinámico

Aprovisionamiento dinámico (con StorageClass)

  • ¿Cuándo? Entornos cloud o SDS con drivers CSI maduros (EBS/PD/Managed Disks, EFS/Filestore/Azure Files, Longhorn, Rook‑Ceph).
  • Ventajas: Simplicidad operativa, escalado, parámetros por clase (tipo de disco, IOPS, fsType), snapshots integrables, WaitForFirstConsumer.
  • Consideraciones: Define una SC por perfil (rendimiento, costes, HA); usa allowVolumeExpansion: true; marca una SC por defecto si procede.

Ejemplo StorageClass (AWS EBS gp3):

# filepath: /k8s/examples/storage/aws-ebs-gp3-sc.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp3
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: ebs.csi.aws.com
parameters:
type: gp3
iops: "3000"
throughput: "125"
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
allowVolumeExpansion: true

Ejemplo PVC dinámico:

# filepath: /k8s/examples/storage/pvc-rwo-dinamico.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: datos-app
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 20Gi
storageClassName: gp3

Aprovisionamiento estático (PV preexistente)

  • ¿Cuándo?
    • NAS/NFS ya desplegado y gestionado por otro equipo.
    • Local PV con afinidad a un nodo (latencia mínima o requisitos de data locality).
    • Integración con cabinas legacy/SAN, entornos air‑gapped.
    • Restauraciones/migraciones donde reaprovechas un disco concreto (attach por volumeName).
  • Ventajas: Control fino del backend, independencia del controlador CSI.
  • Consideraciones: Requiere gestión manual del ciclo de vida y coordinación con operaciones.

Ejemplo PV/PVC estático (NFS) con selector:

# filepath: /k8s/examples/storage/nfs-pv-pvc-estatico.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
labels:
uso: compartido
spec:
capacity:
storage: 200Gi
accessModes: ["ReadWriteMany"]
nfs:
server: 10.0.0.10
path: /export/k8s
persistentVolumeReclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
accessModes: ["ReadWriteMany"]
resources:
requests:
storage: 50Gi
storageClassName: "" # fuerza búsqueda de PVs estáticos
selector:
matchLabels:
uso: compartido

Binding directo a un PV concreto:

# filepath: /k8s/examples/storage/pvc-binding-directo.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: datos-existentes
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
storageClassName: ""
volumeName: pv-lun-42 # debe existir y ser compatible

Profundizando en StorageClass

  • Parámetros comunes:
    • provisioner: driver CSI (p.ej., ebs.csi.aws.com, driver.longhorn.io, rook-ceph.rbd.csi.ceph.com).
    • parameters: tipo de disco/pool, fsType, réplicas (según driver).
    • mountOptions: opciones de montaje (p.ej. noatime, discard).
    • reclaimPolicy: por volumen creado.
    • allowVolumeExpansion: expansión online si el backend lo soporta.
    • volumeBindingMode: recomienda WaitForFirstConsumer para respetar zonas/nodos.
    • allowedTopologies: restringe a zonas/regiones concretas.

Snapshots, clones y expansión

  • VolumeSnapshot: punto en el tiempo de un PVC. Requiere VolumeSnapshotClass.
  • Restore desde snapshot: crear PVC con dataSource apuntando al snapshot.
  • Clones: PVC nuevo con dataSource a otro PVC (misma SC).
  • Expansión: aumentar resources.requests.storage en el PVC si SC lo permite.

Ejemplo Snapshot y Restore (EBS CSI):

# filepath: /k8s/examples/storage/snapshot-restore.yaml
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: csi-aws-vsc
driver: ebs.csi.aws.com
deletionPolicy: Delete
---
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: datos-app-snap
spec:
source:
persistentVolumeClaimName: datos-app
volumeSnapshotClassName: csi-aws-vsc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: datos-app-restore
spec:
storageClassName: gp3
dataSource:
name: datos-app-snap
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 20Gi

Clone de PVC:

# filepath: /k8s/examples/storage/clone-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: datos-app-clone
spec:
storageClassName: gp3
dataSource:
name: datos-app
kind: PersistentVolumeClaim
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 20Gi

Almacenamiento por backend

Nativo de nube (recomendado en cloud)

  • AWS: EBS (RWO), EFS (RWX); GCP: Persistent Disk (RWO), Filestore (RWX); Azure: Managed Disks (RWO), Azure Files (RWX).
  • Pros: Integración nativa, snapshots, rendimiento gestionado.
  • Contras: Costes por GB/IOPS; dependencia del proveedor.

Longhorn (entornos pequeños/on‑prem)

  • SDS ligero con réplica entre nodos y UI integrada.
  • Pros: Fácil instalación, backups a S3, snapshots.
  • Contras: Overhead de red/CPU; menos ideal para clústeres grandes.

SC Longhorn:

# filepath: /k8s/examples/storage/longhorn-sc.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: longhorn
provisioner: driver.longhorn.io
parameters:
numberOfReplicas: "2"
staleReplicaTimeout: "30"
fsType: ext4
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer

Rook‑Ceph (entornos medianos)

  • Operador que despliega Ceph: RBD (bloque, RWO) y CephFS (ficheros, RWX).
  • Pros: Escalable, resiliente, RWX y RWO, múltiples pools.
  • Contras: Mayor complejidad operativa.

SC RBD (RWO):

# filepath: /k8s/examples/storage/rook-ceph-rbd-sc.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: rook-ceph-block
provisioner: rook-ceph.rbd.csi.ceph.com
parameters:
clusterID: rook-ceph
pool: replicapool
imageFeatures: layering
csi.storage.k8s.io/fstype: ext4
reclaimPolicy: Delete
allowVolumeExpansion: true
mountOptions: ["discard"]

SC CephFS (RWX):

# filepath: /k8s/examples/storage/rook-cephfs-sc.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: rook-cephfs
provisioner: rook-ceph.cephfs.csi.ceph.com
parameters:
clusterID: rook-ceph
fsName: myfs
pool: myfs-data0
reclaimPolicy: Delete
allowVolumeExpansion: true

NFS (compartido simple)

  • Pros: RWX sencillo y maduro.
  • Contras: Cuello de botella de metadatos; no ideal para altas escrituras o BD.

PV/PVC NFS (estático):

# filepath: /k8s/examples/storage/nfs-pv-pvc.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv2
spec:
capacity:
storage: 200Gi
accessModes: ["ReadWriteMany"]
nfs:
server: 10.0.0.10
path: /export/prod
persistentVolumeReclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc2
spec:
accessModes: ["ReadWriteMany"]
resources:
requests:
storage: 50Gi
volumeName: nfs-pv2

Almacenamiento local

  • emptyDir (efímero) y Local PV (persistente atado a nodo).
  • Pros: Baja latencia; ideal para cachés o cargas con data locality.
  • Contras: Dependencia del nodo; no hay HA si cae el host.

Local PV:

# filepath: /k8s/examples/storage/local-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv-1
spec:
capacity:
storage: 200Gi
volumeMode: Filesystem
accessModes: ["ReadWriteOnce"]
persistentVolumeReclaimPolicy: Delete
storageClassName: local-storage
local:
path: /mnt/disks/ssd1
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values: ["worker-1"]

Recomendaciones por tamaño de clúster

  • Pequeño (1–5 nodos, single‑zone)

    • Preferido: Longhorn (RWO y RWX básico). NFS para RWX sencillo.
    • En cloud: discos gestionados (EBS/PD/Managed Disks) + EFS/Filestore/Azure Files.
    • Aprovisionamiento: dinámico con SC siempre que sea posible; estático para NFS/local.
  • Mediano (5–30 nodos, multi‑zona posible)

    • Preferido: Rook‑Ceph (RBD para RWO, CephFS para RWX).
    • Alternativa cloud: nativo de nube + servicios RWX (EFS/Filestore/Azure Files).
    • Aprovisionamiento: dinámico con SC; estático para integraciones puntuales o local PV.
  • Grande (30+ nodos o alta IOPS)

    • Preferido: Nativo de nube optimizado (io2, pd-ssd) o Rook‑Ceph con NVMe y red 25/40GbE.
    • RWX exigente: CephFS/Filestore/FSx adecuados al proveedor.
    • Aprovisionamiento: dinámico; usa WaitForFirstConsumer, topologías y cuotas.

Consideraciones transversales:

  • RWO intensivo (BD): bloque SSD/NVMe, antiafinidad de Pods, PDBs, WaitForFirstConsumer.
  • RWX compartido: CephFS/EFS/Filestore/Azure Files según latencia/SLA.
  • Restauraciones/migraciones: PV estático con volumeName o PVC desde snapshot.

Seguridad (DevSecOps)

  • Cifrado en reposo: habilítalo en el backend (EBS/PD/Disks, Ceph, Longhorn).
  • Cifrado en tránsito: NFSv4/TLS, Ceph msgr2, políticas de red.
  • Permisos: fsGroup, fsGroupChangePolicy, SELinux/AppArmor.
  • Secretos: CSI Secrets Store o KMS del proveedor.

Observabilidad y rendimiento

  • Métricas: driver CSI, kubelet, Longhorn, Rook‑Ceph (mgr).
  • Alertas: latencia elevada, degraded/réplicas fuera de sync, uso >80%.
  • Pruebas: fio con perfiles representativos; revisa IOPS/latencia/throughput.

Anti‑patrones

  • Usar NFS para BD de alta escritura.
  • No usar WaitForFirstConsumer en entornos multi‑zona.
  • Mezclar discos heterogéneos sin etiquetado ni afinidades.

Ejemplos Prácticos

Desplegar una app con PVC RWO dinámico:

# filepath: /k8s/examples/storage/deploy-app-rwo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-rwo
spec:
replicas: 1
selector:
matchLabels:
app: app-rwo
template:
metadata:
labels:
app: app-rwo
spec:
containers:
- name: app
image: nginx:1.27
volumeMounts:
- name: datos
mountPath: /var/lib/app
volumes:
- name: datos
persistentVolumeClaim:
claimName: datos-app

StatefulSet con PVC (dinámico) y WaitForFirstConsumer:

# filepath: /k8s/examples/storage/statefulset-rwo.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: db
spec:
serviceName: "db"
replicas: 3
selector:
matchLabels:
app: db
template:
metadata:
labels:
app: db
spec:
containers:
- name: postgres
image: postgres:16
volumeMounts:
- name: datos
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: datos
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: gp3
resources:
requests:
storage: 50Gi

Ejercicios

  1. Crea un PV/PVC estático en NFS usando selector de labels y monta un Pod de prueba.
  2. Define una StorageClass con WaitForFirstConsumer; crea un PVC y verifica la zona donde se crea el volumen.
  3. Implementa Longhorn en 3 nodos, configura numberOfReplicas: 2 y simula la caída de un nodo.
  4. Despliega Rook‑Ceph y crea SC de RBD y CephFS; prueba un Deployment (RWO) y otro (RWX).
  5. Crea un VolumeSnapshot y restaura un PVC; mide diferencias de rendimiento con fio.

Conclusiones

  • Usa aprovisionamiento dinámico con StorageClass como opción por defecto; aporta consistencia y automatización.
  • Recurre a estático cuando necesites reutilizar capacidad existente, local PV o integrarte con backends legacy.
  • Elige el backend según tamaño y requisitos: nube nativa (simplicidad), Longhorn (pequeños), Rook‑Ceph (medianos), NFS (RWX simple), local (latencia mínima).
  • Diseña desde el inicio con topología, seguridad, snapshots y backups en mente.

Recursos Adicionales

  • Documentación de Kubernetes: Volumes, PV/PVC, StorageClass, Snapshots
  • Longhorn Docs
  • Rook‑Ceph Docs
  • Velero (backup y DR)