Publicar Mortem fuera del alcance de Quay.io

Aprox. transl. : A principios de agosto, Red Hat habló públicamente sobre la solución de los problemas de accesibilidad que habían surgido en meses anteriores entre los usuarios de su servicio Quay.io (se basa en un registro de imágenes de contenedores, que la empresa consiguió con la compra de CoreOS). Independientemente de su interés en este servicio como tal, el camino que han seguido los ingenieros de la SRE de la empresa para diagnosticar y eliminar las causas del accidente es instructivo.







En la madrugada del 19 de mayo (EDT), quay.io se estrelló. El desastre afectó tanto a los consumidores de quay.io como a los proyectos de código abierto que utilizaban quay.io como plataforma para crear y distribuir software. Red Hat valora la confianza de ambos.



El equipo de ingenieros de SRE intervino de inmediato e intentó estabilizar el servicio de Quay lo antes posible. Sin embargo, mientras hacían esto, los clientes ya no podían impulsar nuevas imágenes y solo ocasionalmente podían extraer las existentes. Por alguna razón desconocida, la base de datos de quay.io se bloqueó después de que el servicio se escalara a su capacidad máxima.



¿Qué ha cambiado? "- esta es la primera pregunta que se suele hacer en tales casos. Notamos que no mucho antes del problema, el clúster dedicado de OpenShift (en el que se ejecuta quay.io) comenzó a actualizarse a la versión 4.3.19. Dado que quay.io funciona con Red Hat OpenShift Dedicated (OSD), las actualizaciones periódicas eran comunes y nunca causaron problemas. Además, durante los últimos seis meses, hemos actualizado los clústeres de Quay varias veces sin interrumpir el servicio.



Mientras intentábamos restaurar el servicio, otros ingenieros comenzaron a preparar un nuevo clúster de OSD con la versión anterior del software para implementar todo en él en caso de necesidad.



Análisis de raíz de la causa



El síntoma principal del bloqueo fue una avalancha de decenas de miles de conexiones de bases de datos que desactivaron efectivamente la instancia de MySQL. Esto dificultó el diagnóstico del problema. Hemos establecido un límite en la cantidad máxima de conexiones de los clientes para ayudar al equipo de SRE a evaluar el problema. No notamos ningún tráfico inusual en la base de datos: de hecho, la mayoría de las solicitudes eran para lecturas y solo unas pocas para escrituras.



También intentamos identificar un patrón en el tráfico de la base de datos que podría causar esta avalancha. Sin embargo, no fue posible encontrar ningún patrón en los registros. Mientras esperábamos que el nuevo clúster con OSD 4.3.18 estuviera listo, seguimos intentando lanzar los pods de quay.io. Cada vez que el clúster estaba a plena capacidad, la base de datos se congelaba. Esto significaba que era necesario reiniciar la instancia de RDS además de todos los pods de quay.io.



Por la noche, estabilizamos el servicio en modo de solo lectura y deshabilitamos el máximo de funciones no esenciales (por ejemplo, recolección de basura en el espacio de nombres) para reducir la carga en la base de datos. Las heladas cesaron, pero nunca se encontró la razón . El nuevo clúster de OSD estaba listo y migramos el servicio, conectamos el tráfico y continuamos con la supervisión.



Quay.io se ejecutó de manera estable en el nuevo clúster OSD, por lo que volvimos a los registros de la base de datos, pero no pudimos encontrar una correlación que explique los bloqueos. Los ingenieros de OpenShift trabajaron con nosotros para ver si los cambios en Red Hat OpenShift 4.3.19 podrían haber causado problemas con Quay. Sin embargo, no se encontró nada y el problema no se pudo reproducir en el laboratorio .



Segundo fracaso



El 28 de mayo, poco antes del mediodía EDT, quay.io se bloqueó nuevamente con el mismo síntoma: la base de datos estaba bloqueada. Una vez más, pusimos todas nuestras fuerzas en la investigación. En primer lugar, fue necesario restaurar el servicio. Sin embargo , esta vez, reiniciar RDS y reiniciar los pods de quay.io no condujo a nada : otra avalancha de conexiones arrasó la base. ¿Pero por qué?



Quay está escrito en Python y cada pod funciona como un único contenedor monolítico. Muchas tareas paralelas se ejecutan simultáneamente en el tiempo de ejecución del contenedor. Estamos usando una biblioteca geventbajogunicornpara procesar solicitudes web. Cuando Quay recibe una solicitud (a través de nuestra propia API o de la API de Docker), se le asigna un trabajador gevent. Normalmente, este trabajador necesita comunicarse con la base de datos. Después del primer error, descubrimos que los trabajadores de gevent se estaban conectando a la base de datos utilizando la configuración predeterminada.



Dada la gran cantidad de pods de Quay y miles de solicitudes entrantes por segundo, la gran cantidad de conexiones de base de datos podría, en teoría, sobrecargar la instancia de MySQL. Gracias al seguimiento, se conoció que Quay procesa una media de 5 mil solicitudes por segundo. El número de conexiones a la base de datos fue aproximadamente el mismo. 5 mil conexiones con un margen encajan en las capacidades de nuestra instancia RDS (que no se puede decir de decenas de miles).Por alguna razón, hubo picos inesperados en el número de conexiones , sin embargo, no notamos ninguna correlación con las solicitudes entrantes.



Esta vez, estábamos decididos a encontrar y solucionar el origen del problema, no solo a reiniciar. Se han realizado cambios en la base de código de Quay para limitar el número de conexiones a la base de datos para cada trabajador de gevent . Este número se convirtió en un parámetro en la configuración: fue posible cambiarlo "sobre la marcha" sin crear una nueva imagen de contenedor. Para averiguar cuántas conexiones manejar realmente, ejecutamos varias pruebas con un entorno de ensayo que establecía diferentes valores para ver cómo esto afectaría los escenarios de prueba de carga. Como resultado, resultó queQuay comienza a generar errores 502 cuando el número de conexiones supera los 10 K.



Inmediatamente implementamos esta nueva versión en producción y comenzamos a monitorear el programa de conexión de la base de datos. En el pasado, la base estaba bloqueada después de unos 20 minutos. Después de 30 minutos sin problemas, teníamos esperanza y, una hora más tarde, confianza. Restauramos el tráfico a la publicación en el sitio y comenzamos el análisis post mortem.



Habiendo logrado solucionar el problema que condujo al bloqueo, no descubrimos sus verdaderas causas . Se confirmó que no estaba relacionado con ningún cambio en OpenShift 4.3.19, ya que lo mismo sucedió en la versión 4.3.18, que anteriormente funcionaba con Quay sin problemas.



Claramente había algo más acechando en el grupo.



Estudio detallado



Quay.io ha estado usando la configuración predeterminada para conectarse a la base de datos durante seis años sin ningún problema. ¿Qué cambió? Está claro que el tráfico hacia quay.io ha ido creciendo constantemente durante todo este tiempo. En nuestro caso, todo parecía como si se alcanzara un cierto valor umbral, que servía como detonante de una avalancha de conexiones. Continuamos examinando los registros de la base de datos después del segundo bloqueo, pero no encontramos patrones ni relaciones obvias.



Mientras tanto, el equipo de SRE ha estado trabajando en mejoras para la observabilidad de consultas de Quay y el estado general del servicio. Se han implementado nuevas métricas y paneles que muestran qué partes de Quay tienen más demanda de los clientes.



Quay.io funcionó bien hasta el 9 de junio. Por la mañana (vía EDT), nuevamente fuimos testigos de un aumento significativo en el número de conexiones a la base de datos. Esta vez, no hubo tiempo de inactividad , ya que el nuevo parámetro limitaba su número y no permitía exceder el ancho de banda de MySQL. Sin embargo, durante aproximadamente media hora, muchos usuarios notaron que quay.io era lento. Recopilamos rápidamente todos los datos posibles utilizando las herramientas de monitoreo agregadas. De repente surgió un patrón.



Justo antes del salto en las conexiones, se envió una gran cantidad de solicitudes a la API de registro de aplicaciones.... App Registry es una característica poco conocida de quay.io. Le permite almacenar cosas como gráficos y contenedores de Helm que son ricos en metadatos. La mayoría de los usuarios de quay.io no utilizan esta función, pero Red Hat OpenShift la está utilizando activamente. OperatorHub dentro de OpenShift almacena todos los operadores en el Registro de aplicaciones. Estos operadores forman la base para el ecosistema de carga de trabajo de OpenShift y el modelo operativo centrado en socios (en las operaciones del segundo día).



Cada clúster de OpenShift 4 utiliza operadores del OperatorHub integrado para publicar un catálogo de operadores disponibles para la instalación y proporcionar actualizaciones para los que ya están instalados. Con la creciente popularidad de OpenShift 4, la cantidad de clústeres en él ha aumentado en todo el mundo. Cada uno de estos clústeres carga el contenido del operador para ejecutar el OperatorHub integrado utilizando el Registro de aplicaciones dentro de quay.io como backend. Al buscar la fuente del problema, nos perdimos el hecho de que a medida que la popularidad de OpenShift aumentaba gradualmente, también lo hacía la carga en una de las funciones quay.io raramente utilizadas .



Hicimos un análisis del tráfico de solicitudes del Registro de aplicaciones y examinamos el código de registro. Inmediatamente, se revelaron fallas, por lo que las consultas a la base de datos se formaron de manera subóptima. No causaron ningún problema bajo carga ligera, pero cuando aumentó se convirtieron en una fuente de problemas. El registro de aplicaciones resultó tener dos puntos finales problemáticos que no respondieron bien a un aumento en la carga: el primero proporcionó una lista de todos los paquetes en el repositorio, el segundo devolvió todos los blobs para un paquete.



Eliminación de causas



Durante la próxima semana, hemos estado optimizando el código del Registro de aplicaciones y su entorno. Obviamente, se modificaron las consultas SQL ineficientes, se eliminaron las llamadas innecesarias al comando tar(se ejecutaba cada vez que se obtenía un blob) y se agregaba almacenamiento en caché siempre que era posible. Luego, realizamos pruebas de rendimiento exhaustivas y comparamos el rendimiento del Registro de aplicaciones antes y después de los cambios.



Las solicitudes de API que solían tardar hasta medio minuto ahora se completaban en milisegundos . Implementamos los cambios en la producción la próxima semana y quay.io se ha mantenido estable desde entonces. Durante este tiempo, ha habido varios picos de tráfico en el punto final de App Registry, pero las mejoras realizadas han evitado interrupciones de la base de datos.



¿Qué hemos aprendido?



Está claro que cualquier servicio intenta evitar el tiempo de inactividad. En nuestro caso, creemos que las caídas recientes han ayudado a mejorar quay.io. Para nosotros mismos, hemos sacado varias lecciones principales que queremos compartir:



  1. Los datos sobre quién usa su servicio y cómo no son superfluos . Debido a que Quay “simplemente funcionó”, nunca tuvimos que dedicar tiempo a optimizar el tráfico y administrar la carga. Todo esto creó una falsa sensación de seguridad de que el servicio podría escalar indefinidamente.
  2. , . Quay , . , — , .
  3. Evalúe el impacto de cada función de servicio . Los clientes rara vez usaban el Registro de aplicaciones, por lo que no era una prioridad para nuestro equipo. Cuando algunas características del producto apenas se usan, sus errores rara vez aparecen y los desarrolladores dejan de monitorear el código. Es fácil caer presa de la ilusión de que así debería ser, hasta que, de repente, esta característica está en el centro de un incidente masivo.


¿Que sigue?



El trabajo para asegurar la estabilidad del servicio nunca se detiene y lo estamos mejorando constantemente. Los volúmenes de tráfico en quay.io continúan creciendo y reconocemos que debemos hacer nuestro mejor esfuerzo para estar a la altura de la confianza de nuestros clientes. Por tanto, actualmente estamos trabajando en las siguientes tareas:



  1. , RDS.
  2. RDS. . , ( ); .
  3. . , .
  4. firewall’ - (WAF), , quay.io.
  5. , Red Hat OpenShift App Registry (Operator Catalogs), , quay.io.
  6. La compatibilidad con las especificaciones de artefactos de Open Container Initiative (OCI) podría ser un reemplazo a largo plazo para el Registro de aplicaciones. Actualmente se está implementando como funcionalidad nativa de Quay y estará disponible para los usuarios cuando se finalice la especificación.


Todo lo anterior es parte de la inversión continua de Red Hat en quay.io a medida que pasamos de un pequeño equipo similar a una startup a una plataforma madura impulsada por SRE. Sabemos que muchos de nuestros clientes confían en quay.io para su trabajo diario (¡incluido Red Hat!) Y tratamos de ser lo más abiertos posible sobre las interrupciones recientes y los esfuerzos continuos para mejorar.



PD del traductor



Lea también en nuestro blog:






All Articles