Canonical, Elastic und Google arbeiten eng zusammen, um Datenkorruption in Linux zu verhindern

Wir bei Elastic arbeiten ständig an Innovationen und neuen Features. Bevor diese neuen Features veröffentlicht werden, testen wir sie, um sicherzustellen, dass sie so funktionieren, wie sie sollen, und verlässlich sind. Dabei stoßen wir mitunter auf Bugs oder andere Probleme.

Bei Tests für ein neues Feature haben wir einen Linux-Kernel-Bug mit negativen Auswirkungen auf SSDs auf bestimmten Linux-Kerneln gefunden. In diesem Blogartikel beschreiben wir, wie wir bei der Untersuchung des Bugs vorgegangen sind und wie wir dabei erfolgreich mit zwei engen Partnern zusammengearbeitet haben: Google Cloud und Canonical.

Unsere Untersuchung hat zur Veröffentlichung neuer Ubuntu-Kernel geführt, mit denen das Problem behoben wurde.

Vorgeschichte

Im Februar 2019 führten wir einen 10-tägigen Stabilitätstest für die clusterübergreifende Replikation (Cross-Cluster Replication – CCR) durch. Getestet wurde ein Ingestionsaufkommen mit hohem Aktualisierungsanteil und als Tool kam Rally, das Standard-Benchmarking-Tool von Elasticsearch, zum Einsatz. Für den Test wurde das neueste Elasticsearch-Commit aus dem (damals) noch nicht veröffentlichten 7.0-Branch verwendet. Nach ein paar Tagen schlug der Test fehl und in den Elasticsearch-Logs war Folgendes zu lesen:

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

Nach der Entdeckung[1] einer Indexkorruption versucht Elasticsearch, den Index anhand eines Replikats wiederherzustellen. Diese Wiederherstellung läuft in vielen Fällen problemlos und die Datenkorruption bleibt vom Nutzer letztlich unbemerkt, sofern sich niemand die Logdaten ansieht. Unter bestimmten Umständen kann es jedoch trotz der durch Replikate sichergestellten Redundanz zu Datenverlust kommen. Wir wollten daher der Ursache für die Datenkorruption auf den Grund gehen und herausfinden, wie sich das Problem lösen lässt.

Zunächst verdächtigten wir ein neues Elasticsearch-Feature, CCR/Soft Deletes, als Verursacher. Dieser Verdacht löste sich jedoch schnell in Luft auf, denn wir stellten fest, dass wir die Indexkorruption auch mit nur einem Cluster und deaktiviertem „CCR/Soft Deletes“-Feature reproduzieren konnten. Im Zuge unserer weiteren Recherchen konnten wir das Problem auch in Elasticsearch-Versionen nachstellen, die älter waren als die Betaversion des „CCR/Soft Deletes“-Features. Dieses Feature war also unschuldig.

Wir recherchierten weiter und stießen darauf, dass sich die Indexkorruption zwar auf der Google Cloud Platform (GCP) mit dem Ubuntu-Xenial-Image von Canonical und lokalen SSDs mit dem 4.15‑0‑*‑gcp-Kernel, nicht aber in einer Bare-Metal-Umgebung mit demselben Betriebssystem und SSDs mit 4.13- oder 4.15-HWE-Kerneln reproduzieren ließ.

Das lenkte unsere Aufmerksamkeit sofort auf Elastic Cloud, von wo wir uns mehr Daten über das Problem und seine Auswirkungen erhofften. Parallel dazu konnten wir durch Maßnahmen wie Tests mit unterschiedlichen Dateisystemen, die Deaktivierung von RAID und schließlich auch die Verwendung eines neueren Mainline-Kernels eine Reihe von weiteren Verdächtigen ausschließen – keine dieser Maßnahmen brachte eine Besserung.

Weiteres Nachforschen mithilfe des Google Cloud-Supports

Zur Vereinfachung der Problemreproduktion erstellten wir ein paar einfache Reproduktionsskripte und wandten uns an unseren Partner Google, damit dieser sich eingehender mit etwaigen Problemen in seiner Umgebung beschäftigen konnte.

Der GCP-Support konnte anhand der von uns bereitgestellten Skripte das Problem zuverlässig reproduzieren. Bei Google sahen die Fehlermeldungen zum Beispiel wie folgt aus:

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

Innerhalb des Zeitraums, in dem das Problem auftrat, stieg der E/A-Durchsatz über 160 MB/s bei einem IOPS-Wert von 16.000. Das Ergebnis blieb auch nach mehreren Tests dasselbe. Da Elasticsearch (standardmäßig) für einen Teil des Datenspeichers speichereingeblendete Dateien verwendet, vermuteten wir, dass einige Dateizugriffe zu mehr großen Seitenfehlern führten und damit einen Anstieg bei den E/A-Operationen für den Datenträger bewirkten, wodurch dann schließlich das Problem ausgelöst wurde. Um das Auftreten von Seitenfehlern zu reduzieren, versuchten wir es mit einer Vergrößerung des Arbeitsspeichers der GCP-Instanzen von 32 GB auf 52 GB. Nach dieser Arbeitsspeichervergrößerung trat das Problem nicht mehr auf und der E/A-Durchsatz erfolgte mit 50 MB/s bei 4.000 IOPS. 

Der erste Durchbruch war unsere Beobachtung, dass es zwischen GCP und der Bare-Metal-Umgebung einen wichtigen Unterschied gab: Auf dem GCP-Kernel war ein Feature namens Multi-Queue Block Layer (blk_mq) aktiviert, das es auf dem Bare-Metal-Kernel nicht gab[2]. Allerdings ist es so, dass es beim ‑gcp-Image von Ubuntu Linux nach einer bestimmten Version[3] nicht mehr möglich ist, blk_mq[4] über die Kerneloptionen zu deaktivieren. Der GCP-Support zeigte uns, wie wir blk_mq deaktivieren können, indem wir das Ubuntu-Image von GCP exportieren und es ohne das VIRTIO_SCSI_MULTIQUEUE-guestOS-Feature, das Multi-Queue-SCSI[5] ermöglicht, neu erstellen.

Der zweite Durchbruch kam, als wir es schafften, die Indexkorruption in der Bare-Metal-Umgebung zu reproduzieren. Dazu mussten wir blk_mq explizit aktivieren, wobei dies auch auf einem älteren Kernel gelang: 4.13.0-38-generic. Wir konnten auch bestätigen, dass das Problem auf NVMe-SSDs nicht auftritt.

Damit wussten wir, dass es zwei Bedingungen gab, die zu den Datenkorruptionen führten, und dass beide gleichzeitig vorliegen mussten:

  • Es werden SSDs mit SCSI-Schnittstelle verwendet (NVMe-SSDs waren nicht betroffen).
  • blk_mq ist aktiviert.

Der GCP-Support steuerte zwei weitere Workarounds bei (zusätzlich zur ausschließlichen Verwendung von NVMe-SSDs): mehr Arbeitsspeicher für die Instanzen oder Erstellen individueller Instanz-Images mit deaktiviertem Multi-Queue-SCSI-Feature.

Einbeziehung von Canonical

Wir kannten zwar nun ein paar Workarounds, aber das stellte uns noch nicht zufrieden:

  • Das Problem war nicht nur auf Ubuntu-Images auf GCP beschränkt, sondern es trat auch auf Bare Metal auf.
  • Wir wussten nicht, welcher Kernel-Commit für das Problem verantwortlich war.
  • Wir wussten nicht, ob das Problem bei einem neueren Kernel vielleicht schon gelöst wurde.

Dies alles war Anlass für uns, uns mit unserem Partner Canonical zusammenzusetzen, um weiter nachzuforschen.

Canonical begann daraufhin mit umfangreichen Tests anhand der von Elastic bereitgestellten Reproduktionsskripte. Dabei wurde festgestellt, dass das Problem bei Ubuntu-Mainline-Kerneln ≥ 5.0 und der Verwendung von SSDs (sowohl ohne als auch mit mq-deadline-Multi-Queue-I/O-Schedulern) nicht auftrat.

Als Nächstes wurden frühere Kernel-Versionen unter die Lupe genommen, um das Mindest-Delta zwischen einem Kernel zu finden, bei dem die Indexkorruption auftrat, und einem Kernel, bei dem das nicht der Fall war. Durch Nutzung mehrerer paralleler Testumgebungen (ein ganzer Testdurchlauf kann schließlich bis zu fünf Tage dauern) stellte Canonical fest, dass der erste Ubuntu-Mainline-Kernel, bei dem das Problem behoben worden ist[6], der Kernel mit der Nummer 4.19.8 war.

Die fehlenden Backports für den Kernel 4.15.0 und dessen Derivate werden im Canonical-Bug-Tracker unter LP#1848739 beschrieben. Weitere Details können diesem Artikel und dem kernel.org-Bug entnommen werden.

Nachdem Elastic und Canonical bestätigt hatten, dass ein GCP-Kernel-Patch mit allen notwendigen Backports das Problem beseitigt, wurde alles im Ubuntu-Mainline-Kernel 4.15.0 zusammengeführt und die Kernel-Derivate (einschließlich ‑gcp) wurden ebenfalls aktualisiert.

Fazit

Elastic arbeitet ständig daran, neue Features für den Elastic Stack zu entwickeln, um jede unserer drei Hauptlösungen weiter zu verbessern. Dabei werden wir von wirklich talentierten Entwicklern und Partnern unterstützt, die stets wachsam sind, damit Sie Ihre Arbeit in Ruhe erledigen können. Sollten wir bei unseren Tests einmal auf ein Problem stoßen, können Sie sicher sein, dass sowohl wir bei Elastic als auch die Partner, mit denen wir eng zusammenarbeiten, alles tun, um Ihnen ein bestmögliches Nutzungserlebnis zu bieten.

Unsere enge Zusammenarbeit mit Google und Canonical ermöglichte es uns, dem Problem auf den Grund zu gehen, was schließlich zur Veröffentlichung der folgenden korrigierten HWE-Ubuntu-Kernel führte:

  • AWS: ab linux-aws - 4.15.0-1061.65, veröffentlicht am 21.02.2020
  • Azure: ab linux-azure - 4.15.0-1066.71, veröffentlicht am 06.01.2020
  • GCP: ab linux-gcp - 4.15.0-1053.57, veröffentlicht am 05.02.2020
  • Nicht plattformgebunden: ab linux - 4.15.0-88.88, veröffentlicht am 17.02.2020

Mit den oben genannten oder neueren Versionen kommt es nicht mehr zu Indexkorruptionen, wenn SSDs zusammen mit dem aktivierten SCSI-Feature blk-mq genutzt werden.

Falls Sie sich keine Gedanken machen möchten, ob Ihre Umgebung vor diesem Problem geschützt ist, probieren Sie unseren Elastic Cloud-Service aus – die Nutzer dieses Service sind bereits geschützt.

Fußnoten

[1] Aus Kostengründen verzichtet Elasticsearch auf standardmäßige Prüfsummenverifizierungen. Bestimmte Aktionen, wie zum Beispiel das Transferieren von Shards oder das Aufnehmen von Snapshots, können eine häufigere Prüfsummenverifizierung auslösen. In diesen Fällen kann der Eindruck entstehen, dass es diese Aktionen waren, die zugrunde liegende schleichende Datenkorruptionen verursacht haben.

[2] Zur Prüfung, ob bei SCSI- oder Device-Mapper-SSDs blk_mq aktiviert ist, können Sie cat /sys/module/{scsi_mod,dm_mod}/parameters/use_blk_mq verwenden.

[3] Nach https://patchwork.kernel.org/patch/10198305 ist blk_mq für SCSI-Geräte standardmäßig aktiviert. Das Deaktivieren über die Kerneloptionen ist nicht möglich. Dieser Patch wurde auf Ubuntu linux-gcp zurückportiert.

[4] Zur Deaktivierung von blk_mq müssen dem Kernel die folgenden Parameter übergeben werden (z. B. mit grub): GRUB_CMDLINE_LINUX="scsi_mod.use_blk_mq=N dm_mod.use_blk_mq=N". Eine Aktivierung kann erfolgen, indem diese Optionen auf „N“ gesetzt werden, dabei ist aber unbedingt [3] zu beachten.

[5] Beispiele für gcloud-Befehle zur Deaktivierung des VIRTIO_SCSI_MULTIQUEUE-guestOS-Features durch Neuerstellung des Ubuntu-Images:

# 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