QEMU/KVM: Discard-Support für (QCOW2) Images

Ein guter Freund hat mich gestern gebeten, das kurz zu dokumentieren: Wenn man mit QEMU/KVM, z.B. via libvirt interagierend, VMs mit Images im QCOW2-Format anlegt, dann kann man diese sparse erzeugen, es wird also nicht gleich der ganze Platz auf der Festplatte belegt (das funktioniert übrigens wohl auch für Images im raw-Format). Kopiert man jetzt große Datenmengen auf eine VM und löscht diese danach wieder, dann ist der Platz im Host-System erstmal vergeben und wird auch nicht wieder frei gegegeben. Um das zu erreichen sind folgende Schritte notwendig:

  • man definiert die VM neu, so dass sie einen SCSI-Disk-Controller hat und verwendet als Backend-Devices für alle Disks auch SCSI-Disks
  • man definiert bei der Storage den Parameter discard='unmap'
  • man löst periodisch ein fstrim aus

Ich hab mir jetzt mal eine Beispiel-VM genommen und erst mal die Konfiguration mit virsh dumpxml example > example.xml gesichert. In der Maschinen-Definition fand sich folgender Block zur Storage:

<disk type='file' device='disk'>
  <driver name='qemu' type='qcow2'/>
  <source file='/var/lib/libvirt/images/example.qcow2'/>
  <backingStore/>
  <target dev='vda' bus='virtio'/>
  <alias name='virtio-disk0'/>
  <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
</disk>

Ich habe mittels virsh edit example den discard-Parameter eingefügt, den Bus auf SCSI gestellt und die ganzen Address-Angaben entfernt:

<disk type='file' device='disk'>
 <driver name='qemu' type='qcow2' discard='unmap'/>
 <source file='/var/lib/libvirt/images/example.qcow2'/>
 <backingStore/>
 <target dev='sda' bus='scsi'/>
</disk>

Ferner fügte ich weiter unten noch den SCSI-Controller ein:

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

Nachdem ich die VM abgeschaltet und danach neu gestartet hatte war es dann Zeit für einen kurzen Test:

du -sh example.qcow2
# 2.2G    example.qcow2
ssh root@example 'dd if=/dev/zero of=/test.img bs=4M count=250; sync'
du -sh example.qcow2
# 3.2G    example.qcow2
ssh root@example 'rm /test.img; sync; fstrim -av'
# /boot: 0 B (0 bytes) trimmed
# /: 1.3 GiB (1353781248 bytes) trimmed
du -sh example.qcow2
# 2.2G    example.qcow2

Update 10.07.2018: Man beachte, dass ich hier den Befehl du verwendet habe ;-)

Man muss in der VM also jetzt einfach dafür Sorgen, das (semi-)regelmäßig ein fstrim -a aufgerufen wird - hallo, cron. Unter Debian liefert man die systemd-Unit-Files für fstrim.timer und fstrim.service mit:

cp /usr/share/doc/util-linux/examples/fstrim.* /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now fstrim.timer
# Created symlink /etc/systemd/system/timers.target.wants/fstrim.timer → /etc/systemd/system/fstrim.timer.

Und mehr ist nicht dabei :-)