05-10-2024

SSD mirror; LVM on LUKS & LVM resize

/images/filler/hamburger_hafen.jpg

Seit gut einem halben Jahr nutze ich im täglichen Einsatz Arch Linux auch auf meinem Desktop, das kommende Support-Ende von Windows 10 in 2025 [1] und der damit drohende Umstieg auf Windows 11 (nicht solange ich es vermeiden kann Microsoft!) haben mich dazu gebracht, auch in diesem Bereich auf das Pinguin System zu setzen.

Arch Linux im Allgemeinen nutze ich seit etwas über 10 Jahren zufriedenstellend vor allem auf meinen Laptops. Dennoch wollte ich zunächst auf dem Desktop die Gegebenheiten ausloten und habe mir das System im Dualboot auf einer kleinen 256 GB NVMe SSD installiert.

Nach einer erfolgreichen Testphase, bei der ich vor allem die (dank DXVK [2] und Proton [3] ) mittlerweile gut ausgeprägte Gaming-Tauglichkeit (Spiele mit RICOCHET Anti-Cheat ausgeklammert, Hi Activision) und Hardware-Kompatibilität überprüft habe - ist es nun Zeit für den kompletten Umstieg.

Hierzu habe ich mir eine WD Blue SN580 2TB günstig organisiert, auf welcher das System zukünftig leben wird.

Um eine Neuinstallation zu umgehen, habe ich mir zum Ziel genommen, das bestehende System auf den neuen Massenspeicher zu spiegeln, die Partitionen anzupassen und im Nachgang alle notwendigen Änderungen in fstab & grub zu übernehmen (gerade letzteres fiel einfacher aus als gedacht).

Doch zunächst zur Partitionierung; im Grunde fahre ich ein System auf einem verschlüsselten LUKS Container, auf dem ein LVM aufsetzt. EFI sowie Boot Partition sind nicht verschlüsselt. Zur Realisierung habe ich daher auf dem „Bare Metal“ der alten 256 GB SSD (nvme0n1) drei physikalische Partitionen.

nvme0n1, [256GB]

Name

Format

Größe

EFI

fat32

100MB

Boot

ext4

512MB

LUKS

crypto_LUKS

237.9GB

Der encrypted LUKS Container [4] beinhaltet nur eine Volume Group (VG) welche 3 logische Volumen (LVs) beherbergt.

nvme0n1, LUKS Partition

Name

Format

Größe

SWAP

swap

8GB

Root

ext4

48GB

Home

ext4

181.1GB

/images/posts/lvm-on-luks-ssd-mirror/nvme0n1_base.png

Darstellung: Strukturen auf nvme0n1

Ziel der Spiegelung war nun, die bestehende Strukturen beizubehalten, auf die neue SSD zu übertragen und lediglich die Root und Home Partitionen von der Größe her anzupassen.

Um dies zu bewerkstelligen muss allerdings ebenfalls die LUKS Partition, als auch der Speicherplatz, der der Volume Group zur Verfügung steht, erweitert werden. Im Anschluss können dann die zwei Logical Volumes (Root & Home) vergrößert und die ext4 Partitionen angepasst werden.

Doch ein Schritt nach dem anderen. Zunächst startete ich mit der Erzeugung eines Arch Live Mediums, von dem ich die Änderungen am System durchführen konnte.

Live USB

Hierfür habe ich zunächst die aktuelle Arch ISO von archlinux.org heruntergeladen. Die offizielle Guide zur Erstellung eines USB Boot Mediums findet sich in der Wiki .

Selbstverständlich benötigt man einen USB Stick von 4GB+, welcher komplett überschrieben wird.

Danach kann mit

lsblk -o name,model,serial

oder

ls -l /dev/disk/by-id

kann dann der Pfad zum jeweiligen USB Stick gefunden werden, um dann mit

dd if=/pfad/zur/iso of=/usb/stick/pfad bs=4M conv=fsync oflag=direct status=progress

die ISO Datei inklusive Boot Flags auf den Datenträger geschrieben werden.

Wichtig! Das richtige Ziel festlegen, ansonsten überschreibt man womöglich Teile seines Systems.

Live Boot

Mit dem erstellten Stick kann nun in das Live System gebootet werden.

In meinem Fall starte ich nun mit der Einstellung des korrekten Tastatur Layouts [optional].

loadkeys de-latin1

danach kann man sich mit

lsblk

das Layout der Block Devices anschauen. Das müsste so oder so ähnlich aussehen:

NAME           MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINTS
loop0            7:0    0 790.3M  1 loop  /run/archiso/airootfs
sda              8:0    0 447.1G  0 disk
├─sda1           8:1    0   450M  0 part
├─sda2           8:2    0    99M  0 part
├─sda3           8:3    0    16M  0 part
└─sda4           8:4    0 446.6G  0 part
sdb              8:16   0 223.6G  0 disk
└─sdb1           8:17   0 223.6G  0 part
sdc              8:32   0 953.9G  0 disk
├─sdc1           8:33   0    16M  0 part
└─sdc2           8:34   0 953.9G  0 part
nvme0n1        259:0    0 238.5G  0 disk
├─nvme0n1p1    259:1    0   100M  0 part
├─nvme0n1p2    259:2    0   512M  0 part
└─nvme0n1p3    259:3    0 237.9G  0 part
nvme1n1        259:4    0   1.8T  0 disk

Die beiden Block Devices (Source und Target) identifiziert, kann mit der Spiegelung begonnen werden.

Hierfür habe ich plain dd genutzt.

dd if=/dev/nvme0n1 of=/dev/nvme1n1 bs=64M conv=fsync,notrunc oflag=direct status=progress

Flag

Zweck

if

ist das Source File

of

ist das Target File

bs=64M

setzte die Blockgröße beim Schreiben auf 64 Megabyte und erhöht den Durchsatz

conv=fsync

erzwingt das die Daten im Target zuerst geschrieben werden, das garantiert Konsistenz

conv=notrunc

verhindert Kürzungen auf dem Target, mit conv=fsync eigentlich nicht notwendig

oflag=direct

umgeht den Puffer des Betriebsystems und schreibt die Daten direkt auf das Medium, dies erhöht ebenfalls die Performance

status=progess

zeigt einen Fortschritt des Kopiervorgangs an

Das Ganze sieht wie folgt aus

root@archiso ~# dd if=/dev/nvme0n1 of=/dev/nvme1n1 bs=64M conv=fsync,notrunc oflag=direct status=progress
15636365312 bytes (16 GB, 15 GiB) copied, 19s, 823 MB/s

Je nach Größe des zu spiegelnden Mediums und der Lese/Schreibgeschwindigkeit, kann dies einige Minuten dauern.

root@archiso ~# dd if=/dev/nvme0n1 of=/dev/nvme1n1 bs=64M conv=fsync,notrunc oflag=direct status=progress

3815+1 records in
3815+1 records out
256060514304 bytes (256 GB, 238 GiB) copied, 314.535 s, 814 MB/s
dd if=/dev/nvme0n1 of=/dev/nvme1n1 bs=64M conv=fsync,notrunc oflag=direct  0.1s user 76.67s system 24% cpu 5:14.54 total

Damit ist der erste Schritt der Spiegelung abgeschlossen, mit

lsblk

können die Veränderungen auf den Block Devices überprüft werden.

NAME           MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINTS
loop0            7:0    0 790.3M  1 loop  /run/archiso/airootfs
sda              8:0    0 447.1G  0 disk
├─sda1           8:1    0   450M  0 part
├─sda2           8:2    0    99M  0 part
├─sda3           8:3    0    16M  0 part
└─sda4           8:4    0 446.6G  0 part
sdb              8:16   0 223.6G  0 disk
└─sdb1           8:17   0 223.6G  0 part
sdc              8:32   0 953.9G  0 disk
├─sdc1           8:33   0    16M  0 part
└─sdc2           8:34   0 953.9G  0 part
nvme0n1        259:0    0 238.5G  0 disk
├─nvme0n1p1    259:5    0   100M  0 part
├─nvme0n1p2    259:6    0   512M  0 part
└─nvme0n1p3    259:7    0 237.9G  0 part
nvme1n1        259:4    0   1.8T  0 disk
├─nvme1n1p1    259:8    0   100M  0 part
├─nvme1n1p2    259:9    0   512M  0 part
└─nvme1n1p3    259:10   0 237.9G  0 part

Wir sehen, das alte Layout wurde erfolgreich auf die neue Block Device übertragen. Da allerdings auch die alte Partitionstabelle und Zustände des LVM von nvme0n1 zu nvme1n1 übertragen wurde, wird der Speicherbereich am Ende von nvme1n1p3 (dem LUKS Container) nicht realisiert.

/images/posts/lvm-on-luks-ssd-mirror/nvme1n1_base.png

Darstellung: gespiegelte Struktur auf nvme1n1

LUKS Partition Resize

Bevor ich nun auf das Resizing eingehe, einmal ein Recap darüber wohin die Reise führen soll. In meinem Fall ist das.

nvme1n1, [2TB]

Name

Format

Größe

EFI

fat32

100MB

Boot

ext4

512MB

LUKS

crypto_LUKS

1.8TB

nvme1n1, LUKS Partition

Name

Format

Größe

SWAP

swap

8GB

Root

ext4

250GB

Home

ext4

1.4TB

Wie festgelegt, werde ich nur die Root und die Home Partition vergrößern. Die 8 GB SWAP sind für mich ausreichend, da ich auf genügend RAM zurückgreifen kann.

Dabei beginne ich nun damit zuerst die crypt_LUKS Partition zu vergrößern und öffne nvme1n1 mit parted.

parted /dev/nvme1n1

sobald diese geöffnet ist, kann (in meinem Fall) die dritte Partition vergrößert werden.

resizepart 3 100%

die 100% gib an, dass die Partition über alle verfügbaren Sektoren „wachsen“ und diese ausfüllen soll.

LUKS Container Resize

Im nächsten Schritt wird der LUKS Container vergrößert, hierfür muss dieser geöffnet werden. Als Mapping-Namen habe ich lvmcrypt gewählt.

cryptsetup luksOpen /dev/nvme1n1p3 lvmcrypt

Im Anschluss kann dann die Container Größe angepasst werden

cryptsetup resize lvmcrypt

und der Stand mit

lsblk

überprüft werden.

NAME           MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINTS
loop0            7:0    0 790.3M  1 loop  /run/archiso/airootfs
sda              8:0    0 447.1G  0 disk
├─sda1           8:1    0   450M  0 part
├─sda2           8:2    0    99M  0 part
├─sda3           8:3    0    16M  0 part
└─sda4           8:4    0 446.6G  0 part
sdb              8:16   0 223.6G  0 disk
└─sdb1           8:17   0 223.6G  0 part
sdc              8:32   0 953.9G  0 disk
├─sdc1           8:33   0    16M  0 part
└─sdc2           8:34   0 953.9G  0 part
nvme0n1        259:0    0 238.5G  0 disk
├─nvme0n1p1    259:5    0   100M  0 part
├─nvme0n1p2    259:6    0   512M  0 part
└─nvme0n1p3    259:7    0 237.9G  0 part
nvme1n1        259:5    0   1.8T  0 disk
├─nvme1n1p1    259:8    0   100M  0 part
├─nvme1n1p2    259:9    0   512M  0 part
└─nvme1n1p3    259:10   0   1,8T  0 part
  └─lvmcrypt   254:0    0   1,8T  0 crypt
    ├─vg0-swap 254:1    0     8G  0 lvm
    ├─vg0-root 254:2    0    48G  0 lvm
    └─vg0-home 254:3    0 181.6G  0 lvm
/images/posts/lvm-on-luks-ssd-mirror/nvme1n1_luks_resize.png

Darstellung: vergrößerte crypt_LUKS Partition und angepassterer LUKS Container

PV & LVM Resize

Jetzt muss noch das physical volume angepasst werden, dass passiert mit (Anmerkung, den richtigen Mapper beachten)

pvresize /dev/mapper/lvmcrypt
/images/posts/lvm-on-luks-ssd-mirror/nvme1n1_lvm_resize.png

Darstellung: resize des physikalischen Volumens

und letztlich werden die beiden logical volumes mit

lvextend -L +202GB /dev/vg0/root
lvextend -l +100%FREE /dev/vg0/home

anpassen.

Da die logical volumes ext4 partitionen enthalten, werden diese zuerst auf Konsistenz überprüft und abschließend vergrößert, um den jeweilig zur Verfügung stehenden Platz der logical volumes zu nutzen.

e2fsck -f /dev/vg0/root
resize2fs /dev/vg0/root
e2fsck -f /dev/vg0/home
resize2fs /dev/vg0/home
lsblk

.. verrät, der Stand ist faktisch korrekt ..

NAME           MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINTS
loop0            7:0    0 790.3M  1 loop  /run/archiso/airootfs
sda              8:0    0 447.1G  0 disk
├─sda1           8:1    0   450M  0 part
├─sda2           8:2    0    99M  0 part
├─sda3           8:3    0    16M  0 part
└─sda4           8:4    0 446.6G  0 part
sdb              8:16   0 223.6G  0 disk
└─sdb1           8:17   0 223.6G  0 part
sdc              8:32   0 953.9G  0 disk
├─sdc1           8:33   0    16M  0 part
└─sdc2           8:34   0 953.9G  0 part
nvme0n1        259:0    0 238.5G  0 disk
├─nvme0n1p1    259:5    0   100M  0 part
├─nvme0n1p2    259:6    0   512M  0 part
└─nvme0n1p3    259:7    0 237.9G  0 part
nvme1n1        259:5    0   1.8T  0 disk
├─nvme1n1p1    259:8    0   100M  0 part
├─nvme1n1p2    259:9    0   512M  0 part
└─nvme1n1p3    259:10   0   1,8T  0 part
  └─lvmcrypt   254:0    0   1,8T  0 crypt
    ├─vg0-swap 254:1    0     8G  0 lvm
    ├─vg0-root 254:2    0   250G  0 lvm
    └─vg0-home 254:3    0   1.4T  0 lvm
/images/posts/lvm-on-luks-ssd-mirror/nvme1n1_lvm_lv_resize.png

Darstellung: finaler Stand des Resizing

.. es gibt jedoch noch Schönheitsfehler.

Duplizierte UUIDs

So wurde beim Spiegeln, neben der Zustände der Block Devices, ebenfalls die UUIDs und PARTUUIDs übertragen, was eine geschwinde Prüfung mit

blkid /dev/nvme*

ergibt.

/dev/nvme0n1:  PTUUID=“b1aa601b-de7a-[..]“ PTTYPE=“gpt“
/dev/nvme0n1p1: UUID=“3B20-BA28“ BLOCK_SIZE=“512“ TYPE=“vfat“PARTUUID=“ffdf230a-[..]“
/dev/nvme0n1p2: UUID=“36c4aaa4-730e-[..]“ BLOCK_SIZE=“4096“ TYPE=“ext4“ PARTUUID=“f42bfc31-[..]“
/dev/nvme0n1p3: UUID=“be68a2e1-dc0e-[..]“ TYPE=“crypto_LUKS“ PARTUUID=“d7640400-9f06-[..]“

/dev/nvme1n1:  PTUUID=“b1aa601b-de7a-[..]“ PTTYPE=“gpt“
/dev/nvme1n1p1: UUID=“3B20-BA28“ BLOCK_SIZE=“512“ TYPE=“vfat“PARTUUID=“ffdf230a-[..]“
/dev/nvme1n1p2: UUID=“36c4aaa4-730e-[..]“ BLOCK_SIZE=“4096“ TYPE=“ext4“ PARTUUID=“f42bfc31-[..]“
/dev/nvme1n1p3: UUID=“be68a2e1-dc0e-[..]“ TYPE=“crypto_LUKS“ PARTUUID=“d7640400-9f06-[..]“

Das bedeutet, dass es beim Ansprechen der jeweiligen Medien zu Missverständnissen kommen kann (je nachdem, über welches device node die device angesprochen wird). Mehrfach identische UUIDs auszuweisen ist nicht gut.

Um mit dieser Problematik umzugehen gibt es nun drei Möglichkeiten:

  • Physikalische Entfernung der alten SSD

  • Formatierung der alten SSD und damit Zuweisung neuer UUIDs

  • Manuelle Zuweisung neuer UUIDs ohne Formatierung

Da ich zunächst überprüfen wollte ob das neue System bootet, bevor ich nvme0n1 formatiere und ich darüber hinaus zu faul war die SSD physikalisch auszubauen, entschied ich mich für Variante 3 – der manuellen Zuweisung neuer UUIDs, allerdings nur bei den notwendigen Partitionen.

Genauer gesagt, veränderte ich die UUIDs auf den relevanten Partitionen von nvme0n1, sodass nvme1n1 die „alten“ UUIDs behielt, dabei sparte ich mir die Arbeit Einträge in fstab nachträglich anzupassen.

Neue UUIDs

Um die UUID der alten Boot Partition nvme0n1p2 zu verändern, habe ich tune2fs genutzt.

tune2fs -U random /dev/nvme0n1p2

anschließend habe ich noch die UUID der alten crypt_LUKS Partition auf nvme0n1p3 angepasst.

cryptsetup luksUUID –uuid $(uuidgen) /dev/nvme0n1p3

Der Stand kann jetzt nochmal mit

blkid /dev/nvme*

überprüft werden.

grub Anpassungen

Als letzten Schritt, bevor auf die neue SSD gebootet werden kann, habe ich die zu nutzende cryptdevice in der grub Config angepasst. Hierfür habe ich boot sowie root gemounted und hab mich mit chroot in das System begeben.

mount /dev/vg0/root  /mnt
mount /dev/nvme0n1p2 /mnt/boot

mount --bind /dev /mnt/dev
mount --bind /proc /mnt/proc
mount --bind /sys /mnt/sys
mount --bind /run /mnt/run

chroot /mnt

Im Anschluss kann die grub Config mit

vim /etc/default/grub

geöffnet werden.

Die Zeile

GRUB_CMD_LINUX=“cryptdevice=/dev/nvme0n1p3 [..]“

habe ich dann in

GRUB_CMD_LINUX=“cryptdevice=/dev/nvme1n1p3 [..]“

geändert, damit grub die korrekte neue LUKS Partition öffnet. Je nach Config wird hier kein device note, sondern ein eine UUID eingetragen.

Abschließend muss noch die grub Config mit

grub-mkconfig -o /boot/grub/grub.cfg

neue gebaut werden.

Die notwendigen Veränderung sind damit abgeschlossen und es kann mit

exit
umount -R /mnt

chroot verlassen sowie das System unmounted werden.

System Boot

Nachdem nun alle relevanten Änderungen durchgeführt wurden, habe ich das System neugestartet und zunächst im BIOS/EFI überprüft, ob die korrekte boot device ausgewählt ist.

Danach konnte ich ohne Probleme, nach der Eingabe meines Passworts und der regulären Entschlüsselung der LUKS Partition, in meine Desktopumgebung starten.

/images/posts/lvm-on-luks-ssd-mirror/boot_kde.png

Recap

Das Spiegeln des alten Datenträgers verlief gradliniger als zunächst gedacht. Es war das erste Mal, dass ich einen System Umzug in dieser Größenordnung durchgeführt habe und selbst mit Dokumentation meiner Arbeitsschritte für diesen Blog Post, hat es knapp 60 Minuten gedauert diesen abzuschließen.

Mit einer Neuinstallation des Arch Systems und dessen Einrichtung, wäre ich wesentlich länger beschäftigt gewesen, von daher war es die richtige Entscheidung diesen Weg zu wählen.

nvme0n1 wurde nach dem erfolgreichen boot auf nvme1n1 formatiert und wird in Zukunft als separater Datenträger für Datenablagen oder anderes genutzt.

Windows 10 werde ich in der Dual Boot Konfiguration weiterhin nutzen, da es doch noch einige Use-Cases gibt, für die ich das System benötigt. Allerdings kann ich nun bequem mein Linux System für 98% der Zeit nutzen – für allerlei Aufgaben, von einem Produktiveinsatz, bis Gaming – habe ich erst mal genügend Speicherplatz zur Verfügung.