Ingeniería

Canonical, Elastic y Google se unen para evitar el daño de datos en Linux

En Elastic innovamos y lanzamos características nuevas de forma constante. Mientras lanzamos características nuevas, también trabajamos para asegurarnos de que se hayan probado y sean sólidas y confiables; y a veces encontramos errores u otros problemas.

Durante la prueba de una característica nueva descubrimos un error de kernel de Linux que afectaba los discos SSD en ciertos kernels de Linux. En este blog abarcaremos la historia en torno a la investigación y cómo involucró una gran colaboración con dos socios cercanos: Google Cloud y Canonical.

La investigación resultó en el lanzamiento de nuevos kernels de Ubuntu para abordar el problema.

Cómo comenzó

En febrero de 2019, ejecutamos una prueba de estabilidad de 10 días para la replicación entre clusters (CCR) con la herramienta de evaluación comparativa de Elasticsearch, Rally, usando una carga de trabajo de ingesta y con muchas actualizaciones. La versión de Elasticsearch era la publicada más recientemente de la rama 7.0 pendiente de lanzamiento (en ese entonces). Tras varios días, la prueba falló y en los logs de Elasticsearch se reportaba lo siguiente:

… 
Caused by: org.apache.lucene.index.CorruptIndexException: checksum failed (hardware problem?)

Al detectar[1] el daño del índice, Elasticsearch intentará recuperarse a partir de una réplica. En muchos casos la recuperación no presenta problemas y, a menos que el usuario revise los logs, el daño pasará desapercibido. Incluso con la redundancia que ofrecen las réplicas, es posible que se produzca la pérdida de datos en ciertas circunstancias. Por lo tanto, queríamos comprender por qué se producía el daño y explorar cómo podíamos remediar el problema.

Al principio, sospechábamos que una característica nueva de Elasticsearch, CCR/eliminaciones parciales, causaba el problema. Descartamos rápidamente esta sospecha, sin embargo, cuando descubrimos que podíamos reproducir el daño en el índice usando un solo cluster y deshabilitando la característica de CCR/eliminaciones parciales. Después de más investigación, descubrimos que podíamos reproducir el problema en versiones anteriores de Elasticsearch previas al lanzamiento beta de CCR/eliminaciones parciales, lo que sugería que las CCR/eliminaciones parciales no eran la causa del daño en el índice.

Sin embargo, sí notamos que las pruebas e investigación que llevaron al daño en el índice se habían realizado en Google Cloud Platform (GCP) usando la imagen Ubuntu Xenial de Canonical y SSD locales con el kernel 4.15-0-*-gcp y que no podíamos reproducir el problema en un entorno de metal expuesto con el mismo sistema operativo y discos SSD (ya sea con kernels HWE 4.13 o 4.15).

Nuestra atención se centró inmediatamente en Elastic Cloud para obtener más datos sobre el problema y su impacto. En un flujo de trabajo paralelo, descartamos varias sospechas probando diferentes sistemas de archivos, deshabilitando RAID y usando por último un kernel principal más nuevo. Pero nada de esto hizo desaparecer los daños.

Profundizando con el soporte de Google Cloud

En este punto, creamos un conjunto de scripts de fácil reproducción para facilitar las reproducciones e involucramos a nuestro socio Google para comenzar a investigar en mayor profundidad los problemas del entorno.

El soporte de GCP logró reproducir el problema de forma confiable usando los scripts proporcionados, con un mensaje de error de ejemplo:

org.apache.lucene.index.CorruptIndexException: codec footer mismatch (file 
truncated?): actual footer=-2016340526 vs expected footer=-1071082520 
(resource=BufferedChecksumIndexInput(MMapIndexInput(path="/home/dl/es/data/nodes/0/indices/IF1vmFH6RY-MZuNfx2IO4w/0/index/_iya_1e_Lucene80_0.dvd")))

Durante el período en el que ocurrió el problema, el rendimiento de E/S superó los 160 MB/s con 16 000 IOPS. Esta observación fue coherente en varias pruebas. Como Elasticsearch (de forma predeterminada) usa archivos mapeados en memoria en parte de su almacenamiento, sospechábamos que algunos accesos a los archivos causaban fallas de la página más importantes, lo que aumentaba las operaciones de E/S en el disco y desencadenaba el problema en última instancia. Para reducir la ocurrencia de una falla de la página, intentamos aumentar la memoria de las instancias de GCP de 32 GB a 52 GB. Con el aumento de la memoria, el problema ya no ocurría y el rendimiento de E/S era de 50 MB/s con 4000 IOPS. 

El primer descubrimiento sucedió cuando observamos una diferencia entre GCP y el entorno de metal expuesto: el kernel de GCP tenía una característica denominada capa de bloqueo multicolas (blk_mq) habilitada, que el kernel de metal expuesto no[2]. Para complicar aún más las cosas, después de una versión determinada[3] ya no es posible deshabilitar blk_mq[4] en la imagen de Ubuntu Linux -gcp a través de las opciones de kernel. El soporte de GCP nos mostró cómo deshabilitar blk_mq exportando la imagen de Ubuntu de GCP y recreándola sin la característica VIRTIO_SCSI_MULTIQUEUE para SO invitado, que habilita SCSI multicola[5].

El segundo descubrimiento fue lograr reproducir el daño en metal expuesto: fue posible únicamente habilitando explícitamente blk_mq incluso usando un kernel más antiguo: 4.13.0-38-generic. También verificamos que los discos NVMe no muestran este problema.

En este punto sabíamos que los daños ocurren cuando se cumplen las dos condiciones siguientes:

  • Unidades SSD con interfaz SCSI (los discos NVMe no se ven afectados)
  • blk_mq habilitado

El soporte de GCP compartió dos soluciones alternativas adicionales (además de usar solo discos NVMe): aumentar la memoria de la instancia o crear imágenes de la instancia personalizadas con la opción SCSI multicola deshabilitada.

Canonical se une al esfuerzo

Si bien teníamos algunas soluciones alternativas, aún no estábamos satisfechos:

  • El problema no era específico de las imágenes de Ubuntu en GCP, también sucedía en metal expuesto.
  • No sabíamos cuál publicación de kernel introdujo el problema.
  • No sabíamos si ya había una solución disponible en un kernel más nuevo.

Para abordar estos puntos, recurrimos a nuestro socio Canonical para que profundizara un poco más.

Canonical inició un gran esfuerzo de pruebas usando scripts de reproducción de Elastic, primero confirmó que el daño no ocurría en kernels principales de Ubuntu >=5.0 con unidades SSD (sin usar programadores de E/S multicola o usando los mq-deadline).

El paso siguiente fue retroceder en las versiones de kernel para encontrar el delta mínimo entre un kernel que mostraba daño y uno que no. En entornos de pruebas en paralelo múltiples (dado que una ejecución de prueba completa puede tomar hasta cinco días), Canonical descubrió que el kernel principal de Ubuntu 4.19.8 es el primero que incluye soluciones para el daño[6].

Los backports faltantes del kernel 4.15.0 y derivados se describen en el rastreador de errores de Canonical, en LP#1848739, y se incluyen más detalles en este artículo y en el error de kernel.org.

Después de que Elastic y Canonical confirmaran que un kernel de GCP con parche y todos los backports necesarios soluciona el problema, se combinaron en el kernel principal de Ubuntu 4.15.0 y todos los kernels derivados (incluido -gcp) recibieron las soluciones.

Conclusión

Elastic está comprometido con desarrollar características del Elastic Stack nuevas que mejoren cada una de nuestras tres soluciones primarias. Estos esfuerzos cuentan con el soporte de algunos socios e ingenieros muy talentosos que siempre están atentos para que no debas preocuparte. Cuando encontramos problemas (si lo hacemos) durante las pruebas, debes saber que Elastic y su red de socios cercanos no descansarán hasta asegurarse de que tengas la mejor experiencia posible.

Gracias a nuestra estrecha colaboración con Google y Canonical, pudimos llegar a la raíz del problema, lo que llevó al lanzamiento de los siguientes kernels de HWE Ubuntu solucionados:

El uso de las versiones mencionadas anteriormente o más nuevas evitará daños cuando se usen discos SSD junto con la opción SCSI blk-mq habilitada.

Si no quieres preocuparte por si tu entorno está protegido de los daños a los datos, prueba nuestro Elastic Cloud; nuestros usuarios ya están protegidos.

Notas al pie

[1] Elasticsearch no verifica las sumas de comprobación todo el tiempo debido a que es una operación costosa. Ciertas acciones pueden desencadenar la verificación de sumas de comprobación con más frecuencia, como la reubicación de shards o mientras se realiza un snapshot, lo que hará que los daños silenciosos subyacentes aparezcan si fueron causados por estas acciones.

[2] Para comprobar si se usa blk_mq para SCSI o discos de mapeo de dispositivos, usa cat /sys/module/{scsi_mod,dm_mod}/parameters/use_blk_mq.

[3] Después de que se haya forzado https://patchwork.kernel.org/patch/10198305 blk_mq en dispositivos SCSI y no se pueda deshabilitar a través de las opciones del kernel. Este parche se aplicó en backport en Ubuntu linux-gcp.

[4] Para deshabilitar blk_mq, se deben pasar los parámetros siguientes al kernel, por ejemplo, a través de grub: GRUB_CMDLINE_LINUX="scsi_mod.use_blk_mq=N dm_mod.use_blk_mq=N". La habilitación se puede hacer configurando estas opciones como N, pero ten cuidado con [3].

[5] Comandos de ejemplo de gcloud para deshabilitar la característica VIRTIO_SCSI_MULTIQUEUE para SO invitado recreando la imagen de Ubuntu:

# gcloud compute images export --image-project ubuntu-os-cloud --image-family ubuntu-1604-lts --destination-uri=gs://YOUR_BUCKET/ubuntu_16.04.tar.gz 
# gcloud compute images create ubuntu-1604-test --family ubuntu-1604-lts --source-uri=gs://YOUR_BUCKET/ubuntu_16.04.tar.gz

[6] Backports

    - blk-mq: quiesce queue during switching io sched and updating nr_requests 
    - blk-mq: move hctx lock/unlock into a helper 
    - blk-mq: factor out a few helpers from __blk_mq_try_issue_directly 
    - blk-mq: improve DM's blk-mq IO merging via blk_insert_cloned_request feedback 
    - dm mpath: fix missing call of path selector type->end_io 
    - blk-mq-sched: remove unused 'can_block' arg from blk_mq_sched_insert_request 
    - blk-mq: don't dispatch request in blk_mq_request_direct_issue if queue is busy 
    - blk-mq: introduce BLK_STS_DEV_RESOURCE 
    - blk-mq: Rename blk_mq_request_direct_issue() into 
      blk_mq_request_issue_directly() 
    - blk-mq: don't queue more if we get a busy return 
    - blk-mq: dequeue request one by one from sw queue if hctx is busy 
    - blk-mq: issue directly if hw queue isn't busy in case of 'none' 
    - blk-mq: fix corruption with direct issue 
    - blk-mq: fail the request in case issue failure 
    - blk-mq: punt failed direct issue to dispatch list