Performance of Linux md raid-10 arrays – negative impact of the intel_pstate CPU governor for HWP?

Last November I performed some tests with "fio" on Raid arrays with SSDs (4 Samsung EVO 850). The test system ran on Opensuse Leap 42.1 with a Linux kernel of version 4.1. It had an onboard Intel Sunrise Point controller and an i7 6700k CPU.

For md-raid arrays of type Raid10, i.e. for Linux SW Raid10 arrays created via the mdadm command, I was quite pleased with the results. Both for N2 and F2 layouts and especially for situations where the Read/Write load was created by several jobs running in parallel. Depending on the size of the data packets you may, e.g., well reach a Random Read [RR] performance between 1.0 Gbyte/sec and 1.5 GByte/sec for packet sizes ≥ 512 KB and %gt; 1024k, respectively. Even for one job only the RR-performance for such packet sizes lay between 790 MByte/sec and 950 Mbyte/sec - i.e. well beyond the performance of a single SSD.

Significant drop in md-raid performance on Opensuse Leap 42.2 with kernel 4.4

Then I upgraded to Opensuse Leap 42.2 with kernel 4.4. Same system, same HW, same controller, same CPU, same SSDs and raid-10 setup.
I repeated some of my raid-array tests. Unfortunately, I then experienced a significant drop in performance - up to 25% depending on the packet size. With absolute differences in the range of 60 MByte/sec to over 200 Mbyte/sec, again depending on the chosen data packet sizes.

In addition I saw irregular ups and downs in the performance (large spread) for repeated tests with the same fio parameters. Up to some days ago I never found a convincing reason for this strange performance variation behavior of the md-Raid arrays for different kernels and OS versions.

Impact of the CPU governor ?!

Three days ago I read several articles about CPU governors. On my system the "intel_pstate" driver is relevant for CPU power saving. It offers exactly two active governor modes: "powersave" and "performance" (see e.g.: https://www.kernel.org/doc/html/latest/admin-guide/pm/intel_pstate.html).

The chosen CPU governor standard for Opensuse Leap 42.2 is "powersave".

Just for fun I repeated some simple tests for the raid array again - one time with "powersave" active on all CPU cores/threads and a second time with "performance" active on all cores/threads. The discrepancy was striking:


Test setup

HW: CPU i7 6700K, Asus Z170 Extreme 7 with Intel Sunrise Point-H Sata3 controller

Raid-Array: md-raid-10, Layout: N2, Chunk Size: 32k, bitmap: none

SSDs: 4 Samsung Evo 850

Fio parameters:

size=500m
directory=/mnt2
direct=1
ioengine=sync
bs=512k
; bs=1024k
iodepth=1
numjobs=1
[read]
rw=randread
[write]
stonewall
rw=randwrite


Test results:

Fio test case 1 - bs=512k, Random Read [RR] / Random Write [RW]:

Leap 42.1, Kernel 4.1:
RR: 780 MByte/sec - RW: 754 MByte/sec,
RR Spread around 35 Mbyte/sec

Leap 42.2, Kernel 4.4, CPU governor powersave :
RR: 669 MByte/sec - RW: 605 MByte/sec,
RR Spread around 50 Mbyte/sec

Leap 42.2, Kernel 4.4, CPU governor: performance :
RR: 780 MByte/sec - RW: 750 MByte/sec,

RR Spread around 35 Mbyte/sec


Fio test case 2 - bs=1024k, Random Read [RR] / Random Write [RW]:

Leap 42.1, Kernel 4.1:
RR: 860 MByte/sec - RW: 800 MByte/sec
RR Spread around 30 Mbyte/sec

Leap 42.2, Kernel 4.4, CPU governor: powersave :
RR: 735 MByte/sec - RW: 660 MByte/sec
RR Spread > 50 Mbyte/sec

Leap 42.2, Kernel 4.4, CPU governor: performance :
RR: 877 MByte/sec - RW: 792 MByte/sec
RR Spread around 25 Mbyte/sec

Interpretation

The differences are so significant that one begins to worry. The data seem to indicate two points:

  • It seems that a high CPU frequency is required for optimum performance of md-raid arrays with SSDs.
  • It seems that the Leap 42.1 with kernel 4.1 reacts differently to load requests from fio test runs - with resulting CPU frequencies closer to the maximum - than Leap 42.2 with kernel 4.4 in powersave mode. This could be a matter of different CPU governors or major changes in drivers ...

With md-raid arrays active, the CPU governor should react very directly to a high I/O load and a related short increase of CPU consumption. However, the md-raid-modules seem to be so well programmed that the rise in CPU load is on average below any thresholds for the "powersave"-governor to react adequately. At least on Leap 42.2 with kernel 4.4 - for whatever reasons. Maybe the time structure of I/O and CPU load is not analyzed precisely enough or there is in general no reaction to I/O. Or there is a bug in the related version of intel_pstate ...

Anyway, I never saw a rise of the CPU frequency above 2300 Mhz during the tests on Leap 42.2 - but short spikes to half of the maximum frequency are quite normal on a desktop system with some applications active. Never, however, was the top level of 4200 Mhz reached. I tested also with 2000 MB to be read/written in total - then we talk already of time intervals around 3 to 4 secs for the I/O load to occur.

Questions

When reading a bit more, I got the impression, that the admins possibilities to influence the behavior of the "intel_pstate" governors are very limited. So, some questions arise directly:

  1. What is the major difference between OS Leap 42.1 with kernel 4.1 compared to Opensuse 42.2 with kernel 4.4? Does Leap 41.1 use the same CPU governor as Leap 42.2?
  2. Is the use of Intel's standard governor mode "powersave" in general a bad choice on systems with a md-raid for SSDs?
  3. Are there any kernel or intel-pstate parameters that would change the md-raid-performance to the better again?

If somebody knows the answers, please contact me. The whole topic is also discussed here: https://bugzilla.kernel.org/show_bug.cgi?id=191881. At least I hope so ...

Addendum, 19.06.2017:
Answer to question 1 - I checked which CPU governor runs on Opensuse Leap 42.1:
It is indeed the good old (ACPI-based) "ondemand" governor - and not the "new" intel_pstate based "powersave" governor for Intel's HWP. So, the findings described above raise some questions regarding the behavior of the "intel_pstate based "powersave" governor vs. comparable older CPU_FREQ solutions like the "ondemand" governor: The intel_pstate "powersave" governor does not seem to support md-raid as well as the old "ondemand" governor did!

I do not want to speculate too much. It is hard to measure details of the CPU frequency changes on small timescales, and it is difficult to separate the cpu consumption of fio and the md-modules (which probably work multithreaded). But it might be that the "ondemand" governor provides on average higher CPU frequencies to the involved processes over the timescale of a fio run.

Anyway, I would recommend all system admins who use SSD-Raid-arrays under the control of mdadm to check and test for a possible performance dependency of their Raid installation on CPU governors!

 

SSD Raid Arrays unter Linux – IX – Chunk Size und Performance eines md-Raid-10-Arrays

In früheren Blogbeiträgen hatte ich mich Ende 2016 ein wenig mit SSD-Raid-Arrays unter Linux auseinandergesetzt:

SSD Raid Arrays unter Linux – I – ein facettenreiches Thema
SSD Raid Arrays unter Linux – II – Hardwarecontroller ?
SSD Raid Arrays unter Linux – III – SW- Raid vs. Intel-iRST-Raid – Performance?
SSD Raid Arrays unter Linux – IV – OS und Daten auf einem Raid-Array?
SSD Raid Arrays unter Linux – V – SW-Raid vs. iRST-Raid – Boot-Unterstützung?
SSD Raid Arrays unter Linux – VI – SW-Raid vs. iRST-Raid – Flexibilität?
SSD Raid Arrays unter Linux – VII – problematische Aspekte von Raid-5-Arrays
SSD Raid Arrays unter Linux – VIII – Setup von Raid-10-Arrays mit mdadm

Ich möchte in diesem Blog-Beitrag einige Performance-Daten für ein Raid-10-Setup mit SSDs nachreichen. Wir betrachten dabei ein SW-Raid-Array (md-Raid), dass mit Hilfe von "mdadm" erstellt wurde. Die Daten, die auf einem Opensuse-System gewonnen wurden, unterstreichen den großen Einfluss der "Chunk-Size" auf einige Einsatzszenarien. Wichtigstes Ergebnis:

Man kann nicht grundsätzlich davon ausgehen, dass eine große Chunk-Size (≥ 512 KB) die beste Performance liefern wird.

Test-Voraussetzungen

Die Voraussetzungen für die nachfolgend ermittelten Daten waren:

Raid-Array:
Linux SW-Raid-10-Array aus 4 SSDs (Samsung EVO 850); Near N2-Layout. Verwendete Partitionsgrößen auf den SSDs: 40 GiB. Das Array wurde z.B. für eine Chunk Size von 32kiB z.B. erzeugt mit

 
mdadm --create --verbose /dev/md/d02 --level=raid10 --bitmap=none --chunk=32 
--layout=n2 --raid-devices=4 /dev/sda5 /dev/sdb5 /dev/sdc5 /dev/sdd5 

Eine "Bitmap" (s. hierzu den letzten Artikel) wurde also nicht angelegt. Für die "Chunk Size" wurden test-abhängig Werte von 8k, 16k, 32k, 512k verwendet. Vor jedem Einzeltest wurde ein fstrim-Befehl auf die zu testende Partition des Raid-Systems abgesetzt. Hinsichtlich der Schreibtests lagen also optimale Voraussetzungen vor. Es wurden LVM2 Logical Volumes verwendet, die mit einem "ext4"-Fielssystem versehen wurden. Der LVM- und Filesystem-Overhead gegenüber einem Schreiben mit dem Tool "fio" (s.u.) auf unformatierte Partitionen erwies sich als unerheblich.

OS und HW:
Opensuse Leap 42.1 mit Kernel 4.1; i7 6700K; Onboard Z170 Intel Raid-Controller (Kernel-Modul: pinctrl_sunrisepoint). Scheduler: Deadline.
Die Kernelversion ist leider wichtiger als man meinen möchte. Die nachfolgend ermittelten Daten lassen sich z.B. auf einem System mit Opensuse Leap 42.2 mit Kernelversion 4.4 nicht erzielen! Bei gleichem Setup, gleicher FIO-Version und identischem HW-Unterbau ist die Performance zum Teil deutlich schlechter. Für Einzelprozesse und Daten-Paket-Gößen unterhalb 1 MB waren auf derselben HW-Plattform Perfromance-Einbrüch von 25 bis zu 30 % zu verzeichnen. Unverständlicherweise! Im asymptotischen Bereich (große Daten-Pakete, die die Chunk Size weit übersteigen und/oder viele parallel arbeitende Jobs) werden aber die gleichen Werte wie unter einem 4.1 Kernel erreicht. Vorläufige Untersuchungen zeigen, dass die schlechtere Performance ein Effekt ist, der schon bei Einzel-SSDs auftritt und durch das Raid10-System nur noch verstärkt wird.

Test-SW und Datenstruktur:
Ich habe primär "fio" in der Version 2.2.10 und für sequentielles Lesen/Schreiben großer Datenpakete ergänzend auch "gnome-disks" eingesetzt. Es wurden Daten von insgesamt 500 MB Größe geschrieben. Unter "fio" galten dabei besondere Bedingungen: Die Nutzung des Linux-Caches wurde durch Optionen umgangen; wir wollen ja die tatsächliche Raid-Performance testen. Typische Einstellungen für die FIO-Jobs waren in etwa solche wie nachfolgend für einen "Random Write"-Job angegeben:

[global]
size=500m
direct=1
bs=64k
ioengine=sync

[write]
bs=64k
numjobs=1
rw=randwrite

Die fio-Blocksize "bs" wurde im Test zwischen 8k und 20000k variiert - damit wurde im Test abgefragt, wie das System auf unterschiedliche strukturiertes Datenaufkommen reagiert: Kleine einzelne Datenpakete (spike-artig) vs. größere Datenpakete (etwa größere Files).

Nur 1 Job liest oder schreibt auf das Array:
Von großer Bedeutung für die Testergebnisse ist die fio-Einstellung, dass nur genau ein (1) Job zu einem Zeitpunkt ein Datenpaket lesen oder schreiben soll. Die Ergebnisse würden sich drastisch ändern, wenn mehrere Jobs gleichzeitig auf das Raid-System zugreifen würden. Im Besonderen würde die Schreibperformance bei "Random Write"-Tests deutlich nach oben gehen. Damit wird sich ein kommender Artikel befassen.

Man kann in etwa sagen, dass eine zunehmende Zahl von Jobs einen ähnlichen Effekt hat wie eine deutliche Vergrößerung der Größe "bs": Es stehen zu einem Zeitpunkt immer viele Datenblöcke, die über Chunks möglichst parallel auf die Platten geschrieben werden. Die Chunksize wird dann einfach früher und trotz evtl. kleiner "bs"-Werte überschritten.

Schwankungsbandbreite
Die Ergebnisse zu den Transferraten haben eine Schwankungsbandbreite zwischen 8 und 25 MB/sec. Tendenziell ist die Schwankungsbreite bei kleinen Datenpaketgrößen höher. Das liegt u.a. auch an der sonstigen Auslastung des Testsystems. Ich habe versucht, so viele Prozesse wie möglich abzuschalten; ferner wurde jede der durchgeführten Messungen 3 mal wiederholt. Es wurde ein sinnvoller Mittelwert bei leichter Bevorzugung hoher Werte angegeben.

Daten

Die nachfolgende Tabelle ist wie folgt zu lesen:

Am Kopf der verschiedenen Testblöcke ist die Art des Lesen/Schreibens (Random vs. Sequential) angegeben.

  • Die Variation der "Chunk Size" entnimmt man der zweiten Spalte.
  • Es folgen pro Zeile mehrere Blöcke aus jeweils 2 (oder 3) zusammengehörigen Spalten mit der verwendeten fio-"bs" und dem zugehörigen Messwert für die Datentransferrate in MiB/s. Zu den mit "gdisk" bezeichneten Spalten s.u..
  • Die Zeilen mit SSD am Anfang zeigen Werte, die für eine einzelne SSD-Partition außerhalb des Raid-Verbunds gemessen wurden (Einzelzugriff auf eine SSD).
  • Die Spalten mit der Überschrift "gdisk" stehen für zusätzliche Werte, die mit dem Tool "gnome-disks" gewonnen wurden. Sie betreffen nur sequentielle Lese- und Schreibtests.

Grün markierte Werte markieren aus meiner Sicht akzeptable Werte; bei ihnen kommt auch die Performance-Verbesserung durch Einsatz eines Raid-vebrunds gegenüber einer Einzel-SSD voll zum Tragen. Werte für sequentiell Zugriffe und eine Chunk Size von 16K habe ich leider noch nicht erhoben; sorry.

500M

                                     

Random Read

                           

gdisk

   

gdisk

 

chunk

bs

MB/s

bs

MB/s

bs

MB/s

bs

MB/s

bs

MB/s

bs

MB/s

bs

MB/s

MB/s

bs

MB/s

MB/s

SSD

-

8

64

16

126

32

200

64

278

128

327

512

433

1024

460

 

20000

497

 

R10_0

16

8

66

16

149

32

235

64

448

128

604

512

753

1024

832

 

20000

851

 

R10_1

32

8

66

16

128

32

215

64

390

128

720

512

791

1024

868

 

20000

932

 

R10_2

512

8

64

16

128

32

209

64

277

128

318

512

436

1024

851

 

20000

976

 
                                       

Random Write

                                     
 

chunk

bs

MB/s

bs

MB/s

bs

MB/s

bs

MB/s

bs

MB/s

bs

MB/s

bs

MB/s

MB/s

bs

MB/s

MB/s

SSD

-

8

166

16

243

32

409

64

457

128

458

512

478

1024

492

 

20000

502

 

R10_0

16

8

158

16

285

32

484

64

615

128

680

512

730

1024

775

 

20000

790

 

R10_1

32

8

166

16

295

32

384

64

609

128

731

512

754

1024

816

 

20000

850

 

R10_2

512

8

130

16

299

32

347

64

430

128

464

512

463

1024

837

 

20000

873

 
                                       

500M

                                     

Seq Read

                                   
 

chunk

bs

MB/s

bs

MB/s

bs

MB/s

bs

MB/s

bs

MB/s

bs

MB/s

bs

MB/s

MB/s

bs

MB/s

MB/s

SSD

-

8

 

16

371

32

425

64

480

128

483

512

493

1024

506

 

20000

517

 

R10_0

16

8

 

16

 

32

 

64

 

128

 

512

 

1024

 

986

20000

   

R10_1

32

8

 

16

355

32

432

64

796

128

724

512

773

1024

883

950

20000

980

1030

R10_2

512

8

 

16

354

32

425

64

472

128

495

512

485

1024

950

998

20000

1010

1100

                                       

Seq Write

                                     
 

chunk

bs

MB/s

bs

MB/s

bs

MB/s

bs

MB/s

bs

MB/s

bs

MB/s

bs

MB/s

MB/s

bs

MB/s

MB/s

SSD

-

8

 

16

333

32

410

64

456

128

485

512

484

1024

490

 

20000

502

 

R10_0

16

8

 

16

 

32

 

64

 

128

 

512

 

1024

 

380

20000

   

R10_1

32

8

 

16

298

32

377

64

647

128

734

512

756

1024

812

399

20000

850

840

R10_2

512

8

 

16

301

32

370

64

423

128

450

512

464

1024

836

374

20000

886

841

                                       

 

Interpretation

Wir kommen zu folgenden Ergebnissen und möglichen Erklärungen des festgestellten Verhaltens:

Feststellung 1: Besonders das Lesen kleiner Datenpakete liegt den Samsung SSDs nicht.

Erklärungsansatz: Typischer SSD-Leistungseinbruch bei kleinen Datenpaketen
Wir sollten nicht allzu sehr überrascht sein, wenn wir bei kleinen Datenpaketen einen generellen Performance-Verlust der SSDs feststellen. Dieses Verhalten ist schon bei Einzel-SSDs gegeben - und übrigens auch bei klassischen Harddisks nicht anders. Viele Leute sind sich dessen aber nicht bewusst; in der Werbung geben die Hersteller ja meist nur die sequentiell erreichbaren Maximalraten an. Diese Werte spiegeln aber das tatsächliche, im Mittel deutlich kleinere Leistungsvermögen bei kleinen Einzeldatenpaketen überhaupt nicht wieder. Der Fairness sei auch gesagt, dass HDDs im Bereich kleiner Datenpakete sehr viel schlechtere Werte zeigen.

Erklärungsansatz: Erwartbar stärkerer Einbruch der Lese- als der Schreibperformance bei kleinen Datenpaket-Größen
Offenbar ist es ein verbreitetes Phänomen, dass bei vielen SSDs für geringe Datenpaketgrößen die Leseperformance hinter der Schreibperformance zurückfällt. Siehe hierzu:
http://www.anandtech.com/show/6935/seagate-600-ssd-review/5
Ich habe leider keine Ahnung, wie das technisch zu begründen ist. Besseres Caching zu schreibender Daten auf dem internen SSD-Controller? Jedenfalls zeigen meine Daten genau diesen Effekt - z.T. mit einer überraschend großen Diskrepanz zwischen Lese- und Schreibperformance.

Feststellung 2: Erst wenn die Datenpaketgröße die "Chunk Size" (deutlich) übersteigt, hebt sich auch die Performance des Raid-10-Arrays deutlich gegenüber der einer einzelnen SSD ab.

Feststellung 3: Unterhalb einer Datenpaketgröße von 1MB wird auch bei Überschreiten der Chunk Size keine Verdoppelung der Performance gegenüber einer Einzel SSD erreicht.

Erklärungsansatz: Fundamentale Bedeutung der Chunk Size
In den vorhergehenden Blogbeiträgen hatte ich bereits diskutiert, dass die Chunk Size eine Mindestgröße angibt, ab der parallele Zugriffe auf die 2 Stripeset-Komponenten des Raid-10-Arrays überhaupt erst ermöglicht werden. Deshalb würde man für Situationen, in denen der "bs"-Wert - also die Größe des zu verarbeitenden Datenpakets - die Chunk Size unterschreitet, einen deutlichen Einbruch der Performance erwarten. Da das Raid-System auch Overhead verursacht, würde man bei kleinen Datenpaketen ggf. sogar damit rechnen, dass die Performance des Raid-Arrays die einer Einzel-SSD (also außerhalb des Raid-Verbunds) unterschreitet. Statistisch geschieht das tatsächlich; in der Tabelle kommt das durch eine leichte Bevorzugung besserer Werte aber nicht zum Tragen.

Feststellung 3: Eine annähernde Verdoppelung der Leistung wird erst asymptotisch bei großen Datenpaketen erreicht. Das gilt für Random Read/Write wie für Sequential Read/Write. Im asymptotischen Bereich mit Datenpaketgrößen > 1MB sind auf einem Raid-10-Array mit Consumer-SSDs aber Schreibraten jenseits von 850 MB/sec und Leseraten jenseits von 1000 MB/sec möglich (SSD: EVO 850).

Erklärungsansatz: ???
Ehrlich gesagt, hier kann ich keine fundierte Erklärung liefern. Timing-Probleme des SSD-Controllers? Timing-Probleme zwischen Kernel, SW-Raid und dem Controller? Zusammenspiel interner SSD-Caches mit dem Test? Besonders erklärungsbedürftig scheint mir zu sein, dass beim sequentiellen Lesen sowie einer Chunk Sitze von 32 KB im Bereich von Datenpaket-Größen zwischen 64KB und 512 KB keine Systematik vorzuliegen scheint. Es wäre an dieser Stelle auch interessant zu sehen, wie sich eigentlich ein Raid-0-Array verhält.

Feststellung 4: Bei großen Datenpaketen nähern sich die Random-Raten den sequentiellen Raten an.

Erklärungsansatz: Asymptotik
Mit wachsender Größe der Datenpakete gibt man dem System die Möglichkeit, ein immer sequentielleres Verhalten zu erreichen.

Feststellung 5: gnome-disks liefert vor allem beim sequentiellen Schreiben von Datenpaketen mit 1MB Größe seltsame und gegenüber fio viel zu kleine Werte.

Erklärungsansatz: ???
Keine Ahnung. Hier stimmt jedenfalls irgendetwas nicht. Ich persönlich traue hier "gnome-disks" nicht.

Erstes Fazit

Auch wenn wir nicht alle Daten schlüssig erklären können, sind vier Befunde offenkundig:

  • Mit Hilfe eines Raid-10-Arrays kann man eine deutlich höhere Performance als mit Einzel-SSDs erreichen.
  • Eine theoretisch mögliche Verdoppelung von Datentransferraten wird nur asymptotisch für große Datenpakete und sequentielle Zugriffe erreicht.
  • Für Szenarien, in denen nur 1 Job zu einer Zeit Daten liest oder schreibt, hängt der mögliche Performance-Gewinn stark davon, ob die Größe der Datenpakete im Mittel die "Chunk Size" übersteigt oder nicht. Die richtige Wahl der "Chunk Size" ist vor allem dann wichtig, wenn regelmäßig einzelne kleine Datenpakete vom und zum Raid-10-Array transferiert werden müssen. Das kann z.B. für bestimmte Datenbank-Anwendungen relevant sein.
  • Eine Chunk Size von 32K stellt einen guten Kompromiss bzgl. der Unterstützung relativ kleiner Datenpaketgrößen und einer gleichzeitigen Performanceverbesserung gegenüber Einzel-SSDs dar.

Ausblick

Themen weiterer Artikel sollten Test für mehrere parallel lesende/schreibende Jobs, für andere Kernelversionen und auch für Raid-5-Arrays sein. Ich bitte diesbzgl. um etwas Geduld.

 

SSD Raid Arrays unter Linux – VIII – Setup von Raid-10-Arrays mit mdadm

Diese Serie an Posts befasst sich mit SSD-basierten SW-Raid-Systemen auf Linux-Workstations oder kleineren Server-Systemen. In den bisherigen Artikeln der Serie hatten wir uns u.a. mit dem Einsatz von Onboard-SATA3-Controllern auf Mainboards mit Intel-Chipsatz befasst und einen kritischen Blick auf die sog. iRST-Technologie mit "imsm"-Raid-Containern geworfen. Zudem hatten wir ein wenig über die Bootunterstützung im Zusammenspiel mit Grub2 und mögliche, problematische Aspekte von Raid-5-Arrays nachgedacht.

SSD Raid Arrays unter Linux – I – ein facettenreiches Thema
SSD Raid Arrays unter Linux – II – Hardwarecontroller ?
SSD Raid Arrays unter Linux – III – SW- Raid vs. Intel-iRST-Raid – Performance?
SSD Raid Arrays unter Linux – IV – OS und Daten auf einem Raid-Array?
SSD Raid Arrays unter Linux – V – SW-Raid vs. iRST-Raid – Boot-Unterstützung?
SSD Raid Arrays unter Linux – VI – SW-Raid vs. iRST-Raid – Flexibilität?
SSD Raid Arrays unter Linux – VII – problematische Aspekte von Raid-5-Arrays

In diesem Beitrag gehe ich darauf ein, wie ich meine md-basierten Raid-10-Arrays konkret mit Hilfe des Befehls "mdadm" aufsetze (Linux SW-Raid). Dabei diskutiere ich Voraussetzungen und einige wichtige Parameter. Eine Verallgemeinerung der Rezepte auf andere Raid-Varianten wie Raid-5 ist danach relativ einfach.

Voraussetzungen und SSD-Optimierung unter Linux

Da unsere Raid-Arrays aus SSDs zusammengesetzt werden, sind unter Linux einige Regeln zu beachten. Einen guten Überblick geben etwa folgende Artikel:

https://sites.google.com/site/easylinuxtipsproject/ssd
http://www.linux-magazine.com/Issues/2015/172/Tuning-Your-SSD
https://wiki.archlinux.org/index.php/Solid_State_Drives
https://wiki.debian.org/SSDOptimization

Hier einige Punkte, die ich als relevant einstufe:

  1. Auf allen SSDs, die in das Raid-System eingebunden werden, sollte man grundsätzlich ca. 7-12% frei, d.h. ohne Partition, belassen (Overprovisioning).
  2. Ein periodisches Absetzen des TRIM-Befehls "fstrim" per cron-Job ist dem Mounten eines (ext4-) Filesystems mit der "discard"-Option vorzuziehen.
  3. Ext4 sollte als Filesystem verwendet werden (in meinem Fall auf LVM Logical Volumes des Raid-Arrays; s.u.)
  4. Man sollte prüfen, dass die eingesetzten Tools zum Einrichten von Partitionen, LVM und Filesystemen das notwendige Alignment korrekt durchführen (YaST etwa tut dies automatisch, wenn unter den Einstellungen "Optimal" als Option gewählt wurde !)
  5. Partitionen sollte man großzügig anlegen - 25% sollten immer als freier Platz zur Verfügung stehen.
  6. Für LVM sind die Optionen
    "md_chunk_alignment = 1" und "issue_discards = 1"
    in der Datei "/etc/lvm/lvm.conf" zu setzen.
  7. Als Scheduler sollten die Low-Latency-Scheduler "deadline" oder "noop" verwendet werden.
  8. Wahl des AHCI-Modus für den SATA-Contoller (soweit nicht iRST/imsm-Array-Container) konfiguriert werden).

Punkt 1 dient sowohl der Lebensdauer als auch der Performance von SSDs. Bitte beachtet, dass u.a. dieser Punkt gegen den Einsatz der iRST-Technologie für Onboard-Controller spricht. Grund: iRST lässt einem keine Wahl bzgl. der Größe des zweiten Raid-Volumes in einem isms-Raid-Container! Für das zweite Volume wird der gesamte noch verfügbare physikalische Platz der eingebundenen Speichermedien genutzt. Siehe hierzu frühere Beiträge der Serie.

Punkt 2 dient der Performance; der TRIM-Befehl [fstrim unter Linux] ist allerdings auch mit Schreib-/Lösch-Operationen verbunden und belastet daher die SSD. Bei hinreichend großen Partitionen genügt im durchschnittlichen Tagesbetrieb nach meiner Erfahrung ein einmaliges Absetzen von "fstrim" pro Woche, um die Performance aufrecht zu erhalten.

Punkt 3 wird in vielen Internet-Artikeln zu diesem Thema als richtig angesehen, da die Node-Behandlung unter ext4 modernen SSD-Controllern entgegenkommt. Die Bevorzugung von ext4 kann sich jedoch ändern, wenn Btrfs erst einmal einen hinreichenden Reifegrad erhalten hat. Ich ignoriere Btrfs in diesem Artikel, da ich wenig Erfahrung mit diesem Filesystem habe - und die, die ich hatte, waren (noch) nicht gut. Allerdings sind ja auch XFS und zunehmend F2FS sehr populär. Ich behandle auch diese Filesysteme mangels produktiver Erfahrung nachfolgend nicht. Ein Blick in Tests ist aber sicher interessant (s. etwa:
http://www.phoronix.com/scan.php?page=article&item=linux-40-ssd&num=2,
https://www.phoronix.com/scan.php?page=news_item&px=Linux-4.4-FS-4-Way,
https://www.percona.com/blog/2012/03/15/ext4-vs-xfs-on-ssd/,
http://openbenchmarking.org/result/1608041-LO-LINUX44BT99
https://blog.pgaddict.com/posts/postgresql-performance-on-ext4-and-xfs, https://www.phoronix.com/scan.php?page=news_item&px=Linux-4.7-FS-5-Way,
http://infoteh.etf.unssa.rs.ba/zbornik/2016/radovi/RSS-2/RSS-2-8.pdf).
S. aber auch:
https://bbs.archlinux.org/viewtopic.php?id=207938
Die Unterschiede zwischen ext4 und XFS scheinen insgesamt nicht weltbewegend zu sein. Ich ziehe ext4 z.Z. wegen seiner guten Stabilität und wegen seiner guten Wiederherstellungsfähigkeit bei Filesystem-Fehlern im besonderen Btrfs vor. Hinzu kommt, dass ext4 die geringste Schreibbelastung durch Journaling auf die SSDs auszuüben scheint (s. z.B. http://www.ocsmag.com/2016/04/30/using-solid-state-drives-on-linux/).
Ferner ist bei ext4 die Unterstützung des fstrim-Befehls auch dann garantiert, wenn das Filesystem auf einem LVM-Volume und letzteres auf einem Raid-10-Array angelegt wird.

Punkt 4 behandle ich im nachfolgenden Abschnitt genauer.

Punkt 5 dient ebenfalls der Performance: Freie Blöcke des Filesystems stehen hinreichend und zusammenhängend zur Verfügung; eine Fragmentierung von Linux-Filesystemen wird bei genügend freiem Platz weitgehend automatisch vermieden.

Punkt 6 dreht sich einerseits darum, dass ein Physical Volume, das direkt auf einem md-Raid-Array aufsetzt, automatisch an die zugrunde liegende Raid-Array-Struktur (im besonderen die sog. Chunk Size; s.u.) angepasst wird. Durch den 2-ten Parameter informiert LVM den SSD-Controller ferner über die Freigabe von Platz bei Löschungen oder Verkleinerungen von logischen Volumes. Siehe hierzu:
https://linux.die.net/man/5/lvm.conf,
https://nixaid.com/trim-in-linux-with-ssd-disk-lvm-and-encryption/

Punkt 7 hat damit zu tun, dass bestimmte Linux I/O Scheduler, wie der cfq-Scheduler, versuchen, seek-Zeiten für HDDs zu minimieren. Das ist bei SSDs aber überflüssig und verschwendet sogar CPU-Zeit; Scheduler wie "noop" oder "deadline" verringern dann die Latenz. Siehe hierzu und zur Einrichtung des Schedulers für SSDs die folgenden Links:
http://stackoverflow.com/questions/1009577/selecting-a-linux-i-o-scheduler
https://wiki.debian.org/SSDOptimization

Punkt 8 ist eine Standard-Empfehlung, die vor allem im Gegensatz zu potentiell möglichen "IDE"-Einstellungen zu sehen ist. Siehe etwa:
http://www.phoronix.com/scan.php?page=article&item=intel_linux_ahci&num=1
http://archive.benchmarkreviews.com/?option=com_content&task=view&id=505
Auf Systemen mit einem Intel-SATA3-Controller gibt es im BIOS oft auch die Option "RAID". Ich habe bei Einrichtung eines reinen md-basierten Linux-SW-Raids auf solchen Systemen keine Performance-Differenzen zwischen den Einstellungen AHCI und RAID feststellen können. Der RAID-Modus fällt wohl auf den AHCI-Modus zurück, wenn der iRST nicht genutzt wird.

Alignment und die Hierarchie der Speicherorganisation

Punkt 4 ist technisch interessant: Hier geht es um die Ausrichtung von Partitions-Grenzen gemäß der definierten Sektorgrößen der HDDs/SSDs (typischerweise 512 Byte) und ein Ausrichten der Blocksize des Filesystems (für ext4 typischerweise 4KB (genauer 4 KiB (4096 Bytes); 1 MiB = 2048 * 512 Bytes) an der zugrunde liegenden Speicherstruktur.

Eine Erläuterung des Themas findet sich hier:
https://www.thomas-krenn.com/de/wiki/Partition_Alignment
Siehe auch:
https://wiki.ubuntuusers.de/SSD/Alignment/
https://wiki.debian.org/SSDOptimization

Alignment betrifft aber nicht nur Partitionsgrenzen. Alignment ist auf allen Ebenen der HD-/SSD-Speicherorganisation interessant (physikalische Partition, Raid-Array, LVM-Group, LVM-Volume, ext4-Filesystem). Dabei müssen auch Parameter des ext4-Filesystems an die Raid-Speicher-Organisation angepasst werden. In unserem Fall sieht die geplante Hierarchie der Speicherorganisation wie folgt aus:

=> 4 SSDs
=> pro SSD eine unformatierte Raid-Partition (Typ 0xFD)
=> md-Raid-Array mit Striping (je nach Raid-Typ)
=> Eine LVM Volume Group [VG] über das gesamte Raid-Array hinweg; das Raid-Array entspricht dabei einem LVM Physical Volume [PV]
=> Logische LVM Volumes [LVs] mit ext4 Filesystem

Die Anzahl zu lesender und zu schreibender Blöcke für den Erhalt bzw. die Speicherung von Information soll über die gesamte Hierarchie hinweg durch passendes Alignment minimiert werden.

In der Linux-Praxis muss man sich allerdings um ein korrektes Partition-Alignment kaum kümmern. Generell führen "gdisk" und "parted" mit der "-a optimal" Option das notwendige Partition-Alignment korrekt durch. Für Opensuse's YaST-Partitioner kann man unter dem Punkt "Einstellungen" ein optimales Ausrichten von Partitionen anfordern; das führt intern zum Setzen der Option "-a optimal" für "parted". [Ich gehe in diesem Artikel davon aus, dass mit einem GPT-Layout der SSDs gearbeitet wird.]

LVM2 (unter YaST) arbeitet ebenfalls korrekt - und allokiert typischerweise 4MiB große "Physical Extends" [PEs]. (Siehe zu LVM2-Grundlagen etwa https://www.thomas-krenn.com/de/wiki/LVM_Grundlagen). Das passt zu allen vorherigen Einstellungen. Typischerweise wird man eine logische Volume-Gruppe [VG] über das gesamte Array anlegen. Durch die oben bereits diskutierten Parameter in der "/etc/lvm/lvm.conf" nimmt das LVM-PV-Layout auch auf die darunter liegende Raid-Array-Speicherorganisation Rücksicht.

Ein potentiell mögliches LVM-Striping von Logical Volumes über die Physikalischen Volumes (PVs) einer Gruppe hinweg fällt in unserem Fall flach: Das Striping wird ja bereits vom md-Raid-Array übernommen.

[LVM-Seitenaspekte: Würden wir aber - was prinzipiell möglich ist - statt md-Arrays LVM-basierte Raid-Arrays einrichten, müssten wir über die sog. LVM-Stripe-Size genauer nachdenken. Die LVM Stripe-Size gibt vor, ab welcher Größe der zu schreibenden Informationsblöcke auf ein neues PV der Logical Volume Group [VG] geschrieben wird. Die Überlegungen, die wir weiter unten zur Chunk Size der md-Raid-Arrays anstellen, sind dazu ganz analog. Interessant wäre übrigens auch ein LVM-Striping über mehrere md-Raid-Arrays (als PVs der VG) hinweg. Auch dann ist die LVM-Stripe-Size von größerem Interesse. Mangels Masse an SSDs habe ich damit aber noch nicht experimentieren können.]

Bleibt noch das Anpassen bestimmter Parameter (stride, stripe-width) des ext4-Filesystems an die darunter liegende Raid-Struktur. (LVM als Zwischenschicht ist diesbzgl. so gut wie transparent!). Erläutert ist dies z.B. hier
https://gryzli.info/2015/02/26/calculating-filesystem-stride_size-and-stripe_width-for-best-performance-under-raid/

Die Filesystem-Parameter (stride, stripe-width) sorgen für eine optimale Gruppierung von Datenblöcken für den Transfer zum bzw. vom Raid-Array. Dabei spielt die sog. Chunk-Size [CS] des Raid-Arrays eine Rolle; diese wird manchmal analog wie bei LVM LVs auch "Stripe-Size" des Raid-Arrays genannt. Ich komme darauf weiter unten genauer zurück. Im Moment mag genügen, dass z.B. der YaST-Partitioner die genannten Parameter korrekt einstellt, wenn ihm die zugrunde liegenden Raid-Array-Parameter bekannt sind.

Dennoch empfehle ich in jedem Fall manuell zu prüfen, ob die Parameter OK sind. Informationen zu den gewählten Parametern eines konkreten ext4-FS erhält man über tune2fs -l, z.B.:

mytux:~ # tune2fs -l /dev/volssd1/lvssdtest
....
RAID stride:              8
RAID stripe width:        16   

 
Wie man die korrekten optimalen Werte anhand des Raid-Typs (hier Raid 10) und der Raid-Chunk-Size bestimmt, erläutere ich in einem Abschnitt weiter unten.

Die Parameter stride und stripe-width können auch im Nachhinein mit Hilfe von tune2fs angepasst werden (s. http://serverfault.com/questions/663066/is-it-possible-to-update-stripe-width-on-an-existing-and-used-ext4-fs).

Weitere Links zum Alignment von SSD-Raid-Systemen finden sich hier:
http://dennisfleurbaaij.blogspot.de/2013/01/setting-up-linux-mdadm-raid-array-with.html
http://serverfault.com/questions/592149/ssd-software-raid-alignment-necessary
https://www.percona.com/blog/2011/06/09/aligning-io-on-a-hard-disk-raid-the-theory/
https://raid.wiki.kernel.org/index.php/RAID_setup

Welche Kernelversion?

Vor einem Monat hätte ich diesen Absatz noch nicht geschrieben. Inzwischen habe ich aber Tests von Raid-Arrays auf verschiedenen Systemen mit Opensuse Leap 42.1, 42.2, Opensuse Tumbleweed, Debian Jessie und verschiedenen Kernelversionen [4.1, 4.4, 4.7] durchgeführt. Leider ist die Performance von Raid-Arrays, ja sogar der zugrunde liegenden Einzel-SSDs dabei keineswegs eine Konstante. Woran das liegt, ist mir z.Z. ein Rätsel.

Im Moment spreche ich mich bei einem Vorhaben mit SSD-Raid-Arrays aufgrund eigener Tests aber noch klar für die Kernel-Version 4.1 aus.

Das ist aus meiner Sicht diejenige Kernelversion, die in Tests (z.B. mit FIO) konsistent die höchsten Durchsatzraten mit geringster Variationsbreite bietet. Spätere Kernelversionen haben entweder Fehler bzgl. md-Raid-10-Arrays (4.2, 4.3; s. https://wiki.archlinux.org/index.php/RAID) oder liefern seltsam schwankende Performancedaten (s. https://bugzilla.kernel.org/show_bug.cgi?id=191881).

Schritt 1 zum SW-Raid10-Array: Anlegen von Raid-Partitionen auf den SSDs

In meinem Fall sind die 4 SSDs unter /dev/sda, /dev/sdb, /dev/sdc, /dev/sdd ansprechbar. Auf diesen Platten lege ich auf einem System mit UEFI-BIOS zunächst je eine erste efi-Partition (Fat16) an. Dies ist nur eine Vorsichtsmaßnahme für den Fall, dass ich später doch mal eine bootbare Partition in einem der SW-Raid-Arrays selbst anlegen will (s. hierzu die früheren Artikel dieser Serie).

Danach habe ich auf den SSDs für das nachfolgende Beispiel mit Hilfe von "mkfs" je eine weitere unformatierte Partition "/dev/sda[bcd]2" des Typs "0xFD Linux Raid" angelegt. "parted" liefert danach folgende Infos für z.B. "/dev/sdb"; dabei kann die 3-te zusätzlich vorhandene Test-Partion ignoriert werden. Im Moment ist nur die zweite Partition relevant.

(parted) print /dev/sdb
Model: ATA Samsung SSD 850 (scsi)
Disk /dev/sda: 500GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags: 

Number  Start   End    Size    File system  Name     Flags
 1      1049kB  189MB  188MB   fat16        primary  boot
 2      189MB   226GB  225GB                primary  raid
 3      226GB   253GB  26.8GB               primary  raid

 
Warum habe ich die Partition 2 unformatiert gelassen? Weil ich später eine flexible Partitionierung des Raid-Arrays über LVM erreichen will!

Alignment Checks:
Die logische und physikalische Sektorgröße der Samsung EVO 850 ist 512 Byte. Passen dazu die Partitionsgrenzen? Das bekommt man z.B. mit Hilfe von parted heraus.

(parted) align-check                                                      
alignment type(min/opt)  [optimal]/minimal? optimal                       
Partition number? 2                                                       
2 aligned
(parted)   

 
Wie sieht der Output von fdisk aus?

fdisk -l -u /dev/sdb
Disk /dev/sdb: 465.8 GiB, 500107862016 bytes, 976773168 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 3BFE79AE-FA81-474E-BB76-082C630414ED

Device         Start       End   Sectors  Size Type
/dev/sdb1       2048    368639    366592  179M EFI System
/dev/sdb2     368640 440774655 440406016  210G Linux RAID
/dev/sdb3  440774656 493211647  52436992   25G Linux RAID

 
Und was sagt sfdisk?

sfdisk -d /dev/sdb
label: gpt
label-id: 3BFE79AE-FA81-474E-BB76-082C630414ED
device: /dev/sdb
unit: sectors
first-lba: 34
last-lba: 976773134

/dev/sdb1 : start=        2048, size=      366592, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=2B60DB9C-3DE1-45AC-80FC-A51657CA5E49, name="primary"
/dev/sdb2 : start=      368640, size=   440406016, type=A19D880F-05FC-4D3B-A006-743F0F84911E, uuid=2F85AE30-BE40-4D49-BE4C-F240EDF48289, name="primary"
/dev/sdb3 : start=   440774656, size=    52436992, type=A19D880F-05FC-4D3B-A006-743F0F84911E, uuid=8A67ED48-0BE2-4E83-8803-F7A42D91F3CF, name="primary"

 
Man sieht, dass die erste (efi-) Partition auf 1MiB (2048 sectors) ausgerichtet ist. Auch alle anderen Partitionen liegen offenbar genau auf Sektorgrenzen.

Analog verfährt man für die anderen SSDs. Unter Opensuse kann man zum Einrichten der Partition bequem YaST's Partitioner verwenden. Bei der Verwendung der Kommandozeile nicht vergessen, die für Raid-Arrays bestimmte GPT-Partitionen auf den Typ 0xFD zu setzen! (genauer: 0xFD00; s. hierzu https://raid.wiki.kernel.org/index.php/Partition_Types).

Schritt 2 zum SW-Raid10-Verbund: Festlegen bestimmter Parameter des Kommandos "mdadm" - Chunk Size, bitmap, layout

Nun wird es Zeit, das Raid-Array zu definieren. Unter Opensuse könnte man das z.B. mit Hilfe von YaST's Partitioner machen. Davon rate ich aber explizit ab, weil YaST leider nur Standardparameter setzt und keine abweichende Setzung ermöglicht.

Vielmehr ist der Befehl "mdadm" mit bestimmten Parametersetzungen anzuwenden. Es gibt beim Anlegen von Raid-10-Arrays mit mdadm vor allem 3 Parameter, die für die Performance interessant sind:

  • Parameter "chunk", für die Festlegung der sog. "Raid Chunk Size" [CS],
  • Parameter "bitmap"; der legt fest, ob Daten in Transferprozessen besonders markiert werden; er kann später eine evtl. mal notwendig werdende Resynchronisation von Raid-Arrays beschleunigen. Er kostet aber auch Schreib-Performance.
  • Parameter "layout" zur Festlegung des sog. Near-, Far- oder Offset-Layouts eines Raid-10-Arrays

Zur Bedeutung der Chunk Size des Raid-Arrays

Die Chunk-Size legt fest, ab welcher atomaren Größe Information statt auf genau eine auch auf weitere Platten des Raid-Systems geschrieben wird. Ich zitiere von "https://raid.wiki.kernel.org/index.php/RAID_setup":

The chunk-size deserves an explanation. You can never write completely parallel to a set of disks. If you had two disks and wanted to write a byte, you would have to write four bits on each disk. Actually, every second bit would go to disk 0 and the others to disk 1. Hardware just doesn't support that. Instead, we choose some chunk-size, which we define as the smallest "atomic" mass of data that can be written to the devices. A write of 16 kB with a chunk size of 4 kB will cause the first and the third 4 kB chunks to be written to the first disk and the second and fourth chunks to be written to the second disk, in the RAID-0 case with two disks. Thus, for large writes, you may see lower overhead by having fairly large chunks, whereas arrays that are primarily holding small files may benefit more from a smaller chunk size. ....
For optimal performance, you should experiment with the chunk-size, as well as with the block-size of the filesystem you put on the array. For others experiments and performance charts, check out our Performance page...

Die man-Seite zu mdadm offenbart, dass die Chunk-Size in KiB definiert wird. Viele Artikel zu SW-Raid im Internet empfehlen eine Chunk-Size von ≥ 512KiB. 512KiB ist übrigens auch die Default-Einstellung von "mdadm" (welche z.B. auch von YaST herangezogen wird).

Aber ist diese Einstellung auch wirklich gut?
Mißtrauisch sollte einen u.a. die Tatsache machen, dass z.B. Intel bei der Anlage von imsm-Raid-Containern mittels iRST als Default eine viel kleinere Chunk Size von nur 32KiB wählt. Interessant, nicht wahr?

Nun erhält man bei einem Raid-10-System (im sog. NN-Layout; s.u.) ohne Pufferung maximal zweifache Schreib- und Lese-Geschwindigkeiten gegenüber dem Zugriff auf nur eine SSD. Diese Geschwindigkeitsvorteile sind offenbar aber durch einen einzelnen Raid-Zugriff nicht erreichbar, wenn die zu schreibenden oder zu lesenden Informationseinheiten kleiner oder gleich der Chunk-Size sind. Hiermit stellt sich also die Frage nach dem durchschnittlichen Lastprofil für das Raid-Array! Müssen im mittel kleine oder große Datenpakete zum/vom Array transferiert werden?

Was einem in diesem Zusammenhang auch noch einfällt, ist Folgendes:
Es wird vermutlich einen großen Unterschied machen, ob mehrere Jobs des Betriebssystems gleichzeitig Schreib- oder Lese-Operationen verlangen oder ob nur 1 Job sporadisch kleine Informationseinheiten abgreift bzw. schreibt. So wie im letzteren Fall etwa ein (virtualisiertes) Betriebssystem oder ggf. sequentielle Datenbanktransaktionen. Bei mehreren parallelen Jobs kann das System die Daten dagegen zu größeren Transfer-Einheiten bündeln.

So wird es auf einem Fileserver mit vielen Usern und bei statistischer Verteilung von Filedaten über die 2 effektiven Platten des Raid-Systems eine große Chunk-Size eher von Vorteil sein - auch wenn pro Job deutlich kleinere Dateibrocken vorliegen. Im Schnitt über alle Jobs liest man dann immer noch von oder schreibt auf 2 Platten gleichzeitig. Bei einer Linux-Workstation, auf der ggf. ein virtualisierter KVM-Gast ausgeführt wird, kann das aber ganz anders aussehen. Zudem besitzen aktuelle SSDs auch noch einen internen Write Cache, der einem dazwischenfunken kann.

Man muss also die konkrete Festlegung der Chunk Size auf sein Zielszenario und die dort gegebenen Verhältnisse anpassen. Bislang habe ich euch noch keine quantitativen Informationen über den Einfluss der Chunk Size geliefert. Im nächsten Beitrag dieser Serie werde ich aber Messdaten und Anhaltspunkte dafür liefern, dass eine CS von 32KiB gar kein so schlechter Kompromiss ist - zumindest für die Kernelversion 4.1.

Übrigens: Man kann die Chunk Size für ein SW Raid Array bei Bedarf im Nachhinein ändern. Das kostet aber erhebliche (!) Zeit, da das Raid-System umgeschrieben werden muss und es während dieser Zeit auch potentielle Ausfälle verkraften muss. Dafür müssen aufwändige Sicherheitsvorkehrungen getroffen werden. Siehe hierzu etwa:
https://forum.ubuntuusers.de/topic/chunksize-bei-mdadm-raid5-onthefly-aendern/
http://serverfault.com/questions/186518/is-it-possible-to-change-the-raid5-chunk-size-of-an-existing-device

Performance-relevant ist natürlich auch, dass die Chunk Size ein ganzzahliges Vielfaches der Blockgröße des Filesystems sein sollte. Im Beispiel dieses Artikels wähle ich

--chunk=32

Zur Bedeutung des "bitmap"-Parameters

Im letzten Artikel hatte ich ein Szenario beschrieben, in dem eine Platte eines Raid-5-Arrays ausfiel, weil ein SATA-Kabel locker saß. Für solche Fälle können sog. "Bitmaps" helfen, die Rebuild Zeit drastisch zu verkürzen. Hierbei werden bestimmte Raid Chunks, die sich im "Transaktionsstatus" befinden, zunächst als "unclean" markiert. Nach Abschluss der Schreibaktion dagegen als "clean". Durch Zeitstempelvergleiche müssen bei einem nur temporären Ausfall einer Platte (Timeout; verursacht z.B. durch unsaubere Kabelkontakte oder Systemfehler/Systemcrashes) dann nicht alle Blöcke des File-Arrays rekonstruiert werden, sondern nur die als "unclean" markierten.

Zitat aus https://raid.wiki.kernel.org/index.php/Write-intent_bitmap:

When an array has a write-intent bitmap, a spindle (a device, often a hard drive) can be removed and re-added, then only blocks changes since the removal (as recorded in the bitmap) will be resynced. Therefore a write-intent bitmap reduces rebuild/recovery (md sync) time if:
     * the machine crashes (unclean shutdown)
     * one spindle is disconnected, then reconnected.
If one spindle fails and has to be replaced, a bitmap makes no difference.

Aber: die entsprechenden Bitmap-Informationen müssen bei jeder Plattentransaktion auch geschrieben werden. Das kostet Performance; siehe http://blog.liw.fi/posts/write-intent-bitmaps/. Durch Verlagern der Bitmap auf außerhalb des Raid-Systems liegende gepufferte Datenträger kann man die Write-Performance erhöhen. Interne Bitmaps verringern die Schreib-Performance wirklich spürbar - nämlich bis zu 30%.

Nun legt z.B. die Raid-Verwaltung von YaST's Partitioner unter Opensuse leider automatisch eine interne Bitmap an, ohne dass man dies unter YaST beeinflussen könnte. Daher sollte man den "bitmap"-Parameter lieber direkt mit dem mdadm-Befehl unter Kontrolle bekommen.

Da die Rekonstruktion von Raid-10-SSD-Arrays im (NN-Layout) recht schnell von statten geht und sich dabei im Gegensatz zu Raid-5 auch die Schreiboperationen in Grenzen halten, verzichtet man für eine optimale Write-Performance von Raid-10-Arrays über SSDs eher ganz auf das Anlegen einer Bitmap. Meine Wahl für die zugehörige Option des mdadm-Befehls ist also (s. zur Syntax die man-Seite zu mdadm):

--bitmap=none

Erwähnt sei hier, dass das Anlegen von imsm-Raid-Containern mit Hilfe von BIOS-Funktionen (iRST) für den Z170-Sunrise Point-H Sata Controller) von Intel nicht zum automatischen Anlegen einer Bitmap führt. Bei Performancevergleichen zwischen reinen SW-Raids und iRST/imsm-Fake-Raids ist das zu berücksichtigen. Dabei gilt nach eigenen Tests:

Ein mit Hilfe vom mdadm angelegtes reines SW-Raid ist ohne Bitmap mindestens so schnell wie ein mit BIOS-Funktionen angelegter iRST/imsm-Raid-10-Container am gleichen SATA3-Controller.

Zur Bedeutung des Layouts für md-basierte SW-Raid-10-Arrays

Man kann sich Raid-10 z.B. als eine Kombination aus Raid 1 und Raid 0 vorstellen (Stripe of Mirrors). Aber das "raid10"- und das "md_mod"-Modul für "komplexe" SW-Raid10-Arrays unter Linux erlauben über eine spezifische, direkte Raid-10-Umsetzung interessante zusätzliche Dinge. U.a. kann man Daten über verschiedene Bereiche der beteiligten Platten so verteilen, dass im optimalen Fall immer mehr als 2 effektive Platten die Leseperformance beeinflussen. Man spricht dann von sog. "Raid10-Layouts":

  • das sog. Near Layout entspricht im wesentlichen einem konventionellen Raid-1+0-Layout.
  • Dass Far-Layout und Offset-Layout verteilen die Daten dagegen versetzt über die die Laufwerke; man nähert sich dadurch bzgl. des Lesens einem Striping über 4 Disks an. Das erhöht die Leseperformance drastisch in Richtung eines Raid-5-Systems - es kann aber zu Lasten der Schreib-Performance aufgrund zusätzliche Seek-Zeiten gehen.

Bzgl. der präzisen Organisation über verschiedenen Platten bzw. Plattenbereiche hinweg unterscheiden sich die Layouts deutlich. Das beeinflusst nicht nur die Performance: So ist dass Offset-Layout weniger gegen Ausfälle gewappnet als das Near- oder Far-Layout.

Wer mehr über unterschiedliche Layouts von md-Raid-10-Arrays erfahren möchte, kann etwa folgende Artikel zu Rate ziehen, die die Datenveteilung über die beteiligten Devices z.T. mit schönen Grafiken erklären:

http://www.ilsistemista.net/index.php/linux-a-unix/35-linux-software-raid-10-layouts-performance-near-far-and-offset-benchmark-analysis.html?start=1
https://www.suse.com/documentation/sles10/stor_admin/data/raidmdadmr10cpx.html
http://serverfault.com/questions/139022/explain-mds-raid10-f2
http://xmodulo.com/setup-raid10-linux.html
http://blog.jamponi.net/2007/12/some-raid10-performance-numbers.html

Der Leser muss sich für ein bestimmtes Layout entscheiden - und sollte die Performanceunterschiede idealerweise vorher mit Hilfe von Test-Arrays ermitteln. Mir persönlich kommt es auf gute Schreibperformance auch im Random-Bereich an. Ich verhalte mich daher konservativ und nutze das N2-Layout (also ein Near-Layout):

--layout=n2

Das ist übrigens auch der Defaultwert von mdadm.

Schritt 3 zum SW-Raid10-Array: Zusammenbinden der Raw-Partitionen der SSDs mittels "mdadm"

Wir sind nun soweit, den mdadm-Befehl abzusetzen. Danach wird Linux sofort beginnen, das Raid-Array aufzubauen; dies entspricht einem erstmaligen Synchronisationsprozess.

mdadm --create --verbose /dev/md127 --level=raid10 --bitmap=none --chunk=32 --layout=n2 --raid-devices=4 /dev/sda2 /dev/sdb2 /dev/sdc2 /dev/sdd2

 
Das Raid Device sollte hier mit "/dev/mdijk" bezeichnet werden. "ijk" steht dafür für drei Ziffern. Ein Device mit denselben Ziffern sollte es natürlich noch nicht geben (Check etwa mit cat /proc/mdstat). Daneben kann man dem Device aber auch noch einen echten "Namen" geben (s.u.).

Ist das Raid Array fertig sychronisiert, so zeigt einem etwa ein Blick in "/proc/mdstat":

mytux:~ # cat /proc/mdstat
Personalities : [raid10] [raid6] [raid5] [raid4] 
md127 : active raid10 sdc2[2] sdb2[1] sdd2[3] sda2[0]
      440143872 blocks super 1.2 32K chunks 2 near-copies [4/4] [UUUU

 

Optionaler Schritt 3.1 zum SW-Raid10-Verbund: Beschleunigung des initialen oder eines (Re-) Synchronisations-Prozesses

Die Synchronisation der Disks oder Partitionen, aus denen ein Array besteht, erfordert Zeit und belastet natürlich CPU wie I/O-Subsysteme. Wie für echte HW-Raid-Controller gibt es daher auf für SW-Raids Parameter, die es erlauben, die Performance der entsprechenden Hintergrundsprozesse zu steuern. Defaultwerte sorgen meist für eine sehr geringe Zusatzbelastung zum laufenden Linux-Betrieb.

Ich setze auf meinem System daher regelmäßig zwei relevante Parameter (speed_limit_max, speed_limit_min) nach oben - dies führt zu einer substanziellen Beschleunigung des Sync-Vorgangs, der für Raid10-SSD-Arrays mit effektiven 400GB auf modernen Systemen deutlich unter 5 Minuten abgeschlossen werden kann. Bei Default-Einstellungen würde das viel länger dauern.

echo 500000 > /proc/sys/dev/raid/speed_limit_max
echo 200000 > /proc/sys/dev/raid/speed_limit_min

Das kann man auch während des laufenden Sync-Prozesses absetzen. Wie sich das auf die Sync-Performance auswirkt, kann man etwa an folgendem Output für ein anderes Array auf einem anderen System ablesen:

alpha:~ # echo 500000 > /proc/sys/dev/raid/speed_limit_max
alpha:~ # echo 200000 > /proc/sys/dev/raid/speed_limit_min
alpha:~ # cat /proc/mdstat
Personalities : [raid10] 
md_d0 : active raid10 sdd3[3] sdc3[2] sdb3[1] sda3[0]
      167651328 blocks super 1.2 512K chunks 2 near-copies [4/4] [UUUU]
      [===>.................]  resync = 16.3% (27408896/167651328) finish=6.0min speed=384214K/sec
      
unused devices: <none>
alpha:~ # cat /proc/mdstat
Personalities : [raid10] 
md_d0 : active raid10 sdd3[3] sdc3[2] sdb3[1] sda3[0]
      167651328 blocks super 1.2 512K chunks 2 near-copies [4/4] [UUUU]
      [===>.................]  resync = 18.8% (31654272/167651328) finish=4.6min speed=486124K/sec
      
unused devices: <none>
alpha:~ # cat /proc/mdstat
Personalities : [raid10] 
md_d0 : active raid10 sdd3[3] sdc3[2] sdb3[1] sda3[0]
      167651328 blocks super 1.2 512K chunks 2 near-copies [4/4] [UUUU]
      [===>.................]  resync = 19.3% (32462592/167651328) finish=4.2min speed=525478K/sec

 
Man beachte das schnelle Ansteigen des "speed"-Wertes nach Anheben der Werte von "speed_limit_max" und "speed_limit_min".

Optionaler Schritt 3.2: Explizite Benennung des Raid Arrays mittels der "mdadm"-Option "--name"

Eine explizite Benennung des Raid-Arrays setzt die Verwendung der Option "--name" voraus. Siehe hierzu die man-Seiten zu mdadm. Der dafür angegebene String schlägt sich in einem Devicenamen unter /dev/md/name nieder.

Beispiel - wir benennen nachfolgend das Device mit "d00":

mdadm --create /dev/md127 --name=d00 --level=raid10 --bitmap=none --chunk=32 --layout=n2 --raid-devices=4 /dev/sda2 /dev/sdb2 /dev/sdc2 /dev/sdd2

 

Das Array "/dev/md127" erscheint dann wie folgt

mytux:~ # cat /proc/mdstat
md127 : active raid10 sdb2[1] sdd2[3] sda2[0] sdc2[2]
      440143872 blocks super 1.2 32K chunks 2 near-copies [4/4] [UUUU]

mytux:~ # mdadm --detail /dev/md127
/dev/md127:
        Version : 1.2
  Creation Time : Fri Dec  2 12:07:25 2016
     Raid Level : raid10
     Array Size : 440143872 (419.75 GiB 450.71 GB)
  Used Dev Size : 220071936 (209.88 GiB 225.35 GB)
   Raid Devices : 4
  Total Devices : 4
    Persistence : Superblock is persistent

    Update Time : Fri Jan 13 14:56:39 2017
          State : clean 
 Active Devices : 4
Working Devices : 4
 Failed Devices : 0
  Spare Devices : 0

         Layout : near=2
     Chunk Size : 32K

           Name : mytux:d00  (local to host mytux)
           UUID : 96fc09a4:f70044d1:4258b0dd:bdfb2b81
         Events : 19

    Number   Major   Minor   RaidDevice State
       0       8        2        0      active sync set-A   /dev/sda2
       1       8       18        1      active sync set-B   /dev/sdb2
       2       8       34        2      active sync set-A   /dev/sdc2
       3       8       50        3      active sync set-B   /dev/sdd2

 

Tatsächlich findet man folgende Verlinkung vor:

mytux:~ # ls -la /dev/md  
total 0
....
lrwxrwxrwx  1 root root     8 Jan 13 16:10 d00 -> ../md127
....

 

Warum braucht man das?
Ich habe darüber auch eine Weile gerätselt, bis mir aufgefallen ist, dass ein und dasselbe Raid-Array "/dev/md127" bei einem Neustart des Systems auch mal als "/dev/md126" erscheinen kann. Stellt sich die Frage, wer eigentlich beim Booten die Devicenamen festlegt. Antwort: "udev".

Offenbar gibt es hierbei aber Freiheitsgrade. Daher die explizite Namensgebung, um Eindeutigkeit zu schaffen. Nimmt ein User keine explizite Namensvergabe vor, so greifen bestimmte Regeln. Aus "/dev/md129" wird dann etwa der Name "129" (mit dem eindeutigen Device "/dev/md/129").

Da der Name mit anderen Raid-Informationen im Superblock der beteiligten Partitionen hinterlegt wird, kann das Device /dev/md/name immer eindeutig rekonstruiert werden.

Wichtig wird dies etwa bei gezielt vorgenommenen Einträgen in der "/etc/fstab". Da "/dev/md/name" eindeutig ist, kann man in der "/etc/fstab" damit operieren. Z.B., wenn man das Raid Array mit einem Filesystem versehen hat. Allerdings gilt: Best Practice wäre in der "/etc/fstab" die Nutzung der UUID, die dem Raid Array auch zugeteilt wird - wie man oben sieht.

Der YaST-Partitioner zeigt die Raid-Devices übrigens immer unter dem jeweils vergebenen Namen an.

Dennoch muss ich sagen, dass ich die Logik der Namensvergabe unter mdadm bis heute nicht völlig verstanden habe. Es werden nämlich auch Einträge zu den benannten Raid Arrays in der Datei "/etc/mdadm.conf" vorgenommen (Opensuse; Einstellung des Ortes der Datei über sysconfig. Auf anderen Systemen liegt mdadm.conf möglicherweise unter "/etc/mdadm/mdadm.conf"). Diese Datei wird bei Existenz beim Booten ggf. für das "Assemblen" eines Raid Arrays herangezogen.

Im Besonderen kann es dann zu Problemen kommen, wenn Raid Arrays unter einer bestimmten Betriebssystem-Installation auf der gleichen Hardware-Plattform erzeugt, modifiziert und dann unter einer anderen Betriebssystem-Installation weiterverwendet werden sollen. Manuelle Korrekturen der Datei mdadm.conf nimmt man etwa mit dem sukzessiven Absetzen von befehlen der Art

mdadm -Db /dev/md/name >> /etc/mdadm.conf

für alle Raid-Array Devices vor. Oder alternativ und gleichzeitig für alle Arrays :

mdadm --detail --scan >> /etc/mdadm.conf

Die alten Einträge zu Raid Arrays in der Datei sollte man natürlich löschen. Nach einer manuellen Korrektur von mdadm.conf sollte man unter bestimmten Betriebssystemversionen zur Sicherheit zudem auch noch "mkinitrd" bzw. "update-initramfs -u" laufen lassen - je nachdem wie das "initramfs" angelegt wird.

Der Kernel braucht die Datei "mdadm.conf" allerdings nicht zwingend; er kann die notwendigen Infos auch direkt aus Superblock-Informationen der beteiligten Raid-Partitionen auslesen.

Ich bin bisher ganz gut damit gefahren, den "--name"-Parameter explizit zu verwenden, udev werkeln zu lassen - und bei Inkonsistenzen oder Problemen beim Wechsel zwischen verschiedenen Betriebssystem-Installationen die jeweilige "/etc/mdamd.conf" ganz zu löschen.

Empfehlung
Ich empfehle, als Namen immer explizit eine 3-stellige Nummer anzugeben - z.B. --name=127. "udev" richtet nimmt dann auch die Anlage des Devices als "/dev/md127" vor. Also:

mdadm --create /dev/md127 --name=127 --level=raid10 --bitmap=none --chunk=32 --layout=n2 --raid-devices=4 /dev/sda2 /dev/sdb2 /dev/sdc2 /dev/sdd2

 
Das funktioniert gut - auch über verschiedene Betriebssysteminstallationen und mdadm-Versionen hinweg.

Umbenennung eines Arrays:
Der Name eines Arrays kann im Nachhinein noch geändert werden. Siehe
http://askubuntu.com/questions/63980/how-do-i-rename-an-mdadm-raid-array
http://www.unixbulletin.com/rename-an-mdadm-raid-array/

Wie geht man dabei vor? Nehmen wir ein Beispiel-Array, dem die Bezeichnung "--name=d00" mitgegeben wurde. Wir wollen das Array künftig unter "/dev/md126" bzw. "/dev/md/126" ansprechen. Folgende Befehlskette ist für die Umbenennung nötig.

mytux:~ # mdadm --stop /dev/md/d00
mytux:~ # mdadm --assemble /dev/md/126 --name=126 --update=name /dev/sda2 /dev/sdb2 /dev/sdc2 /dev/sdd2
mytux:~ # mdadm -Db /dev/md/126 >> /etc/mdadm.conf

 
Danach:

  • Löschen alter Einträge für d00 in /etc/mdadm.conf.
  • Dann Reboot.

Links zum Thema der Raid-Array-Benennung
https://bugzilla.redhat.com/show_bug.cgi?id=606481
https://bugzilla.redhat.com/show_bug.cgi?id=1201962
http://serverfault.com/questions/494569/mdadm-raid-device-name-changed-on-reboot
https://ubuntuforums.org/showthread.php?t=2265120
http://unix.stackexchange.com/questions/23879/using-mdadm-examine-to-write-mdadm-conf
https://wiki.ubuntuusers.de/Software-RAID/
http://unix.stackexchange.com/questions/80501/no-etc-mdadm-conf-in-centos-6
http://askubuntu.com/questions/63980/how-do-i-rename-an-mdadm-raid-array

Schritt 4 zum SW-Raid10-Array: Anlegen einer LVM2 Volume Group und Logical Volumes

Nun könnte man das Array "/dev/mdxxx", wenn man will, direkt formatieren und mit einem ext4-Filesystem versehen. Ich ziehe aus Flexibilitätsgründen aber vor, LVM einzusetzen. Das Anlegen von LVM Volume Groups und LVM Volumes erfolgt nach Lehrbuch. Ich gehe hier aus Platzgründen nicht auf Details ein. Standardparameter können herangezogen werden.

Auf das Setzen der Parameter

"md_chunk_alignment = 1" und "issue_discards = 1"

in der Datei "/etc/lvm/lvm.conf" hatte ich bereits hingewiesen. Siehe hierzu auch :
http://pof.eslack.org/2013/01/12/ssd-alignment-on-linux-with-ext4-and-lvm/
Unter Opensuse kann man erneut YaST's Partitioner zum bequemen Anlegen der Volume Groups und Volumes verwenden.

Schritt 5 zum SW-Raid10-Array: Berechnung und Check der Parameter stride und stripe-width des ext4-Filesystems

Bleibt noch, entweder das Raid-Array direkt zu oder angelegte LVM Logical Volumes mit einem ext4-Filesystem zu versehen. Wir hatten schon erwähnt, dass dabei folgende Größen berücksichtigt werden sollten:

  • Raid-Chunk-Size [CS]: Die Menge an Daten, die auf ein Laufwerk (genauer eine laufwerksbezogene 0xFD-Partition) des Raid-Arrays geschrieben wird, bevor ein Wechsel des Laufwerks erfolgt (s.o.). je nach Striping des Raid-Arrays kann dabei auf mehrere Laufwerke gleichzeitig geschrieben werden.
  • Block-Size [BS] des Filesystems : Das ist die Größe der Dateisystemblöcke in Bytes. Typisch unter Linux: 4096 Byte (4 KiB).
  • Stride-Size [STS] des Filesystems: Entspricht der Chunk Size als Vielfachem der BS. Bei 32 KiB Chunk Size mit 4 KiB Blöcken ergibt sich STS = 32 KiB/ 4 KiB = 8. Für eine Chunk-Size von 512KiB : STS = 128.
  • stripe-width [SW] des Filesystems: Entspricht der Menge an Blöcken, die insgesamt geschrieben wird, wenn volle Chunks parallel auf die Laufwerke des Raid-Arrays geschrieben werden. Diese berechnet sich daher als
    STS * Anzahl der effektiv nutzbaren Disk-Partitionen [ENDP].

In eckigen Klammern habe ich dabei Abkürzungen angegeben, die ich nachfolgend in Formeln verwende.

Diese Größen gehen in die notwendige Berechnung der stride- und stripe-width-Parameter des ext4-Filesystems ein. Ist N die Zahl der eingesetzten Raid-Disks (bzw. der zugrunde liegenden Partitionen), so gilt für die Anzahl effektiv nutzbarer Devices ENDP :

ENDP = N/2 für Raid 10 Systeme
ENDP = N-1 für Raid 5 Systeme

Also:

  • Bei einem Raid 10 mit (Partitionen von) 4 SSDs: ENDP = 2.
  • Bei einem RAID 5  mit (Partitionen von) 4 SSDs : ENDP = 3.

Für eine Chunk-Size von 32 KiB ergibt sich auf einem Raid-10-System mit 4 SSDs also:

SW = STS * ENDP = 8 * 2 = 16

Einen Online-Calculator findet man hier: https://busybox.net/~aldot/mkfs_stride.html

Wie man diese Parameter bei Anwendung des mkfs-Befehls einsetzt, entnimmt man der man-Seite zu mkfs.ext4. Siehe hierzu die Option "-E" mit nachfolgenden komma-separierten Optionen.

Hat man das Filesystem angelegt, kann man die Parameterwerte mit "tune2fs -l" prüfen. Testen sollte man auch, ob man nach einem Mounten /z.B. auf /mnt) den "fstrim"-Befehl erfolgreich ausführen kann (z.B. "fstrim -v /mnt"). Das sollte bei einem Raid-10-Array anstandslos funktionieren!

Fazit

Die Anlage eines md-Raid-10-Arrays mit Hilfe des mdadm-Befehls ist eigentlich simpel. Dennoch gibt es selbst bei einer Entscheidung für ein Raid-10-Array eine Vielzahl von Überlegungen, die im Vorfeld anzustellen sind. Ich habe versucht, einige Aspekte zu diskutieren. Letztlich wird es einem aber nicht erspart bleiben, im Vorfeld der Ausstattung eines Produktiv-Systems ein paar Tests zu fahren.

Der "bitmap"-Parameter ist durchaus performance-relevant; insbesondere ist bei Performancevergleichen mit iRST/imsm-Raid-Containern zu beachten, ob auf dem Fake-Array eine Bitmap genutzt wird oder nicht.

Ein zentraler Parameter in dem ganzen Spiel ist aus meiner Sicht die Chunk-Size. Wir werden im nächsten Beitrag sehen, dass gerade im Random Read/Write-Bereich für Einzeljobs, die kleine Datenmengen schreiben, viel von dieser Größe abhängt.

Ich nehme hier mal vorweg, dass man hier zunächst die Extreme 32KiB und 512KiB für sein Zielszenario austesten sollte. Speziell für Fileserver sind große Chunk-Werte eher sinnvoll.

Ausblick

Im nächsten Artikel

SSD Raid Arrays unter Linux – IX – Chunk Size und Performance eines md-Raid-10-Arrays

präsentiere ich dann einige Ergebnisse von FIO-Tests für verschiedene Chunk-Size-Parameter unter Kernel 4.1.

Weitere Links

https://wiki.archlinux.org/index.php/RAID
https://wiki.ubuntuusers.de/Software-RAID/
https://wiki.mikejung.biz/Software_RAID
https://infogalactic.com/info/Non-standard_RAID_levels
http://forum.openmediavault.org/index.php/Thread/1333-Tuning-of-ext4-on-LVM-and-RAID/
http://en.linuxreviews.org/Mdadm