Solución de problemas de Linux: navegar por una tormenta perfecta

Uno de los problemas más difíciles en DevOps es con aquellos que bloquean activamente su propia investigación. En la contienda por el título de liga, el segundo peor tipo de problema es el que ocurre de forma intermitente. Este artículo es una historia de aventuras en un momento en que el sistema de integración continua / implementación continua (CI / CD) ascendente de Podman se encontró con ambos simultáneamente.

Índice

    una tormenta perfecta

    Había una vez, Podman La automatización de las pruebas comenzó a hacerse demasiado grande para sus pantalones de "niño grande". Esto sucedió hace años cuando prácticamente todos los sistemas de CI/CD estaban basados ​​en contenedores. Podman, al ser una herramienta de administración (y depuración) de contenedores (y pods), no se puede ejercer completamente en un contenedor. Quizás peor, la mayoría de los servicios de automatización de productos básicos solo admitían Ubuntu. Rápidamente se convirtió en un fracaso absoluto, ya que Podman tenía que ejecutarse en máquinas virtuales (VM). También se esperaba que funcionara en varias distribuciones, incluida la distribución ascendente de Red Hat, Fedora.

    Al tener experiencia con flujos de trabajo de CI/CD injertados bajo una configuración cloud-sdk + SSH (y/o Ansible) para el acceso a la nube, la mayor complejidad siempre parece ser un problema. Entonces un día me encontré Cirrus-CI. Cirrus-CI es una herramienta de automatización centrada en Git capaz de orquestar contenedores y máquinas virtuales, utilizando una gran cantidad de proveedores de nube. Permite que el equipo de Podman pague y administre el servicio en la nube independientemente de su orquestación. Pudimos mantener el control total sobre las máquinas virtuales y el registro de datos, y Cirrus-CI solo se encargó de la orquestación.

    El flujo de trabajo general se ve así:

    1. Un desarrollador envía los cambios de código propuestos aguas arriba en Repositorio Git de Podman.
    2. Cirrus-CI avisa, lee un archivo de configuración y luego lanza las máquinas virtuales necesarias en nuestra nube.
    3. Los servicios de metadatos nativos de la nube manejan la ejecución de scripts y la reconexión a Cirrus-CI.
    4. Cirrus-CI elimina las máquinas virtuales y proporciona comentarios de aprobación/rechazo al desarrollador.
    5. Se realizan cambios y el ciclo se repite hasta que se acepta el código.

    Durante años, este flujo de trabajo ha funcionado casi sin problemas, con la mayoría de los problemas centrados en las pruebas y la creación de scripts, fallas que el equipo de Podman soluciona fácilmente. En raras ocasiones, los problemas dentro de Cirrus-CI, Internet y/o nuestro proveedor de la nube conducen a la huérfana (error de eliminación) de las máquinas virtuales. De lo contrario, el personal de soporte de Cirrus Labs ha sido fantástico, muy accesible, con una capacidad de respuesta y responsabilidad de primer nivel.

    Luego, un día de octubre de 2020, después de rotar un montón de imágenes de VM recién actualizadas (es decir, la imagen de disco copiada para cada nueva instancia de VM), las tareas aparentemente aleatorias comenzaron a fallar. La investigación inicial sobre el lanzamiento del guión no proporcionó ninguna información. Literalmente, todas las salidas se detendrían repentinamente, sin un patrón discernible de otras fallas. Como era de esperar, Cirrus-CI limpiaría diligentemente la VM y presentaría la falla resultante al desarrollador para su descubrimiento. Muchas veces, cuando se vuelve a ejecutar el trabajo fallido, se realiza correctamente sin incidentes.

    Esta situación se prolongó durante varias semanas sin ningún problema relacionado con las interrupciones de la infraestructura en nuestra nube, GitHub o Cirrus. El problema era algo raro, tal vez un puñado de fallas por día, de cientos de trabajos exitosos. La solución de problemas era difícil y la repetición perpetua de tareas no podía ser una solución a largo plazo. Aparte de los informes de incidentes regulares de mi equipo, no pude discernir ningún patrón de fallas de alto nivel. Dado que las nuevas imágenes de VM ofrecían beneficios significativos, el costo de la restauración también habría sido alto.

    Para casi cualquier problema sistémico como este, encontrar patrones de comportamiento es una parte clave de la solución de problemas exitosa. Dado que el éxito profesional confiable es el estado deseado, tener al menos una noción o pista era un requisito estricto. Desafortunadamente, en este caso, nuestra capacidad para observar patrones se vio severamente limitada por las fallas aleatorias y la característica altamente deseable: limpiar las máquinas virtuales en la nube en desuso, que desperdician dinero real.

    La realización de pruebas manuales simples y repetidas no reprodujo el problema en absoluto. Agregar más recursos de CPU y memoria tampoco afectó el comportamiento. Pasé días haciendo lluvia de ideas y pensando en opciones para recopilar datos adicionales asociados con el ajedrez. Finalmente, me pareció que necesitaba una forma de detener selectivamente la limpieza de la máquina virtual, pero solo en los casos en que las pruebas fallaron.

    En otras palabras, tenía que asociar de alguna manera la aprobación de la prueba (no solo el aprobado/reprobado) con el permiso para limpiar. Fue entonces cuando recordé una pequeña casilla de verificación que vi una vez mientras buscaba en la WebUI de nuestra nube: Protección contra la eliminación. Cuando se establece este indicador, Cirrus-CI se quejará fuertemente porque está atascado para eliminar una máquina virtual, pero de lo contrario no se verá afectado.

    Necesitaba instrumentar nuestro flujo de trabajo para que las propias máquinas virtuales pudieran configurar y deshabilitar su propio indicador de protección contra eliminación. Alguna cosa como esta:

    1. La VM activa la protección contra el borrado en sí misma.
    2. Ejecutar pruebas.
    3. VM desactiva su propia protección contra la eliminación.
    4. Cirrus-CI limpia o no limpia la máquina virtual y la convierte en un mal olor.

    De esa manera, una vez que se complete la prueba, Cirrus-CI felizmente eliminará la máquina virtual como de costumbre. Sin embargo, si ocurriera el problema, las pruebas no se completarían y Cirrus-CI encontraría la protección de eliminación (todavía) habilitada. Afortunadamente, este flujo de trabajo fue completamente posible de lograr a través de opciones de línea de comandos simples del programa de utilidad de administración de la nube que pude instalar en las máquinas virtuales.

    Con esta instrumentación de flujo de trabajo en su lugar, todo lo que tenía que hacer era activar repetidamente la matriz de prueba y esperar a que aparecieran las máquinas virtuales huérfanas. El destino me estaba sonriendo ese día cuando el problema se repitió casi al instante. Sin embargo, lo ejecuté varias veces, por lo que solo tendría algunas máquinas virtuales huérfanas para inspeccionar.

    Inesperadamente, fue entonces cuando la situación se volvió aún más interesante: no podía usar SSH en máquinas virtuales huérfanas. De hecho, ni siquiera responderían a los pings provenientes de dentro o fuera de nuestra nube. Realicé un restablecimiento completo en una máquina virtual y verifiqué los registros del sistema después de que comenzó. No había nada. Cremallera. Nada. Ni un solo ápice de pista aparte de lo que ya sabíamos: las pruebas simplemente dejaron de ejecutarse, muertas, junto con el resto del sistema.

    Como ya era mi día de suerte, decidí dar un paso más y una vez más fui a profundizar en mi interfaz de usuario web en la nube. Finalmente, encontré otra pequeña configuración increíblemente útil: Registro de salida de la consola serie. Era esencialmente una línea de comunicación directa de bajo nivel directamente al kernel. Si sucediera algo lo suficientemente horrible como para que el sistema colapsara por completo, el kernel seguramente gritaría desde su puerto serie virtual. ¡Bingo, Yahtzee y huzzah!

    [ 1203.905090] BUG: kernel NULL pointer dereference, address: 0000000000000000
    [ 1203.912275] #PF: supervisor read access in kernel mode
    [ 1203.917588] #PF: error_code(0x0000) - not-present page
    [ 1203.922881] PGD 8000000124e2d067 P4D 8000000124e2d067 PUD 138d5e067 PMD 0
    [ 1203.929939] Oops: 0000 [#1] SMP PTI
    ...blah...blah...blah
    [ 1204.052766] Call Trace:
    [ 1204.055440]  bfq_idle_extract+0x52/0xb0
    [ 1204.059468]  bfq_put_idle_entity+0x12/0x60
    [ 1204.063722]  bfq_bfqq_served+0xb0/0x190
    [ 1204.067718]  bfq_dispatch_request+0x2c2/0x1070

    ¡Bueno, hola, mi viejo amigo! ¡Realmente fue un día de suerte!

    Era un kernel panic que había visto un año antes y trabajé incansablemente durante meses con la ingeniería del kernel anterior para solucionarlo. Este fue un error en el subsistema de almacenamiento del kernel responsable de mejorar la eficiencia y garantizar que los preciosos 0 y 1 se escriban en el disco. En este caso, en lugar de corromper el almacenamiento, el núcleo lanzó una bandera blanca y detuvo todo en seco.

    Habiendo trabajado en prácticamente el mismo problema antes, ya estaba familiarizado con la solución de una línea. No hubo necesidad de arrastrar a mi equipo a través de meses de depuración nuevamente. Simplemente podría informar la recurrencia e implementar con confianza la solución alternativa sabiendo que esto resolvería el problema al 100 %:

    echo mq-deadline > /sys/block/sda/queue/scheduler

    Ahora, no recomiendo ejecutar este comando de cualquier manera en cada sistema Linux que posea. En este caso específico, sabía por experiencia que era bastante seguro intercambiar el algoritmo de almacenamiento en búfer (también conocido como Elevator I/O). Ningún desarrollador de Podman notaría el cambio en los puntajes de las pruebas.

    Conclusión

    Si usted o alguien a quien ama está teniendo pánico del kernel, le recomiendo que consulte este artículo reciente sobre el tema. De lo contrario, el punto principal a recordar es este: al solucionar los problemas de la persiana, es absolutamente vital resolver los aspectos oscuros de las causas. En segundo lugar, trabaje con los datos para hacer que el problema sea más reproducible. Le resultará difícil hacer lo segundo sin la ayuda de lo primero.

    Artículos de interés

    Subir