lunes, 26 de abril de 2021

Liveness

Hablábamos en el artículo anterior sobre Readiness, una sonda de kubernetes que indica cuando un pod está listo para atender tráfico, dejándo la plataforma de enviárselo en caso de no superar la sonda. Aquí puedes echarle un ojo.

Un pod puede no ser capaz de atender tráfico temporalmente (está arrancando por ejemplo) o debido a un error del que no pueda recuperarse solo. Para este segundo caso tenemos otro tipo de sonda, llamada Liveness, que es explicada por David Fowler en el siguiente tweet:

 

Definición de liveness de David Fowler

Esta sonda "pregunta" a un pod si sigue vivo, para en caso contrario reiniciarlo. Podemos verlo de un modo muy claro en la siguiente imagen (cortesía de la documentación de Google Cloud Platform):

 

Liveness en funcionamiento

 

Como veis, si la sonda no se supera, el pod es reiniciado, pudiendo esto resolver un problema del pod, que le impidiese hacer su trabajo con normalidad.

 

¿Siempre necesito definir esta sonda?

Para poder responder debemos entender el ciclo de vida de un pod, que podemos encontrar en la documentación oficial.
 
Allí se hace referencia a que los contenedores de un pod (si, no olvidemos que en un pod se ejecutan 1 o más contenedores) pueden ser reiniciados cuando terminan, dependiendo de la "restart policy" definida.
 
Esta política admite tres valores:

  • Always (valor por defecto)
  • On failure
  • Never

 
Salvo que utilicemos la opción "never", nuestros contenedores serán rearrancados después de terminar, usando un retardo exponencial (10s, 20s, 40s…) que está limitado a un máximo de hasta 5 minutos.
 
Si fijamos el valor "On failure" solo serán rearrancados contenedores que finalicen con un valor de retorno distinto a 0.
 
Explicado lo anterior, parece que dejando el valor por defecto, vamos a conseguir que nuestros pods siempre esten "vivos", ya que si sus contendores acabasen, por el motivo que fuere, serían reiniciados.
 
Pero la clave es: ¿mi contenedor siempre va a terminar si encuentra algún error o no es capaz de funcionar correctamente? ¿Estamos seguros de que no va a entrar en un estado de dead lock? ¿Y en un bucle infinito?
 
Si no estamos seguros de poder responder afirmativamente a las preguntas anteriores, mejor será definir una sonda de liveness, que se encargue de reiniciar el pod si este no es capaz de responder satisfactoriamente.


Definiendo la sonda de liveness

Debemos hacerlo en nuestros deployments:

La sonda anterior tratará de conectar con el puerto 80 cada 5 segundos, y en caso de poder hacerlo la prueba se considerará exitosa. En caso contrario, tras dos fallos consecutivos, el pod será reiniciado.
 
En este caso he definido un probe tcp, aunque no es la única opción disponible, ya que también podemos definir probes http o probes basados en comandos. En la documentación oficial podemos verlo en detalle, no obstante aquí os dejo un fragmento de una sonda http:
Ten en cuenta que tu eres el responsable de impementar el endpoint /healthy en tu aplicación. En el mismo puedes devolver Ok sin más (sonda "tonta") o realizar comprobaciones avanzadas, como tratar de ver si la base de datos está accesible (sonda "lista").


¿Http, tcp o comandos?

No es una respuesta sencilla, pero de entrada diría que una buena opción para empezar sería la siguiente:

  1. Http si está disponible
  2. Tcp si el pod no "habla" http
  3. Comando si el pod no "escucha" peticiones (hace tareas batch por ejemplo)

Pero no se trata de una "ley" a cumplir siempre, aquí lo que mejor funciona es el clásico "depende". Depende de tu aplicación y su contexto :-)
 
Lo que si me parece interesante es comentar que si tu pod tiene su contenedor principal y un contenedor sidecar, tipo proxy inverso nginx por ejemplo, una sonda tcp puede resultar insuficiente: podría conectar con el nginx sin problema, pudiendo estar el contenedor principal bloqueado. 

 

¿Cómo sería una sonda basada en un comando?

Como comentaba, si tu pod no escucha peticiones, no son viables las sondas http o tcp, y debemos basarnos en comandos.
 
Un ejemplo sería ver si el pod está generando periódicamente un fichero concreto en un directorio. En caso de que el pod no estuviese vivo, no podría generar el fichero y la sonda fallaría.
 
Ese script status_check.sh será el encargado de comprobar que el fichero existe y que ha sido creado recientemente (basándose en su fecha). En caso de no encontrar el fichero o detectar que no es reciente, el script saldrá con error haciendo que la sonda no se supere.

Recursos

Os dejo un par de enlaces interesantes:

La documentación oficial, donde entran en detalle de todas las configuraciones que podemos definir en una sonda, como el periodo de ejecución y los umbrales para considerar una sonda como exitosa o fallida.

Por otro lado, os dejo un proyecto opensource, Xabaril Health Checks, que si trabajais en .net core (o net 5) os ayudará a implementar sondas (readiness o liveness) basándoos en los health checks ofrecidos por la plataforma.

Espero que esta entrada os haya gustado. Saludos

lunes, 19 de abril de 2021

Readiness

¿Cuánto tiempo pasa desde que una aplicación es arrancada hasta que es capaz de desempeñar su trabajo con normalidad?

 

En aplicaciones de escritorio hay mucha variación, tenemos algunas en las que prácticamente ese tiempo es nulo, mientras que en otras, es tan elevado, que se recurre a técnicas como la splash screen.

 

Sin embargo en aplicaciones web en general, y en API's REST en particular, no solemos fijarnos tanto en esto, ya que el tiempo usualmente es mucho más pequeño.

Fijaos en esta sencilla API escrita en .net 5 (un solo fichero): 




¿Cuánto tardará en empezar a atender peticiones desde que sea iniciada? ¿Algunos milisegundos? Es posible. Fijaos que ese pequeño tiempo puede generarnos problemas, sobre todo si estamos en escenarios de auto-escalado o si buscamos despliegues sin parada (Aquí te dejo una sesión en la que hablo de este tipo de despliegues).


Por otro lado, imagina una aplicación que necesite descargar configuración o secretos desde un recurso externo antes de poder empezar a atender peticiones. En este caso ese tiempo será muy superior.


Mi definición de "readiness"


"Es el estado de una aplicación en el cual está capacitada para hacer su trabajo con normalidad"

En el supuesto anterior, la aplicación arranca y necesita cierto tiempo para descargar recursos externos (secretos u otra configuración). Una vez hecho está "ready", puede hacer su trabajo con normalidad y por tanto atender peticiones de usuario.



Imagén explicativa de Readiness. Un pod arranca y necesita tiempo hasta que puede aceptar tráfico


¿Qué es un readiness probe?


Es una sonda o verificación que nos indicará si una aplicación se encuentra en un estado de "ready" o "not ready". En base al resultado de la verificación, se enviará tráfico o no a la aplicación.

Veámoslo en el caso de kubernetes. Allí, los pods se crean y destruyen por diferentes motivos, alguno de los cuales no requieren nuestra intervención (un pod es movido de nodo, o simplemente falla y es rearrancado). Por tanto no sería nada deseable que un pod que aún no esté listo recibiese tráfico, ya que generaría errores.

Para conseguirlo, debemos definir una "readiness probe" en nuestros deployments, del siguiente modo:



En este caso he definido un probe http, aunque no es la única opción disponible, ya que también podemos definir probes tcp o probes basados en comandos. En la documentación oficial podemos verlo.

 

Como veis, he definido un periodo de ejecución de la sonda de 5 segundos, además de un umbral de 2 (threshold), que os paso a explicar:


  • Inicialmente, un pod arranca en estado "Not ready"
  • Una vez que supera la sonda de readiness pasa a estado "Ready"
  • Si la sonda falla 2 veces seguidas (aquí está ese umbral), pasa de nuevo a estado "Not ready", y no recibirá tráfico.

Solo hay un matiz a lo anterior: si no hemos definido el Readiness en nuestro deployment, el pod siempre será considerado como "Ready".

 Con todo esto, mi deployment de kubernetes queda así:



 


¿Por qué es importante el readiness en escenarios de auto-escalado?


Lo mencioné de pasada antes, merece explicarlo:

 

Si tenemos reglas de autoescalado, tarde o temprano, y en base a estas reglas, nuevos pods serán creados para atender la creciente demanda. En esos casos, una vez que el pod es creado, empezará a recibir tráfico de inmediato, a no ser que definamos las probes de readiness adecuadas. 

 

Si recibe tráfico de inmediato, y no tenemos reglas de readiness, vamos a tener momentos "problemáticos", que son esos tiempos muertos desde que un pod arranca hasta que está listo para atender tráfico.

 

Sin embargo si definimos sondas de readiness, el nuevo pod no recibirá tráfico hasta que éstas sondas son satisfactorias, evitando así cualquier tipo de downtime.



¿Hay más tipos de sonda?


Si, tenemos sondas de Liveness y de Startup, las veremos en otras entradas.


Espero que os haya resultado interesante.