DaemonSets y StatefulSets en Kubernetes
Ya hemos visto los deployments, pero existen otros objetos similares que nos permiten gestionar múltiples pods de forma automática. En este capítulo vamos a verlos: los DaemonSets y los StatefulSets.
🎥 Vídeo completo, utilicemos los capítulos de youtube para saltar a la sección que nos interese: https://youtu.be/zNEKiAy1F6Q
Al igual que los deployments, permiten gestionar pods de forma automática, pero tienen unas características diferentes que los hacen más adecuados para ciertos casos de uso.
Comparemos primero los Deployments con los DaemonSets y StatefulSets:
| Característica | Deployments | DaemonSets | StatefulSets |
|---|---|---|---|
| Propósito principal | Gestionar aplicaciones sin estado | Ejecutar pods en todos los nodos | Gestionar aplicaciones con estado |
| Escalabilidad | Escalado automático y manual | No escalable, un pod por nodo | Escalado manual |
| Identidad de los pods | No garantiza identidad única | No garantiza identidad única | Garantiza identidad única |
| Persistencia de datos | No garantiza persistencia | No garantiza persistencia | Volumen por réplica (volumeClaimTemplates) |
| Casos de uso comunes | Aplicaciones web, APIs | Agentes de monitoreo, logging | Bases de datos, sistemas distribuidos |
| Distribución de pods | Basado en el número deseado | Uno por nodo o subconjunto de nodos | Basado en el número deseado |
| Orden de despliegue | No garantiza orden específico | No garantiza orden específico | Garantiza orden de despliegue |
DaemonSets
Los DaemonSets son objetos de Kubernetes que garantizan que un pod se ejecute en todos los nodos de un clúster, o en un subconjunto específico de ellos. Esto es útil para aplicaciones que tienen que ejecutarse en cada nodo, como agentes de monitorización, recolectores de logs o servicios de red.
Si un nodo se añade al clúster, el DaemonSet automáticamente crea un pod en ese nodo. Si un nodo se elimina, el pod asociado también se elimina. Simple y efectivo.
Veamos un ejemplo de un DaemonSet que ejecuta un pod en todos los nodos del clúster:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
labels:
k8s-app: fluent-bit-logging
spec:
selector:
matchLabels:
name: fluent-bit
template:
metadata:
labels:
name: fluent-bit
spec:
containers:
- name: fluent-bit
image: fluent/fluent-bit:5.1.3
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
Este DaemonSet ejecutará un pod de Fluent Bit en cada nodo del clúster. Fluent Bit es un recolector de logs ligero que puede recopilar y enviar logs a diferentes destinos; en este caso podemos suponer que se está utilizando para recopilar los logs de todos los nodos del clúster (en un despliegue real montaríamos además /var/log del nodo con un volumen hostPath y le pasaríamos su configuración).
Para aplicar la configuración anterior, usaríamos el siguiente comando:
kubectl apply -f daemonset.yaml
Podríamos consultar el estado del DaemonSet con el comando:
kubectl get daemonset fluent-bit
Como vemos, todos estos objetos se gestionan de forma similar a los deployments: solo hay que especificar el tipo de objeto después de las instrucciones get, edit, describe o delete.
Podríamos borrar el DaemonSet con el siguiente comando:
kubectl delete daemonset fluent-bit
StatefulSets
Los StatefulSets son objetos de Kubernetes que se utilizan para gestionar aplicaciones con estado. A diferencia de los deployments, los StatefulSets garantizan que los pods tengan una identidad única y estable, lo que es esencial para aplicaciones como bases de datos o sistemas distribuidos.
Cada pod recibe un nombre único y predecible (redis-0, redis-1, redis-2...) y un nombre DNS estable que no cambia, aunque el pod se reinicie o se reprograme en otro nodo. Además, si lo declaramos con volumeClaimTemplates, cada pod va asociado a su propio volumen persistente, que se mantiene incluso si el pod se elimina.
¿Ya nos imaginamos por dónde van los tiros? Efectivamente, esto es ideal para bases de datos o sistemas con almacenamiento persistente.
Veamos un ejemplo de un StatefulSet que ejecuta Redis con un maestro y dos réplicas. El maestro acepta escrituras y las réplicas copian sus datos en tiempo real (y solo admiten lecturas):
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
spec:
serviceName: redis
replicas: 3
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:8.0
command: ["sh", "-c"]
args:
- |
if [ "$(hostname)" = "redis-0" ]; then
exec redis-server
else
exec redis-server --replicaof redis-0.redis 6379
fi
ports:
- containerPort: 6379
name: redis
El truco está en el script de arranque: como el nombre de cada pod es predecible, el que se llama redis-0 arranca como maestro y el resto se conectan a él como réplicas a través de su DNS estable, redis-0.redis. Esto solo es posible gracias a la identidad fija que dan los StatefulSets; con un Deployment los nombres serían aleatorios.
Ojo, es un ejemplo didáctico: si el maestro cae, ninguna réplica se promociona automáticamente. En producción se usa Redis Sentinel o un operador que gestione esa conmutación por error (failover).
Fijémonos en el campo serviceName: los StatefulSets necesitan un servicio "headless" asociado (por eso lo declaramos arriba con clusterIP: None), que es el que proporciona a cada pod su nombre DNS estable. Veremos los servicios en detalle en el capítulo de Services.
Para no complicar el ejemplo hemos dejado fuera el almacenamiento, así que los datos se pierden si un pod se recrea. En un caso real añadiríamos volumeClaimTemplates para que cada réplica tenga su propio volumen persistente; lo veremos en Almacenamiento y volúmenes en Kubernetes.
Para aplicar la configuración anterior, usaríamos el siguiente comando:
kubectl apply -f statefulset.yaml
Para comprobar que la replicación funciona, escribimos una clave en el maestro y la leemos desde una réplica:
kubectl exec redis-0 -- redis-cli set saludo hola
kubectl exec redis-1 -- redis-cli get saludo
kubectl exec redis-1 -- redis-cli info replication # role:slave, master_host:redis-0.redis
Y de igual forma que con los anteriores, podríamos utilizar los comandos get, edit, describe o delete para gestionar el StatefulSet:
kubectl <comando> statefulset redis
Conclusión
Junto con los deployments, ya hemos visto los principales tipos de objetos que nos permiten gestionar pods de forma automática en Kubernetes. En el siguiente capítulo veremos los jobs y cronjobs, que son objetos que nos permiten ejecutar tareas programadas o de forma puntual.
No nos preocupemos, porque iremos viendo todos estos objetos en profundidad en los casos prácticos.
- Lista de vídeos en Youtube: Curso Kubernetes
