StatefulSets & Almacenamiento Permanente en Kubernetes
Implemente aplicaciones con estado con escalado ordenado y datos persistentes
Kubernetes StatefulSets son la solución ideal para gestionar aplicaciones con estado que requieren identidades estables, almacenamiento persistente y patrones de despliegue ordenados, esenciales para bases de datos, sistemas distribuidos y capas de caché.
Si eres nuevo en Kubernetes o estás configurando un clúster, considera explorar distribuciones de Kubernetes como k3s o MicroK8s para desarrollo, o instalar Kubernetes con Kubespray para clústeres de producción.
Esta imagen agradable fue generada por modelo AI Flux 1 dev.
¿Qué son los StatefulSets?
Los StatefulSets son un objeto de API de carga de trabajo de Kubernetes diseñado específicamente para gestionar aplicaciones con estado. A diferencia de los Deployments, que tratan a todos los pods como intercambiables, los StatefulSets mantienen una identidad única para cada pod con garantías sobre el orden y la unicidad.
Funciones clave:
- Identificadores de red estables: Cada pod recibe un nombre de host predecible que persiste incluso tras reinicios
- Almacenamiento persistente: Solicitudes de volumen persistentes dedicadas que siguen a los pods incluso tras reschedules
- Despliegue ordenado: Los pods se crean secuencialmente (0, 1, 2…) y se terminan en orden inverso
- Actualizaciones ordenadas: Las actualizaciones progresivas se realizan en orden, asegurando la estabilidad de la aplicación
Los StatefulSets son esenciales para aplicaciones como PostgreSQL, MySQL, MongoDB, Cassandra, Elasticsearch, Kafka, ZooKeeper, Redis y etcd—cualquier carga de trabajo donde la identidad del pod y la persistencia de datos importen.
Entendiendo el almacenamiento persistente en Kubernetes
Kubernetes proporciona una capa de abstracción de almacenamiento sofisticada que desconecta la gestión del almacenamiento del ciclo de vida del pod:
Componentes de almacenamiento
PersistentVolume (PV): Un fragmento de almacenamiento en el clúster provisto por un administrador o creado dinámicamente mediante una StorageClass. Los PV existen independientemente de los pods.
PersistentVolumeClaim (PVC): Una solicitud de almacenamiento por parte de un pod. Los PVC se vinculan a PV disponibles que coincidan con sus requisitos (tamaño, modo de acceso, clase de almacenamiento).
StorageClass: Define diferentes “clases” de almacenamiento con diversos proveedores (AWS EBS, GCE PD, Azure Disk, NFS, etc.) y parámetros como replicación, niveles de rendimiento y políticas de respaldo.
Modos de acceso
- ReadWriteOnce (RWO): Volumen montado como de lectura y escritura por un solo nodo
- ReadOnlyMany (ROX): Volumen montado como de solo lectura por muchos nodos
- ReadWriteMany (RWX): Volumen montado como de lectura y escritura por muchos nodos (requiere backends de almacenamiento especiales)
Arquitectura de almacenamiento de StatefulSets
Los StatefulSets utilizan volumeClaimTemplates para crear automáticamente PersistentVolumeClaims para cada réplica de pod. Esto es fundamentalmente diferente de los Deployments:
Cómo funcionan los volumeClaimTemplates
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql-service
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: data
mountPath: /var/lib/mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "fast-ssd"
resources:
requests:
storage: 10Gi
Cuando creas este StatefulSet:
- Kubernetes crea el pod
mysql-0y el PVCdata-mysql-0 - Luego crea el pod
mysql-1y el PVCdata-mysql-1 - Finalmente crea el pod
mysql-2y el PVCdata-mysql-2
Cada pod recibe su propio volumen persistente de 10 GB dedicado. Si mysql-1 se elimina o se reschedulea, Kubernetes lo recrea y vuelve a adjuntar el mismo PVC data-mysql-1, preservando todos los datos.
Crear un servicio sin dirección IP (headless) para StatefulSets
Los StatefulSets requieren un servicio headless para proporcionar identidades de red estables:
apiVersion: v1
kind: Service
metadata:
name: mysql-service
spec:
clusterIP: None # Esto lo convierte en un servicio headless
selector:
app: mysql
ports:
- port: 3306
name: mysql
Esto crea entradas DNS para cada pod:
mysql-0.mysql-service.default.svc.cluster.localmysql-1.mysql-service.default.svc.cluster.localmysql-2.mysql-service.default.svc.cluster.local
Las aplicaciones pueden conectarse directamente a instancias específicas de pod usando estos nombres DNS estables.
Patrones de despliegue de StatefulSets
Para equipos que gestionan despliegues complejos de Kubernetes, Helm Charts ofrecen una forma poderosa de empaquetar y desplegar StatefulSets con plantillas, versionado y gestión de dependencias. Helm simplifica la gestión de configuraciones de StatefulSet en diferentes entornos.
Escalado ordenado
Al escalar de 3 a 5 réplicas:
kubectl scale statefulset mysql --replicas=5
Kubernetes crea los pods en orden: mysql-3 → espera a que esté listo → mysql-4
Al escalar de 5 a 3 réplicas:
kubectl scale statefulset mysql --replicas=3
Kubernetes termina en orden inverso: mysql-4 → espera a que termine → mysql-3
Actualizaciones progresivas
Los StatefulSets admiten dos estrategias de actualización:
OnDelete: Actualizaciones manuales—los pods solo se actualizan cuando los eliminas RollingUpdate: Actualizaciones automáticas secuenciales en orden inverso
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2 # Solo actualiza pods con ordinal >= 2
El parámetro partition permite despliegues canarios—puedes actualizar primero los pods con números altos y probar antes de desplegar a todas las réplicas.
Mejores prácticas de almacenamiento
Provisionamiento dinámico
Siempre usa StorageClasses para el provisionamiento dinámico de volúmenes:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp3
iops: "3000"
throughput: "125"
allowVolumeExpansion: true
reclaimPolicy: Retain
allowVolumeExpansion: Habilita el redimensionamiento de PVC sin recrearlos
reclaimPolicy: Retain mantiene los datos del PV después de eliminar el PVC, Delete los elimina automáticamente
Políticas de retención de PVC
Kubernetes 1.23+ admite persistentVolumeClaimRetentionPolicy:
spec:
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain # Mantiene los PVC cuando se elimina el StatefulSet
whenScaled: Delete # Elimina los PVC al escalar hacia abajo
Opciones:
- Retain: Mantiene los PVC (comportamiento predeterminado, más seguro)
- Delete: Elimina automáticamente los PVC (útil para entornos de desarrollo)
Estrategias de respaldo
Snapshots de volumen: Usa recursos de VolumeSnapshot para crear copias de seguridad en punto de tiempo
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: mysql-snapshot-20251113
spec:
volumeSnapshotClassName: csi-snapclass
source:
persistentVolumeClaimName: data-mysql-0
Respaldo a nivel de aplicación: Usa herramientas como mysqldump, pg_dump o Velero para respaldos específicos de bases de datos
Replicación distribuida: Configura la replicación a nivel de aplicación (replicación de MySQL, replicación en streaming de PostgreSQL) como primera línea de defensa
Casos de uso reales
Clúster de base de datos (PostgreSQL)
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16
ports:
- containerPort: 5432
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "standard"
resources:
requests:
storage: 20Gi
Cache distribuido (Redis)
Para clústeres de Redis, necesitas tanto un StatefulSet como una configuración cuidadosa:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
spec:
serviceName: redis-service
replicas: 6
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7-alpine
command:
- redis-server
- "--appendonly"
- "yes"
- "--appendfsync"
- "everysec"
ports:
- containerPort: 6379
name: redis
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
Cola de mensajes (Kafka)
Kafka requiere tanto almacenamiento persistente para los registros como identidades de red estables para la coordinación de brokers:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: kafka
spec:
serviceName: kafka-service
replicas: 3
selector:
matchLabels:
app: kafka
template:
metadata:
labels:
app: kafka
spec:
containers:
- name: kafka
image: confluentinc/cp-kafka:7.5.0
ports:
- containerPort: 9092
name: kafka
env:
- name: KAFKA_BROKER_ID
valueFrom:
fieldRef:
fieldPath: metadata.name
volumeMounts:
- name: data
mountPath: /var/lib/kafka/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
Monitoreo y solución de problemas
Para una referencia completa de los comandos de Kubernetes utilizados en esta sección, consulta la Hoja de trucos de Kubernetes.
Verificar el estado de los StatefulSets
# Ver detalles del StatefulSet
kubectl get statefulset mysql
kubectl describe statefulset mysql
# Ver el orden de creación y el estado de los pods
kubectl get pods -l app=mysql -w
# Ver el estado de los PVC
kubectl get pvc
kubectl describe pvc data-mysql-0
Problemas comunes
Pod atascado en Pendiente: Verificar el estado del PVC y la disponibilidad del almacenamiento
kubectl describe pod mysql-0
kubectl get events --sort-by='.lastTimestamp'
Almacenamiento lleno: Ampliar el PVC si la StorageClass lo permite
kubectl patch pvc data-mysql-0 -p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'
Pod que no se termina: Verificar bloqueos a nivel de aplicación o problemas de desmontaje de volúmenes
kubectl delete pod mysql-0 --grace-period=0 --force
Métricas para monitorear
- Uso de almacenamiento: Monitorear la capacidad y el porcentaje de uso del PVC
- Rendimiento de E/S: Registrar IOPS, throughput y latencia
- Reinicios de pod: Reinicios frecuentes pueden indicar problemas de almacenamiento
- Tiempo de vinculación de PVC: Tiempos lentos sugieren problemas de provisionamiento
Estrategias de migración
Al migrar a StatefulSets, asegúrate de que tu clúster de Kubernetes esté correctamente configurado. Para configuraciones de laboratorio o clústeres pequeños, revisa nuestra comparación completa de distribuciones de Kubernetes para elegir la plataforma adecuada para tus requisitos de carga de trabajo.
De Deployment a StatefulSet
- Crear StatefulSet con volumeClaimTemplates
- Escalar hacia abajo el Deployment de forma suave
- Restaurar datos desde respaldos a los pods de StatefulSet
- Actualizar referencias de DNS/Servicio
- Eliminar el Deployment antiguo y los PVCs
Respaldo antes de la migración
# Crear snapshots de los PVCs existentes
kubectl get pvc -o yaml > pvc-backup.yaml
# Crear snapshots de volumen
kubectl apply -f volume-snapshot.yaml
Consideraciones de seguridad
Encriptación de almacenamiento
Habilita la encriptación en reposo usando parámetros de StorageClass:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: encrypted-storage
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp3
encrypted: "true"
kmsKeyId: arn:aws:kms:us-east-1:123456789012:key/abcd-1234
Control de acceso
Usa RBAC para restringir quién puede crear/modificar StatefulSets y PVCs:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: statefulset-manager
rules:
- apiGroups: ["apps"]
resources: ["statefulsets"]
verbs: ["get", "list", "create", "update", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "create", "delete"]
Políticas de red
Restringir la comunicación entre pods:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: mysql-network-policy
spec:
podSelector:
matchLabels:
app: mysql
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: backend
ports:
- protocol: TCP
port: 3306
Optimización de rendimiento
Capas de rendimiento de almacenamiento
Elige StorageClasses adecuadas según la carga de trabajo:
- Alta IOPS: Bases de datos con lectura/escritura aleatoria intensiva (gp3, io2)
- Alto throughput: Agregación de logs, análisis (st1, sc1)
- Equilibrada: Aplicaciones generales (gp3)
Distribución de pods
Usa anti-affinidad de pods para distribuir pods de StatefulSet entre zonas de disponibilidad:
spec:
template:
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: mysql
topologyKey: topology.kubernetes.io/zone
Solicitudes y límites de recursos
Establece recursos adecuados para un rendimiento consistente:
resources:
requests:
cpu: "2"
memory: "4Gi"
ephemeral-storage: "10Gi"
limits:
cpu: "4"
memory: "8Gi"
ephemeral-storage: "20Gi"
Patrones avanzados
Aplicación con estado con contenedores de inicialización
Usa contenedores de inicialización para la inicialización de bases de datos:
spec:
template:
spec:
initContainers:
- name: init-mysql
image: mysql:8.0
command:
- bash
- "-c"
- |
if [[ ! -d /var/lib/mysql/mysql ]]; then
mysqld --initialize-insecure --datadir=/var/lib/mysql
fi
volumeMounts:
- name: data
mountPath: /var/lib/mysql
Pods con múltiples contenedores para sidecars
Añade sidecars de respaldo o agentes de monitoreo:
spec:
template:
spec:
containers:
- name: mysql
image: mysql:8.0
# ... configuración de mysql ...
- name: backup-sidecar
image: mysql-backup:latest
volumeMounts:
- name: data
mountPath: /var/lib/mysql
readOnly: true
ConfigMaps para configuración dinámica
Separa la configuración de la definición de StatefulSet:
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
data:
my.cnf: |
[mysqld]
max_connections=200
innodb_buffer_pool_size=2G
---
spec:
template:
spec:
containers:
- name: mysql
volumeMounts:
- name: config
mountPath: /etc/mysql/conf.d
volumes:
- name: config
configMap:
name: mysql-config
Enlaces útiles
- Documentación de Kubernetes StatefulSets
- Volúmenes persistentes
- Clases de almacenamiento
- Snapshots de volumen
- Mejores prácticas de StatefulSet
- Drivers CSI
- Soluciones de respaldo de Velero
- Almacenamiento Rook Ceph
- Hoja de trucos de Kubernetes
- Instalar Kubernetes con Kubespray
- Comparación de distribuciones de Kubernetes para un laboratorio de 3 nodos
- Distribuciones de Kubernetes - visión general de kubeadm, k3s, MicroK8s, Minikube, Talos Linux y RKE2
- Maestra Helm Charts: Guía de gestión de paquetes de Kubernetes