Technique

Canonical, Elastic et Google s'associent pour éviter la corruption de données sous Linux

Chez Elastic, nous innovons et proposons en permanence de nouvelles fonctionnalités. À chaque lancement, nous nous assurons également que ces dernières sont testées, solides et fiables. Parfois, nous identifions des bugs ou d'autres problèmes.

Alors que nous testions une nouvelle fonctionnalité, nous avons identifié un bug dans un noyau Linux qui impacte les disques SSD sur certains noyaux Linux. Dans cet article, nous vous racontons la manière dont une investigation a été lancée et comment son succès a reposé sur une formidable collaboration entre deux partenaires de confiance, à savoir Google Cloud et Canonical.

Suite à cette investigation, de nouveaux noyaux Ubuntu ont été lancés afin de régler le problème.

L'événement déclencheur

En février 2019, nous avons mené un test de stabilité de 10 jours pour la réplication inter-clusters (CCR) à l'aide de l'outil standard d'évaluation comparative d'Elasticsearch, appelé Rally, en nous fondant sur une charge de travail d'ingestion impliquant de nombreuses mises à jour. La version d'Elasticsearch représente la dernière validation de l'unité 7.0, qui n'était pas encore commercialisée à l'époque. Au bout de quelques jours, le test a échoué comme l'atteste le reporting des logs d'Elasticsearch :

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

Lors de la détection d'un index corrompu[1], Elasticsearch tente de le récupérer à l'aide d'une réplique. La plupart du temps, cette récupération s'effectue sans problème et la corruption est indétectable, sauf si l'utilisateur consulte les logs. Or, malgré la redondance des répliques, il est possible de perdre des données dans certains cas. Par conséquent, nous voulions comprendre la provenance de cette corruption et la manière dont nous pouvions résoudre le problème.

Tout d'abord, nous avons pensé que le problème venait d'une nouvelle fonctionnalité d'Elasticsearch, la CCR ou les suppressions partielles. Toutefois, nous avons rapidement écarté cette possibilité, quand nous avons compris que nous pouvions reproduire la corruption de l'index à l'aide d'un seul cluster et en désactivant la fonctionnalité de CCR ou de suppressions partielles. Après avoir mené des recherches supplémentaires, nous avons compris que nous pouvions reproduire le problème dans des versions d'Elasticsearch antérieures au lancement de la version bêta de la CCR et des suppressions partielles. Comme le suggérait cette découverte, la CCR et les suppressions partielles n'étaient donc pas la cause de la corruption de l'index.

Cependant, nous avons noté que le test et les recherches ayant entraîné la corruption de l'index ont été menés sur Google Cloud Platform (GCP) à l'aide de l'image de Xenial d'Unbuntu par Canonical et de SSD locaux avec un noyau 4.15-0-*-gcp. Nous avons remarqué que nous pouvions reproduire le problème dans un environnement nu avec les mêmes disques SSD et le même système d'exploitation (qui utilisent des noyaux de compatibilité matérielle 4.13 ou 4.15).

Nous nous sommes alors intéressés à Elastic Cloud pour obtenir davantage de données sur le problème et son impact. Dans le cadre de tâches parallèles, nous avons écarté plusieurs causes suspectes en testant différents systèmes de fichiers, en désactivant RAID et, enfin, en utilisant un noyau à usage général plus récent. Aucune de ces actions n'a permis de résoudre le problème.

Des recherches plus précises à l'aide du support technique de Google Cloud

Nous avons créé un ensemble de scripts de reproduction simples afin de faciliter ce processus. Nous avons demandé à notre partenaire Google d'investiguer plus en détail les problèmes liés à l'environnement.

Le support technique de GCP a pu reproduire le problème de manière fiable à l'aide de ces scripts et d'un message d'erreur comme exemple :

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")))

Lorsque le problème est survenu, le débit d'entrées et de sorties a dépassé 160 Mo/s avec 16 000 entrées et sorties par seconde. Nous avons mené plusieurs tests et observé le même résultat à chaque fois. Étant donné qu'Elasticsearch utilise (par défaut) des fichiers à la mémoire mappée pour une partie de son stockage, nous avons pensé que certains accès de fichiers provoquaient des défaillances de pages encore plus importantes, ce qui augmentait le nombre d'opérations d'entrées et de sorties sur le disque et ainsi engendrait le problème. Pour diminuer l'apparition de la défaillance d'une page, nous avons essayé d'augmenter la mémoire des instances de GCP en la faisant passer de 32 Go à 52 Go. Grâce à cette mémoire accrue, le problème n'est pas réapparu et le débit des entrées et sorties s'élevait à seulement 50 Mo/s avec 4 000 entrées et sorties par seconde. 

Nous avons obtenu notre première avancée quand nous avons détecté une différence entre GCP et l'environnement nu. Le noyau de GCP était doté d'une fonctionnalité appelée couche de blocs multi-files d'attente (blk_mq) qui était activée, contrairement au noyau de l'environnement nu[2]. Pour compliquer encore les choses, après une certaine version[3], il est impossible de désactiver blk_mq[4] sur l'image -gcp Ubuntu Linux via les options du noyau. Le support technique de GCP nous a montré comment désactiver blk_mq en exportant l'image Ubuntu de GCP et en la recréant sans la fonctionnalité guestOS VIRTIO_SCSI_MULTIQUEUE, qui active l'interface SCSI multi-files d'attente[5].

Nous avons obtenu une seconde avancée quand nous avons réussi à reproduire la corruption dans l'environnement nu seulement en activant explicitement la fonctionnalité blk_mq même en utilisant un noyau plus ancien comme 4.13.0-38-generic. En outre, nous avons vérifié que les disques NVMe n'affichaient pas ce problème.

Nous savions donc que les corruptions survenaient lorsque les deux conditions suivantes étaient réunies :

  • Les lecteurs SSD utilisent l'interface SCSI. (Les disques NVMe ne sont pas touchés.)
  • La fonctionnalité blk_mq est activée.

Outre l'utilisation exclusive de disques NVMe, le support technique de GCP a fait part de deux solutions temporaires supplémentaires, à savoir augmenter la mémoire des instances ou créer des images d'instances personnalisées en activant l'interface SCSI multi-files d'attente.

Une aide bienvenue de la part de Canonical

Bien qu'utiles, les solutions temporaires ne nous satisfaisaient pas :

  • Le problème ne touchait pas que les images Ubuntu sur GCP. Il survenait aussi dans l'environnement nu.
  • Nous ignorions quelle validation de noyau a engendré le problème.
  • Nous ne savions pas si un nouveau noyau contenait déjà un correctif.

Pour résoudre tous ces points encore en suspens, nous nous sommes tournés vers notre partenaire Canonical afin de mener une investigation plus poussée.

Canonical a lancé un test de grande ampleur en utilisant les scripts de reproduction d'Elastic, tout d'abord en confirmant que la corruption ne touchait pas les noyaux Ubuntu à usage général >=5.0 utilisant des lecteurs SSD (avec soit des planificateurs d'entrées et sorties multi-files d'attente mq-deadline, soit aucun planificateur).

La prochaine étape a consisté à passer en revue les anciennes versions du noyau afin de trouver le delta minimum entre un noyau présentant la corruption et un autre non touché. À l'aide de plusieurs environnements de tests utilisés en parallèle (étant donné qu'un seul test peut prendre jusqu'à 5 jours), Canonical a déterminé que 4.19.8 est le premier noyau Ubuntu à usage général à inclure les correctifs pour la corruption[6].

Les rétroportages manquants pour le noyau 4.15.0 et les dérivés sont décrits dans le dispositif de suivi des bugs de Canonical sous la référence LP#1848739. Pour en savoir plus à ce sujet, lisez cet article et consultez cette page du site kernel.org.

Après avoir confirmé qu'un noyau GCP corrigé, comprenant tous les rétroportages requis, règle le problème, Elastic et Canonical les ont fusionnés dans le principal noyau 4.15.0 Ubuntu. Par conséquent, tous les noyaux dérivés (y compris les noyaux -gcp) ont intégré les correctifs.

Conclusion

Elastic s'est engagé à développer de nouvelles fonctionnalités de la Suite Elastic qui améliorent nos trois principales solutions. Pour atteindre cet objectif, plusieurs de nos talentueux ingénieurs et partenaires sont sur le pont en permanence. Par conséquent, dès la détection éventuelle de problèmes lors d'un test, Elastic et son réseau de partenaires proches mettront tout en œuvre pour vous garantir la meilleure expérience possible.

Grâce à notre collaboration étroite avec Google et Canonical, nous avons pu enquêter en profondeur sur le problème et proposer les noyaux Ubuntu HWE corrigés suivants :

Grâce aux noyaux ci-dessus ou à des versions plus récentes, vous éviterez toute corruption quand vous utiliserez des disques SSD et activerez la fonctionnalité blk_mq sur l'interface SCSI.

Vous vous demandez comment protéger votre environnement contre cette corruption de données ? Notre conseil : essayez Elastic Cloud. Nos utilisateurs sont déjà protégés.

Notes

[1] Elasticsearch ne vérifie pas en permanence la somme de contrôle, car cette opération est coûteuse. Certaines actions peuvent entraîner une vérification de la somme de contrôle plus régulière, comme la relocalisation des partitions ou lors de la prise d'un snapshot. Ainsi, les corruptions silencieuses sous-jacentes sont identifiées, même si ces actions les génèrent.

[2] Pour s'assurer de l'activation de la fonctionnalité blk_mq sur les disques de mappage des périphériques ou SCSI, utilisez cat /sys/module/{scsi_mod,dm_mod}/parameters/use_blk_mq.

[3] Après https://patchwork.kernel.org/patch/10198305, la fonctionnalité blk_mq est activée pour les périphériques SCSI et ne peut pas être désactivée via les options du noyau. Ce correctif a été rétroporté sur le noyau linux-gcp d'Ubuntu.

[4] Pour désactiver la fonctionnalité blk_mq, les paramètres suivants doivent être configurés dans le noyau, par exemple à l'aide de grub : GRUB_CMDLINE_LINUX="scsi_mod.use_blk_mq=N dm_mod.use_blk_mq=N". Pour l'activer, configurez ces options sur N en gardant à l'esprit la note [3].

[5] Voici des exemples de commandes gcloud permettant de désactiver la fonctionnalité guestOS VIRTIO_SCSI_MULTIQUEUE en recréant l'image 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] Rétroportages

    - 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