Nota de traducción.: a principios de agosto, Red Hat informó públicamente sobre la solución a los problemas de disponibilidad que habían surgido en meses anteriores para los usuarios de su servicio. (con un registro para imágenes de contenedores heredado por la compañía con la compra de CoreOS). Independientemente de su interés en este servicio como tal, es fascinante el camino que recorrieron los ingenieros de SRE de la compañía para diagnosticar y resolver las causas de la interrupción.

El 19 de mayo, temprano en la mañana (hora del este de EE. UU. - EDT), el servicio quay.io se cayó. La interrupción afectó tanto a los consumidores de quay.io como a los proyectos de código abierto que utilizan quay.io como plataforma para construir y distribuir software. Red Hat valora la confianza de ambos.
El equipo de ingenieros de SRE se puso a trabajar de inmediato para estabilizar el servicio de Quay lo más rápido posible. Sin embargo, mientras se ocupaban de eso, los clientes se vieron impedidos de enviar nuevas imágenes, y solo ocasionalmente podían recuperar las existentes. Por una razón desconocida, la base de datos de quay.io se bloqueó tras escalar el servicio a su plena capacidad.
«¿Qué ha cambiado?» — esta es la primera pregunta que se suele hacer en tales casos. Notamos que poco antes del problema, el clúster de OpenShift Dedicated (en el que se ejecuta quay.io) comenzó a actualizarse a la versión 4.3.19. Dado que quay.io funciona en Red Hat OpenShift Dedicated (OSD), las actualizaciones regulares eran una operación habitual y nunca habían causado problemas. Además, en los seis meses anteriores habíamos actualizado los clústeres de Quay varias veces sin interrupciones en 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 desplegarlo en caso de ser necesario.
Análisis de causas raíz
El principal síntoma de la falla fue una avalancha de decenas de miles de conexiones a la base de datos, lo que hizo que la instancia de MySQL resultara prácticamente inoperante. Esto dificultó la diagnosis del problema. Establecimos un límite en el número máximo de conexiones de los clientes para ayudar al equipo de SRE a evaluar la situación. No notamos tráfico inusual hacia la base de datos: de hecho, la mayoría de las solicitudes eran de lectura, y solo unas pocas eran de escritura.
También intentamos identificar un patrón en el tráfico de la base de datos que pudiera haber provocado esta avalancha. Sin embargo, no se encontraron patrones en los registros. A la espera de la disponibilidad del nuevo clúster con OSD 4.3.18, continuamos intentando iniciar los pods de quay.io. Cada vez que el clúster alcanzaba su máxima capacidad, la base de datos se bloqueaba. Esto significaba que era necesario reiniciar la instancia de RDS además de todos los pods de quay.io.
Para la tarde, estabilizamos el servicio en modo solo lectura y desactivamos la mayoría de las funciones no esenciales (como la recolección de basura en el espacio de nombres) para reducir la carga en la base de datos. Los bloqueos cesaron, pero la causa nunca fue encontrada. El nuevo clúster OSD estaba listo, y trasladamos el servicio, conectamos el tráfico y continuamos con la monitorización.
Quay.io funcionó de manera estable en el nuevo clúster OSD, así que volvimos a los registros de la base de datos, pero no pudimos encontrar ninguna correlación que explicara los bloqueos. Los ingenieros de OpenShift trabajaron junto a nosotros, tratando de entender si los cambios en Red Hat OpenShift 4.3.19 podrían haber causado problemas con Quay. Sin embargo, no se descubrió nada, y no pudimos reproducir el problema en condiciones de laboratorio.
El segundo fallo
el 28 de mayo, poco antes del mediodía EDT, quay.io volvió a caer con el mismo síntoma: el funcionamiento de la base de datos se bloqueaba. Y nuevamente volcamos todos nuestros esfuerzos en la investigación. Primero, era necesario restaurar el funcionamiento del servicio. Sin embargo, esta vez reiniciar RDS y reiniciar los pods de quay.io no dio resultados: otra avalancha de conexiones inundó la base de datos. ¿Pero por qué?
Quay está escrito en Python, y cada pod opera como un único contenedor monolítico. En el entorno de ejecución del contenedor, se ejecutan muchas tareas en paralelo. Usamos la biblioteca gevent la competencia de gunicorn para manejar las solicitudes web. Cuando Quay recibe una solicitud (a través de nuestra propia API o a través de la API de Docker), se le asigna un worker de gevent. Normalmente, este worker debe conectarse a la base de datos. Después de la primera falla, descubrimos que los workers de gevent se conectaban a la base de datos utilizando la configuración por defecto.
Dado el considerable número de pods de Quay y miles de solicitudes por segundo, un gran número de conexiones a la base de datos podría teóricamente sobrecargar la instancia de MySQL. Gracias al monitoreo, se supo que Quay maneja en promedio 5,000 solicitudes por segundo. Aproximadamente el mismo número de conexiones a la base de datos. 5,000 conexiones se ajustaban bien a las capacidades de nuestra instancia de RDS (lo 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 decidimos firmemente encontrar y eliminar la fuente del problema, en lugar de limitarnos a reiniciar. En la base de código de Quay se hicieron cambios que limitaban el número de conexiones a la base de datos para cada worker gevent. Este número se convirtió en un parámetro de configuración: era posible cambiarlo 'en caliente', sin necesidad de reconstruir una nueva imagen del contenedor. Para averiguar cuántas conexiones se podían manejar realmente, se llevaron a cabo varias pruebas con el entorno de staging, en las que se establecían diferentes valores para ver cómo esto afectaría a los escenarios de pruebas de carga. Al final, se descubrió que Quay comienza a arrojar errores 502 cuando el número de conexiones supera las 10,000.
Inmediatamente desplegamos esta nueva versión en producción y comenzamos a monitorear el gráfico de conexiones a la base de datos. En el pasado, la base se bloqueaba aproximadamente después de 20 minutos. Tras 30 minutos sin problemas, comenzamos a tener esperanzas, y tras una hora, confianza. Recuperamos el tráfico de escritura en el sitio y comenzamos el análisis postmortem.
Logrando sortear el problema que causaba el bloqueo, no pudimos determinar sus verdaderas causas.Se confirmó que no estaba relacionada 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 ningún problema.
Algo más estaba claramente oculto en el clúster.
Un examen detallado
Quay.io ha utilizado la configuración predeterminada para conectarse a la base de datos sin problemas durante seis años. ¿Qué ha cambiado? Es evidente que durante todo este tiempo, el tráfico en quay.io ha ido en aumento constante. En nuestro caso, parecía que se había alcanzado un umbral que desencadenó una avalancha de conexiones. Continuamos revisando los registros de la base de datos después del segundo fallo, pero no encontramos patrones ni conexiones obvias.
Mientras tanto, el equipo de SRE estaba trabajando en mejoras en la observabilidad de las solicitudes en Quay y en la salud general del servicio. Se desplegaron nuevas métricas y paneles de monitoreo, que mostraban qué partes de Quay son más demandadas por los clientes.
Quay.io funcionó normalmente hasta el 9 de junio. En la mañana (hora EDT) 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 un nuevo parámetro limitaba su cantidad y no permitía exceder la capacidad de MySQL. Sin embargo, durante aproximadamente media hora, muchos usuarios experimentaron una lentitud en el funcionamiento de quay.io. Rápidamente recopilamos todos los datos posibles utilizando las herramientas de monitoreo añadidas. De repente, apareció un patrón.
Justo antes del aumento en el número de conexiones, un gran número de solicitudes llegaron al API de App Registry. App Registry es una función poco conocida de quay.io. Permite almacenar cosas como charts de Helm y contenedores con metadatos ricos. La mayoría de los usuarios de quay.io no utilizan esta función, pero Red Hat OpenShift la utiliza activamente. OperatorHub en OpenShift almacena todos los operadores en App Registry. Estos operadores forman la base del ecosistema de cargas de trabajo de OpenShift y del modelo operativo (dentro de las operaciones del "segundo día", Day 2) orientado a partners.
Cada clúster de OpenShift 4 utiliza operadores del OperatorHub integrado para publicar el catálogo de operadores disponibles para la instalación y proporcionar actualizaciones para los ya instalados. A medida que aumentó la popularidad de OpenShift 4, también se incrementó el número de clústeres en todo el mundo. Cada uno de estos clústeres carga el contenido de los operadores para iniciar el OperatorHub integrado, utilizando App Registry dentro de quay.io como backend. En la búsqueda de la causa del problema, pasamos por alto que, con el crecimiento gradual de la popularidad de OpenShift, también aumentaba la carga sobre una de las funciones raramente usadas de quay.io..
Realizamos un análisis del tráfico de solicitudes de App Registry y revisamos el código del registro. Inmediatamente se revelaron deficiencias que hacían que las consultas a la base de datos se formaran de manera no óptima. Con baja carga, estas no presentaban problemas, pero a medida que aumentaba, se convertían en fuente de inconvenientes. App Registry tenía dos endpoints problemáticos que respondían mal al aumento de la carga: el primero devolvía la lista de todos los paquetes en el repositorio, el segundo entregaba todos los blobs para un paquete.
Eliminación de las causas
Durante la siguiente semana nos dedicamos a optimizar el código del propio App Registry y su entorno. Se reescribieron las consultas SQL ineficaces, se eliminaron llamadas innecesarias al comando tar (que se ejecutaba en cada extracción de blobs), se agregó caché donde fue posible. Luego se realizó una prueba de rendimiento a gran escala y se comparó la velocidad de App Registry antes y después de los cambios.
Las solicitudes de API que antes tomaban hasta medio minuto, ahora se ejecutaban en milisegundos.. La semana siguiente implementamos los cambios en producción, y desde entonces quay.io ha funcionado de manera estable. Durante este tiempo, hubo varios picos bruscos de tráfico en el endpoint de App Registry, pero las mejoras realizadas evitaron interrupciones en la base de datos.
¿Qué hemos aprendido?
Es evidente que cualquier servicio busca evitar tiempos de inactividad. En nuestro caso, creemos que las recientes fallas ayudaron a que quay.io fuera mejor. De esto hemos aprendido algunas lecciones clave que queremos compartir:
- Los datos sobre quién y cómo usa su servicio nunca son superfluos.Como Quay 'simplemente funcionaba', nunca tuvimos la necesidad de dedicar tiempo a optimizar el tráfico y gestionar la carga. Todo esto creó una falsa sensación de seguridad de que el servicio podría escalar indefinidamente.
- Cuando el servicio falla, la recuperación de su funcionamiento es la principal prioridad.. Debido a que Quay continuó sufriendo de una base de datos bloqueada durante la primera interrupción, nuestros procedimientos estándar no tuvieron el efecto esperado y no pudimos restaurar el servicio con su ayuda. Esto llevó a una situación en la que se necesitó tiempo para analizar y recopilar datos con la esperanza de encontrar la causa principal, en lugar de dirigir todos los esfuerzos a la recuperación de la operatividad.
- Evalúe el impacto de cada una de las funciones del servicio. Los clientes rara vez utilizaban App Registry, por lo que no era una prioridad para nuestro equipo. Cuando algunas funciones del producto se utilizan muy poco, los errores de estas aparecen raramente y los desarrolladores dejan de supervisar el código. Es fácil caer en el error de pensar que así debe ser, hasta que de repente esta función se encuentra en el centro de un incidente a gran escala.
¿Qué sigue?
El trabajo para garantizar la estabilidad del servicio nunca se detiene y estamos mejorándolo continuamente. Los volúmenes de tráfico en quay.io siguen creciendo y somos conscientes de que debemos hacer todo lo posible para cumplir con la confianza de nuestros clientes. Por lo tanto, actualmente estamos trabajando en las siguientes tareas:
- Despliegue de réplicas de bases de datos solo de lectura para ayudar al servicio a manejar el tráfico correspondiente en caso de problemas con la instancia principal de RDS.
- Actualización de la instancia de RDS. La versión actual por sí misma no es un problema. Más bien, solo queremos eliminar una pista falsa (que seguimos durante la interrupción); mantener el software actualizado ayudará a eliminar otro factor en caso de futuras desconexiones.
- Caching adicional en todo el clúster. Seguimos buscando áreas donde el caching pueda reducir la carga en la base de datos.
- Adición de un firewall de aplicaciones web (WAF) para ver quién y por qué se conecta a quay.io.
- A partir de la próxima versión, los clústeres de Red Hat OpenShift abandonarán App Registry en favor de Catálogos de Operadores (Operator Catalogs) basados en imágenes de contenedores disponibles en quay.io.
- La sustitución a largo plazo de App Registry podría ser el soporte para las especificaciones de artefactos de la Open Container Initiative (OCI). 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 las continuas inversiones de Red Hat en quay.io a medida que hacemos la transición de un pequeño equipo "de estilo startup" a una plataforma madura, gestionada por SRE. Sabemos que muchos de nuestros clientes dependen de quay.io en su trabajo diario (¡incluyendo a Red Hat!) y nos esforzamos por ser lo más transparentes posible acerca de las recientes interrupciones y los esfuerzos continuos para mejorar.
P.D. del traductor
También puedes leer en nuestro blog:
- «»;
- «»;
- «».
Fuente: habr.com
