Während meiner Benchmarks für den gemeinsamen Technical Report mit NetApp & Veeam TR-4948 wurde mir bewusst, wie wichtig das Thema LVM für eine gute Storage Performance ist. In aktuellen Veeam Projekten nutzen wir bevorzugt Hardened Repositories mit einer Kombination aus einem Rackserver + einer E-Series als direct attached Storage via FC oder SAS.
Die allermeisten Storage Systeme am Markt profitieren von Parallelität, also mehreren RAIDs, Volumes und LUNs, die optimalerweise bei Dual-Controller Systemen wie der NetApp E-Series über beide Controller präsentiert werden, um die maximale Performance zu erreichen. Dieser Ansatz hat jedoch den Nachteil, dass man dadurch sehr viele, unter Umständen kleine Objekte in Form von LUNs erzeugt. Besonders in Backup Umgebungen ist man jedoch daran interessiert, möglichst große und zusammenhängende Speicherbereiche zu haben, da die Job Administration sonst sehr komplex werden kann. Und genau diese beiden gegensätzlichen Ansätze lassen sich mit LVM perfekt vereinen.
Offtopic: Stand heute ist es leider so, dass mit der kommenden Version v13 von Veeam Backup & Replication das Deployment von Hardened Repositories deutlich vereinfacht wird, leider aber noch ohne external Storage Support. Bedeutet: Hier ist weiterhin ein manuelles Deployment z.B. mittels Rocky Linux nötig.
Bevor es mit den eigentlichen Tests losgeht, hier ein paar Worte zum Thema LVM unter Linux. LVM steht für Logical Volume Manager und ist eine Abstraktionsebene zu den physikalischen Speichergeräten. Mittels LVM lassen sich logische Datenträger auch über mehrere Festplatten hinweg erzeugen, und genau darum soll es in diesem Beitrag gehen.
LVM Linear
Wenn man von LVM spricht, geht es meistens um das „Standard LVM“ im linearen Modus. Hier werden mehrere Disks/LUNs logisch zusammengefasst und die Daten sequentiell auf die verfügbaren Member Disks geschrieben. Dabei wird eine Disk erst komplett gefüllt, bevor die nächste genutzt wird. Dieser Modus ist sehr einfach zu verwalten, lässt sich gut erweitern und eignet sich hervorragend, wenn man einfach nur einen Namespace mit großer Kapazität benötigt, Performance aber nicht elementar ist. Ein Vorteil ist noch das die Disks auch unterschiedlich groß sein können.

LVM Striping
Im Gegensatz dazu lässt sich durch Striping eine geringere Latenz und ein deutlich höherer Durchsatz erreichen, was LVM Striping zu einem mächtigen Feature macht. Dabei werden die Daten gleichmäßig über alle Disks/LUNs verteilt, alle Reads und Writes landen mit der angegebenen Stripe Size auf den Member Disks welche gleichmäßig gefüllt werden. Aufgrund des Stripings müssen alle Disks/LUNs innerhalb eines Stripesets gleich groß sein, das sollte man z.B. für Erweiterungen beachten. Hat man unterschiedliche Größen müsste man ein neues Stripeset beginnen und würde dann logischerweise auch nur hierrüber die Daten verteilen.

LVM Linear konfigurieren
Die Devices sdb, sdc, sdd und sde sind jeweils 100GB große VMDKs und werden für das LVM verwendet.
root@vhr-rocky veeam]# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 100G 0 disk
├─sda1 8:1 0 600M 0 part /boot/efi
├─sda2 8:2 0 1G 0 part /boot
└─sda3 8:3 0 98.4G 0 part
├─rl-root 253:0 0 60.8G 0 lvm /
├─rl-swap 253:1 0 7.9G 0 lvm [SWAP]
└─rl-home 253:2 0 29.7G 0 lvm /home
sdb 8:16 0 100G 0 disk
sdc 8:32 0 100G 0 disk
sdd 8:48 0 100G 0 disk
sde 8:64 0 100G 0 disk
sdf 8:80 0 110G 0 disk
sdg 8:96 0 110G 0 disk
sdh 8:112 0 110G 0 disk
sdi 8:128 0 110G 0 disk
Erzeugt die Volumegroup mit den Namen „vg_linear“
[root@vhr-rocky veeam]# vgcreate vg_linear /dev/sdb /dev/sdc /dev/sdd /dev/sde
Physical volume "/dev/sdb" successfully created.
Physical volume "/dev/sdc" successfully created.
Physical volume "/dev/sdd" successfully created.
Physical volume "/dev/sde" successfully created.
Volume group "vg_linear" successfully created
Logical Volume „lv_linear“ anlegen.
[root@vhr-rocky veeam]# lvcreate -l 100%FREE -n lv_linear vg_linear
Logical volume "lv_linear" created.
lvdisplay zeigt Details zum Logical Volume, die relevanten Teile habe ich hervorgehoben.
[root@vhr-rocky veeam]# lvdisplay -m /dev/vg_linear/lv_linear
--- Logical volume ---
LV Path /dev/vg_linear/lv_linear
LV Name lv_linear
VG Name vg_linear
LV UUID 57GeWQ-Miyb-GX07-kXPW-rjjl-sfNV-1g0ouO
LV Write Access read/write
LV Creation host, time vhr-rocky, 2025-05-27 11:54:49 +0200
LV Status available
# open 0
LV Size 399.98 GiB
Current LE 102396
Segments 4
Allocation inherit
Read ahead sectors auto
- currently set to 256
Block device 253:3
--- Segments ---
Logical extents 0 to 25598:
Type linear
Physical volume /dev/sdb
Physical extents 0 to 25598
Logical extents 25599 to 51197:
Type linear
Physical volume /dev/sdc
Physical extents 0 to 25598
Logical extents 51198 to 76796:
Type linear
Physical volume /dev/sdd
Physical extents 0 to 25598
Logical extents 76797 to 102395:
Type linear
Physical volume /dev/sde
Physical extents 0 to 25598
Anschließend noch das Filesystem anlegen, mounten und die Filesystem Rechte vergeben.
mkfs.xfs -b size=4096 -m reflink=1,crc=1 /dev/vg_linear/lv_linear
mkdir /mnt/linear
mount /dev/vg_linear/lv_linear /mnt/linear/
sudo chown -R veeam:veeam /mnt/linear
sudo chmod 700 /mnt/linear
LVM Striping konfigurieren
Die Devices sdf, sdf, sdh und sdi sind jeweils 110GB große VMDKs und werden für das LVM verwendet.
root@vhr-rocky veeam]# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 100G 0 disk
├─sda1 8:1 0 600M 0 part /boot/efi
├─sda2 8:2 0 1G 0 part /boot
└─sda3 8:3 0 98.4G 0 part
├─rl-root 253:0 0 60.8G 0 lvm /
├─rl-swap 253:1 0 7.9G 0 lvm [SWAP]
└─rl-home 253:2 0 29.7G 0 lvm /home
sdb 8:16 0 100G 0 disk
sdc 8:32 0 100G 0 disk
sdd 8:48 0 100G 0 disk
sde 8:64 0 100G 0 disk
sdf 8:80 0 110G 0 disk
sdg 8:96 0 110G 0 disk
sdh 8:112 0 110G 0 disk
sdi 8:128 0 110G 0 disk
Erzeugt die Volumegroup mit den Namen „vg_striping“
[root@vhr-rocky veeam]# vgcreate vg_striping /dev/sdf /dev/sdg /dev/sdh /dev/sdi
Physical volume "/dev/sdf" successfully created.
Physical volume "/dev/sdg" successfully created.
Physical volume "/dev/sdh" successfully created.
Physical volume "/dev/sdi" successfully created.
Volume group "vg_striping" successfully created
Logical Volume „lv_striping“ anlegen. -i
steht hierbei für die Anzahl der Stripes, -I
für die Stripe Size.
64k sind der Default für die Stripesize, leider konnte ich hier noch nicht alle Varianten testen, es ist aber sehr wahrscheinlich das größere Stripe Sizes wie 128k oder 256k gut zum Veeam Workload passen.
lvcreate -l 100%FREE -i 4 -I 64k -n lv_striping vg_striping
Logical volume "lv_striping" created.
lvdisplay zeigt Details zum Logical Volume, die relevanten Teile habe ich hervorgehoben.
root@vhr-rocky veeam]# lvdisplay -m /dev/vg_striping/lv_striping
--- Logical volume ---
LV Path /dev/vg_striping/lv_striping
LV Name lv_striping
VG Name vg_striping
LV UUID yTt0vn-d2W2-e02j-zRTV-XdEM-DKrJ-VRS8LQ
LV Write Access read/write
LV Creation host, time vhr-rocky, 2025-05-27 12:06:26 +0200
LV Status available
# open 0
LV Size 439.98 GiB
Current LE 112636
Segments 1
Allocation inherit
Read ahead sectors auto
- currently set to 1024
Block device 253:4
--- Segments ---
Logical extents 0 to 112635:
Type striped
Stripes 4
Stripe size 64.00 KiB
Stripe 0:
Physical volume /dev/sdf
Physical extents 0 to 28158
Stripe 1:
Physical volume /dev/sdg
Physical extents 0 to 28158
Stripe 2:
Physical volume /dev/sdh
Physical extents 0 to 28158
Stripe 3:
Physical volume /dev/sdi
Physical extents 0 to 28158
Anschließend noch das Filesystem anlegen, mounten und die Filesystem rechte vergeben.
mkfs.xfs -b size=4096 -m reflink=1,crc=1 /dev/vg_striping/lv_striping
mkdir /mnt/striping
mount /dev/vg_striping/lv_striping /mnt/striping/
sudo chown -R veeam:veeam /mnt/striping
sudo chmod 700 /mnt/striping
Benchmarks
Wie üblich in einer Lab Umgebung erfolgt dieser Test virtuell, das hat aber den Vorteil das ich mittels QoS ganz geschickt Performance Limitierungen setzen kann.
Test Setup:
– Rocky Linux 9.5 / minimal Installation als VMware VM
– 9 VMDKs (1x OS / 4x LVM Linear / 4x LVM Striping)
– NetApp NFS Storage als Datastore
– Integration in Veeam als Hardened Repository
Die 8 VMDKs für die LVMs sind mittels NetApp QoS auf 50MB/s beschränkt, um den Effekt der Parallelität optimal zeigen zu können. Wichtig bei QoS auf VMDKs, das Ziel muss die „*-flat.vmdk“ Datei sein und nicht nur die Descriptor Datei.
cl2::*> qos policy-group create -policy-group 50mb_max -vserver DEMO5 -is-shared false -max-throughput 50mb/s
cl2::*> volume file modify -vserver DEMO5 -volume vol_DEMO5_SSD_NFS_DS_01 -qos-policy-group 50mb_max -file /vhr-rocky/vhr-rocky_1-flat.vmdk
Die eigentlichen Tests erfolgen mittels FIO. Auch wenn es für diesen Test unerheblich ist nutze ich die Pattern welche Veeam für die Simulation von Backup Disk I/O empfiehlt, diese sind im KB2014 beschrieben.
LVM Linear
Erwarungsgemäß erreicht man hier maximal 50mb/s, was der maximalen Geschwindigkeit einer VMDK entspricht.
[root@vhr-rocky veeam]# fio --name=full-write-test --filename=/mnt/linear/test_full.dat --size=25G --bs=512k --rw=write --ioengine=libaio --direct=1 --time_based --runtime=60s -numjobs 16
full-write-test: (g=0): rw=write, bs=(R) 512KiB-512KiB, (W) 512KiB-512KiB, (T) 512KiB-512KiB, ioengine=libaio, iodepth=1
...
fio-3.35
Starting 16 processes
Jobs: 16 (f=16): [W(16)][61.7%][w=50.0MiB/s][w=100 IOPS][eta 00m:23s]
Mittels dem Tool iostat
lässt sich der Effekt sehr gut verdeutlichen da man hier die Performance der zugrunde liegenden Disks sieht. Output für bessere Lesbarkeit etwas gekürzt.
[root@vhr-rocky veeam]# iostat -xdmt 2
Device r/s rMB/s rrqm/s %rrqm r_await rareq-sz w/s wMB/s wrqm/s %util
dm-0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
dm-1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
dm-2 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
dm-3 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
dm-4 0.00 0.00 0.00 0.00 0.00 0.00 100.00 50.00 0.00 100.00
sda 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sdb 0.00 0.00 0.00 0.00 0.00 0.00 100.00 50.00 0.00 100.00
sdc 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sdd 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sde 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sdf 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sdg 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sdh 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sdi 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
Auch innerhalb von Veeam lässt sich genau dieser Effekt zeigen. An dieser Stelle sei erwähnt, dass der „Speed“ immer der maximale Speed ist, der während eines Jobs erreicht wurde. Die grüne Linie ist lesend, also vom Quellsystem, die rote Linie ist der Schreibvorgang ins Repository, in unserem Fall also das LVM.

LVM Striping
Im Gegensatz dazu erreicht man mit Striping den gewünschten Effekt, da alle 4 VMDKs parallel genutzt werden.
[root@vhr-rocky veeam]# fio --name=full-write-test --filename=/mnt/striping/test_full.dat --size=25G --bs=512k --rw=write --ioengine=libaio --direct=1 --time_based --runtime=60s -numjobs 16
full-write-test: (g=0): rw=write, bs=(R) 512KiB-512KiB, (W) 512KiB-512KiB, (T) 512KiB-512KiB, ioengine=libaio, iodepth=1
...
fio-3.35
Starting 16 processes
Jobs: 16 (f=16): [W(16)][13.3%][w=200MiB/s][w=399 IOPS][eta 00m:52s]
[root@vhr-rocky veeam]# iostat -xdmt 2
05/27/2025 02:28:11 PM
Device r/s rMB/s rrqm/s %rrqm r_await rareq-sz w/s wMB/s wrqm/s %util
dm-0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
dm-1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
dm-2 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
dm-3 0.00 0.00 0.00 0.00 0.00 0.00 3198.00 199.88 0.00 100.00
dm-4 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sda 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sdb 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sdc 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sdd 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sde 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sdf 0.00 0.00 0.00 0.00 0.00 0.00 400.00 50.00 400.00 100.00
sdg 0.00 0.00 0.00 0.00 0.00 0.00 400.00 50.00 400.00 100.00
sdh 0.00 0.00 0.00 0.00 0.00 0.00 399.50 49.94 400.00 100.00
sdi 0.00 0.00 0.00 0.00 0.00 0.00 399.50 49.94 400.00 100.00

Abschließend sei erwähnt, dass sich der identische Ansatz natürlich auch auf Server mit vielen internen Disks und damit mehreren RAIDs anwenden lässt. Wie jede zusätzliche Virtualisierungsschicht fügt natürlich auch LVM mehr Komplexität hinzu, welche Auswirkungen das auf mögliche Erweiterungs- oder Ausfallszenarien hat werde ich in einem kommenden Artikel beleuchten.