"Liberar" espacio en la imagen de una MV de manera rústica

per Victor Carceler darrera modificació 2021-01-07T09:46:24+01:00

Al trabajar con máquinas virtuales resulta habitual utilizar ficheros dispersos como respaldo para los discos. De este modo es posible, por ejemplo, crear un fichero que aparenta contener 2TiB de datos pero que inicialmente no ocupa nada. A medida que se escriben datos en su interior el SO anfitrión va reservando espacio físico. Si en la MV se escribe hasta su capacidad máxima el fichero acabará consumiendo los 2TiB.

Pero como ninguna máquina consume el 100% de la capacidad de disco (si esto ocurre el administrador tendrá un problema), resulta muy ventajoso el uso de ficheros dispersos. A esta manera de no reservar el almacenamiento hasta que no se consume se la llama thin provisioning.

Caso práctico en el instituto:

En el instituto ocurre exactamente lo explicado. Hay una máquina física, Rohan, en la que se ejecutan máquinas virtuales (con KVM/libvirt). La MV para la que se ha reservado un disco más grande corresponde a una instalación de owncloud en la que se cuelgan ficheros. Y precisamente cuenta con un disco duro respaldado por un fichero disperso de hasta 2TiB.

En este momento se puede ver la diferencia entre el tamaño aparente y el espacio consumido en disco:

root@rohan:/mnt/pool/images# ll -lh owncloud.img 
-rw------- 1 libvirt-qemu kvm 2,0T mar 24 20:09 owncloud.img
root@rohan:/mnt/pool/images# du -hs owncloud.img
1,4T    owncloud.img
root@rohan:/mnt/pool/images#

Es decir, aunque el fichero aparenta ser un fichero de 2TiB únicamente consume 1.4TiB en disco.

Podría pensarse que se han subido ficheros a la MV hasta consumir 1.4TiB de disco, pero esto, como se puede ver en la MV no es exactamente así:

vcarceler@owncloud:~$ df -h
S.ficheros                  Tamaño Usados  Disp Uso% Montado en
/dev/mapper/MAIN_VG-ROOT_LV   2,0T   1,1T  928G  54% /
none                          4,0K      0  4,0K   0% /sys/fs/cgroup
udev                          990M   4,0K  990M   1% /dev
tmpfs                         201M   368K  200M   1% /run
none                          5,0M      0  5,0M   0% /run/lock
none                         1001M      0 1001M   0% /run/shm
none                          100M      0  100M   0% /run/user
vcarceler@owncloud:~$

Umm... en la MV solamente se han escrito 1.1TiB y sin embargo la imagen consume 1.4TiB. ¿Cómo es posible?

La explicación es bien sencilla, cada vez que alguien sube un fichero a la MV el SO anfitrión se ve obligado a buscar espacio para guardar los contenidos. De manera que el fichero disperso pasa a consumir más espacio (es menos disperso). Pero los usuarios no mantienen de manera inmutable sus ficheros, de vez en cuando los borran y suben otros. Y aqui está la cuestión, cuando un usuario borra un fichero, lo único que hace la MV es modificar su sistema de archivos (XFS sobre LVM en este caso) para dejar de referenciar los bloques que tenían los datos del fichero borrado. La próxima vez que se necesiten se podrán reciclar esos bloques, pero en el fichero disperso no se vuelven a hacer 'agujeros'.

Así que al escribir datos en la MV el fichero disperso crece, pero al borrar datos en la MV el fichero disperso no decrece.

Después de un año de funcionamiento, se puede ver que en la MV owncloud se consume espacio, en diciembre se hizo una gran limpieza que redujo el espacio consumido del 70% al 50%, pero que esa reducción no se trasladó a Rohan.

Consumo de disco de owncloud Consumo de disco de Rohan
df-year-owncloud.png df-year-rohan.png

 

El comando TRIM: la manera eficiente de informar de los bloques que ya no se utilizan

El comando TRIM permite informar a los dispositivos SSD (o volúmenes thin provisioned como nuestros ficheros dispersos) de aquellos bloques que ya no se utilizan. Si se cuenta con soporte para TRIM es posible montar el sistema de archivos con la opción discard para que al borrar datos se envíen los comandos TRIM al dispositivo.

Pero aún es mejor opción utilizar el comando fstrim que permite analizar un sistema de archivos montado y enviar los comandos TRIM correspondientes al espacio no utilizado.

Lamentablemente en las MVs utilizadas esta opción no está disponible.

Y entonces ¿cómo ganar espacio aunque sea de manera rupestre?

Pues escribiendo muchos ceros (o bytes con el mismo valor) en el interior de la máquina virtual.

Realmente este truco funciona porque, en la máquina física, los discos de las máquinas virtuales están en un pool ZFS que tiene activa la compresión (con lz4). Así al utilizar en la máquina virtual dd para construir un gran fichero con ceros (fichero que finalmente se borrará), estamos obligando al fichero de respaldo a sobreescribir bloques que contenían datos antiguos con bloques puestos a 0. Pero, aquí está la magia, gracias a la compresión de ZFS estos bloques que tienen todos los bytes con el mismo valor alcanzan un ratio de compresión muy alto. De modo que se substituyen bloques por otros que ocupan menos espacio.

Así, después de escribir un fichero de 250GB con ceros en el interior de la MV owncloud, se puede ver que el fichero de respaldo ha reducido su espacio utilizado.

root@rohan:/mnt/pool/images# du -hs owncloud.img 
1,2T owncloud.img
root@rohan:/mnt/pool/images#

Y lo mejor es que este proceso no genera ninguna carga I/O apreciable en los discos físicos porque prácticamente no se está escribiendo nada.

Otra MV en la que se puede liberar espacio no utilizado es la que se dedica a mantener la web del centro ejecutando Plone. En este caso el fichero disperso es de hasta 250GiB y consume en disco 160GiB.

root@rohan:/mnt/pool/images# ll -lh plone.img 
-rw------- 1 libvirt-qemu kvm 250G mar 26 13:22 plone.img
root@rohan:/mnt/pool/images# du -hs plone.img
161G    plone.img
root@rohan:/mnt/pool/images#

Pero si se examina el sistema de archivos de la MV se descubre que únicamente hay 86GiB consumidos.

vcarceler@plone:~$ df -h
S.ficheros     Tamaño Usados  Disp Uso% Montado en
/dev/vda1        244G    86G  146G  37% /
none             4,0K      0  4,0K   0% /sys/fs/cgroup
udev             487M   4,0K  487M   1% /dev
tmpfs            100M   368K  100M   1% /run
none             5,0M      0  5,0M   0% /run/lock
none             497M      0  497M   0% /run/shm
none             100M      0  100M   0% /run/user
vcarceler@plone:~$

Así que se puede crear un fichero de 100GiB de ceros:

vcarceler@plone:~$ time dd if=/dev/zero of=zeros bs=1M count=100000
100000+0 registros leídos
100000+0 registros escritos
104857600000 bytes (105 GB) copiados, 333,21 s, 315 MB/s

real    5m33.234s
user    0m0.154s
sys     0m54.124s
vcarceler@plone:~$

Y reducir el archivo de la MV a 97GiB.

root@rohan:/mnt/pool/images# du -hs plone.img 
97G    plone.img
root@rohan:/mnt/pool/images#

Enlaces: