StatefulSets

Un StatefulSet es el objeto de la API workload que se usa para gestionar aplicaciones con estado.

Gestiona el despliegue y escalado de un conjunto de Pods, y garantiza el orden y unicidad de dichos Pods.

Al igual que un Deployment, un StatefulSet gestiona Pods que se basan en una especificación idéntica de contenedor. A diferencia de un Deployment, un StatefulSet mantiene una identidad asociada a sus Pods. Estos pods se crean a partir de la misma especificación, pero no pueden intercambiarse; cada uno tiene su propio identificador persistente que mantiene a lo largo de cualquier re-programación.

Un StatefulSet opera bajo el mismo patrón que cualquier otro controlador. Se define el estado deseado en un objeto StatefulSet, y el controlador del StatefulSet efectúa las actualizaciones que sean necesarias para alcanzarlo a partir del estado actual.

Usar StatefulSets

Los StatefulSets son valiosos para aquellas aplicaciones que necesitan uno o más de los siguientes:

  • Identificadores de red estables, únicos.
  • Almacenamiento estable, persistente.
  • Despliegue y escalado ordenado, controlado.
  • Actualizaciones en línea ordenadas, automatizadas.

De los de arriba, estable es sinónimo de persistencia entre (re)programaciones de Pods. Si una aplicación no necesita ningún identificador estable o despliegue, eliminación, o escalado ordenado, deberías desplegar tu aplicación con un controlador que proporcione un conjunto de réplicas sin estado, como un Deployment o un ReplicaSet, ya que están mejor preparados para tus necesidades sin estado.

Limitaciones

  • El almacenamiento de un determinado Pod debe provisionarse por un Provisionador de PersistentVolume basado en la storage class requerida, o pre-provisionarse por un administrador.
  • Eliminar y/o reducir un StatefulSet no eliminará los volúmenes asociados con el StatefulSet. Este comportamiento es intencional y sirve para garantizar la seguridad de los datos, que da más valor que la purga automática de los recursos relacionados del StatefulSet.
  • Los StatefulSets actualmente necesitan un Servicio Headless como responsable de la identidad de red de los Pods. Es tu responsabilidad crear este Service.
  • Los StatefulSets no proporcionan ninguna garantía de la terminación de los pods cuando se elimina un StatefulSet. Para conseguir un término de los pods ordenado y controlado en el StatefulSet, es posible reducir el StatefulSet a 0 réplicas justo antes de eliminarlo.
  • Cuando se usan las Actualizaciones en línea con la Regla de Gestión de Pod (OrderedReady) por defecto, es posible entrar en un estado inconsistente que requiere de una intervención manual para su reparación.

Componentes

El ejemplo de abajo demuestra los componentes de un StatefulSet:

  • Un servicio Headless, llamado nginx, se usa para controlar el dominio de red.
  • Un StatefulSet, llamado web, que tiene una especificación que indica que se lanzarán 3 réplicas del contenedor nginx en Pods únicos.
  • Un volumeClaimTemplate que proporciona almacenamiento estable por medio de PersistentVolumes provisionados por un provisionador de tipo PersistentVolume.
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx # tiene que coincidir con .spec.template.metadata.labels
  serviceName: "nginx"
  replicas: 3 # por defecto es 1
  template:
    metadata:
      labels:
        app: nginx # tiene que coincidir con .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: registry.k8s.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "my-storage-class"
      resources:
        requests:
          storage: 1Gi

Selector de Pod

Debes poner el valor del campo .spec.selector de un StatefulSet para que coincida con las etiquetas de su campo .spec.template.metadata.labels. Antes de Kubernetes 1.8, el campo .spec.selector se predeterminaba cuando se omitía. A partir de la versión 1.8, si no se especifica un selector de coincidencia de Pods, se produce un error de validación durante la creación del StatefulSet.

Identidad de Pod

Los Pods de un StatefulSet tienen una identidad única que está formada por un ordinal, una identidad estable de red, y almacenamiento estable. La identidad se asocia al Pod, independientemente del nodo en que haya sido (re)programado.

Índice Ordinal

Para un StatefulSet con N réplicas, a cada Pod del StatefulSet se le asignará un número entero ordinal, desde 0 hasta N-1, y que es único para el conjunto.

ID estable de Red

El nombre de anfitrión (hostname) de cada Pod de un StatefulSet se deriva del nombre del StatefulSet y del número ordinal del Pod. El patrón para construir dicho hostname es $(statefulset name)-$(ordinal). Así, el ejemplo de arriba creará tres Pods denominados web-0,web-1,web-2. Un StatefulSet puede usar un Servicio Headless para controlar el nombre de dominio de sus Pods. El nombre de dominio gestionado por este Service tiene la forma: $(service name).$(namespace).svc.cluster.local, donde "cluster.local" es el nombre de dominio del clúster. Conforme se crea cada Pod, se le asigna un nombre DNS correspondiente de subdominio, que tiene la forma: $(podname).$(governing service domain), donde el servicio en funciones se define por el campo serviceName del StatefulSet.

Como se indicó en la sección limitaciones, la creación del Servicio Headless encargado de la identidad de red de los pods es enteramente tu responsabilidad.

Aquí se muestran algunos ejemplos de elecciones de nombres de Cluster Domain, nombres de Service, nombres de StatefulSet, y cómo impactan en los nombres DNS de los Pods del StatefulSet:

Cluster DomainService (ns/nombre)StatefulSet (ns/nombre)StatefulSet DomainPod DNSPod Hostname
cluster.localdefault/nginxdefault/webnginx.default.svc.cluster.localweb-{0..N-1}.nginx.default.svc.cluster.localweb-{0..N-1}
cluster.localfoo/nginxfoo/webnginx.foo.svc.cluster.localweb-{0..N-1}.nginx.foo.svc.cluster.localweb-{0..N-1}
kube.localfoo/nginxfoo/webnginx.foo.svc.kube.localweb-{0..N-1}.nginx.foo.svc.kube.localweb-{0..N-1}

Almacenamiento estable

Kubernetes crea un PersistentVolume para cada VolumeClaimTemplate. En el ejemplo de nginx de arriba, cada Pod recibirá un único PersistentVolume con una StorageClass igual a my-storage-class y 1 GiB de almacenamiento provisionado. Si no se indica ninguna StorageClass, entonces se usa la StorageClass por defecto. Cuando un Pod se (re)programa en un nodo, sus volumeMounts montan los PersistentVolumes asociados con sus PersistentVolume Claims. Nótese que los PersistentVolumes asociados con los PersistentVolume Claims de los Pods no se eliminan cuando los Pods, o los StatefulSet se eliminan. Esto debe realizarse manualmente.

Etiqueta de Nombre de Pod

Cuando el controlador del StatefulSet crea un Pod, añade una etiqueta, statefulset.kubernetes.io/pod-name, que toma el valor del nombre del Pod. Esta etiqueta te permite enlazar un Service a un Pod específico en el StatefulSet.

Garantías de Despliegue y Escalado

  • Para un StatefulSet con N réplicas, cuando los Pods se despliegan, se crean secuencialmente, en orden de {0..N-1}.
  • Cuando se eliminan los Pods, se terminan en orden opuesto, de {N-1..0}.
  • Antes de que una operación de escalado se aplique a un Pod, todos sus predecesores deben estar Running y Ready.
  • Antes de que se termine un Pod, todos sus sucesores deben haberse apagado completamente.

El StatefulSet no debería tener que indicar un valor 0 para el campo pod.Spec.TerminationGracePeriodSeconds. Esta práctica no es segura y se aconseja no hacerlo. Para una explicación más detallada, por favor echa un vistazo a cómo forzar la eliminación de Pods de un StatefulSet.

Cuando el ejemplo nginx de arriba se crea, se despliegan tres Pods en el orden web-0, web-1, web-2. web-1 no se desplegará hasta que web-0 no esté Running y Ready, y web-2 no se desplegará hasta que web-1 esté Running y Ready. En caso de que web-0 fallase, después de que web-1 estuviera Running y Ready, pero antes de que se desplegara web-2, web-2 no se desplegaría hasta que web-0 se redesplegase con éxito y estuviera Running y Ready.

Si un usuario fuera a escalar el ejemplo desplegado parcheando el StatefulSet de forma que replicas=1, web-2 se terminaría primero. web-1 no se terminaría hasta que web-2 no se hubiera apagado y eliminado por completo. Si web-0 fallase después de que web-2 se hubiera terminado y apagado completamente, pero antes del término de web-1, entonces web-1 no se terminaría hasta que web-0 estuviera Running y Ready.

Reglas de Gestión de Pods

En Kubernetes 1.7 y versiones posteriores, el StatefulSet permite flexibilizar sus garantías de ordenación al mismo tiempo que preservar su garantía de singularidad e identidad a través del campo .spec.podManagementPolicy.

Gestión de tipo OrderedReady de Pods

La gestión de tipo OrderedReady de pods es la predeterminada para los StatefulSets. Implementa el comportamiento descrito arriba.

Gestión de tipo Parallel de Pods

La gestión de tipo Parallel de pods le dice al controlador del StatefulSet que lance y termine todos los Pods en paralelo, y que no espere a que los Pods estén Running y Ready o completamente terminados antes de lanzar o terminar otro Pod.

Estrategias de Actualización

En Kubernetes 1.7 y a posteriori, el campo .spec.updateStrategy del StatefulSet permite configurar y deshabilitar las actualizaciones automátizadas en línea para los contenedores, etiquetas, peticiones/límites de recursos, y anotaciones de los Pods del StatefulSet.

On Delete

La estrategia de actualización OnDelete implementa el funcionamiento tradicional (1.6 y previo). Cuando el campo .spec.updateStrategy.type de un StatefulSet se pone al valor OnDelete, el controlador del StatefulSet no actualizará automáticamente los Pods del StatefulSet. Los usuarios deben eliminar manualmente los Pods para forzar al controlador a crear nuevos Pods que reflejen las modificaciones hechas al campo .spec.template del StatefulSet.

Rolling Updates

La estrategia de actualización RollingUpdate implementa una actualización automatizada en línea de los Pods del StatefulSet. Es la estrategia por defecto cuando el campo .spec.updateStrategy se deja sin valor. Cuando el campo .spec.updateStrategy.type de un StatefulSet se pone al valor RollingUpdate, el controlador del StatefulSet lo eliminará y recreará cada Pod en el StatefulSet. Procederá en el mismo orden en que ha terminado los Pod (del número ordinal más grande al más pequeño), actualizando cada Pod uno por uno. Esperará a que el Pod actualizado esté Running y Ready antes de actualizar su predecesor.

Particiones

La estrategia de actualización RollingUpdate puede particionarse, indicando el valor del campo .spec.updateStrategy.rollingUpdate.partition. Si se indica una partición, todos los Pods con un número ordinal mayor o igual que el de la partición serán actualizados cuando el campo .spec.template del StatefulSet se actualice. Todos los Pods con un número ordinal que sea menor que el de la partición no serán actualizados, e incluso si son eliminados, serán recreados con la versión anterior. Si el campo .spec.updateStrategy.rollingUpdate.partition de un StatefulSet es mayor que el valor del campo .spec.replicas, las modificaciones al campo .spec.template no se propagarán a sus Pods. En la mayoría de ocasiones, no necesitarás usar una partición, pero pueden resultar útiles si quieres preparar una actualización, realizar un despliegue tipo canary, o llevar a cabo un despliegue en fases.

Retroceso Forzado

Cuando se usa Actualizaciones en línea con el valor de la Regla de Gestión de Pod (OrderedReady) por defecto, es posible acabar en un estado inconsistente que requiera de una intervención manual para arreglarlo.

Si actualizas la plantilla Pod a una configuración que nunca llega a Running y Ready (por ejemplo, debido a un binario incorrecto o un error de configuración a nivel de aplicación), el StatefulSet detendrá el despliegue y esperará.

En este estado, no es suficiente con revertir la plantilla Pod a la configuración buena. Debido a un problema conocido, el StatefulSet seguirá esperando a que los Pod estropeados se pongan en Ready (lo que nunca ocurre) antes de intentar revertirla a la configuración que funcionaba.

Antes de revertir la plantilla, debes también eliminar cualquier Pod que el StatefulSet haya intentando ejecutar con la configuración incorrecta. El StatefulSet comenzará entonces a recrear los Pods usando la plantilla revertida.

Siguientes pasos

Última modificación September 28, 2023 at 12:35 PM PST: content: es: Fix incorrect letter case for data storage units (1e3b672e72)