RAID1, LUKS und LVM ersetzen mein altes Speichersystem

, Linux, RAID, LVM, LUKS

Seltsame Timeout-Meldungen im Kernel Log, schlechte SMART-Werte und langsame Zugriffszeiten deuten auf sterbende Platten hin. Aus diesem Grund mussten neue Festplatten her und bei dieser Gelegenheit konnte ich mein gesamtes Speichersystem der Workstation umbauen und optimieren.

Planung

Der Ist-Zustand

Zur Zeit als ich diesen Artikel verfasste bestand mein System aus insgesamt vier Festplatten die unterschiedlich genutzt worden und aus verschiedene Hardware-Techniken bestanden. Die einzige Gemeinsamkeit die diese Platten aufwiesen war die SATA-Schnittstelle. Von SATA der ersten Generation bis zur dritten war alles vertreten. Dabei waren eine SSD und drei HDD‘s verbaut. Aufgrund der unterschiedlichen Größen und Einbaudaten der Festplatten wuchs das Speichersystem, historisch bedingt, sehr ungünstig an. So habe ich insgesamt drei RAID1 Arrays verwaltet die sich unter nichtssagenden Einhängepunkten befanden. Mit GraphViz habe ich dieses Setup graphisch dargestellt zum besseren Verständnis und zur Veranschaulichung des Chaos.

Das alte Speichersystem

Der Soll-Zustand

Der Soll-Zustand ergab sich aus langen Überlegungen und Vorarbeiten zu den Themen LVM, Vollverschlüsselung, Wartbarkeit, sowie dem Aspekt der Benutzbarkeit. Grundgedanke an dem Konzept war die vereinfachte Benutzung der Einhängepunkte. Diese sollten sprechend werden, was bei der täglichen Arbeit in einer Konsole auf diesem System, ein großen Vorteil erbringt. Des Weiteren sollten die Daten per se besser gekapselt werden, systematisch nach Kategorien. So das zum Beispiel meine Musik fortan seine eigene Partition erhält. Dabei sollte aber die Möglichkeit einer späteren, einfachen, Vergrößerung der Partitionen offen bleiben, was grundsätzlich für ein LVM Setup spricht. Im Bereich der Datensicherheit bin ich mit den Jahren auch ein ganzes Stück sensibler geworden, was mich veranlasst hat die Vollverschlüsselung des Speichersystems mit LUKS in Angriff zu nehmen. Denn LUKS sollte, meiner Meinung nach, nicht nur auf mobilen Geräten Anwendung finden. In jeder Lebenslage, ob gerichtlich bedingt oder aus welchem Grund auch immer, sollte ausschließlich man selbst Zugriff auf seine Privatdaten haben. Und natürlich wollte ich mir, wie immer, mein System möglichst ausfallsicher gestalten. Deswegen soll auch eine meiner altbewährtesten Lösungen, das RAID1 Array (Spiegelung), auch in diesem System wieder eingesetzt werden. Auch dieses Setup habe ich im folgenden graphisch zur Veranschaulichung dargestellt.

Das neue Speichersystem

Die Umsetzung

Zufallsdaten Einspielen

Nachdem die Festplatten ankamen fing der Spaß auch schon an. Also die Bauteile ausgepackt, angeschlossen und für die Vollverschlüsselung vorbereitet. Doch wie geht das am besten? Was gibt es für „schmutzige Untergrund Tricks“? Man ließt zu diesem Thema einiges, und einiges erscheint mir tatsächlich plausibel genug um es auch umzusetzen. So habe ich beide Platten erst einmal vorsorglich mit Zufallsdaten beschrieben. Diese Maßnahme erschwert spätere Nachforschungen, da nicht mehr abzuschätzen ist wie viele Daten sich tatsächlich auf den Datenträgern befinden. Denn nachdem die Vollverschlüsselung mit LUKS aufgesetzt ist, lassen sich die durch LUKS verschlüsselten Daten nicht mehr von den Zufallsdaten unterscheiden. Clever, nicht? Fand ich auch und aus diesem Grund habe ich diesen langfristigen Prozess auch umgesetzt. Doch nicht aber mit /dev/urandom und dd wie anzunehmen war, nein, mit einer viel schnelleren Methode. Indem man initial die Blockgeräte direkt mit LUKS und einer zufälligen Passphrase startet, und danach /dev/null bis zum Ende hineinschreiben lässt, ist dieser Prozess rund sechs mal schneller als eine reine /dev/urandom Lösung. Schade nur das ich vor ein paar Wochen noch die erste Lösung einem Kollegen empfohlen habe, für die sichere Lösung seiner Festplatten. Dabei wird er sicher eine wirklich lange Zeit für diesen Prozess gebraucht haben.

Folgende Befehle sind notwendig für das Einspielen der Zufallsdaten auf beiden Festplatten:

# Zufalls Passphrase für LUKS (256 random bits, verschiedene für beide Platten)
dd if=/dev/urandom bs=1 count=32 | base64

# LUKS auf den Platten einrichten (Ohne Dateisystem, direkt auf dem Blocklayer)
# Hierbei wird die Passphrase definiert, für jede Platte
cryptsetup luksFormat /dev/sdb
cryptsetup luksFormat /dev/sdc

# LUKS Geräte einhängen zum beschreiben (Passphrase wird dabei abgefragt)
cryptsetup luksOpen /dev/sdb cdb
cryptsetup luksOpen /dev/sdc cdc

# Beschreiben der LUKS Geräte mit /dev/null (~6x schneller als /dev/urandom)
# Durch die LUKS Verschlüsselung erreichen wir ein adequates Resultat zu /dev/urandom
dd if=/dev/zero of=/dev/mapper/cdb bs=8M
dd if=/dev/zero of=/dev/mapper/cdc bs=8M

# Bei großen Platten (z.B. 2TB/SATA3) dauert dieser Prozess rund 4h
# Um sich den aktuellen Stand anzeigen zu lassen sendet man das Signal
# USR1 an die dd Prozesse, diese geben dann Status Informationen auf Stderr aus
ps a | grep dd
watch -n 10 "iostat; kill -USR1 21818; kill -USR1 21819"

# Nachdem die Platten befüllt sind entfernen wir die LUKS Geräte
# und überschreiben jeglichen LUKS Payload
cryptsetup luksClose cdb
cryptsetup luksClose cdc

dd if=/dev/urandom of=/dev/sdb bs=512 count=2056
dd if=/dev/urandom of=/dev/sdc bs=512 count=2056

# Zur Überprüfung das jeglicher LUKS Payload entfernt wurde
cryptsetup luksDump /dev/sdb
#> Gerät /dev/sdb ist kein gültiges LUKS-Gerät.

cryptsetup luksDump /dev/sdc
#> Gerät /dev/sdc ist kein gültiges LUKS-Gerät.

RAID1 Array Aufsetzen

Partitionieren der ersten Platte

Da meine SSD sowieso schon mit der moderneren GPT Partitionstabelle beschrieben ist, bietet sich im Zuge des Umbaus dieser Schritt auch für das neue Speichersystem an. Für das erdachte Setup ist lediglich eine Partition notwendig die wir in der GTP eintragen müssen. Als grundsätzlichen Hinweis für jedes RAID1 Array ist hierbei zu erwähnen, dass am Ende der Festplatte 100 Megabyte ungenutzt verbleiben sollten, um mögliche Größenunterschiede zwischen verschiedenen Festplatten zu kompensieren.

Zu diesem Zweck müssen folgende Befehle ausgeführt werden:

gdisk /dev/sdb

Command (? for help): n
#> Partition number (1-128, default 1):
#> First sector (34-3907029134, default = 2048) or {+-}size{KMGTP}:
#> Last sector (2048-3907029134, default = 3907029134) or {+-}size{KMGTP}: -100M
#> Current type is 'Linux filesystem'
#> Hex code or GUID (L to show codes, Enter = 8300): fd00
#> Changed type of partition to 'Linux RAID'

Command (? for help): p
#> Disk /dev/sdb: 3907029168 sectors, 1.8 TiB
#> Logical sector size: 512 bytes
#> Disk identifier (GUID): BB8FE5D8-FCF5-45C5-81DF-C2C83F665E51
#> Partition table holds up to 128 entries
#> First usable sector is 34, last usable sector is 3907029134
#> Partitions will be aligned on 2048-sector boundaries
#> Total free space is 206814 sectors (101.0 MiB)
#>
#> Number  Start (sector)    End (sector)  Size       Code  Name
#>    1            2048      3906824334   1.8 TiB     FD00  Linux RAID

Command (? for help): w

#> Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
#> PARTITIONS!!

Do you want to proceed? (Y/N): Y
#> OK; writing new GUID partition table (GPT) to /dev/sdb.
#> The operation has completed successfully.
Übertragen des Layouts und starten des RAID Arrays

Den voran gegangenen Schritt wollen wir natürlich nicht noch einmal wiederholen, aus diesem Grund kopieren wir das erstellte Layout einfach auf die zweite Platte über. Und im gleichem Atemzug lässt sich gleich ein Backup der GPT erstellen.

# Anlegen einer Sicherheitskopie der Partitionstabelle
sgdisk /dev/sdb -b /tmp/raid-ws.gpt
#> The operation has completed successfully.

# Kopieren des Layouts von /dev/sdb nach /dev/sdc
sgdisk /dev/sdb -R=/dev/sdc
#> The operation has completed successfully.

# Setzen einer neuen UUID für die zweite Platte,
# da sie eins zu eins von der ersten Platte übernommen wurde
sgdisk -G /dev/sdc
#> The operation has completed successfully.

# Laden der erforderlichen Linux Treiber
modprobe raid1 && modprobe dm-mod

# Starten eines neuen RAID1 Arrays
mdadm --create /dev/md0 --level=1 --raid-devices=2 /dev/sd[bc]1
#> mdadm: Note: this array has metadata at the start and
#>     may not be suitable as a boot device.  If you plan to
#>     store '/boot' on this device please ensure that
#>     your boot-loader understands md/v1.x metadata, or use
#>     --metadata=0.90
#> Continue creating array? (y/n) y
#> mdadm: Defaulting to version 1.2 metadata
#> mdadm: array /dev/md0 started.

Bei einer rund 1.8TB großen Partition, die zu spiegeln ist, dauert der Resync rund 3h. Um sich den aktuellen Status des Prozesses anzeigen zu lassen eignet sich folgender Befehl:

watch -n1 cat /proc/mdstat

Ein Auszug der Ausgabe des Befehls zeigt unter anderem den Fortschritt des Prozesses:

Every 1,0s: cat /proc/mdstat                                                                                                                                                                                                                           Sat Mar  9 15:54:58 2013

Personalities : [linear] [raid0] [raid1] [raid10] [raid6] [raid5] [raid4] [multipath]
md0 : active raid1 sdc1[1] sdb1[0]
      1953279872 blocks super 1.2 [2/2] [UU]
      [=====>...............]  resync = 26.4% (516340608/1953279872) finish=155.5min speed=153981K/sec

unused devices: <none>

Nachdem das RAID1 Array vollständig synchronisiert wurde, sollten wir noch validieren das, dass Array wirklich sauber ist. Nachdem wir das sichergestellt haben, sammeln wir noch die Informationen zu den vorliegenden Arrays und speichern die in der mdadm-Konfigurationsdatei.

Folgende Befehle sind dafür notwendig:

# Abschließende Diagnose durch den Nutzer, ist alles sauber verlaufen, etc.
mdadm --misc --detail /dev/md0 | less

# Informationen zu den vorhandenen RAID Arrays zusammentragen
mdadm --examine --scan > /etc/mdadm.conf

Verschlüsseln des RAID Arrays mit LUKS

Endlich kommen wir zu den delikaten Schritten für das Setup. Denn was nun folgt sollte nicht mehr so schlimm zeitintensiv sein, wie die vorangegangen Schritte. Als nächstes wartet die Erstellung des LUKS Gerätes auf uns, dabei werden wir initial eine Passphrase vergeben und zusätzlich noch ein neues Keyfile generieren, dass sich zum Beispiel auf einem USB-Stick unterbringen lässt.

# Laden des Verschlüsselungs-Device-Mapper Treibers
modprobe dm-crypt

# LUKS Setup für das neue RAID1 Array durchführen
cryptsetup --cipher=aes-xts-plain --verify-passphrase --key-size=512 luksFormat /dev/md0

# Öffnen des LUKS Gerätes unter dem Namen "lvpool"
cryptsetup luksOpen /dev/md0 lvpool

# Anlegen eines neuen Keyfiles für unser LUKS Gerät
dd if=/dev/urandom of=/tmp/ws.key bs=512 count=4

# Einspielen des Keyfiles in unser LUKS Gerät
cryptsetup luksAddKey /dev/md0 /tmp/ws.key

# Verifizieren des LUKS Gerätes, Key 0 und 1 sollten auf ENABLED stehen
cryptsetup luksDump /dev/md0

# Zu Backupzwecken sollte ein Header Backup des LUKS Gerätes angelegt werden
cryptsetup luksHeaderBackup /dev/md0 --header-backup-file /tmp/md0-luks-ws.img

# Nachdem das Keyfile auf einen Datenträger platziert wurde, sollte
# es restlos von dem eigentlichen System entfernt werden
shred --remove --zero /tmp/ws.key

Anlegen der LVM Gruppen

Schlussendlich kommen wir nun zum letzten der Schritt, dem Aufsetzen des LVM Setups. Die folgenden Befehle beziehen sich explizit auf den Soll-Zustand der erreicht werden soll für meine Zwecke. Die Aufteilung kann völlig anders für die eigenen Zwecke anderer aussehen.

# Anlegen einer Physical Group mit dem Namen "lvpool"
pvcreate /dev/mapper/lvpool
#> Physical volume "/dev/mapper/lvpool" successfully created

# Details zu allen vorhandenen PV's anzeigen
pvdisplay

# Anlegen einer Volume Group mit dem Namen "vgroup"
vgcreate vgroup /dev/mapper/lvpool
#> Volume group "vgroup" successfully created

# Details zu allen vorhandenen VG's anzeigen
vgdisplay

# Anlegen der Logical Volumes
lvcreate --size 200G --name lvwork vgroup
lvcreate --size 100G --name lvprojects vgroup
lvcreate --size 10G --name lvdocs vgroup
lvcreate --size 50G --name lvsnapshots vgroup
lvcreate --size 75G --name lvsoftware vgroup
lvcreate --size 100G --name lvmusic vgroup
lvcreate --size 20G --name lvpictures vgroup
lvcreate --size 450G --name lvmovies vgroup
#> Logical volume "lvXXX" created

# Details zu allen vorhandenen LV's anzeigen
lvdisplay

# Formatieren aller LV's mit dem ext4 Filesystem
mkfs.ext4 -L "Work" /dev/mapper/vgroup-lvwork
mkfs.ext4 -L "Projects" /dev/mapper/vgroup-lvprojects
mkfs.ext4 -L "Docs" /dev/mapper/vgroup-lvdocs
mkfs.ext4 -L "Snapshots" /dev/mapper/vgroup-lvsnapshots
mkfs.ext4 -L "Software" /dev/mapper/vgroup-lvsoftware
mkfs.ext4 -L "Music" /dev/mapper/vgroup-lvmusic
mkfs.ext4 -L "Pictures" /dev/mapper/vgroup-lvpictures
mkfs.ext4 -L "Movies" /dev/mapper/vgroup-lvmovies

# Anlegen der Einhängepunkte
mkdir /mnt/work
mkdir /mnt/projects
mkdir /mnt/docs
mkdir /mnt/snapshots
mkdir /mnt/software
mkdir /mnt/music
mkdir /mnt/pictures
mkdir /mnt/movies

Abschließende Worte

Wer sich die Mühe gemacht hat mitzurechnen bei meinem Setup, was die Verteilung des Speicherplatzes anging, dem wird aufgefallen sein, dass ich lediglich 1005GB von 1.8TB initial genutzt habe. Und darin liegt auch ein großartiger Vorteil in der Nutzung des LVM. Sollte ich jemals mehr Speicher in einem logischen Volume brauchen, habe ich noch rund 800GB Luft, und kann diesen freien Speicher einfach zu den bestehenden oder neuen logischen Volumes hinzufügen.

Selbiges Setup wird demnächst auf meinem Server ausgeführt, was jedoch dort in einer viel umfangreicheren Umstrukturierung münden wird. Denn ich habe mich entschlossen, meinen Server in einen vServer Host zu transformieren, und Services kategorisiert in eigenen Linux Containern (Prozess-Blasen) unterzubringen. Diese Umstrukturierung wird einher gehen mit erweiterter Hardware, denn ich brauche mehr Arbeitsspeicher für meine Vorhaben. Aber dazu später mehr, denn das wird sicher auch in einem Howto enden.

Ich hoffe dem interessierten Leser hat es gefallen und/oder es war hilfreich. 😃

Quellen


Vorheriger ArtikelNächster Artikel