Discard für KVM-Gäste

Das qcow2-Imageformat in KVM erlaubt es die Abbilddateien als Sparse-Files zu erstellen. Sprase-Dateien belegen nur den tatsächlich verwendeten Speicherplatz. So lässt sich einer VM ein großzügiges Image bereitstellen, ohne dass der unbelegte Speicherplatz für alle anderen VMs verloren ist. Beim Backup können Sparse-Dateien schneller gesichert werden und auf SSDs sind sie gut für die Performance (sofern das Dateisystem auf dem sie liegen ebenfalls Discard unterstützt).

Das Ganze hat allerdings einen Hakten, solange die VM freie Blöcke nicht an den Hypervisor meldet, weiß dieser nicht, welche Teile das Image er verwerfen kann. Das heißt: Ohne die Mitarbeit der VM kann ein qcow2-Image nicht wieder kleiner werden. Dieser kleine Artikel beschreibt, wie eine KVM VM konfiguriert werden muss, damit Sparse-Dateien optimal genutzt werden können.

Datensicherung

Bevor man anfängt, die Konfiguration einer bestehenden VM zu verändern, ist es sehr empfehlenswert die bisher funktionierende Konfiguration zu sichern. Am einfachsten geht das über virsh dumpxml <VM> > backup.xml.

Die Sicherung lässt sich dann über virsh define <Dateiname> wieder einspielen.

VM Einstellen

Jetzt wo das Sicherheitsnetz steht, kann man beherzt zu virsh edit <VM> greifen, um die Konfiguration der VM zu bearbeiten.

Erst einmal muss der Typ der VM ausreichend aktuell sein, um discard zu unterstützen. Ein Maschinentyp ab pc-i440fx-2.1 ist ausreichend. Die aktuelle Version (Mai 2019) ist pc-i440fx-2.3. Der von der VM verwendete Maschinentyp ist im Attribut machine des Tags type unter os hinterlegt.

<os>
  <type arch='x86_64' machine='pc-i440fx-2.3'>hvm</type>
  <boot dev='hd'/>
</os>

Die Nutzung von discard ist nur über das virtuelle SCSI device (virtio-scsi) möglich. Direkt über virtio angebundene Festplatten unterstützen kein discard und müssen auf virtio-scsi umgezogen werden.

Small Computer Systems Interface

Unterhalb des Tags devices muss zuerst der SCSI-Controller aktiviert werden. Am einfachsten geht das durch das Hinzufügen der Zeile <controller type='scsi' index='0' model='virtio-scsi'/>. Virsh ergänzt dann alle weiteren Einstellungen automatisch:

<controller type='scsi' index='0' model='virtio-scsi'>
  <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</controller>

Wurde zu einer VM der SCSI Controller neu hinzugefügt, sollte die VM nochmal neu gestartet werden, damit der neue Controller erkannt wird. Grundsätzlich lässt sich das vorhanden sein des Controllers leicht über lspci prüfen. Die Ausgabe sollte eine Zeile enthalten die der folgenden ähnelt:

00:04.0 SCSI storage controller: Red Hat, Inc Virtio SCSI

Zusätzlich sollte man nun die init-Ramdisk neu zu bauen, da diese ggf. bisher die SCSI-Treiber nicht enthalten hat und ansonsten der nächste Bootvorgang ein vorzeitiges Ende findet.

Bevor die bestehende virtio-Festplatte über virtio-scsi angebunden wird, sollte auch noch die GRUB-Konfiguration kontrollieren werden. Sind dort noch die Namen von Festplatten als vda1 oder ähnlich eingetragen, müssen diese unbedingt zuerst auf eine UUID- oder Namensbasierte Zuordnung umgestellt werden. Tut man dies nicht, geht das beim nächsten Neustart ebenfalls nicht gut aus. 

Sind alle Anpassungen erfolgreich durchgeführt, kann die virtuelle Festplatte umkonfiguriert werden. Hierfür ergänzen wir zuerst einmal im driver-Tag das Attribut discard='unmap'. Dies sorgt dafür, das die entsprechenden Blöcke in der qcow2-Datei als ungenutzt markiert und an das Betriebssystem zurück gegeben werden. Unter target wird das dev-Tag auf sda (oder sdb, etc.) geändert, damit es zur neuen Anbindung über SCSI passt. Das bus-Tag muss dann natürlich noch auf scsi geändert werden. Ein eventuell vorhandenes address-Tag löschen wir einfach. Es wird durch virsh automatisch wieder mit den korrekten Einstellungen ergänzt.

Schlussendlich sieht das ganze dann ungefähr so aus:

<disk type='file' device='disk'>
  <driver name='qemu' type='qcow2' discard='unmap'/>
  <source file='/srv/vms/mydisk.qcow2'/>
  <target dev='sda' bus='scsi'/>
  <address type='drive' controller='0' bus='0' target='0' unit='0'/>
</disk>

<controller type='scsi' index='0' model='virtio-scsi'/>

Testlauf

Jetzt kann die angepasste VM (neu) gestartet werden. Hat diese erfolgreich gebootet, kann man als root fstrim -av aufrufen. Das Kommando sollte nun ausgeben, wie viel Speicher es als frei markiert hat. Erscheint an Stelle dessen eine Fehlermeldung, muss nochmal die Konfiguration der VM geprüft werden.

Ein einfaches ls -l zur Kontrolle der Veränderung des qcow2-Images fällt allerdings eher ernüchternd aus: Die Datei hat sich nur unwesentlich verkleinert. Dies hat damit zu tun, das qcow2 Sparse-Files unterstützt. Das heißt, ein qcow2-Image kann Löcher enthalten, die keinen Speicherplatz belegen. Das Ändert erst einmal nichts an der angezeigten Dateigröße. Um den tatsächlich belegten Speicherplatz zu ermitteln, eignet sich entweder du -h oder ls -lsh. Das Werkzeug du zeigt grundsätzlich nur den tatsächlich auf dem Datenträger belegten Speicherplatz an und mit der Option -s kann das auch das ls-Kommand. Es schreibt diesen Wert dann zusätzlich in die erste Spalte seiner Ausgabe:

5.6G -rw-rw---- 1 libvirt-qemu libvirt-qemu 12G May 20 19:33 mydisk.qcow2

Das Image mydisk.qcow2 ist also nominell 12 GiByte groß, belegt auf dem Datenträger jedoch nur 5,6 GiByte.

Sharing is caring

Liegen die frei gewordenen Blöcke auf einer SSD hat das Discard bisher nur dazu geführt, dass mehr Speicherplatz frei ist. Um diese freien Blöcke schlussendlich an die SSD zu melden, muss auf dem Hostsystem ebenfalls fstrim ausgeführt werden. Moderne systemd-basierte Distributionen bringen hierfür meist eine Unit mit dem einprägsamen Namen fstrim.timer mit. Einmal über systemctl enable --now fstrim.timer aktiviert sorgt diese Unit dafür, dass der VMHost einmal in der Woche die freien Blöcke an die SSD zurückgibt.