Comprobaciones de estado profundas de Kubernetes / Sudo Null IT News

Los sistemas distribuidos a menudo se caracterizan como un arma de doble filo. Hay mucho material excelente en Internet sobre sus lados grandes y feos. Pero esta publicación es de una naturaleza ligeramente diferente. En general, suelo estar a favor de los sistemas distribuidos en los casos en los que realmente son necesarios, pero en este post te contaré cómo uno de mis errores al trabajar con un sistema distribuido tuvo consecuencias de gran alcance.

El error que cometí ahora ocurre en muchas empresas y puede provocar una avalancha de fracasos. llamémosla control de salud profundo en Kubernetes.

El entrelazamiento de Kubernetes: una historia sobre la vida, la disponibilidad y los peligros de la comprobación profunda del estado

Kubernetes es una plataforma de orquestación de contenedores. No es casualidad que Kubernetes sea popular entre los especialistas en la creación de sistemas distribuidos, ya que permite formalizar la infraestructura en forma de una abstracción razonable de la nube nativa. Con esta abstracción, los desarrolladores pueden configurar y ejecutar aplicaciones sin tener que ser expertos en redes.

Kubernetes permite y recomienda configurar varios tipos de sondas: actividad, preparación y arranque. Estas sondas son conceptualmente simples y se pueden describir de la siguiente manera:

  • con ayuda muestras de viabilidad Le decimos a Kubernetes si es necesario reiniciar el contenedor. Si la prueba de viabilidad es negativa, la aplicación se reiniciará. Este método puede detectar ciertos problemas (por ejemplo, interbloqueos) y permitir el acceso a la aplicación nuevamente. Por ejemplo, colegas de Cloudflare describieron Aquícómo reiniciar a los consumidores Kafka atascados de esta manera.

  • Pruebas de preparación Utilizadas únicamente con aplicaciones basadas en http, estas sondas indican que el contenedor está listo para recibir tráfico. Se considera que un Pod está listo para recibir tráfico cuando todos sus contenedores están listos. Si algún contenedor en un pod no pasa la prueba de preparación, se elimina del balanceador de carga del servicio y no recibirá ninguna solicitud HTTP. Pero, si la prueba de preparación falla, el dispositivo no se reinicia y, si falla la prueba de viabilidad, se reinicia.

  • Pruebas de lanzamiento Normalmente se recomienda su uso con aplicaciones heredadas que tardan mucho tiempo en iniciarse. Una vez que una aplicación pasa las pruebas de ejecución, las pruebas de actividad y preparación posteriores no se tienen en cuenta.

En el resto de esta publicación, nos centraremos en las pruebas de preparación que se aplican a las aplicaciones basadas en HTTP.

¿Cuándo estará lista mi solicitud?

A primera vista, una pregunta muy sencilla, ¿verdad? “La aplicación estará lista para funcionar tan pronto como pueda responder a las solicitudes de los usuarios”, se podría decir. Considere una aplicación de empresa de pagos donde pueda consultar su saldo. Cuando un usuario abre una aplicación móvil, envía una solicitud a uno de los muchos servicios backend que tiene. El servicio que recibe la solicitud deberá entonces:

  • Valide el token de usuario comunicándose con el servicio de autenticación.

  • Llame al servicio donde se mantiene el saldo.

  • Enviar un evento a Kafka balance_viewed.

  • (A través de otro punto final) permiten al usuario cerrar su cuenta, lo que provoca que se actualice una fila en la propia base de datos de ese servicio.

Por tanto, se puede argumentar que el funcionamiento exitoso de una aplicación que atiende a los usuarios depende de:

  • Disponibilidad del servicio de autenticación

  • Disponibilidad de servicio para consultar saldo.

  • Disponibilidad de Kafka

  • Disponibilidad de nuestra base de datos

El gráfico de estas dependencias se verá así:

Por lo tanto, es posible escribir un punto final de preparación que devuelva JSON y código. 200 si todo lo anterior está disponible:

{
    "available":{
        "auth":true,
        "balance":true,
        "kafka":true,
        "database":true
    }
}

En este caso, la “disponibilidad” puede entenderse de forma ligeramente diferente para diferentes elementos:

  • Cuando trabajamos con servicios de autenticación y saldo, debemos asegurarnos de recibir el código. 200 desde sus puntos finales listos.

  • Al trabajar con Kafka, nos aseguramos de poder emitir un evento para un tema llamado chequeo de salud.

  • Trabajando con la base de datos, realizamos SELECT 1;

Si alguna de estas operaciones no tiene éxito, le devolveremos false para clave JSON y también error HTTP 500. Esta situación se considera una falla del sondeo de preparación y, como resultado, Kubernetes eliminará este pod del balanceador de carga del servicio. A primera vista, esto puede parecer razonable, pero en realidad, esta práctica a veces conduce a una avalancha de fracasos. Por lo tanto, quizás anulemos una de las ventajas más importantes de los microservicios (el aislamiento de fallas).

Imagine el siguiente escenario: el servicio de autenticación falló y todos los demás servicios utilizados en nuestra empresa dependen de él para realizar pruebas de preparación profundas:

En este caso, debido al fallo del servicio de autenticación, todos nuestros pods serán eliminados del servicio de equilibrio de carga. La negativa se volverá global:

Peor aún, casi no tendremos métricas para determinar la causa de esta falla. Dado que las solicitudes no llegan a nuestros pods, no sabemos cuánto aumentar todas esas métricas de Prometheus que hemos colocado cuidadosamente en nuestro código. En cambio, tendremos que considerar cuidadosamente todos aquellos pods que están marcados como no listos en nuestro clúster.

Después de esto, tendremos que llegar al punto final responsable de su preparación para identificar qué dependencia causó la falla y luego rastrear todo el árbol. El servicio de autenticación podría estar inactivo debido a que falló una de sus propias dependencias.

Algo como esto:

Mientras tanto, nuestros usuarios ven lo siguiente:

 upstream connect error or disconnect/reset before headers. reset reason: connection failure

No es el mensaje de error de mejor calidad, ¿verdad? Podemos hacerlo y lo haremos mucho mejor.

Entonces, ¿cómo se juzga si una solicitud está lista?

La aplicación está lista si puede producir una respuesta. Esto podría ser una respuesta fallida, pero en ese caso aún ejecuta parte de la lógica empresarial. Por ejemplo, si el servicio de autenticación falla, entonces podemos (y debemos) intentarlo nuevamente, programando un retraso exponencial y mientras tanto aumentamos el valor del contador de fallas. Si alguno de estos contadores alcanza un umbral que usted considera inaceptable (según los SLO definidos en su organización), entonces puede declarar un incidente con una zona de impacto claramente definida.

Se espera que durante este tiempo los segmentos individuales de su sistema empresarial puedan seguir funcionando, ya que no todo el sistema dependía de este servicio fallido.

Una vez solucionado el incidente, debes considerar si el servicio necesita esta dependencia y qué se puede hacer para deshacerse de ella. Por ejemplo, ¿es posible pasar a un modelo de autenticación que tenga menos estado? ¿Puedo usar el caché? ¿Es posible interrumpir automáticamente determinadas secuencias de acciones del usuario? ¿Deberían trasladarse algunos de los subprocesos de trabajo que no requieren tantas dependencias a otro servicio para aislar mejor las fallas en el futuro?

Conclusión

Basándome en conversaciones con colegas, puedo suponer que esta publicación resultará bastante holívar. Algunos podrían pensar que soy un idiota sólo porque implemento profundos controles de cordura, ya que naturalmente pueden provocar fallos similares a avalanchas. Otros compartirán esta publicación en sus feeds y preguntarán: “¿Estamos probando la cocción incorrectamente?” – y aquí el señor intervendrá en la discusión y comenzará a demostrar que su caso es especial, por lo que tales controles son recomendables para ellos. Quizás sea así, en cuyo caso me gustaría leer más sobre su situación.

Al crear un sistema distribuido, lo complicamos aún más. Cuando se trabaja con sistemas distribuidos, siempre se debe mostrar un saludable pesimismo y pensar inmediatamente en posibles fallas. Quiero decir, no esperes que te rechacen todo el tiempo, pero prepárate para ello. Necesitamos comprender la naturaleza interconectada de nuestros sistemas y saber que desde un único punto de falla, los problemas se extienden como ondas en el agua.

La principal conclusión de mi historia de Kubernetes es no abandonar por completo los controles de salud profundos, sino utilizarlos con cuidado. Aquí hay un equilibrio crítico: sopesar los méritos de las comprobaciones detalladas del rendimiento frente a la probabilidad de fallos generalizados del sistema. Mejoramos como desarrolladores cuando aprendemos de nuestros errores y ayudamos a otros a hacer lo mismo. Así es como logramos seguir siendo resilientes mientras trabajamos con sistemas cada vez más complejos.

Mate

Noticias, reseñas de productos y concursos del equipo de Timeweb.Cloud en nuestro canal de Telegram

Ir ↩

Lea también:

Publicaciones Similares

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *