Gestión de Aplicaciones con Estado en Kubernetes Mediante StatefulSets

Kubernetes ofrece varios tipos de controladores para gestionar cargas de trabajo, siendo Deployment el estándar para servicios sin estado. Sin embargo, las aplicaciones que requieren identidad estable y persistencia de datos necesitan mecanismos específicos, como los StatefulSets.

En un entorno sin estado, los contenedores son intercambiables; si uno falla o se reinicia, otro toma su lugar sin afectar la lógica de negocio, ya que no hay dependencia entre ellos ni almacenamiento local vinculado. En contraste, las aplicaciones con estado suelen tener relaciones jerárquicas (como maestro-esclavo) y dependen fuertemente de recursos externos.

El desafío principal al escalar o migrar pods con estado radica en mantener la correspondencia entre la instancia del pod y los recursos de datos asociados. StatefulSet resuelve esto asegurando identificadores únicos por pod y gestionando el ciclo de vida de los volúmenes de almacenamiento.

Gestión de la Identidad de Red y Topología

Una característica distintiva es la asignación de nombres estables. A diferencia de los pods creados por Deployments, cuyo nombre puede variar tras un reinicio, StatefulSet genera hostnames derivados del nombre del recurso y un índice secuencial (ej. pod-name-0, pod-name-1).

Para acceder a estos pods de manera fiable, Kubernetes utiliza servicios DNS internos. La clave es utilizar un servicio Headless. Al configurar la propiedad clusterIP como None, el servicio no asigna una IP virtual, sino que expone directamente los registros DNS para cada endpoint activo.

# Definición de Servicio Cabeza Sin Carga (Headless Service)
apiVersion: v1
kind: Service
metadata:
  name: db-service-headless
spec:
  clusterIP: None 
  ports:
    - name: mysql-port
      port: 3306
      targetPort: 3306
  selector:
    component: database

Una vez aplicado este manifiesto, cada pod asociado obtendrá un registro DNS completo siguiendo el patrón:

db-service-headless.{namespace}.svc.cluster.local.{pod-index}

Ejemplo de despliegue de StatefulSet conectado a este servicio:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: database-cluster
spec:
  serviceName: db-service-headless
  replicas: 2
  selector:
    matchLabels:
      component: database
  template:
    metadata:
      labels:
        component: database
    spec:
      containers:
        - name: mariadb
          image: mariadb:10.5
          ports:
            - containerPort: 3306
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-secret
                  key: password

Al verificar los pods con kubectl get pods, se observa que mantienen sus nombres fijos. Incluso si se eliminan manualmente, el controlador recreará exactamente el mismo pod con el mismo nombre, permitiendo que las conexiones DNS existentes sigan funcionando.

Control de Persistencia de Datos (PVC y PV)

La segunda pieza fundamental es el almacenamiento persistente. En Kubernetes, esto se logra mediante Volúmenes Persistentes (PV) y Reclamaciones de Volumen Persistentes (PVC).

Cuando un StatefulSet requiere acceso a disco, se define la esrtuctura de volúmenes dentro del especificador del pod. Aunque existe un volumen único por pod (mediante patrones de variables dinámicas), a menudo se utilizan clases de almacenamiento específicas para garantizar la ubicación física correcta.

A continuación, creamos un volúmen físico local simulando almacenamiento directo:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-storage-backup
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteMany
  storageClassName: fast-local
  local:
    path: /mnt/data/persistent
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
                - worker-node-01

Posteriormente, generamos la solicitud de almacenamiento (PVC) que permite a los pods consumir ese espacio:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-disk-claim
spec:
  storageClassName: fast-local
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi

Para integrar esto con el StatefulSet, modificamos el manifiesto anterior añadiendo la sección volumeMounts:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: database-cluster
spec:
  serviceName: db-service-headless
  replicas: 2
  selector:
    matchLabels:
      component: database
  template:
    metadata:
      labels:
        component: database
    spec:
      volumes:
        - name: storage-volume
          persistentVolumeClaim:
            claimName: app-disk-claim
      containers:
        - name: mariadb
          image: mariadb:10.5
          volumeMounts:
            - mountPath: /var/lib/mysql
              name: storage-volume
          ports:
            - containerPort: 3306

Al ejecutar el despliegue, podemos observar cómo el volumen se monta correctamente dentro del contenedor. Si ingresamos a la terminal del pod y guardamos un archivo de configuración en el directorio montado:

echo "config_updated" > /var/lib/mysql/settings.dat

Siguiente paso crítico: eliminar el pod. Dado el orden estricto de StatefulSet, el sistema detiene el pod antes de iniciar el siguiente. Al borrar el pod específico y verificar después del reinicio:

$ kubectl delete pod database-cluster-0
$ kubectl exec -it database-cluster-0 -- cat /var/lib/mysql/settings.dat

El contenido persistirá. Esto confirma que aunque el contenedor (el pod) sea efímero y se destruya, la capa de almacenamiento (PVC/PV) sobrevive y mantiene la integridad de los datos, restableciendo la conexión automáticamente al nuevo pod.

Análisis Comparativo de Comportamiento

StatefulSet introduce semánticas operativas distintas al modelo de replica set estándar:

  • Ordenación: Los pods se crean y eliminan de forma secuencial (de 0 a N, y viceversa).
  • Identidad Única: El hostname no cambia, facilitando la descubrimiento de services en clústeres distribuidos.
  • Escalabilidad Controlada: Permite crecer o reducir instancias manteniendo la integridad de los nodos de datos.

Este enfoque es esencial para bases de datos relacionales, sistemas de colas y aplicaciones que requieren coordinación compleja, donde la pérdida de contexto o datos durante una reprogramación no es aceptable.

Etiquetas: Kubernetes statefulset persistent-volumes pv pvc

Publicado el 9-9 16:27