Proxmox: Bei Debian-Gastsystem Zvols verkleinern & mehrere Zvols zusammenfügen

Ausgangssituation

Bevor ich auf Enterprise SSDs umgestiegen hin hatte ich ein ZFS Mirror aus 2 Consumer NVMe SSDs sowie ein ZFS Mirror aus 2 NAS HDDs. Die Consumer SSD waren schön schnell, gehen aber sofort beim ständigen Schreiben von temporären Daten kaputt. Die NAS HDDs waren ziemlich langsam, nutzen sich aber nicht bei jedem kleinen Schreibvorgang ab. Die Idee war also alle Verzeichnisse, dessen Dateien ständig geändert werden müssen, auf dem HDD Pool zu speichern und alle statischen Daten auf dem schnellen SSD Pool.
Am meisten ändern sich die Dateien bei Debian im „/var“-Verzeichnis sowie auf der Swap-Partition. Also habe ich für jede VM eine kleine extra Zvol (virtuelle Festplatte) für die Swap-Partition angelegt. Sowie eine große Zvol dessen ext4 Partition unter dem Pfad „/var“ eingebunden wurde.
Diese beiden Zvols wurden dann auf dem HDD Pool gespeichert.
Für alles Andere wurde eine große dritte Zvol erstellt, welche den Bootloader und eine ext4-Partition beherbergte, die als Root-Partition eingebunden wurde.

Inzwischen habe ich aber weder den HDD Pool, noch den Consumer SSD Pool im Betrieb und alle VMs sind auf einen neuen Pool aus Enterprise SSDs umgezogen. Die neuen Enterprise SSDs sind robust genug um ständig beschrieben zu werden und zudem noch schnell. Es gab also keinen Grund mehr, warum ich drei einzelne Zvols anstatt einer einzigen großen Zvol verwenden sollte, wenn nun doch eh alle Zvols auf dem selben Pool liegen. Wenigstens die Zvol für „/var“ und die Root-Zvol wollte ich gerne zu einer einzelnen neuen Zvol kombinieren.

Ein weiteres Problem habe ich auch erst später entdeckt. Ich war sehr großzügig was die Kapazität der Zvols anging, da ich Thin Provisioning eingestellt hatte. Da dies nur Platz verbraucht, wenn wirklich etwas in die Zvols geschrieben wird, nimmt eine 30GB Zvol mit 10GB Daten nicht mehr Platz ein als eine 120GB Zvol mit 10GB Daten. Ich hatte also haufenweise Zvols die keine 10% voll waren, weil ich dachte, so spare ich mir später Arbeit, da ich diese nicht nachträglich vergrößern muss, wenn sie von Haus aus bereits sehr groß sind.

Diese Faulheit hat sich dann aber später gerächt, als ich Backups erstellen wollte. Auch wenn die Zvol zu 90% leer ist muss beim Backup die komplette Zvol von vorne bis hinten einmal eingelesen werden. Das Backup dauert also unnötig 10-fach so lange. Und da ich meine Backups sicher haben will nutze ich als „Mode“ die Option „Stop“, weshalb die VMs während des ganzen Backups gestoppt sein müssen. So waren die VMs dann bei jedem Backup für 3 Stunden abgeschaltet und nicht benutzbar.

Bei der Gelegenheit wollte ich also auch gleich alle Zvols verkleinern, was auch nur geht, indem man alle Daten auf eine neue kleinere Zvol verschiebt.

Das ganze Vorhaben ist ziemlich kompliziert und aufwändig aber Dank der Hilfe der netten Leute aus dem debian.de IRC Chat habe ich es dann doch hinbekommen.

Anleitung

Folgende Anleitung ist relativ speziell und bezieht sich auf ein Debian 10.5 Gastsystem auf einem Proxmox 6.2 Hypervisor. Bei anderen Betriebssystemen muss man vielleicht anders vorgehen, also bitte erst alles einmal komplett lesen und nicht einfach drauflos nachmachen.

Alle Befehle beziehen sich hier auf meinen Fall und müssen bei euch entsprechend abgeändert werden.

Wir werden in ein Live Debian booten und dort die Root-Zvol (sda) mit der „/var“-Zvol (sdb) in eine viel kleinere neue Zvol (sdd) kombinieren. Außerdem wird die Swap-Zvol (sdc) durch eine kleinere Swap-Zvol (sde) ausgetauscht.
Danach wird die fstab an die neuen Partitionen angepasst, sowie die Initramfs und der grub-bootloader, basierend auf den neuen Partitionen, neu gebaut und auf die neue Zvol geschrieben.

1.) Backup erstellen

Wir werden alle Zvols löschen also sollte vorher unbedingt ein Backup der VM erstellt werden. So kann dann schnell und einfach die ursprüngliche VM wieder aus dem Backup hergestellt werden. Snapshots helfen euch auch nicht, da diese sogar alle vorher gelöscht werden müssen, da sich sonst keine Zvols löschen lassen:

2.) Daten der alten Zvols herausfinden

Wir müssen später alle neuen virtuellen Festplatten manuell neu partitionieren und die Daten von den alten Partitionen/Ordnern in die richtigen neuen Partitionen/Ordner kopieren. Das geht natürlich nur, wenn Ihr genau wisst was auf jeder Partition drauf ist und wo diese Partition eingebunden wird. Das sollte nun als erstes herausgefunden und notiert werden.

Nützlich dazu sind z.B. folgende 2 Befehle:

fdisk -l
cat /etc/fstab

Ersterer zeigt euch die Größe der virtuellen Festplatten, sowie die Größe, Position und Formatierung der Partitionen.
Letzterer sagt euch welche Partition unter welchem Mount Punkt eingebunden wurde.

Das Alles wird später benötigt.

3.) Debian Live CD ISO runterladen und als primäre Bootquelle einbinden

Da es sich bei mir um Debian-Gastsysteme handelt werde ich von einer Debian Live CD booten. Es muss zwingend von einem Live Linux gebootet werden und nicht von der virtuellen Festplatte selbst, da das Live Linux rein aus dem RAM läuft und auf keine virtuellen Festplatten zugreifen muss. Es können also alle virtuellen Festplatten beliebig Aus- und wieder Eingebunden werden.

Eine aktuelle Debian Live CD ISO bekommt ihr hier.

Diese dann per Proxmox hochladen:

ISO in der VM unter CD/DVD einbinden:

Und CD-ROM bei der Bootreihenfolge nach ganz oben setzen:

4.) Neue Zvols erstellen

Bei dieser Gelegenheit auch gleich prüfen, ob ihr ein Raidz Pool benutzt und die „Block Size“ auf dem Standard von „8k“ belassen habt. Falls ja hier lesen warum das nicht gut ist und ihr Platz verschwendet. Das ist nur behebbar, wenn man Zvols neu erstellt und da wir das jetzt sowieso tun, kann man das gleich mitlösen.
Falls ja muss einfach nur ein korrekter Wert für die volblocksize ausgerechnet und bei dem jeweiligen Pool unter „Block Size“ eingesetzt werden. Dann normal hier weitermachen und den Rest der anderen Anleitung ignorieren.

Als Erstes die VM herunterfahren und zwei neue kleinere virtuelle Festplatten anlegen. Die erste neue virtuelle Festplatte sollte groß genug sein, dass da alle Daten der bisherigen virtuellen Festplatten zusammen draufpassen und noch etwas Platz frei ist. Die zweite neue virtuelle Festplatte wird die neue kleinere Swap-Platte:

5.) Von Live CD booten und Festplatten identifizieren

Nun die VM starten und es sollte von der Live CD gebootet werden. Dort „Debian Gnu/Linux Live“ wählen und mit Enter bestätigen:

Bei mir funktionieren Sonderzeichen nicht wie sie sollten. Falls das bei euch auch der Fall sein sollte, hier eine Liste welches Zeichen bei mir auf welcher Taste lag:
„=“ ist „´“
„/“ ist „/“ auf dem Numblock
„-“ ist „-“ auf dem Numblock
„*“ ist „*“ auf dem Numblock
„_“ ist Shift + „ß“
Auch geht per Proxmox kein Einfügen für Text, also muss jeder Befehl (und jede UUID) per Hand eingetippt werden. Mit „Shift“ + „Bild hoch“ bzw. „Shift“ + „Bild Runter“ kann man übrigens durch den Text scrollen. Auch „Y“ und „Z“ können vertauscht sein.

Wir müssen die neuen virtuellen Festplatten formatieren, aber dazu brauchen wir „parted“ was nicht vorinstalliert ist. Um dieses zu installieren folgende Befehle eingeben:

sudo apt update
sudo apt upgrade
sudo apt install -y parted

Danach müssen wir feststellen, welche virtuelle Festplatte hier im Linux welche Bezeichnung (sda, sdb usw.) erhalten hat. Dazu folgendes eingeben:

sudo fdisk -l

Welche virtuelle Festplatte hinter welchem Namen steckt könnt ihr ganz gut an der Größe und den vorhandenen Partitionen erkennen. Dazu müsst ihr natürlich wissen was für Partitionen ihr auf den alten virtuellen Festplatten hattet und wie diese eingebunden wurden.

In meinem Fall sehen die virtuellen Festplatten und Partitionen so aus:

  • /dev/sda (120GB, meine alte Root-Zvol)
    • /dev/sda1 (120GB, ext4, wird als unter „/“ also als root eingebunden)
  • /dev/sdb (120GB, meine alte „Var“-Zvol)
    • /dev/sdb1 (120GB, ext4, wird als „/var“ eingebunden)
  • /dev/sdc (8GB, meine alte Swap-Zvol)
    • /dev/sdc1 (8GB, linux-swap, wird als Swap eingebunden)
  • /dev/sdd (64GB, meine neue kombinierte Zvol)
  • /dev/sde (2GB, meine neue Swap-Zvol)

6.) Neue virtuelle Festplatte (sde) für Swap partitionieren und formatieren

Dies ist ziemlich schnell erledigt, da Swap-Partitionen nur temporäre Daten speichern. Es muss also nichts an Daten von der alten Swap-Partition auf die neue Swap-Partition übernommen werden.

Die virtuelle Festplatte für die neue Swap-Partition ist „/dev/sde“ und da sie neu ist hat sie weder eine Partitionstabelle noch irgendwelche Partitionen.

Mit folgendem Befehl starten wir parted, sagen parted, dass wir die virtuelle Festplatte „/dev/sde“ partitionieren wollen und legen fest, dass die neuen Partitionen auf 1MiB ausgerichtet werden sollen (was für die meisten Blockgrößen gut sein sollte):

sudo parted --align optimal /dev/sde

Danach erstellen wir eine neue GPT-Partitionstabelle:

mktable gpt

Und erstellen eine neue erste Partition mit dem Namen „swap“ vom Typ „linux-swap“, welche 100% des freien Speichers belegt:

mkpart swap linux-swap 0% 100%

Und beenden dann das Partitionieren:

quit

Nun nur noch die neu erstellte Partition für Swap formatieren:

sudo mkswap /dev/sde1

In der Ausgabe sollte eine UUID wie diese auftauchen:
„UUID=63d8458b-9c7e-48d6-bdb0-90c0fdbd18be“
Diese muss vom Bildschirm fehlerfrei abgetippt werden, da wir sie später an 2 Stellen wieder eintippen müssen.

7.) NEUE kombinierte VIRTUELLE FESTPLATTE (SDD) PARTITIONIEREN UND FORMATIEREN

Wie ihr diese neue virtuelle Festplatte partitionieren müsst hängt ganz davon ab, wie eure alten Partitionen aussahen und wie ihr die neuen Partitionen haben wollt.

In meinem Fall möchte ich die neuen Partitionen so haben:

  • sdd: 64GB, GPT-Partitionstabelle, Booten über BIOS und nicht UEFI
    • sdd1: 1 MiB Größe, Start bei 1 MiB, Ende bei 3 MiB, BIOS_GRUB aktiviert (auf diese Partition möchte ich den neuen Grub-Bootloader installieren)
    • sdd2: 512 MiB Größe, Start bei 3 MiB, Ende bei 515 MiB (soll als „/boot“ eingebunden werden)
    • sdd3: maximale Größe, Start bei 515 MiB, Ende bei -1 MiB d.h. so, dass da am Ende noch genau 1 MiB frei bleiben (soll die Root-Partition werden, also eingebunden als „/“)

Wenn ihr per UEFI booten wollt und nicht per BIOS dann solltet ihr z.B. statt der Grub-Partition als erste Partition eine extra EFI-Partition (ESP) von ca. 300 MiB Größe erstellen, sowie bei dieser das Boot-Flag setzen.

Meine Partitionen erstelle ich so…

Folgender Befehl startet parted, sagt parted, dass wir die virtuelle Festplatte „/dev/sdd“ partitionieren wollen und legt fest, dass die neuen Partitionen auf 1MiB ausgerichtet werden sollen:

sudo parted --align optimal /dev/sdd

Dann eine GPT-Partitionstabelle erstellen:

mktable gpt

Festlegen, dass alle folgenden Größenangaben als MiB und nicht als MB anzusehen sind:

unit mib

Erste Partition erstellen. Ist 1 MiB Groß, soll „grub“ heißen und soll den Grub-Bootloader beherbergen:

mkpart grub 1 3

Parted sagen, dass die erste Partition für GRUB genutzt werden soll:

set 1 bios_grub on

Zweite Partition erstellen. Soll „boot“ heißen, 512 MiB groß sein und später als „/boot“ eingehängt werden:

mkpart boot 3 515

Dritte Partition erstellen. Soll „root“ heißen, den kompletten restlichen Platz belegen (bis auf 1 MiB was am Ende frei bleiben soll) und später als „/“ also root eingehängt werden:

mkpart root 515 -1

Dann das Partitionieren beenden:

quit

Bei dem Formatieren der Partitionen habt ihr eigentlich freie Wahl, da wir später sowieso die fstab ändern müssen und Nichts auf Blockebene kopieren.
Meine alten Partitionen hatten ext4 als Dateisystem und meine neuen Partitionen sollen das auch bekommen.

Folgende Befehle formatieren die neue Boot- und Root-Partition mit ext4:

sudo mkfs.ext4 /dev/sdd2
sudo mkfs.ext4 /dev/sdd3

In der Ausgabe von beiden Befehlen müsste wieder eine UUID wie diese bei sein:
„UUID=63d8458b-9c7e-48d6-bdb0-90c0fdbd18be“

Beide UUIDs müssen wieder abgetippt werden, da wir sie später brauchen.

8.) Daten von alten Partitionen auf neue PArtitionen kopieren

Da nun alle neuen Partitionen erstellt und formatiert sind können wir anfangen die Daten von den alten Partitionen auf die neuen zu übertragen.
Dazu müssen als erstes sowohl die alten Partitionen lesend eingebunden, als auch die neue Partitionen schreibend eingebunden werden.

Für jede Partition erstellen wir als erstes einen neuen Ordner als Mount-Punkt. Dafür habe ich den Ordner „/mnt“ gewählt und die neuen Ordner heißen der Übersicht halber einfach so wie die Partition, die dort eingebunden werden soll:

sudo mkdir /mnt/sda1
sudo mkdir /mnt/sdb1
sudo mkdir /mnt/sdd2
sudo mkdir /mnt/sdd3

Dann binden wir die alten Partitionen lesend unter den neu erstellten Mount-Punkten ein:

sudo mount -r /dev/sda1 /mnt/sda1
sudo mount -r /dev/sdb1 /mnt/sdb1

Der komplette Inhalt der Partition sda1 ist so nun z.B. unter dem Ordner „/mnt/sda1“ lesbar.

Dann binden wir die beiden neuen Partitionen ein. Diesmal aber beschreibbar:

sudo mount -w /dev/sdd2 /mnt/sdd2
sudo mount -w /dev/sdd3 /mnt/sdd3

Als nächstes folgt das Kopieren der Daten.
Da ich früher keine eigene Boot-Partition hatte befinden sich alle Boot-Dateien auf der Root-Partition (sda1) innerhalb des „/boot“-Ordners. Da die neue Boot-Partition (sdd2) aber direkt nach „/boot“ eingebunden wird, darf ich nicht einfach den kompletten „/boot“-Ordner von sda1 nach sdd2 kopieren, sondern nur dessen Inhalt.

Das mache ich mit folgendem Befehl:

sudo rsync -avc --progress /mnt/sda1/boot/* /mnt/sdd2/

Das Programm rsync wird hierfür zum Kopieren benutzt. Die Parameter bedeuten:

„-avc“ besagt das alle Dateiattribute wie Rechte, Besitzer usw. von der Quelle übernommen werden sollen, das rekursiv kopiert werden soll (also auch alle Unterverzeichnisse etc.), das symbolische Links kopiert werden, das alle Dateien nach dem Kopieren auf Übereinstimmung geprüft werden sollen (um Kopierfehler zu erkennen) und das alles was kopiert wird einzeln angezeigt wird

„–progress“ zeigt beim Kopieren den Fortschritt an

„/mnt/sda1/boot/*“ ist die Quelle. Das Sternchen besagt, dass alle Dateien und Ordner die innerhalb des Ordners „/mnt/sda1/boot“ liegen kopiert werden sollen. „/mnt/sda1/boot“ verweißt auf den Ordner, der bei mir früher einfach unter „/boot“ zu finden war, da ja „/mnt/sda1“ auf die Partition verweist, welche sonst einfach als root, also unter „/“ eingebunden wurde.

„/mnt/sdd2/“ ist das Ziel, in das alle Daten von der Quelle hineinkopiert werden. sdd2 ist meine neue Boot-Partition welche später in Linux unter „/boot“ eingebunden wird.

Als Nächstes wird alles, bis auf den „/boot“-Ordner und dessen Inhalte, von der alten Root-Partition (sda1) in die neue Root-Partition (sdd3) kopiert. Der „/boot“ Ordner braucht nicht mehr mitkopiert zu werden, da wir diesen ja eben schon in eine eigene Partition kopiert haben und diese nach dem Einbinden dessen Stelle einnehmen wird.

Dazu wird dieser Befehl benutz:

sudo rsync -avcr --progress --exclude=/boot/ /mnt/sda1/* /mnt/sdd3/

Neu ist hier der Parameter „–exclude=/boot/“, welcher sich auf einen Pfad relativ zur Quelle bezieht. Wird beim Kopieren der Inhalte des Ordners „/mnt/sda1“ ein Ordner „boot“ gefunden, so wird dieser ausgelassen und nicht kopiert.

Als letztes muss Alles aus der alten „/var“-Partition (sdb1) in den Ordner „/var“ auf der neuen Root-Partition (sdd3) kopiert werden. Ist hier quasi das Gegensätzliche, was wir eben mit der Boot-Partition gemacht haben.

Dazu benutzen wir diesen Befehl:

sudo rsync -avcr --progress /mnt/sdb1/* /mnt/sdd3/var/

9.) Fstab anpassen

Damit Debian weiß, wie welche Partition wohin eingebunden werden soll, liest es nach dem Booten die Datei /etc/fstab. Nun haben wir aber keine eigene „/var“-Partition mehr, aber stattdessen eine neue „/boot“-Partition. Auch haben sich alle UUIDs geändert und müssen durch die neuen UUIDs ersetzt werden.

Dazu öffnen wir die alte fstab-Datei auf der neuen Root-Partition mit dem Texteditor nano:

sudo nano /mnt/sdd3/etc/fstab

Meine alte fstab-Datei sieht so aus:

# <file system>                           <mount point>   <type>  <options>       <dump>  <pass>

UUID=081c109d-9c5f-4378-8eb3-27ab5dd36730 /               ext4    noatime,nodiratime,errors=remount-ro 0       1
UUID=dc1faac1-12b8-4af6-af57-be6980fbf262 /var            ext4    noatime,nodiratime 0       2
UUID=a38b85de-6a21-41dc-9ffb-1f90cf4b68b0 none            swap    sw              0       0

Das Erste ist meine alte Root-Partition zu erkennen an dem Mount-Punkt „/“. Die neue Root-Partition ist ebenfalls ext4 und der Mount-Punkt bleibt auch gleich. Hier muss also nur die alte UUID durch die UUID der neuen Root-Partition ersetzt werden.

Das Dritte ist meine alte Swap-Partition. Hier hat sich ebenfalls nichts außer der UUID geändert. Hier ebenfall die alte UUID entfernen und die UUID der neuen Swap-Partition eintippen.

Das Zweite ist meine alte „/var“-Partition, welche nun nicht mehr existiert. Diese Zeile kann und muss komplett gelöscht werden. Verloren geht aber nichts, denn alles was früher von der „/var“-Partition im Mount-Punkt „/var“ eingebunden wurde ist jetzt einfach Bestandteil der neuen Root-Partition und deshalb sind die selben Dateien am selben Platz zu finden.

Dafür gibt es jetzt aber eine neue „/boot“-Partition die in dem Mount-Punkt „/boot“ eingebunden werden muss. Dafür muss eine neue Zeile eingefügt werden.
Am Ende sieht meine neue fstab-Datei dann z.B. so aus:

# <file system>                           <mount point>   <type>  <options>       <dump>  <pass>

UUID=03b77228-ed4c-4218-910e-11b9f77c4b46 /               ext4    noatime,nodiratime,errors=remount-ro 0       1
UUID=8883dbc8-80f8-49b8-8c5f-13a32baefe98 none            swap    sw              0       0
UUID=bbf85ecb-cc61-40ed-ba7b-d7b804ee845e /boot           ext4    noatime,nodiratime 0       2

Mit STRG+X, dann Enter und Y kann nano verlassen und die Änderungen gespeichert werden.

Nun die VM mit folgendem Befehl herunterfahren:

sudo shutdown now

10.) Initramfs und Grub-Bootloader neu bauen

Jetzt, da Alles von den alten Zvols auf die neuen Zvols kopiert ist, brauchen wir die alten 3 Zvols nicht mehr weiter und können diese von der VM entfernen:

Danach die VM wieder starten und erneut in das Live Debian booten.

Um das Initramfs und den Grub-Bootloader neu bauen zu können, müssen wir erst alle neuen Partitionen sowie wichtige System-Ordner des Live Debians so in ein temporäres Root-Verzeichnis mounten, wie sich diese auch am Ende vorfinden werden. Danach können wir mit „chroot“ dieses temporäre Root-Verzeichnis als aktives Root-Verzeichnis nutzen und dann sollte bis zum Reboot Alles genau an der Stelle sein, die es auch später haben wird.
Erst wenn alle Pfade stimmen kann ein neues passendes Initramfs und ein Bootloader neu gebaut werden.

Dadurch das wir die alten Zvols entfernt haben, haben sich im Live Debian auch alle Namen für die virtuellen Festplatten und dessen Partitionen geändert. Was vorher sdd1, sdd2 und sdd3 war ist nun sda1, sda2 und sda3. Und was vorher sde1 war ist nun sdb1.
Die neue „/boot“-Partition ist nun also sda2, die neue Root-Partition sda3 und die neue Swap-Partition sdb1.
Das kann bei jedem anders aussehen, also noch einmal gucken, welche Partition nun welchen Namen hat:

sudo fdisk -l

Wenn das mit dem Wechseln der Namen geklärt ist erstellen wir einen neuen Ordner für das temporäre Root-Verzeichnis:

sudo mkdir /mnt/root

Dann binden wir unsere neue Root-Partition in das temporäre Root-Verzeichnis ein:

sudo mount -t ext4 /dev/sda3 /mnt/root

Der Parameter „-t ext4“ gibt an, dass es sich bei der einzubindenden Partition um ein ext4 Dateisystem handelt. Wenn ihr ein anderes Dateisystem formatiert habt müsst ihr den Wert entsprechend anpassen.

Nun erstellen wir im eingebundenen temporären Root-Verzeichnis einen neuen Ordner „boot“:

sudo mkdir /mnt/root/boot

In diesem neuen Ordner binden wir die neue „/boot“-Partition ein:

sudo mount -t ext4 /dev/sda2 /mnt/root/boot

Außerdem müssen noch 2 System-Ordner eingebunden werden:

sudo mount --bind /dev /mnt/root/dev
sudo mount -t proc proc /mnt/root/proc

Dann setzen wir unser temporäres Root-Verzeichnis als aktuelles Root-Verzeichnis:

sudo chroot /mnt/root /bin/bash -i

Alles was im Live Debian im Ordner „/mnt/root“ war ist für uns jetzt nur noch als „/“ zu erreichen, da wir quasi in den Ordner „/mnt/root“ gewechselt haben und dieser nun unser Root-Verzeichnis bildet.

Nun binden wir noch den Systemordner „/sys“ ein:

mount -t sysfs sys /sys

Bei mir hat das Bauen des Initramfs immer Warnungen gemeldet, weil die UUID des neuen Swaps nicht mit der alten UUID in der Datei „/etc/initramfs-tools/conf.d/resume“ übereinstimmt. Um diese Warnungen zu vermeiden kann dort die alte UUID gegen de UUID der neuen Swap-Partition ausgetauscht werden:

nano /etc/initramfs-tools/conf.d/resume

Nach dem Ändern der UUID wieder mit STRG+X, dann Enter, dann Y bestätigen.

Nun kann endlich das Initramfs neu gebaut werden:

update-initramfs -u

Sollte es keine Fehler geben kann der Grub-Bootloader neu gebaut werden:

dpkg-reconfigure grub-pc

Am Ende muss eine virtuelle Festplatte gewählt werden, auf welcher der Bootloader installiert werden soll. Hier „/dev/sda“ mit den Pfeiltasten und der Leertaste markieren, mit Tab die Auswahlliste verlassen und mit den Pfeiltasten und Enter das Schreiben bestätigen.

Wenn man UEFI benutzt muss ein anderer Bootloader installiert werden. Müsst ihr dann gucken welchen man statt dem grub-pc nimmt.

Wenn alles geklappt hat kann man die chroot shell verlassen:

exit

Und dann die VM herunterfahren:

sudo shutdown now

11.) Testen ob alles läuft

Als erstes die ISO wieder aus dem CD-Laufwerk entfernen:

Dann bei der Bootreihenfolge die Zvol mit der neuen Root-Partition an erste Stelle (ganz oben) setzen:

Jetzt kann die VM gestartet werden und eigentlich sollte das Booten klappen und alles gewohnt so laufen wie bisher.

Wenn alles passt können dann auch die 3 alten Zvols noch endgültig gelöscht werden:

Und das Alles jetzt immer und immer wieder wiederholen, bis alle eure VMs zusammenführt und verkleinert sind. Ab der zehnten VM geht es dann aber recht flott, weil man alle Befehle inzwischen auswendig kennt.^^

MfG

Björn

Proxmox: Block Size von virtuellem Datenträger nachträglich ändern

Warum die Block Size nachträglich ändern?

Wenn man einen neuen ZFS Pool erstellt sollte man sich genau überlegen, welche Block Size man in Proxmox für den Pool einträgt, bevor man irgendwelche VMs erstellt. Denn jeder danach neu erstellte Datenträger (zvol), der auf diesem Pool liegen soll, wird automatisch mit der dort festgelegten Block Size (volblocksize) erstellt und kann später nicht mehr geändert werden.
Dies kann sehr problematisch werden, denn standardmäßig nutzt Proxmox eine volblocksize von 8K. Das mag für einen normalen ZFS Mirror passen, für ein Raidz Array von Laufwerken mit 4K logischer Sektorgröße oder höher (bzw. ashift von 12 oder mehr) taugt das nichts.
Wenn man den Pool z.B. mit einem ashift-Wert von 12 erstellt, dann ist die kleinstmögliche Größeneinheit, welche auf das einzelne physikalische Laufwerk geschrieben werden kann, 4 KB. Für jedes virtuelle Laufwerk wird auf dem Pool ein eigenes zvol angelegt, welches quasi als virtuelle Festplatte dient. Ähnlich wie beim Pool selbst kann man auch für jedes zvol die kleinstmögliche Größeneinheit einstellen. Diesen Wert nennt man volblocksize.

Wenn man ein Raidz-Array einem Mirror vorzieht, dann eigentlich nur, weil bei einem Raidz weniger Platz für die Ausfallsicherheit verloren geht. Denn schneller ist ein Mirror eigentlich immer. Man kann aber eben auch nur den halben Platz nutzen, da alle Daten doppelt gespeichert werden müssen.
Im Gegensatz dazu verliert man bei einem Raidz1 mit 3 Laufwerken in Theorie nur 1/3 der Kapazität und bei einem Raidz1 mit 5 Laufwerken nur 1/5 der Kapazität. Dies ist aber auch nur der Fall, wenn die volblocksize auch erhöht wurde.
Denn mit dem Standardwert für volblocksize von 8K wird alles auf der virtuellen Festplatte in 8KB Blöcken gespeichert. Diese 8 KB Blöcke müssen dann aber noch vom Pool gespeichert werden und da kommt des dann zum Problem. Denn wenn die Laufwerke des Pools z.B. eine logische Sektorgröße von 4K haben, dann ist die Auflösung einfach viel zu grob, da eben nur ein vielfaches von 4 KB gespeichert werden kann. Im Endeffekt werden kann viele „angebrochene“ 4 KB Blöcke geschrieben und ein 8 KB Block auf dem zvol braucht dann doch 16 KB Platz auf dem Pool. Man verliert also trotzdem die Hälfte der Kapazität und nicht nur die theoretischen 1/3 oder gar 1/5.

Wer wissen will, was eine brauchbare volblocksize für einen Raidz1 Pool ist, der sollte sich diese Tabelle anschauen. Dort ist für Raidz1 (und Raidz2/Raidz3 über die Reiter ganz unten) tabellarisch aufgelistet, wieviel Kapazität man tatsächlich verliert, wenn man eine bestimmte volblocksize mit einer bestimmten Anzahl von Laufwerken nutzt.
Nehmen wir meinen Raidz1 Pool aus 5 SDDs mit einer logischen Sektorgröße von 4K (ashift=12) als Beispiel:

In der ersten Spalte der Tabelle sieht man die Blockgröße in Sektoren.
In der ersten Zeile steht die Anzahl der Laufwerke, aus denen der Raidz1 Pool besteht.
Die Prozentzahlen zeigen den zusätzlich nötigen Platz für die Parität an, welcher zusätzlich zu den reinen Daten verbraucht wird. Wenn ich 1 GB an reinen Daten speichern will und dort 25% stehen, dann werden 1,25 GB tatsächlich verbraucht.

Möchte man die optimale volblocksize für einen Raidz1 Pool herausfinden, so geht man erst in die Spalte, welche der Anzahl der Laufwerke des eigenen Pools entspricht. Ich habe 5 SSDs also nehme ich die Spalte in der ich die 5 eingekreist habe.
Normalerweise möchte man die Blockgröße kleinstmöglich haben und im Idealfall sollte diese auch einem Vielfachen von Zwei entsprechen (diese sind in der Tabelle fett geschrieben). Ich nehme also die oberste Zeile in der Tabelle die fett geschrieben ist und wo der Prozentwert möglichst klein ist. In meinem Fall wäre es die rot eingekreiste Zeile mit der Blockgröße in Sektoren von 8.
Bei einer Blockgröße von 8 Sektoren und 5 SSDs würde ich also 25% zusätzlichen Platz benötigen. Ich würde also 1/5 der Gesamtkapazität für die Parität verlieren, was ja auch genau das Optimum bei 5 SSDs wäre.

Möchte man die entsprechende volblocksize für Proxmox haben, dann muss man den Wert von „Blockgröße in Sektoren“ mit der Blockgröße seines Pools multiplizieren. Meine SSDs nutzen eine logische Sektorgröße von 4K also habe ich den Pool mit einem ashift-Wert von 12 erstellt, wodurch der Pool ebenfalls eine Blockgröße von 4 KB hat. Bei einer „Blockgröße in Sektoren“ von 8 und einer Pool-Blockgröße von 4 KB wäre die volblocksize also 32K (8 * 4KB).

Meine volblocksize ist falsch. Wie ändere ich die nun?

Wie gesagt, man kann die volblocksize von zvols nicht mehr nachträglich ändern. Beim Pool ist über die GUI zwar jederzeit die Blockgröße änderbar, diese wird aber nur auf neu erstellte virtuelle Festplatten angewandt.
Es führt also nichts drum herum in jeder VM alle virtuelle Festplatten durch neu erstellte virtuelle Festplatten zu ersetzen.

1.) Block Size für den Pool ändern

Als erstes muss über das Proxmox Webinterface unter „Datacenter -> Storage -> DeinPool“ der Wert für „Block Size“ auf den neuen Wert angepasst werden. Jede danach neu erstelle virtuelle Festplatte des Pools nutzt dann diesen Wert als volblocksize. Wie man den passenden Wert herausfinden kann man der Tabelle oben entnehmen.

2.) Backup von der VM erstellen

Wir werden alle virtuellen Festplatten der VM löschen, also sollte vorher ein Backup angelegt werden, damit im Ernstfall problemlos die alte VM wieder durch das Backup eingespielt werden kann. Da wir die Festplatten löschen helfen auch keine Snapshots. Falls Snapshots vorhanden sind müssen diese sogar vorher noch gelöscht werden, da sich sonst keine virtuelle Festplatte löschen lässt, für die noch Snapshots existieren.

Als „Mode“ sollte „Stop“ gewählt werden. Wir müssen die VM sowieso mehrfach herunterfahren und „Stop“ ist die sicherste Art ein Backup zu erstellen.

3.) Linux Live CD als primäres Bootquelle setzen

Um die Daten von der alten virtuellen Festplatte auf die neue virtuelle Festplatte zu kopieren werden wir eine Debian Live CD nutzen und mit dieser per dd-Befehl die Festplatten auf Blockebene klonen. Dies klont auch gleich die komplette Partitionstabelle, Bootloader, Partitionen usw. und wir haben wirklich eine 1:1-Kopie der Festplatte und müssen nachträglich nichts mehr ändern.
Eine Debian Live CD ISO bekommt ihr hier.
Auf den Server kopieren könnt ihr die ISO auch direkt über das Proxmox Webinterface unter „Storage View -> DeinServerName -> Dein StorageFürISOs -> Content -> Upload“:

Danach die VM herunterfahren. Sobald diese aus ist dort die Debian Live CD einbinden:

Und die Boot-Reihenfolge so ändern, dass da CD/DVD ganz oben steht:

Beim nächsten Start der VM solltet Ihr dann in ein Live Debian booten anstatt in das normale Betriebssystem. Das hat den Vorteil, dass das Live Debian auf keinerlei virtuelle Festplatten zugreifen muss, da es rein aus dem RAM heraus läuft und alle Festplatten ausgebunden werden können um sie korrekt zu klonen.

4.) Neue virtuelle Festplatten erstellen

Als nächstes muss für jede bestehende virtuelle Festplatte eine genau gleich große neue virtuelle Festplatte erstellt werden. Hier einfach alle Einstellungen von der alten Festplatte für die neue Festplatte übernehmen:

5.) Live CD booten und Festplatten herausfinden

VM starten und bei der VM unter „Console“ (oder Doppelklick auf die VM für Vollbild im eigenen Fenster) die VM per VNC bedienen. Im Boot-Bildschirm „Debian GNU/Linux Live“ auswählen und mit Enter bestätigen:

Danach folgendes eintippen:

sudo lsblk

Der Befehl sollte euch alle verbundenen virtuellen Festplatten auflisten. Nun muss herausgefunden werden, was der Name der alten Festplatte und was der Name der neuen Festplatte ist. Bei mir sieht die Ausgabe so aus:

Mich interessieren nur die Festplatten, welche in der Spalte „TYPE“ den Wert „disk“ haben. Von denen gibt es 3 Stück und deren Namen kann man in der Spalte „NAME“ entnehmen. In meinem Fall heißen die Festplatten „sda“, „sdb“ und „sdc“.
In der Spalte „SIZE“ steht dessen Größe und sowohl meine alte als auch meine neue Festplatte habe ich mit einer Kapazität von 16 GB erstellt. Uns interessieren hier also nur die Festplatten „sda“ und „sdc“, da nur diese die beiden Festplatten die wir klonen wollen sein können.
Dann muss man noch wissen, welche von beiden Festplatten denn die Neue und welche die Alte ist. Ist in dem Fall ziemlich einfach. Meine alte Festplatte war partitioniert und die neue Festplatte ist frisch erstellt und somit unpartitioniert. „sda“ hat 3 Partitionen (sda1, sda2 und sda3) und „sdc“ hat keine einzige Partition. Somit muss „sda“ die alte und „sdc“ die neue Festplatte sein.
Hier jetzt bitte die Namen der jeweiligen Festplatten mitschreiben und wirklich sicherstellen, dass das nur genau die Festplatten sein können die man denkt, damit nicht die falsche Festplatte unwiderruflich überschrieben wird.
Die Namen „sda“ und „sdc“ sind dort übrigens verkürzt dargestellt. Der volle Pfad zu den Festplatten hat immer ein „/dev/“ vorangestellt. Statt „sda“ und „sdc“ müssen wir also später „/dev/sda“ und „/dev/sdc“ verwenden.

6.) Festplatten klonen

Das Klonen der Festplatten machen wir mit folgendem Befehl:

sudo dd conv=sparse if=/dev/ZuKlonendeFestplatte of=/dev/NeueFestplatte bs=8K status=progress

Zu den Parametern:
„conv=spare“ sorgt dafür, dass beim Klonen das Thin Provisioning erhalten bleibt. Nutzt man kein Thin Provisioning kann der Parameter weggelassen werden.

„if=/dev/ZuKlonendeFestplatte“ besagt was kopiert werden soll. Hier sollte eure alte Festplatte stehen.

„of=/dev/NeueFestplatte“ besagt wohin kopiert werden soll. Hier sollte eure neue Festplatte stehen.

„bs=4K“ sagt in welchen Blockgrößen der Kopiervorgang stattfinden soll. Hier bin ich nicht ganz sicher was der beste Wert wäre. Ich denke mit 8K macht ihr nichts falsch, wenn eure alte virtuelle Festplatte mit einer Block Size von 8K erstellt wurde.

„status=progress“ zeigt euch an wie weit das kopieren fortgeschritten ist

Ich nutze Thin Provisioning, meine alte virtuelle Festplatte hatte eine Blocksize von 8K, meine alte Festplatte ist „/dev/sda“ und diese möchte ich auf die neue leere Festplatte „/dev/sdc“ klonen. Ich führe also folgendes aus:

sudo dd conv=sparse if=/dev/sda of=/dev/sdc bs=8K status=progress

Tipp falls die Zeichen „=“ und „/“ nicht über Shift+7 und Shift+0 funktionieren sollten. „=“ ist bei mir in der VM über die Taste „`“ und „/“ über die Taste „/“ auf dem Num-Block eintippbar.

Das Klonen sieht dann so aus:

Danach noch einmal ausführen:

sudo lsblk

Wenn alles geklappt hat sollte eure neue Festplatte nun die gleiche Anzahl und Größe an Partitionen haben wie eure alte Festplatte.
Bei mir sind die Partitionen „sdc1“, „sdc2“ und „sdc3“ neu hinzugekommen:

Nun könnt ihr die VM mit folgendem Befehl herunterfahren:

sudo shutdown now

7.) Alte virtuelle Festplatte aushängen

Eure alte und neue virtuelle Festplatte sollte nun von Sicht der VM aus absolut identisch sein. Das heißt aber auch, dass da nun alle Partitionen mit genau der gleichen UUID doppelt in der VM vorhanden sind. Das hat den Vorteil, dass da keine Systemeinstellungen abgeändert werden müssen, aber es muss die alte virtuelle Festplatte zuvor erst entfernt werden, bevor man die VM wieder starten kann.

Also erst die alte virtuelle Festplatte aushängen (Detach-Knopf):

Dann die ISO wieder entfernen:

Und bei der Bootreihenfolge die neue virtuelle Festplatte nach ganz oben setzen:

8.) Testen ob alles so läuft wie bisher

Wenn Ihr jetzt die VM erneut startet, dann sollte eigentlich alles genau so sein wie bisher.
Wenn ihr sehen wollt ob die VM nun weniger Platz verbraucht, dann könnt ihr als root auf dem Proxmox Server selbst folgenden Befehl eingeben:

zfs list

Dort seht ihr alle virtuellen Festplatten und was diese an Platz auf dem Pool belegen. Der Wert in der Spalte „REFER“ bezieht sich auf die Größe der virtuellen Festplatte ohne Snapshots. Die neue virtuelle Festplatte sollte dort bei „REFER“ nun eigentlich weniger Platz auf dem Pool verbrauchen.
Ist etwas schwer zu erkennen welche denn nun genau die gesuchten virtuellen Festplatten sind. Wenn der Name „DeinPool/DeinVMDataset/vm-123-disc-4“ ist, dann ist es die Festplatte Nr. 4 von der VM mit der ID 123. Welche Festplatte wie heißt steht sonst auch unter „Hardware“ bei den „Festplatten“ in der VM.

Sollte alles passen könnt ihr die alte ausgehängt virtuelle Festplatte dann auch endgültig löschen:

Das geht wie gesagt aber auch erst, wenn ihr alle Snapshots gelöscht habt.

Ich hoffe das konnte wem helfen und falls ja würde ich mich über einen Kommentar freuen.

MfG

Björn