Die Festplatten Performance ist immer wieder ein entscheidendes Kriterium für den Betrieb der Applikation, relevant oftmals bei Datenbanken oder anderen Anwendungen welche viel Schreiben. Viele Applikationen nutzen mittlerweile verschiedene Caching-Methoden um ein Bottleneck bei der Festplatte zu kompensieren.
Warum man "dd" nicht zum Messen der I/O Leistung nutzen sollte
Oftmals findet man dazu im Linux Umfeld den üblichen Benchmark per vorinstalliertem "dd
", doch mit "dd
" lässt sich keine qualifizierte Aussage über die I/O Performance treffen. Hier spielen viele Faktoren mit rein, ohne ein ordentliches Caching wird heute in der Regel kein System mehr ausgeliefert. Der Cache, egal wie groß, spielt häufig eine entscheidende Rolle für die Performance und lässt sich in der virtuellen Maschine nicht global deaktivieren. Der Host-Node wird daher in der Regel die mit "dd
" erstellten Files in den Cache laden und erst später schreiben jedoch direkt dem Betriebssystem den Schreibvorgang bestätigen. Für den Test der reinen darunterliegenden I/O Performance lässt sich "dd
" daher nicht anwenden.
Grundsätzlich läuft "dd
" auch nur über einen Thread und führt einen sequentiellen Schreibvorgang aus. Doch für die üblichen Applikationen wie eine Datenbank, Webserver oder Mail Server ist ein sequentieller Schreibtest nicht aussagekräftig. Bei den Applikationen treten häufiger viele kleine Schreib- und Lesezugriffe auf, eher selten wird ein Datenbank Server mehrere GB am Stück schreiben müssen.
Mit "dd
" lässt sich ebenfalls auch nicht die Leseleistung testen.
Insgesamt kann man daher festhalten, dass ein einfacher "dd
" Test keine Aussagekräftigen und zuverlässigen Werte zurückliefert und daher keine gute Option zum Benchmarken darstellt.
Wie misst man aber nun am besten die Festplatte?
Eine deutlich bessere Alternative zu "dd
" ist "fio
", mithilfe von "fio
" lassen sich die IOPS des Storage am sinnvollsten Messen. In der Regel lässt sich "fio
" direkt über das Betriebssystem installieren, für Debian z.B. mit "apt install fio
".
Grundsätzlich kann man "fio
" mit verschiedenen Parametern und mit unterschiedlichen Workloads ausführen, nachfolgen dazu ein paar fertige Befehle. Bei der Ausführung der "fio
" Befehle wird im aktuellen Ordner eine 4 GB Datei mit dem Namen "benchmark
" angelegt. Die Datei kann abschließend auch wieder gelöscht werden. Da die meisten Anwendungen wie z.B. Datenbanken auch mit 4 KB Blöcken arbeiten, haben wir diesen Wert ebenfalls für die Benchmarks vorgesehen. Um hier ein wenig Last zu simulieren, arbeiten wir mit 64 gleichzeitigen Operationen.
Zufällige Lese und Schreib Messung
Mit diesem Befehl simulieren wir eine gemischte Workload von 75% Lesevorgänge und 25% Schreibvorgänge, das entspricht in der Regel der normalen und übliche Festplatten Auslastung.
fio --randrepeat=1 --ioengine=libaio --direct=1 --gtod_reduce=1 --name=benchmark --filename=benchmark --bs=4k --iodepth=64 --size=4G --readwrite=randrw --rwmixread=75
Eine Beispielausgabe von einem Testsystem auf unserem hochverfügbaren CEPH Cluster sieht wie folgt aus:
root@benchmark ~# fio --randrepeat=1 --ioengine=libaio --direct=1 --gtod_reduce=1 --name=benchmark --filename=benchmark --bs=4k --iodepth=64 --size=4G --readwrite=randrw --rwmixread=75
benchmark: (g=0): rw=randrw, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=64
fio-3.12
Starting 1 process
benchmark: Laying out IO file (1 file / 4096MiB)
Jobs: 1 (f=1): [m(1)][100.0%][r=48.8MiB/s,w=15.9MiB/s][r=12.5k,w=4081 IOPS][eta 00m:00s]
benchmark: (groupid=0, jobs=1): err= 0: pid=847: Sat Jan 23 21:06:48 2021
read: IOPS=13.1k, BW=51.3MiB/s (53.8MB/s)(3070MiB/59790msec)
bw ( KiB/s): min= 5248, max=65824, per=100.00%, avg=52775.98, stdev=12281.56, samples=119
iops : min= 1312, max=16456, avg=13194.00, stdev=3070.40, samples=119
write: IOPS=4392, BW=17.2MiB/s (17.0MB/s)(1026MiB/59790msec); 0 zone resets
bw ( KiB/s): min= 1744, max=21896, per=100.00%, avg=17637.75, stdev=4090.14, samples=119
iops : min= 436, max= 5474, avg=4409.42, stdev=1022.53, samples=119
cpu : usr=6.46%, sys=22.21%, ctx=1103655, majf=0, minf=15
IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=100.0%
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.1%, >=64=0.0%
issued rwts: total=785920,262656,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=64
Run status group 0 (all jobs):
READ: bw=51.3MiB/s (53.8MB/s), 51.3MiB/s-51.3MiB/s (53.8MB/s-53.8MB/s), io=3070MiB (3219MB), run=59790-59790msec
WRITE: bw=17.2MiB/s (17.0MB/s), 17.2MiB/s-17.2MiB/s (17.0MB/s-17.0MB/s), io=1026MiB (1076MB), run=59790-59790msec
Disk stats (read/write):
rbd0: ios=785523/262569, merge=62/55, ticks=1395961/1698534, in_queue=1793376, util=99.88%
Wir können hier sehen, dass im Schnitt 13.194 IOPS Lesend und 4409 IOPS Schreibend gemessen wurden. Das lässt sich anhand der beiden Zeilen auslesen (Orange hervorgehoben):
read: IOPS=13.1k, BW=51.3MiB/s (53.8MB/s)(3070MiB/59790msec)
bw ( KiB/s): min= 5248, max=65824, per=100.00%, avg=52775.98, stdev=12281.56, samples=119
iops : min= 1312, max=16456, avg=13194.00, stdev=3070.40, samples=119
write: IOPS=4392, BW=17.2MiB/s (17.0MB/s)(1026MiB/59790msec); 0 zone resets
bw ( KiB/s): min= 1744, max=21896, per=100.00%, avg=17637.75, stdev=4090.14, samples=119
iops : min= 436, max= 5474, avg=4409.42, stdev=1022.53, samples=119
Zufällige Lese Messung
Mit diesem Befehl simulieren wir zufällige Lesevorgänge, das könnte z.B. dem Abrufen der Bilderdatenbank von verschiedenen Nutzern in einer Nextcloud entsprechen.
fio --randrepeat=1 --ioengine=libaio --direct=1 --gtod_reduce=1 --name=benchmark --filename=benchmark --bs=4k --iodepth=64 --size=4G --readwrite=randread
Eine Beispielausgabe von einem Testsystem auf unserem hochverfügbaren CEPH Cluster sieht wie folgt aus:
root@benchmark ~# fio --randrepeat=1 --ioengine=libaio --direct=1 --gtod_reduce=1 --name=benchmark --filename=benchmark --bs=4k --iodepth=64 --size=4G --readwrite=randread
benchmark: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=64
fio-3.12
Starting 1 process
Jobs: 1 (f=1): [r(1)][100.0%][r=96.8MiB/s][r=24.8k IOPS][eta 00m:00s]
benchmark: (groupid=0, jobs=1): err= 0: pid=852: Sat Jan 23 21:08:32 2021
read: IOPS=24.3k, BW=95.1MiB/s (99.7MB/s)(4096MiB/43070msec)
bw ( KiB/s): min=85992, max=101520, per=100.00%, avg=97392.50, stdev=3096.82, samples=86
iops : min=21498, max=25380, avg=24348.14, stdev=774.21, samples=86
cpu : usr=7.59%, sys=26.90%, ctx=1039405, majf=0, minf=76
IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=100.0%
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.1%, >=64=0.0%
issued rwts: total=1048576,0,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=64
Run status group 0 (all jobs):
READ: bw=95.1MiB/s (99.7MB/s), 95.1MiB/s-95.1MiB/s (99.7MB/s-99.7MB/s), io=4096MiB (4295MB), run=43070-43070msec
Disk stats (read/write):
rbd0: ios=1044415/11, merge=0/1, ticks=591632/30, in_queue=45548, util=99.82%
Wir können hier sehen, dass im Schnitt 24348 IOPS Lesend gemessen wurden. Das lässt sich anhand der Zeile auslesen (Orange hervorgehoben):
read: IOPS=24.3k, BW=95.1MiB/s (99.7MB/s)(4096MiB/43070msec)
bw ( KiB/s): min=85992, max=101520, per=100.00%, avg=97392.50, stdev=3096.82, samples=86
iops : min=21498, max=25380, avg=24348.14, stdev=774.21, samples=86
Das höhere Ergebnis kommt dadurch zustande, dass hier lediglich Lesend getestet wurde. Die virtuelle Maschine musste hier also nicht zeitgleich Lese und Schreiboperationen ausführen sondern konnte die vollen Ressourcen für die Leseoperationen nutzen.
Zufällige Schreib Messung
Mit diesem Befehl simulieren wir zufällige Schreibvorgänge, das könnte z.B. dem Hochladen von mehreren Dateien von verschiedenen Nutzern in einer Nextcloud entsprechen.
fio --randrepeat=1 --ioengine=libaio --direct=1 --gtod_reduce=1 --name=benchmark --filename=benchmark --bs=4k --iodepth=64 --size=4G --readwrite=randwrite
Eine Beispielausgabe von einem Testsystem auf unserem hochverfügbaren CEPH Cluster sieht wie folgt aus:
root@benchmark ~# fio --randrepeat=1 --ioengine=libaio --direct=1 --gtod_reduce=1 --name=benchmark --filename=benchmark --bs=4k --iodepth=64 --size=4G --readwrite=randwrite
benchmark: (g=0): rw=randwrite, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=64
fio-3.12
Starting 1 process
Jobs: 1 (f=1): [w(1)][100.0%][w=31.5MiB/s][w=8055 IOPS][eta 00m:00s]
benchmark: (groupid=0, jobs=1): err= 0: pid=907: Sat Jan 23 21:11:29 2021
write: IOPS=7518, BW=29.4MiB/s (30.8MB/s)(4096MiB/139472msec); 0 zone resets
bw ( KiB/s): min= 856, max=50056, per=100.00%, avg=30094.62, stdev=9216.80, samples=278
iops : min= 214, max=12514, avg=7523.64, stdev=2304.21, samples=278
cpu : usr=3.46%, sys=11.80%, ctx=1146778, majf=0, minf=16
IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=100.0%
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.1%, >=64=0.0%
issued rwts: total=0,1048576,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=64
Run status group 0 (all jobs):
WRITE: bw=29.4MiB/s (30.8MB/s), 29.4MiB/s-29.4MiB/s (30.8MB/s-30.8MB/s), io=4096MiB (4295MB), run=139472-139472msec
Disk stats (read/write):
rbd0: ios=27/1048306, merge=0/61, ticks=82/8777831, in_queue=6522172, util=99.91%
Wir können hier sehen, dass im Schnitt 7523 IOPS Schreibend gemessen wurden. Das lässt sich anhand der Zeile auslesen (Orange hervorgehoben):
write: IOPS=7518, BW=29.4MiB/s (30.8MB/s)(4096MiB/139472msec); 0 zone resets
bw ( KiB/s): min= 856, max=50056, per=100.00%, avg=30094.62, stdev=9216.80, samples=278
iops : min= 214, max=12514, avg=7523.64, stdev=2304.21, samples=278
Das höhere Ergebnis kommt dadurch zustande, dass hier lediglich Schreibend getestet wurde. Die virtuelle Maschine musste hier also nicht zeitgleich Lese und Schreiboperationen ausführen sondern konnte die vollen Ressourcen für die Schreiboperationen nutzen.
Latenzmessung mit "ioping
"
Mit dem Tool "ioping
" lässt sich die Latenz beim Zugriff auf die Festplatte messen. Das Tool "ioping
" lässt sich ganz einfach mittels "apt install ioping
" auf dem System installieren.
Mit dem Befehl "ioping -c 10 .
" kann man nun einen Test auf die lokale Festplatte starten.
root@benchmark ~# ioping -c 10 .
4 KiB <<< . (ext4 /dev/rbd0): request=1 time=674.9 us (warmup)
4 KiB <<< . (ext4 /dev/rbd0): request=2 time=857.9 us
4 KiB <<< . (ext4 /dev/rbd0): request=3 time=842.6 us
4 KiB <<< . (ext4 /dev/rbd0): request=4 time=807.5 us
4 KiB <<< . (ext4 /dev/rbd0): request=5 time=797.7 us
4 KiB <<< . (ext4 /dev/rbd0): request=6 time=733.3 us
4 KiB <<< . (ext4 /dev/rbd0): request=7 time=768.9 us
4 KiB <<< . (ext4 /dev/rbd0): request=8 time=617.5 us (fast)
4 KiB <<< . (ext4 /dev/rbd0): request=9 time=935.1 us (slow)
4 KiB <<< . (ext4 /dev/rbd0): request=10 time=887.3 us
--- . (ext4 /dev/rbd0) ioping statistics ---
9 requests completed in 7.25 ms, 36 KiB read, 1.24 k iops, 4.85 MiB/s
generated 10 requests in 9.00 s, 40 KiB, 1 iops, 4.44 KiB/s
min/avg/max/mdev = 617.5 us / 805.3 us / 935.1 us / 87.9 us
Wir können nun sehen, dass die Latenz im Schnitt bei 805.3 us liegt. Das lässt sich anhand der Zeile auslesen (Orange hervorgehoben):
min/avg/max/mdev = 617.5 us / 805.3 us / 935.1 us / 87.9 us
Es sollten bei der Messung keine größeren Differenzen auftreten, der Durchschnitt sollte ebenfalls bei unter 1 ms liegen.