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: k8s.gcr.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 Domain | Service (ns/nombre) | StatefulSet (ns/nombre) | StatefulSet Domain | Pod DNS | Pod Hostname |
---|---|---|---|---|---|
cluster.local | default/nginx | default/web | nginx.default.svc.cluster.local | web-{0..N-1}.nginx.default.svc.cluster.local | web-{0..N-1} |
cluster.local | foo/nginx | foo/web | nginx.foo.svc.cluster.local | web-{0..N-1}.nginx.foo.svc.cluster.local | web-{0..N-1} |
kube.local | foo/nginx | foo/web | nginx.foo.svc.kube.local | web-{0..N-1}.nginx.foo.svc.kube.local | web-{0..N-1} |
cluster.local
a menos que
se configure de otra forma.
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
- Sigue el ejemplo de cómo desplegar un aplicación con estado.
- Sigue el ejemplo de cómo desplegar Cassandra con StatefulSets.