Simplificando Kubernetes
Día 4
Contenido del Día 4
- Simplificando Kubernetes
Inicio de la Lección del Día 4
¿Qué veremos hoy?
Hoy es el día para hablar sobre dos objetos muy importantes en Kubernetes: los ReplicaSets
y los DaemonSets
.
Ya sabemos lo que es un Deployment
y también entendemos los detalles de un Pod
, así que ahora vamos a conocer estos dos conceptos que están fuertemente relacionados con los Deployment
y los Pod
. Cuando hablamos de Deployment
, es imposible no mencionar los ReplicaSets
, ya que un Deployment
es un objeto que crea un ReplicaSet
, y a su vez, un ReplicaSet
es un objeto que crea un Pod
. Todo está conectado.
Por otro lado, nuestro querido DaemonSet
es un objeto que crea un Pod
, y este Pod
es un objeto que se ejecuta en todos los nodos del clúster. Es sumamente importante para nosotros, ya que con un DaemonSet
podemos asegurarnos de tener al menos un Pod
funcionando en cada nodo del clúster. Por ejemplo, imagina que necesitas instalar los agentes de Datadog
o un exporter
de Prometheus
en todos los nodos del clúster. Para eso, necesitas un DaemonSet
.
A lo largo del día de hoy, aprenderemos cómo asegurarnos de que nuestros Pods
estén funcionando correctamente utilizando las Probes
de Kubernetes.
Hablaremos sobre las Readiness Probe
, Liveness Probe
y Startup Probe
, y por supuesto, proporcionaremos ejemplos prácticos y detallados.
Hoy es el día en el que aprenderás sobre estos dos objetos tan importantes y asegurarte de que nunca implementemos nuestros Pods
en producción sin verificar primero que estén funcionando correctamente y siendo supervisados por las Probes
de Kubernetes.
¡Vamos allá! #VAIIII
ReplicaSet
Una cosa es muy importante de saber: cuando creamos un Deployment
en Kubernetes, automáticamente estamos creando, además del Deployment
, un ReplicaSet
, y este ReplicaSet
es quien crea los Pods
que están dentro del Deployment
.
¿Confuso, verdad?
No, no lo es, y te lo explicaré.
Cuando creamos un Deployment
, Kubernetes crea un ReplicaSet
para generar y gestionar las réplicas de los Pods
en nuestro clúster. Este es el encargado de observar los Pods
y garantizar el número de réplicas que hemos definido en el Deployment
.
Es posible crear un ReplicaSet
sin un Deployment
, pero no es una buena práctica, ya que el ReplicaSet
no tiene la capacidad de gestionar versiones de Pods
ni de realizar RollingUpdate
de los Pods
.
Y aquí está lo interesante: cuando actualizamos la versión de un Pod
con el Deployment
, este crea un nuevo ReplicaSet
para gestionar las réplicas de los Pods
. Una vez que la actualización termina, el Deployment
elimina las réplicas del ReplicaSet
antiguo y solo deja las réplicas del nuevo ReplicaSet
.
Sin embargo, no se elimina el ReplicaSet
antiguo; se mantiene allí, ya que puede usarse para hacer un Rollback
de la versión del Pod
en caso de problemas. Sí, cuando necesitamos hacer un Rollback
de una actualización en nuestros Pods
, el Deployment
simplemente cambia el ReplicaSet
que se utiliza para gestionar las réplicas de los Pods
, utilizando el ReplicaSet
antiguo.
¿Lo hacemos en la práctica?
Creo que te ayudará a entenderlo mejor.
El Deployment y el ReplicaSet
Crearemos un Deployment
con el nombre de nginx-deployment
y crearemos 3 réplicas del Pod
de nginx
.
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-deployment
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx-deployment
strategy: {}
template:
metadata:
labels:
app: nginx-deployment
spec:
containers:
- image: nginx
name: nginx
resources:
limits:
cpu: "0.5"
memory: 256Mi
requests:
cpu: 0.25
memory: 128Mi
Verifiquemos si se ha creado el Deployment
.
kubectl get deployments
Nuestra salida se verá similar a esto.
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 7s
¡Simple, ya lo sabíamos! ¡Jeferson, quiero saber sobre el ReplicaSet
!
Calma, porque nuestro querido Deployment
ya ha creado el ReplicaSet
por nosotros.
Verifiquemos el ReplicaSet
que se ha creado.
kubectl get replicasets
Nuestra salida se verá similar a esto.
NAME DESIRED CURRENT READY AGE
nginx-deployment-6dd8d7cfbd 1 1 1 37s
Un detalle importante en la salida anterior es que el ReplicaSet
tiene el mismo nombre que el Deployment
, seguido de un sufijo aleatorio. Además, en esta salida podemos ver que el ReplicaSet
actualmente tiene 1 réplica del Pod
de nginx
en ejecución, según lo que definimos en el Deployment
.
Aumentemos el número de réplicas del Pod
de nginx
a 3.
kubectl scale deployment nginx-deployment --replicas=3
Esta es una forma de aumentar el número de réplicas del Pod
de nginx
sin necesidad de editar el Deployment
. Sin embargo, no lo recomiendo; prefiero editar el Deployment
y luego aplicar los cambios nuevamente. Esto es una cuestión de preferencia y organización. Personalmente, no me gusta la idea de tener que usar scale
en el Deployment
para aumentar o disminuir el número de réplicas del Pod
de nginx
, sin tener ese cambio registrado en git
, por ejemplo.
Modifiquemos el Deployment
para tener 3 réplicas.
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-deployment
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx-deployment
strategy: {}
template:
metadata:
labels:
app: nginx-deployment
spec:
containers:
- image: nginx
name: nginx
resources:
limits:
cpu: "0.5"
memory: 256Mi
requests:
cpu: 0.25
memory: 128Mi
¡Listo! Ahora conoces las dos opciones para aumentar el número de réplicas del Pod
de nginx
. Siéntete libre de elegir la opción que te parezca mejor.
Yo continuaré usando la opción de editar el Deployment
y luego aplicar los cambios.
kubectl apply -f nginx-deployment.yaml
Verifiquemos nuevamente nuestro ReplicaSet
.
kubectl get replicasets
Nuestra salida será similar a esta.
NAME DESIRED CURRENT READY AGE
nginx-deployment-6dd8d7cfbd 3 3 3 5m24s
Observe que el nombre del ReplicaSet
sigue siendo el mismo, pero el número de réplicas cambió a 3. Cuando solo modificamos el número de réplicas en nuestro Deployment
, el ReplicaSet
permanece igual, ya que su función principal es administrar las réplicas del Pod
de nginx
.
Ahora cambiemos la versión de nginx
a la 1.19.2.
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-deployment
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx-deployment
strategy: {}
template:
metadata:
labels:
app: nginx-deployment
spec:
containers:
- image: nginx:1.19.2
name: nginx
resources:
limits:
cpu: "0.5"
memory: 256Mi
requests:
cpu: 0.25
memory: 128Mi
Aplicaremos los cambios.
kubectl apply -f nginx-deployment.yaml
¡Listo! Ahora el Deployment
está utilizando la versión 1.19.2 de nginx
.
Vamos verificar nuevamente nuestro ReplicaSet
.
kubectl get replicasets
Tendremos la siguiente salida.
NAME DESIRED CURRENT READY AGE
nginx-deployment-6dd8d7cfbd 0 0 0 8m53s
nginx-deployment-7858bcf56f 1 1 1 13s
Ahora sí, tenemos un nuevo ReplicaSet
con el nombre nginx-deployment-7858bcf56f
, y el antiguo ReplicaSet
con el nombre nginx-deployment-6dd8d7cfbd
ha sido vaciado, ya que ya no forma parte del Deployment
, pero permanece en el clúster, ya que puede usarse para hacer un rollback a la versión anterior de nginx
.
Vamos a ver un detalle interesante en nuestro Deployment
.
kubectl describe deployment nginx-deployment
Observa la línea relacionada con el ReplicaSet
que está siendo gestionado por el Deployment
.
NewReplicaSet: nginx-deployment-7858bcf56f (3/3 replicas created)
Sí, en la salida de describe
podemos ver que el Deployment
está gestionando el ReplicaSet
con el nombre nginx-deployment-7858bcf56f
y que tiene 3 réplicas del Pod
de nginx
en ejecución.
Si deseas hacer el rollback a la versión anterior de nginx
, simplemente sigue los pasos que ya vimos anteriormente.
kubectl rollout undo deployment nginx-deployment
Con esto se realizará el rollback a la versión anterior de nginx
y el ReplicaSet
con el nombre nginx-deployment-7858bcf56f
se vaciará, mientras que el ReplicaSet
con el nombre nginx-deployment-6dd8d7cfbd
se llenará nuevamente con 3 réplicas del Pod
de nginx
.
Volvamos a listar nuestros ReplicaSets
.
kubectl get replicasets
Lo que tenemos ahora es esto.
NAME DESIRED CURRENT READY AGE
nginx-deployment-6dd8d7cfbd 3 3 3 15m
nginx-deployment-7858bcf56f 0 0 0 6m28s
Y si volvemos a observar el Deployment
.
kubectl describe deployment nginx-deployment
Tendremos la siguiente salida.
NewReplicaSet: nginx-deployment-6dd8d7cfbd (3/3 replicas created)
¡Realmente es bastante simple, verdad?
Ahora ya sabes cómo gestionar las réplicas del Pod
de nginx
usando el Deployment
y, por consiguiente, el ReplicaSet
.
Creando un ReplicaSet
Como se mencionó anteriormente, es posible crear un ReplicaSet
sin usar un Deployment
, aunque insisto, no lo hagas, ya que el Deployment
es la forma más fácil de gestionar los ReplicaSets
y la salud de los Pods
.
Pero adelante, si deseas crear un ReplicaSet
sin utilizar un Deployment
, simplemente sigue estos pasos.
Para nuestro ejemplo, crearemos un archivo llamado nginx-replicaset.yaml
y colocaremos el siguiente contenido:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
labels:
app: nginx-app
name: nginx-replicaset
spec:
replicas: 3
selector:
matchLabels:
app: nginx-app
template:
metadata:
labels:
app: nginx-app
spec:
containers:
- image: nginx:1.19.2
name: nginx
resources:
limits:
cpu: "0.5"
memory: 256Mi
requests:
cpu: 0.25
memory: 128Mi
Antes de aplicar nuestro manifiesto, comprendamos lo que estamos haciendo.
Si observas el archivo, no hay nada nuevo, es decir, nada que no hayas aprendido hasta ahora. La principal diferencia es que ahora estamos usando kind: ReplicaSet
en lugar de kind: Deployment
, e incluso la APIVersion
es la misma.
Ahora apliquemos nuestro manifiesto.
kubectl apply -f nginx-replicaset.yaml
La salida será la siguiente.
NAME DESIRED CURRENT READY AGE
nginx-deployment-6dd8d7cfbd 3 3 3 21m
nginx-deployment-7858bcf56f 0 0 0 12m
nginx-replicaset 3 3 3 6s
Ahora tenemos 3 ReplicaSets
siendo gestionados por Kubernetes, de los cuales 2 están siendo gestionados por el Deployment
y el otro es el que acabamos de crear.
Veamos la lista de los Pods
en ejecución.
kubectl get pods
Todo funcionando mágicamente, ¿verdad?
Ahora hagamos una prueba, cambiemos la versión de nginx
a la versión 1.19.3. Para ello, editaremos el archivo nginx-replicaset.yaml
y cambiaremos la versión de nginx
a la 1.19.3.
apiVersion: apps/v1
kind: ReplicaSet
metadata:
labels:
app: nginx-app
name: nginx-replicaset
spec:
replicas: 3
selector:
matchLabels:
app: nginx-app
template:
metadata:
labels:
app: nginx-app
spec:
containers:
- image: nginx:1.19.3
name: nginx
resources:
limits:
cpu: "0.5"
memory: 256Mi
requests:
cpu: 0.25
memory: 128Mi
Listo, ahora vamos a aplicar los cambios.
kubectl apply -f nginx-replicaset.yaml
Vamos a listar los Pods
nuevamente.
kubectl get pods
Observe que no ocurrió nada, es decir, el ReplicaSet
no realizó el rollout de la nueva versión de nginx
. Esto sucede porque el ReplicaSet
no gestiona las versiones, solo se asegura de que el número de réplicas del Pod
esté siempre activo.
Si observas los detalles del ReplicaSet
, verás que está gestionando 3 réplicas del Pod
y que la imagen de nginx
es la versión 1.19.3, sin embargo, no recreó los Pods
con la nueva versión de nginx
, lo hará solo si eliminas manualmente los Pods
o si el Pod
muere por alguna razón.
kubectl describe replicaset nginx-replicaset
Ahora vamos a eliminar uno de los Pods
para que el ReplicaSet
cree un nuevo Pod
con la nueva versión de nginx
.
kubectl delete pod nginx-replicaset-8r6md
Ahora el ReplicaSet
creará un nuevo Pod
con la nueva versión de nginx
, lo que nos generará un problema, ya que ahora tendremos dos versiones de nginx
ejecutándose en nuestro clúster.
kubectl get pods -o=jsonpath='{range .items[*]}{"\n"}{.metadata.name}{"\t"}{range .spec.containers[*]}{.image}{"\t"}{end}{end}'
Esta es una forma de listar los Pods
y las imágenes que están utilizando, sé que es bastante extraño, pero explicaré lo que está sucediendo.
kubectl get pods
: este comando lista todos los Pods en el clúster.-o=jsonpath
: este parámetro especifica que queremos usar la salida en formato JSONPath para mostrar la información de los Pods.'{range .items[*]}{"\n"}{.metadata.name}{"\t"}{range .spec.containers[*]}{.image}{"\t"}{end}{end}'
: esta es la expresión JSONPath que define el formato de salida del comando. Utiliza la función range para iterar sobre todos los objetositems
(es decir, los Pods) devueltos por el comandokubectl get pods
. Luego muestra el nombre del Pod({.metadata.name})
seguido de un tabulador(\t)
, e itera sobre todos los contenedores({range .spec.containers[*]})
dentro del Pod, mostrando la imagen utilizada por cada uno({.image})
. Finalmente, agrega un salto de línea(\n)
y cierra el segundo rango con{end}{end}
.
Sí, lo sé, sigue siendo confuso.
Pero te contaré un secreto, con el tiempo y utilizando esto repetidamente, las cosas empiezan a volverse más fáciles, ¡así que no te rindas! ¡Hacia adelante, ni para tomar impulso!
Aún hablaremos con más detalles sobre cómo usar metadata
para tener una salida más amigable y precisa.
Desactivando el ReplicaSet
Para eliminar el ReplicaSet
y todos los Pods
que está gestionando, simplemente ejecuta el siguiente comando.
kubectl delete replicaset nginx-replicaset
Si deseas hacerlo utilizando el archivo de manifiesto, ejecuta el siguiente comando.
kubectl delete -f nginx-replicaset.yaml
Listo, se ha eliminado nuestro ReplicaSet
y todos los Pods
que estaba gestionando.
A lo largo de nuestra sesión, ya hemos aprendido cómo crear un ReplicaSet
y cómo funciona, pero aún hay mucho más que aprender, así que continuemos.
El DaemonSet
Ya sabemos qué es un Pod
, un Deployment
y un ReplicaSet
, pero ahora es el momento de conocer otro objeto en Kubernetes: el DaemonSet
.
El DaemonSet
es un objeto que garantiza que todos los nodos en el clúster ejecuten una réplica de un Pod
. En otras palabras, asegura que todos los nodos del clúster tengan una copia del mismo Pod
.
El DaemonSet
es muy útil para ejecutar Pods
que deben ejecutarse en todos los nodos del clúster, como por ejemplo, un Pod
que realiza el monitoreo de registros o un Pod
que realiza el monitoreo de métricas.
Algunos casos de uso comunes de los DaemonSets
son:
- Ejecutar agentes de monitoreo, como el
Prometheus Node Exporter
oFluentd
. - Ejecutar un proxy de red en todos los nodos del clúster, como
kube-proxy
,Weave Net
,Calico
oFlannel
. - Ejecutar agentes de seguridad en cada nodo del clúster, como
Falco
oSysdig
.
Por lo tanto, si nuestro clúster tiene 3 nodos, el DaemonSet
garantizará que todos los nodos ejecuten una réplica del Pod
que está gestionando, es decir, 3 réplicas del Pod
.
Si agregamos otro nodo al clúster, el DaemonSet
garantizará que todos los nodos ejecuten una réplica del Pod
que está gestionando, es decir, 4 réplicas del Pod
.
Creando un DaemonSet
Vamos con nuestro primer ejemplo, vamos a crear un DaemonSet
que asegurará que todos los nodos del clúster ejecuten una réplica del Pod
del node-exporter
, que es un exportador de métricas para Prometheus
.
Para lograrlo, vamos a crear un archivo llamado node-exporter-daemonset.yaml
y agregar el siguiente contenido.
apiVersion: apps/v1 # Versión de la API de Kubernetes del objeto
kind: DaemonSet # Tipo de objeto
metadata: # Información sobre el objeto
name: node-exporter # Nombre del objeto
spec: # Especificación del objeto
selector: # Selector del objeto
matchLabels: # Etiquetas que se usarán para seleccionar los Pods
app: node-exporter # Etiqueta que se usará para seleccionar los Pods
template: # Plantilla del objeto
metadata: # Información sobre el objeto
labels: # Etiquetas que se agregarán a los Pods
app: node-exporter # Etiqueta que se agregará a los Pods
spec: # Especificación del objeto, en este caso, la especificación del Pod
hostNetwork: true # Habilita el uso de la red del host, usar con precaución
containers: # Lista de contenedores que se ejecutarán en el Pod
- name: node-exporter # Nombre del contenedor
image: prom/node-exporter:latest # Imagen del contenedor
ports: # Lista de puertos que se expondrán en el contenedor
- containerPort: 9100 # Puerto que se expondrá en el contenedor
hostPort: 9100 # Puerto que se expondrá en el host
volumeMounts: # Lista de puntos de montaje de volúmenes en el contenedor, ya que node-exporter necesita acceso a /proc y /sys
- name: proc # Nombre del volumen
mountPath: /host/proc # Ruta donde se montará el volumen en el contenedor
readOnly: true # Habilita el modo de solo lectura
- name: sys # Nombre del volumen
mountPath: /host/sys # Ruta donde se montará el volumen en el contenedor
readOnly: true # Habilita el modo de solo lectura
volumes: # Lista de volúmenes que se utilizarán en el Pod
- name: proc # Nombre del volumen
hostPath: # Tipo de volumen
path: /proc # Ruta del volumen en el host
- name: sys # Nombre del volumen
hostPath: # Tipo de volumen
path: /sys # Ruta del volumen en el host
He dejado el archivo comentado para facilitar la comprensión, ahora vamos a crear el DaemonSet
utilizando el archivo de manifiesto.
kubectl apply -f node-exporter-daemonset.yaml
Ahora verifiquemos si el DaemonSet
se ha creado.
kubectl get daemonset
Como podemos ver, el DaemonSet
se ha creado exitosamente.
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
node-exporter 2 2 2 2 2 <none> 5m24s
Si deseamos verificar los Pods
que el DaemonSet
está gestionando, solo ejecutamos el siguiente comando.
kubectl get pods -l app=node-exporter
Solo para recordar, estamos utilizando el parámetro -l
para filtrar los Pods
que tienen la etiqueta app=node-exporter
, que es el caso de nuestro DaemonSet
.
Como podemos ver, el DaemonSet
está gestionando 2 Pods
, uno en cada nodo del clúster.
NAME READY STATUS RESTARTS AGE
node-exporter-k8wp9 1/1 Running 0 6m14s
node-exporter-q8zvw 1/1 Running 0 6m14s
Nuestros Pods
de node-exporter
se han creado con éxito, ahora verifiquemos si se están ejecutando en todos los nodos del clúster.
kubectl get pods -o wide -l app=node-exporter
Con el comando anterior, podemos ver en qué nodo se está ejecutando cada Pod
.
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
node-exporter-k8wp9 1/1 Running 0 3m49s 192.168.8.145 ip-192-168-8-145.ec2.internal <none> <none>
node-exporter-q8zvw 1/1 Running 0 3m49s 192.168.55.68 ip-192-168-55-68.ec2.internal <none> <none>
Como podemos ver, los Pods
de node-exporter
se están ejecutando en ambos nodos del clúster.
Para ver los detalles del DaemonSet
, solo ejecutamos el siguiente comando.
kubectl describe daemonset node-exporter
El comando anterior devolverá una salida similar a la siguiente.
Name: node-exporter
Selector: app=node-exporter
Node-Selector: <none>
Labels: <none>
Annotations: deprecated.daemonset.template.generation: 1
Desired Number of Nodes Scheduled: 2
Current Number of Nodes Scheduled: 2
Number of Nodes Scheduled with Up-to-date Pods: 2
Number of Nodes Scheduled with Available Pods: 2
Number of Nodes Misscheduled: 0
Pods Status: 2 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: app=node-exporter
Containers:
node-exporter:
Image: prom/node-exporter:latest
Port: 9100/TCP
Host Port: 9100/TCP
Environment: <none>
Mounts:
/host/proc from proc (ro)
/host/sys from sys (ro)
Volumes:
proc:
Type: HostPath (bare host directory volume)
Path: /proc
HostPathType:
sys:
Type: HostPath (bare host directory volume)
Path: /sys
HostPathType:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 9m6s daemonset-controller Created pod: node-exporter-q8zvw
Normal SuccessfulCreate 9m6s daemonset-controller Created pod: node-exporter-k8wp9
En la salida anterior, podemos ver información muy importante relacionada con el DaemonSet
, como por ejemplo, el número de nodos que el DaemonSet
está gestionando, el número de Pods
que se están ejecutando en cada nodo, etc.
Creación de un DaemonSet utilizando el comando kubectl create
Aún puedes crear un DaemonSet
utilizando el comando kubectl create
, pero prefiero usar el archivo de manifiesto, ya que así puedo versionar mi DaemonSet
. Sin embargo, si deseas crear un DaemonSet
utilizando el comando kubectl create
, simplemente ejecuta el siguiente comando.
kubectl create daemonset node-exporter --image=prom/node-exporter:latest --port=9100 --host-port=9100
En el comando anterior, faltan algunos parámetros, pero los dejé así para facilitar la comprensión. Si deseas ver todos los parámetros que se pueden usar en el comando kubectl create daemonset
, simplemente ejecuta el siguiente comando.
kubectl create daemonset --help
A mí me gusta usar kubectl create
solo para crear un archivo de ejemplo, para que pueda basarme en él al crear mi archivo de manifiesto. Sin embargo, si deseas crear un manifiesto para un DaemonSet
utilizando el comando kubectl create
, simplemente ejecuta el siguiente comando.
kubectl create daemonset node-exporter --image=prom/node-exporter:latest --port=9100 --host-port=9100 -o yaml --dry-run=client > node-exporter-daemonset.yaml
¡Así de simple! Te explicaré lo que está sucediendo en el comando anterior.
kubectl create daemonset node-exporter
- Crea unDaemonSet
llamadonode-exporter
.--image=prom/node-exporter:latest
- Utiliza la imagenprom/node-exporter:latest
para crear losPods
.--port=9100
- Define el puerto9100
para elPod
.--host-port=9100
- Define el puerto9100
para el nodo.-o yaml
- Define el formato del archivo de manifiesto comoyaml
.--dry-run=client
- Ejecuta el comando sin crear elDaemonSet
, solo simula la creación delDaemonSet
.> node-exporter-daemonset.yaml
- Redirige la salida del comando al archivonode-exporter-daemonset.yaml
.
¡Más sencillo, ¿verdad?
Añadiendo un nodo al clúster
Ahora que ya sabemos cómo crear un DaemonSet
, vamos a aumentar el número de nodos en nuestro clúster.
Actualmente tenemos dos réplicas.
kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-192-168-55-68.ec2.internal Ready <ninguno> 113m v1.23.16-eks-48e63af
ip-192-168-8-145.ec2.internal Ready <ninguno> 113m v1.23.16-eks-48e63af
Vamos a aumentar el número de nodos a 3.
Estoy utilizando eksctl
para crear el clúster, por lo que utilizaré el comando eksctl scale nodegroup
para aumentar el número de nodos en el clúster. Si estás utilizando otro administrador de clúster, puedes usar el comando que prefieras para aumentar el número de nodos en el clúster.
eksctl scale nodegroup --cluster=eks-cluster --nodes 3 --name eks-cluster-nodegroup
2023-03-11 13:31:48 [ℹ] scaling nodegroup "eks-cluster-nodegroup" in cluster eks-cluster
2023-03-11 13:31:49 [ℹ] waiting for scaling of nodegroup "eks-cluster-nodegroup" to complete
2023-03-11 13:33:17 [ℹ] nodegroup successfully scaled
Verifiquemos si el nodo ha sido agregado al clúster.
kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-192-168-45-194.ec2.internal Ready <ninguno> 47s v1.23.16-eks-48e63af
ip-192-168-55-68.ec2.internal Ready <ninguno> 113m v1.23.16-eks-48e63af
ip-192-168-8-145.ec2.internal Ready <ninguno> 113m v1.23.16-eks-48e63af
Listo, ahora tenemos 3 nodos en el clúster.
Pero la pregunta que no quiere callar es: ¿El DaemonSet
ha creado un Pod
en el nuevo nodo?
Vamos a verificarlo.
kubectl get pods -o wide -l app=node-exporter
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
node-exporter-k8wp9 1/1 Running 0 20m 192.168.8.145 ip-192-168-8-145.ec2.internal <ninguno> <ninguno>
node-exporter-q8zvw 1/1 Running 0 20m 192.168.55.68 ip-192-168-55-68.ec2.internal <ninguno> <ninguno>
node-exporter-xffgq 1/1 Running 0 70s 192.168.45.194 ip-192-168-45-194.ec2.internal <ninguno> <ninguno>
Parece que tenemos un nuevo Pod
en el nodo ip-192-168-45-194.ec2.internal
, pero verifiquemos si el DaemonSet
está gestionando ese nodo.
kubectl describe daemonset node-exporter
Desired Number of Nodes Scheduled: 3
Current Number of Nodes Scheduled: 3
Number of Nodes Scheduled with Up-to-date Pods: 3
Number of Nodes Scheduled with Available Pods: 3
Number of Nodes Misscheduled: 0
Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Todo está en paz y armonía, el DaemonSet
está gestionando el nuevo Pod
en el nuevo nodo
. Y, por supuesto, si por alguna razón el Pod
se cae, el DaemonSet
creará un nuevo Pod
en el mismo nodo
. Y, por supuesto, en la versión 2, si la cantidad de nodos disminuye, el DaemonSet
eliminará los Pods
en exceso. Y hablando de eso, déjame disminuir el número de nodos en el clúster para ahorrar algunos Dólares/Euros.
eksctl scale nodegroup --cluster=eks-cluster --nodes 2 --name eks-cluster-nodegroup
Eliminando un DaemonSet
Para eliminar un DaemonSet
es muy sencillo, simplemente ejecuta el comando kubectl delete daemonset <nombre-del-daemonset>
.
kubectl delete daemonset node-exporter
daemonset.apps "node-exporter" eliminado
O también puedes eliminar el DaemonSet
a través del manifiesto.
kubectl delete -f node-exporter-daemonset.yaml
¡Así de simple!
Creo que el tema del DaemonSet
ya está bastante claro. Aún veremos todos estos objetos que hemos aprendido hasta ahora varias veces a lo largo de nuestro recorrido, así que no te preocupes, practicaremos mucho más.
Las sondas de Kubernetes
Antes de continuar, quería presentarte algo nuevo además de los dos nuevos objetos que ya has aprendido hoy. Quería que terminaras este día sintiéndote seguro de que eres capaz de crear un Pod
, un Deployment
, un ReplicaSet
o un DaemonSet
, pero también de que puedes supervisar tus aplicaciones que se ejecutan dentro del clúster de manera efectiva y utilizando los recursos que Kubernetes ya nos proporciona.
¿Qué son las sondas?
Las sondas son una forma de supervisar tu Pod
y saber si está en un estado saludable o no. Con ellas, puedes asegurarte de que tus Pods
se están ejecutando y respondiendo de manera correcta, y lo que es más importante, que Kubernetes está evaluando lo que se está ejecutando dentro de tu Pod
.
Hoy en día, tenemos disponibles tres tipos de sondas: livenessProbe
, readinessProbe
y startupProbe
. Vamos a explorar en detalle cada una de ellas.
Sonda de Integridad (Liveness Probe)
La livenessProbe
es nuestra sonda de verificación de integridad, lo que hace es comprobar si lo que se está ejecutando dentro del Pod
está saludable. Creamos una forma de probar si lo que tenemos dentro del Pod
está respondiendo como se espera. Si la prueba falla, el Pod
se reiniciará.
Para que quede más claro, utilizaremos nuevamente el ejemplo con Nginx
. Me gusta usar Nginx
como ejemplo porque sé que todos lo conocen, y así es mucho más fácil entender lo que está sucediendo. Después de todo, estás aquí para aprender Kubernetes, y si es con algo que ya conoces, es mucho más fácil de entender.
Bueno, vamos allá. Es hora de crear un nuevo Deployment
con Nginx
. Utilizaremos el ejemplo que ya utilizamos cuando aprendimos sobre Deployment
.
Para ello, crea un archivo llamado nginx-liveness.yaml
y pega el siguiente contenido.
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-deployment
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx-deployment
strategy: {}
template:
metadata:
labels:
app: nginx-deployment
spec:
containers:
- image: nginx:1.19.2
name: nginx
resources:
limits:
cpu: "0.5"
memory: 256Mi
requests:
cpu: 0.25
memory: 128Mi
livenessProbe: # Aquí es donde agregaremos nuestra livenessProbe
tcpSocket: # Aquí usaremos tcpSocket para conectarnos al contenedor a través del protocolo TCP
port: 80 # Puerto TCP al que nos conectaremos en el contenedor
initialDelaySeconds: 10 # Cuántos segundos esperaremos antes de realizar la primera verificación
periodSeconds: 10 # Cada cuántos segundos realizaremos la verificación
timeoutSeconds: 5 # Cuántos segundos esperaremos antes de considerar que la verificación ha fallado
failureThreshold: 3 # Cuántos fallos consecutivos aceptaremos antes de reiniciar el contenedor
Con esto tenemos algunas novedades, y solo utilizamos una sonda, que es la livenessProbe
.
Lo que declaramos con la regla anterior es que queremos probar si el Pod
está respondiendo a través del protocolo TCP, utilizando la opción tcpSocket
, en el puerto 80 que se definió mediante la opción port
. También hemos definido que queremos esperar 10 segundos para realizar la primera verificación utilizando initialDelaySeconds
, y debido a periodSeconds
, queremos que se realice la verificación cada 10 segundos. Si la verificación falla, esperaremos 5 segundos, debido a timeoutSeconds
, para volver a intentarlo, y como usamos failureThreshold
, si falla 3 veces seguidas, reiniciaremos el Pod
.
¿Quedó más claro? Sigamos con otro ejemplo.
Digamos que ya no queremos utilizar tcpSocket
, sino httpGet
para intentar acceder a un endpoint dentro de nuestro Pod
.
Para ello, modifiquemos nuestro nginx-deployment.yaml
de la siguiente manera.
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-deployment
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx-deployment
strategy: {}
template:
metadata:
labels:
app: nginx-deployment
spec:
containers:
- image: nginx:1.19.2
name: nginx
resources:
limits:
cpu: "0.5"
memory: 256Mi
requests:
cpu: 0.25
memory: 128Mi
livenessProbe: # Aquí es donde agregaremos nuestra livenessProbe
httpGet: # Aquí usaremos httpGet para conectarnos al contenedor a través del protocolo HTTP
path: / # Endpoint que usaremos para conectarnos al contenedor
port: 80 # Puerto TCP al que nos conectaremos en el contenedor
initialDelaySeconds: 10 # Cuántos segundos esperaremos antes de realizar la primera verificación
periodSeconds: 10 # Cada cuántos segundos realizaremos la verificación
timeoutSeconds: 5 # Cuántos segundos esperaremos antes de considerar que la verificación ha fallado
failureThreshold: 3 # Cuántos fallos consecutivos aceptaremos antes de reiniciar el contenedor
Observa que ahora hemos cambiado algunas cosas, aunque mantenemos el mismo objetivo: verificar si Nginx
está respondiendo correctamente. Cambiamos la forma en que lo probamos. Ahora estamos usando httpGet
para verificar si Nginx
responde correctamente a través del protocolo HTTP, y para eso, usamos el endpoint /
y el puerto 80.
Lo nuevo aquí es la opción path
, que es el endpoint que usaremos para verificar si Nginx
responde correctamente, y, por supuesto, httpGet
es la forma en que realizamos nuestra prueba, utilizando el protocolo HTTP.
Elige cuál de los dos ejemplos prefieres y crea tu Deployment
con el siguiente comando.
kubectl apply -f nginx-deployment.yaml
Para verificar si el Deployment
se creó correctamente, ejecuta el siguiente comando.
kubectl get deployments
Deberías ver algo similar a esto.
NAME READY STATUS RESTARTS AGE
nginx-deployment-7557d7fc6c-dx48d 1/1 Running 0 14s
nginx-deployment-7557d7fc6c-tbk4w 1/1 Running 0 12s
nginx-deployment-7557d7fc6c-wv876 1/1 Running 0 16s
Para obtener más detalles sobre tu Pod
y verificar si nuestra sonda está funcionando correctamente, usa el siguiente comando.
kubectl describe pod nginx-deployment-7557d7fc6c-dx48d
La salida debería ser similar a esta.
Name: nginx-deployment-589d6fc888-42fmg
Namespace: default
Priority: 0
Service Account: default
Node: ip-192-168-39-119.ec2.internal/192.168.39.119
Start Time: Thu, 16 Mar 2023 18:49:53 +0100
Labels: app=nginx-deployment
pod-template-hash=589d6fc888
Annotations: kubernetes.io/psp: eks.privileged
Status: Running
IP: 192.168.49.40
IPs:
IP: 192.168.49.40
Controlled By: ReplicaSet/nginx-deployment-589d6fc888
Containers:
nginx:
Container ID: docker://f7fc28a1fafbf53471ba144d4fb48bc029d289d93b3565b839ae89a1f38cd894
Image: nginx:1.19.2
Image ID: docker-pullable://nginx@sha256:c628b67d21744fce822d22fdcc0389f6bd763daac23a6b77147d0712ea7102d0
Port: <none>
Host Port: <none>
State: Running
Started: Thu, 16 Mar 2023 18:49:59 +0100
Ready: True
Restart Count: 0
Limits:
cpu: 500m
memory: 256Mi
Requests:
cpu: 250m
memory: 128Mi
Liveness: http-get http://:80/ delay=10s timeout=5s period=10s #success=1 #failure=3
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-8srlq (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-8srlq:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: Burstable
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 21s default-scheduler Successfully assigned default/nginx-deployment-589d6fc888-42fmg to ip-192-168-39-119.ec2.internal
Normal Pulling 20s kubelet Pulling image "nginx:1.19.2"
Normal Pulled 15s kubelet Successfully pulled image "nginx:1.19.2" in 4.280120301s (4.280125621s including waiting)
Normal Created 15s kubelet Created container nginx
Normal Started 15s kubelet Started container nginx
Aquí tenemos la información más importante para nosotros en este momento:
Liveness: http-get http://:80/ delay=10s timeout=5s period=10s #success=1 #failure=3
La salida de arriba es parte de la salida del comando kubectl describe pod
. Todo está funcionando maravillosamente bien.
Ahora, hagamos lo siguiente: cambiemos nuestro Deployment
para que nuestra sonda falle. Para ello, vamos a cambiar el endpoint
que estamos usando. Cambiaremos el path
a /giropops
.
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-deployment
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx-deployment
strategy: {}
template:
metadata:
labels:
app: nginx-deployment
spec:
containers:
- image: nginx:1.19.2
name: nginx
resources:
limits:
cpu: "0.5"
memory: 256Mi
requests:
cpu: 0.25
memory: 128Mi
livenessProbe: # Aquí es donde vamos a agregar nuestra sonda de integridad (`livenessProbe`).
httpGet: # Aquí vamos a utilizar `httpGet`, donde nos conectaremos al contenedor a través del protocolo HTTP.
path: /giropops # ¿Qué `endpoint` vamos a utilizar para conectarnos al contenedor?
port: 80 # ¿Qué puerto TCP vamos a utilizar para conectarnos al contenedor?
initialDelaySeconds: 10 # ¿Cuántos segundos vamos a esperar para realizar la primera verificación?
periodSeconds: 10 # ¿Cada cuántos segundos vamos a ejecutar la verificación?
timeoutSeconds: 5 # ¿Cuántos segundos vamos a esperar antes de considerar que la verificación ha fallado?
failureThreshold: 3 # ¿Cuántas fallas consecutivas vamos a permitir antes de reiniciar el contenedor?
Vamos aplicar los cambios en nuestro Deployment
:
kubectl apply -f deployment.yaml
Después de un tiempo, notarás que Kubernetes ha finalizado la actualización de nuestro Deployment
.
Si esperas un poco más, te darás cuenta de que los Pods
se están reiniciando con frecuencia.
Todo esto se debe a que nuestra livenessProbe
está fallando, ya que el endpoint
está incorrecto.
Podemos ver más detalles sobre lo que está ocurriendo en la salida del comando kubectl describe pod
:
kubectl describe pod nginx-deployment-7557d7fc6c-dx48d
Name: nginx-deployment-7557d7fc6c-dx48d
Namespace: default
Priority: 0
Service Account: default
Node: ip-192-168-39-119.ec2.internal/192.168.39.119
Start Time: Thu, 16 Mar 2023 18:51:00 +0100
Labels: app=nginx-deployment
pod-template-hash=7557d7fc6c
Annotations: kubernetes.io/psp: eks.privileged
Status: Running
IP: 192.168.44.84
IPs:
IP: 192.168.44.84
Controlled By: ReplicaSet/nginx-deployment-7557d7fc6c
Containers:
nginx:
Container ID: docker://c070d9c08bec40ad14562512d7bd8507a44279a327f1b3ecac1621da7ccf21b4
Image: nginx:1.19.2
Image ID: docker-pullable://nginx@sha256:c628b67d21744fce822d22fdcc0389f6bd763daac23a6b77147d0712ea7102d0
Port: <none>
Host Port: <none>
State: Running
Started: Thu, 16 Mar 2023 18:51:41 +0100
Last State: Terminated
Reason: Completed
Exit Code: 0
Started: Thu, 16 Mar 2023 18:51:02 +0100
Finished: Thu, 16 Mar 2023 18:51:40 +0100
Ready: True
Restart Count: 1
Limits:
cpu: 500m
memory: 256Mi
Requests:
cpu: 250m
memory: 128Mi
Liveness: http-get http://:80/giropops delay=10s timeout=5s period=10s #success=1 #failure=3
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-4sk2f (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-4sk2f:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: Burstable
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 44s default-scheduler Successfully assigned default/nginx-deployment-7557d7fc6c-dx48d to ip-192-168-39-119.ec2.internal
Normal Pulled 4s (x2 over 43s) kubelet Container image "nginx:1.19.2" already present on machine
Normal Created 4s (x2 over 43s) kubelet Created container nginx
Warning Unhealthy 4s (x3 over 24s) kubelet Liveness probe failed: HTTP probe failed with statuscode: 404
Normal Killing 4s kubelet Container nginx failed liveness probe, will be restarted
Normal Started 3s (x2 over 42s) kubelet Started container nginx
En la última parte de la salida del comando kubectl describe pod
, puedes observar que Kubernetes está intentando ejecutar nuestra livenessProbe
y está fallando. Incluso muestra cuántas veces ha intentado ejecutar la livenessProbe
y ha fallado, lo que resultó en la reinicialización de nuestro Pod
.
Creo que ahora está más claro cómo funciona la livenessProbe
. Ahora es el momento de pasar a la siguiente sonda, la readinessProbe
.
Sonda de preparación (Readiness Probe)
La readinessProbe
es una forma en que Kubernetes verifica si su contenedor está listo para recibir tráfico, es decir, si está preparado para recibir solicitudes externas.
Esta es nuestra sonda de lectura, que comprueba si nuestro contenedor está listo para recibir solicitudes. Si está listo, aceptará solicitudes; de lo contrario, no las aceptará y se eliminará de la dirección del servicio. Esto impide que el tráfico llegue a él.
Aunque aún veremos qué son los service
y endpoint
, por ahora basta con saber que el endpoint
es la dirección que nuestro service
usará para acceder a nuestro Pod
. Sin embargo, dedicaremos todo un día a hablar sobre service
y endpoint
, así que por ahora, relájate.
Retomando el tema, nuestra sonda actual garantizará que nuestro Pod
esté en condiciones de recibir solicitudes.
Vamos a un ejemplo para aclarar esto. Para ello, crearemos un archivo llamado nginx-readiness.yaml
y agregaremos el siguiente contenido:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-deployment
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx-deployment
strategy: {}
template:
metadata:
labels:
app: nginx-deployment
spec:
containers:
- image: nginx:1.19.2
name: nginx
resources:
limits:
cpu: "0.5"
memory: 256Mi
requests:
cpu: 0.25
memory: 128Mi
readinessProbe: # Donde definimos nuestra sonda de preparación
httpGet: # El tipo de prueba que vamos a ejecutar, en este caso, una prueba HTTP
path: / # La ruta que vamos a probar
port: 80 # El puerto que vamos a probar
initialDelaySeconds: 10 # El tiempo que esperaremos antes de ejecutar la sonda por primera vez
periodSeconds: 10 # Cada cuánto tiempo vamos a ejecutar la sonda
timeoutSeconds: 5 # Cuánto tiempo esperaremos antes de considerar que la sonda ha fallado
successThreshold: 2 # Cuántas veces la sonda debe pasar para considerar que el contenedor está listo
failureThreshold: 3 # Cuántas veces la sonda debe pasar para considerar que el contenedor NO está listo
Vamos a verificar si nuestros Pods
están en funcionamiento:
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-fbdc9b65f-trnnz 0/1 Running 0 6s
nginx-deployment-fbdc9b65f-z8n4m 0/1 Running 0 6s
nginx-deployment-fbdc9b65f-zn8zh 0/1 Running 0 6s
Podemos observar que ahora los Pods
tardan un poco más en estar listos, ya que estamos ejecutando nuestra readinessProbe
, y por esta razón debemos esperar los 10 segundos iniciales que definimos para que se ejecute la prueba por primera vez, ¿recuerdas?
Si esperas un momento, verás que los Pods
estarán listos, y puedes comprobarlo ejecutando el comando:
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-fbdc9b65f-trnnz 1/1 Running 0 30s
nginx-deployment-fbdc9b65f-z8n4m 1/1 Running 0 30s
nginx-deployment-fbdc9b65f-zn8zh 1/1 Running 0 30s
Listo, como por arte de magia, ahora nuestros Pods
están listos para recibir solicitudes.
Echemos un vistazo a la descripción de nuestro Pod
:
kubectl describe pod nginx-deployment-fbdc9b65f-trnnz
Name: nginx-deployment-fbdc9b65f-trnnz
Namespace: default
Priority: 0
Service Account: default
Node: ip-192-168-39-119.ec2.internal/192.168.39.119
Start Time: Thu, 16 Mar 2023 19:10:07 +0100
Labels: app=nginx-deployment
pod-template-hash=fbdc9b65f
Annotations: kubernetes.io/psp: eks.privileged
Status: Running
IP: 192.168.49.40
IPs:
IP: 192.168.49.40
Controlled By: ReplicaSet/nginx-deployment-fbdc9b65f
Containers:
nginx:
Container ID: docker://09538e27e29c5c649efa88fe148336abd5a47dd4e5a8d32b40b268fb1818dfc4
Image: nginx:1.19.2
Image ID: docker-pullable://nginx@sha256:c628b67d21744fce822d22fdcc0389f6bd763daac23a6b77147d0712ea7102d0
Port: <none>
Host Port: <none>
State: Running
Started: Thu, 16 Mar 2023 19:10:08 +0100
Ready: True
Restart Count: 0
Limits:
cpu: 500m
memory: 256Mi
Requests:
cpu: 250m
memory: 128Mi
Readiness: http-get http://:80/ delay=10s timeout=5s period=10s #success=2 #failure=3
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-zpfvb (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-zpfvb:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: Burstable
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 60s default-scheduler Successfully assigned default/nginx-deployment-fbdc9b65f-trnnz to ip-192-168-39-119.ec2.internal
Normal Pulled 59s kubelet Container image "nginx:1.19.2" already present on machine
Normal Created 59s kubelet Created container nginx
Normal Started 59s kubelet Started container nginx
Listo, nuestra sonda está ahí y funcionando, y con esto podemos asegurarnos de que nuestros Pods
estén listos para recibir solicitudes.
Vamos a cambiar nuestro path
a /giropops
y ver qué sucede:
...
readinessProbe: # Aquí es donde definimos nuestra sonda de lectura
httpGet: # El tipo de prueba que vamos a realizar, en este caso, vamos a realizar una prueba HTTP
path: /giropops # La ruta que vamos a probar
port: 80 # El puerto que vamos a probar
initialDelaySeconds: 10 # El tiempo que vamos a esperar para ejecutar la sonda por primera vez
periodSeconds: 10 # Con qué frecuencia vamos a ejecutar la sonda
timeoutSeconds: 5 # El tiempo que vamos a esperar para considerar que la sonda falló
successThreshold: 2 # El número de veces que la sonda debe pasar para considerar que el contenedor está listo
failureThreshold: 3 # El número de veces que la sonda debe fallar para considerar que el contenedor no está listo
kubectl apply -f nginx-deployment.yaml
deployment.apps/nginx-deployment configured
Muy bien, ahora veamos el resultado de este lío:
kubectl get pods
En este punto, puedes ver que Kubernetes está intentando actualizar nuestro Deployment
, pero no está teniendo éxito, ya que la sonda falló en el primer Pod
que intentó actualizar.
NAME READY STATUS RESTARTS AGE
nginx-deployment-5fd6c688d8-kjf8d 0/1 Running 0 93s
nginx-deployment-fbdc9b65f-trnnz 1/1 Running 0 9m21s
nginx-deployment-fbdc9b65f-z8n4m 1/1 Running 0 9m21s
nginx-deployment-fbdc9b65f-zn8zh 1/1 Running 0 9m21s
Vamos a verificar nuestro rollout
:
kubectl rollout status deployment/nginx-deployment
Esperando a que el despliegue "nginx-deployment" termine: 1 de 3 nuevas réplicas han sido actualizadas...
Aun después de un tiempo, nuestro rollout
no ha finalizado, sigue esperando a que la sonda pase.
Podemos ver los detalles del Pod
con problema:
kubectl describe pod nginx-deployment-5fd6c688d8-kjf8d
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 4m4s default-scheduler Successfully assigned default/nginx-deployment-5fd6c688d8-kjf8d to ip-192-168-8-176.ec2.internal
Normal Pulled 4m3s kubelet Container image "nginx:1.19.2" already present on machine
Normal Created 4m3s kubelet Created container nginx
Normal Started 4m3s kubelet Started container nginx
Warning Unhealthy 34s (x22 over 3m44s) kubelet Readiness probe failed: HTTP probe failed with statuscode: 404
Solamente he pegado la parte final de la salida, que es la parte más interesante en este momento. Es en esta parte donde podemos ver que nuestro Pod
no está saludable, y por lo tanto, Kubernetes no puede actualizar nuestro Deployment
.
Sonda de Inicio
Ha llegado el momento de hablar sobre la sonda de inicio (startupProbe
), que en mi humilde opinión, es la menos utilizada pero muy importante.
Esta sonda es responsable de verificar si nuestro contenedor se ha iniciado correctamente y si está listo para recibir solicitudes.
Es bastante similar a la sonda de preparación (readinessProbe
), pero la diferencia es que la sonda de inicio (startupProbe
) se ejecuta solo una vez al comienzo de la vida de nuestro contenedor, mientras que la sonda de preparación (readinessProbe
) se ejecuta periódicamente.
Para entenderlo mejor, veamos un ejemplo creando un archivo llamado nginx-startup.yaml
:"
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-deployment
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx-deployment
strategy: {}
template:
metadata:
labels:
app: nginx-deployment
spec:
containers:
- image: nginx:1.19.2
name: nginx
resources:
limits:
cpu: "0.5"
memory: 256Mi
requests:
cpu: 0.25
memory: 128Mi
startupProbe: # Donde definimos nuestra sonda de inicio
httpGet: # El tipo de prueba que ejecutaremos, en este caso, ejecutaremos una prueba HTTP
path: / # La ruta que probaremos
port: 80 # El puerto que probaremos
initialDelaySeconds: 10 # El tiempo que esperaremos para ejecutar la sonda por primera vez
periodSeconds: 10 # Cada cuánto tiempo ejecutaremos la sonda
timeoutSeconds: 5 # El tiempo que esperaremos para considerar que la sonda falló
successThreshold: 2 # El número de veces que la sonda debe pasar para considerar que el contenedor está listo
failureThreshold: 3 # El número de veces que la sonda debe fallar para considerar que el contenedor no está listo
Vamos a aplicar nuestra configuración:
kubectl apply -f nginx-startup.yaml
Cuando intentes aplicarla, recibirás un error porque successThreshold
no puede ser mayor que 1, ya que startupProbe
se ejecuta solo una vez, ¿recuerdas?
De la misma manera, failureThreshold
tampoco puede ser mayor que 1, así que vamos a modificar nuestro archivo a:
...
startupProbe: # Donde definimos nuestra sonda de inicio
httpGet: # El tipo de prueba que ejecutaremos, en este caso, ejecutaremos una prueba HTTP
path: / # La ruta que probaremos
port: 80 # El puerto que probaremos
initialDelaySeconds: 10 # El tiempo que esperaremos para ejecutar la sonda por primera vez
periodSeconds: 10 # Cada cuánto tiempo ejecutaremos la sonda
timeoutSeconds: 5 # El tiempo que esperaremos para considerar que la sonda falló
successThreshold: 1 # El número de veces que la sonda debe pasar para considerar que el contenedor está listo
failureThreshold: 1 # El número de veces que la sonda debe fallar para considerar que el contenedor no está listo
Ahora aplicamos la configuración nuevamente:
kubectl apply -f nginx-startup.yaml
Listo, ¡aplicado! ¡Uf! \o/
Observe que tu definición es muy similar a la readinessProbe
, pero recuerda que solo se ejecutará una vez, cuando se inicie el contenedor. Por lo tanto, si algo sale mal después de eso, no te ayudará, ya que no se ejecutará de nuevo.
Por esta razón, es muy importante siempre tener una combinación de sondas, para tener un contenedor más resistente y poder detectar problemas más rápidamente.
Vamos a verificar si nuestros Pods
están saludables:
NAME READY STATUS RESTARTS AGE
nginx-deployment-6fbd5f9794-66sww 1/1 Running 0 2m12s
nginx-deployment-6fbd5f9794-cmwq8 1/1 Running 0 2m12s
nginx-deployment-6fbd5f9794-kvrp8 1/1 Running 0 2m12s
Si deseas verificar si nuestra sonda está presente, simplemente usa el siguiente comando:
kubectl describe pod nginx-deployment-6fbd5f9794-66sww
Verás algo similar a esto:
Startup: http-get http://:80/ delay=10s timeout=5s period=10s #success=1 #failure=1
Ejemplo con todas las sondas
Vamos a nuestro ejemplo final de hoy, utilizaremos todas las sondas que hemos visto hasta ahora. Crearemos un archivo llamado nginx-todas-probes.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-deployment
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx-deployment
strategy: {}
template:
metadata:
labels:
app: nginx-deployment
spec:
containers:
- image: nginx:1.19.2
name: nginx
resources:
limits:
cpu: "0.5"
memory: 256Mi
requests:
cpu: 0.25
memory: 128Mi
livenessProbe: # Donde definimos nuestra sonda de vida
exec: # El tipo exec se utiliza cuando queremos ejecutar algo dentro del contenedor.
command: # Donde definiremos qué comando ejecutaremos
- curl
- -f
- http://localhost:80/
initialDelaySeconds: 10 # El tiempo que esperaremos para ejecutar la sonda por primera vez
periodSeconds: 10 # Cada cuánto tiempo ejecutaremos la sonda
timeoutSeconds: 5 # El tiempo que esperaremos para considerar que la sonda falló
successThreshold: 1 # Cuántas veces debe pasar la sonda para considerar que el contenedor está listo
failureThreshold: 3 # Cuántas veces debe fallar la sonda para considerar que el contenedor no está listo
readinessProbe: # Donde definimos nuestra sonda de disponibilidad
httpGet: # El tipo de prueba que ejecutaremos, en este caso, ejecutaremos una prueba HTTP
path: / # La ruta que probaremos
port: 80 # El puerto que probaremos
initialDelaySeconds: 10 # El tiempo que esperaremos para ejecutar la sonda por primera vez
periodSeconds: 10 # Cada cuánto tiempo ejecutaremos la sonda
timeoutSeconds: 5 # El tiempo que esperaremos para considerar que la sonda falló
successThreshold: 1 # Cuántas veces debe pasar la sonda para considerar que el contenedor está listo
failureThreshold: 3 # Cuántas veces debe fallar la sonda para considerar que el contenedor no está listo
startupProbe: # Donde definimos nuestra sonda de inicio
tcpSocket: # El tipo de prueba que ejecutaremos, en este caso, ejecutaremos una prueba TCP
port: 80 # El puerto que probaremos
initialDelaySeconds: 10 # El tiempo que esperaremos para ejecutar la sonda por primera vez
periodSeconds: 10 # Cada cuánto tiempo ejecutaremos la sonda
timeoutSeconds: 5 # El tiempo que esperaremos para considerar que la sonda falló
successThreshold: 1 # Cuántas veces debe pasar la sonda para considerar que el contenedor está listo
failureThreshold: 3 # Cuántas veces debe fallar la sonda para considerar que el contenedor no está listo
Listo, estamos utilizando las tres sondas, vamos a aplicarlas:
kubectl apply -f nginx-todas-probes.yaml
Y veremos si nuestros Pods
están saludables:
kubectl get pods
Revisa la salida del describe pods
para ver si nuestras sondas están presentes:
...
Liveness: exec [curl -f http://localhost:80/] delay=10s timeout=5s period=10s #success=1 #failure=3
Readiness: http-get http://:80/ delay=10s timeout=5s period=10s #success=1 #failure=3
Startup: tcp-socket :80 delay=10s timeout=5s period=10s #success=1 #failure=3
¡Todas están ahí! ¡Maravilloso!
Ahora podemos decir que sabemos cómo cuidar bien de nuestros Pods
, mantenerlos siempre saludables y bajo control.
No olvides consultar la documentación oficial de Kubernetes para obtener más información sobre las sondas y, por supuesto, si tienes alguna pregunta, no dudes en preguntar.
Tu tarea
Tu tarea es practicar todo lo que has aprendido hasta ahora. Lo más importante es replicar todo el contenido que se ha presentado hasta ahora para que puedas afianzarlo y, lo más importante, interiorizarlo.
Crea tus propios ejemplos, lee la documentación, haz preguntas y, por supuesto, si tienes alguna pregunta, no dudes en preguntar.
Todo lo que crees a partir de ahora debería tener sondas definidas para garantizar el buen funcionamiento de tu clúster.
Sin olvidar que es inadmisible tener un clúster de Kubernetes con tus pods
en ejecución sin sondas configuradas adecuadamente, así como límites de recursos.
¡Eso es todo, así de simple! :D
Final del Día 4
Durante el Día 4, aprendiste todo sobre ReplicaSet
y DaemonSet
. Hoy fue importante para entender que un clúster de Kubernetes es mucho más que un conjunto de Pods
ejecutándose en un grupo de nodos. Y aún estamos al principio de nuestra jornada, todavía veremos muchos, quizás decenas de objetos que nos ayudarán a administrar nuestro clúster de manera más efectiva.
Hoy también aprendiste cómo garantizar pruebas en tus contenedores, ya sea en el momento del inicio o durante la ejecución, para que nuestras aplicaciones sean más estables y confiables."