A diferencia de un Deployment, un StatefulSet mantiene una identidad fija para cada uno de sus Pods.

Por lo tanto cada Pod es distinto (tiene una identidad única), y este hecho tiene algunas consecuencias:

  • El nombre de cada Pod tendrá un número (1,2,…) que lo identifica y que nos proporciona la posibilidad de que la creación actualización y eliminación sea ordenada.
  • Si un nuevo Pod es recreado, obtendrá el mismo nombre (hostname), los mismos nombres DNS (aunque la IP pueda cambiar) y el mismo volumen que tenía asociado.
  • Necesitamos crear un servicio especial, llamado Headless Service, que nos permite acceder a los Pods de forma independiente, pero que no balancea la carga entre ellos, por lo tanto este servicio no tendrá una ClusterIP.

La definición del objeto StatefulSet la tenemos guardada en el fichero statefulset.yaml:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: bitnami/nginx
        ports:
        - containerPort: 8080
          name: web
        volumeMounts:
        - name: www
          mountPath: /app
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

  • Se indica los Pods que vamos a controlar por medio de la etiqueta selector.
  • Como hemos indicado cada Pods va a tener asociado un volumen persistente, se hace la definición de la reclamación del volumen con la etiqueta volumeClaimTemplates.
  • Se indica el punto de montaje en el contenedor, con la etiqueta volumeMounts.

Por otro lado la definición del recurso Headless Service la tenemos en el fichero service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 8080
    name: web
  clusterIP: None
  selector:
    app: nginx
  • En este caso no se balancea la carga a cada pod, sino que podemos acceder a cada Pod de manera independiente, con el nombre:

      <nombre_del_pod>.<nombre-statefulset>.<nombre-proyecto>.svc.cluster.local
    
  • Por lo tanto no va tener asignada ninguna Cluster IP (clusterIP: None).
  • Se seleccionan los Pods a los que se puede acceder por medio de la etiqueta declarada en el apartado selector.

Creación ordenada de Pods

Lo primero creamos el recurso Headless Service y vamos comprobar la creación ordenados de Pods, para ello en un terminal observamos la creación de Pods y en otro terminal creamos los Pods:

watch oc get pod
oc apply -f statefulset.yaml

Comprobamos la identidad de red estable

Vemos los hostname y los nombres DNS asociados:

for i in 0 1; do oc exec web-$i -- sh -c 'hostname'; done
web-0
web-1

oc run -it --image busybox:1.28 dns-test --restart=Never --rm
/ # nslookup web-0.nginx
...
Address 1: 10.128.8.181 web-0.nginx.josedom24-dev.svc.cluster.local

/ # nslookup web-1.nginx
...
Address 1: 10.128.43.7 web-1.nginx.josedom24-dev.svc.cluster.local

Eliminación de Pods

En un terminal observamos la creación de Pods y en otro terminal eliminamos los Pods:

watch oc get pod
oc delete pod -l app=nginx

Comprobamos la identidad de red estable

Volvemos a crear el recurso StatefulSet y comprobamos que los hostnames y los nombres DNS asociados no han cambiado (las IP pueden cambiar):

oc apply -f statefulset.yaml
for i in 0 1; do oc exec web-$i -- sh -c 'hostname'; done

oc run -it --image busybox:1.28 dns-test --restart=Never --rm
/ # nslookup web-0.nginx
/ # nslookup web-1.nginx

Escribiendo en los volúmenes persistentes

Comprobamos que se han creado volúmenes para los Pods:

oc get pvc

Creamos el fichero index.html en el directorio que hemos montado (directorio DocumentRoot del servidor web):

for i in 0 1; do oc exec "web-$i" -- sh -c 'echo "$(hostname)" > /app/index.html'; done
for i in 0 1; do oc exec -i -t "web-$i" -- sh -c 'cat  /app/index.html'; done
web-0
web-1

Volvemos a eliminar los Pods, y comprobamos que la información es persistente al estar guardadas en los volúmenes:

oc delete pod -l app=nginx
oc apply -f statefulset.yaml
for i in 0 1; do oc exec -i -t "web-$i" -- sh -c 'cat  /app/index.html'; done

Escalar el StatefulSet

Para escalar el despliegue:

oc scale sts web --replicas=5

Comprobamos los Pods y los volúmenes:

oc get pod,pvc

Si reducimos el número de Pods los volúmenes no se eliminan.

Gestión de StatefulSet desde la consola web

Para gestionar los objetos StatefulSet desde la consola web, escogemos la vista Admionistrator y la opción Workloads -> StatefulSets:

st

En esa pantalla además, tenemos la opción de crear un nuevo recurso con el botón Create StatefulSet. Si escogemos un objeto determinado obtendremos la descripción del mismo:

st

También podemos gestionar los objetos PersistentVolumeClaim que se han creado para cada uno de los Pods, en la sección Storage - > PersistentVolumeClaim:

st

Borrado del escenario

Para terminar eliminamos el statefulset y el service:

oc delete -f statefulset.yaml
oc delete -f service.yaml

Para borrar los volúmenes:

oc delete --all pvc

Actualizado: