Pruebas de vida de nuestros contenedores en Kubernetes

Cuando dejas tus contenedores a cargo de Kubernetes puedes estar [email protected] de que este cuidará de ellos y de su salud. Sin embargo, es importante que tengas en cuenta que una cosa es que tu contenedor esté up & running y otra que la aplicación que vive en él esté dando el servicio que esperas. Hoy quiero hablarte de cómo configurar en tus contenedores una sonda que ayude a Kubernetes a saber que la aplicación está respondiendo correctamente, a través de lo que se conoce como Liveness probe.

Liveness probe

Cada nodo de Kubernetes trae consigo un agente llamado kubelet, que tiene como cometido asegurarse de que tus pods están sanos. Por cada pod podemos además configurar una prueba/s que indican que estos se están comportando de manera correcta, más allá de que el contenedor se esté ejecutando. Estas pruebas pueden ser de tres tipos: a través de un comando que se ejecuta dentro del contenedor, una llamada HTTP o bien TCP. Un ejemplo de cómo se configuran podría ser la siguiente:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-liveness
spec:
  containers:
  - image: nginx
    name: web
    livenessProbe:
      httpGet:        
        path: /health-check
        port: 80

Como ves, a través del apartado livenessProbe de cada contenedor de tu pod puedes configurar una o varias de las pruebas mencionadas. En este ejemplo he configurado una del tipo httpGet, donde indicamos un path y un puerto en el que cada cierto tiempo se lanzará una llamada. Si el resultado es satisfactorio (cualquier código mayor o igual a 200 y menos que 400) significara que tu aplicación está funcionando correctamente.

Si creas este nuevo pod:

kubectl create -f nginx-liveness.yaml

y consultas tus pods después de unos segundos te darás cuenta de que, aunque el mismo esté en modo running, este se habrá reiniciado varias veces:

El contenedor se reinicia varias veces al no cumplirse el liveness probe

Es totalmente normal, ya que al hacer la comprobación se da cuenta de que el error que está recibiendo por parte de nuestro nginx es un 404 (Not found), ya que la ruta elegida para la sonda no existe. Para intentar resolverlo, Kubernetes reinicia nuestro pod, después de varias ejecuciones, intentando recuperar nuestra aplicación.

Para entender mejor el funcionamiento y el estado de pod ejecuta el siguiente comando para nuestro nginx-liveness:

kubectl describe po nginx-liveness

El comando describe nos da mucha información sobre el recurso elegido. Para este escenario nos interesa ver dos secciones concretas:

Por un lado, en el apartado Containers puedes ver en la sección Liveness las pruebas creadas y la configuración de las mismas:

En el apartado Containers se ve la configuración de la prueba

Si bien no hemos configurado todos los parámetros que aparecen en dicha sección, tenemos varios que podemos establecer, para asegurarnos de que nuestra prueba sea válida para nosotros y nuestra aplicación en concreto:

  • delay: como su propio nombre indica es el retardo con el cual se lanza la prueba cuando el contenedor es iniciado. Este valor es importante porque a veces nuestra aplicación puede tardar un tiempo en estar lista, aunque el contenedor se haya iniciado.
  • timeout: se trata del tiempo en el que se espera que el contenedor responda a la petición, por lo que en ocasiones es necesario incrementar dicho valor si se trata de una petición que, en los casos de una prueba de salud, puede que estés comprobando ciertos aspectos que tarden algo más de un segundo. Es importante también no hacer peticiones muy pesadas que afecten al rendimiento general de contenedor.
  • period: si no especificamos ningún valor, la prueba se lanza cada 10 segundos.
  • success: indica cuántas veces seguidas tiene que ser satisfactoria la prueba para que esta sea válida, antes de decidir que se debe reiniciar el pod. En este tipo de pruebas el valor solo puede ser 1.
  • failure: por defecto, el pod no se reinicia cada vez que la prueba falla, sino que se espera a que se realicen 3 intentos de cada prueba.

Si quisiéramos controlar todos estos valores, podemos establecerlos en el mismo apartado de liveness:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-liveness
spec:
  containers:
    - image: nginx
      name: web
      livenessProbe:
        httpGet:
          path: /health-check
          port: 80
        initialDelaySeconds: 3
        timeoutSeconds: 3
        periodSeconds: 60
        successThreshold: 1 #Must be 1 for liveness
        failureThreshold: 5

Si vuelves a crear el pod y revisas la configuración del apartado Liveness verás que ahora se ajusta a los valores establecidos en su definición.

Cambiados los valores por defecto de los parámetros de la prueba

Otro apartado importante a revisar es Events, donde podemos encontrar información sobre el por qué del fallo de nuestra prueba:

En el apartado Events se ve el resultado de la prueba

En este ejemplo, como te comenté antes, sabemos que el motivo de que la misma falle es porque al lanzar una petición contra una ruta que no existe nuestro servidor nginx devolverá un 404. Perfecto para probar la efectividad de esta configuración 😀

Otra de las pruebas que podríamos configurar es la ejecución de un comando dentro de nuestro contenedor. Para verlo con ejemplo simple, vamos a modificar el pod anterior con la siguiente:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-liveness
spec:
  containers:
    - image: nginx
      name: web
      livenessProbe:
        exec:
          command:
            - ls
            - /tmp/iamalive

Para este escenario, debemos utilizar la sección exec. En este caso ejecuto el comando ls sobre un archivo que en teoría debería de existir para que la prueba sea válida.

Si elimino el pod y lo creo de nuevo:

kubectl delete po nginx-liveness && kubectl create -f nginx-liveness.yaml

Comprobarás que inicialmente el pod es creado y arrancado correctamente y, después de 30 segundos, el mismo volverá a reiniciarse ya que dicha prueba no finaliza de manera satisfactoria, puesto que el archivo no existe.

Liveness probe falla porque el archivo no existe

Es importante tener configuradas este tipo de pruebas por cada uno de nuestros contenedores en Kubernetes, ya que es la forma de asegurarnos de que no solo se está ejecutando el contenedor sino que el contenido del mismo está actuando según esperamos. Ten en cuenta que también es importante configurarlas en el sitio adecuado, ya que si la prueba implica la llamada a otro contenedor, por ejemplo un base de datos, y esta falla por el problema está en otro sitio se estará reiniciando por los motivos equivocados.

¡Saludos!