6. Le stockage LVM-thin — pourquoi c'est intelligent
6.1 Le thin-provisioning expliqué
La pile LVM que l'installateur a posée sur votre nœud, du disque aux disques des VM. La partition sda3 devient un volume physique, réuni dans le groupe de volumes pve.
Celui-ci est découpé en root, swap et data, le pool fin que Proxmox appelle local-lvm. Chaque disque de VM ou de conteneur y est un volume fin. Survolez un élément pour lire son rôle et le résultat du lab.
Quand vous créez un disque de 32 Go pour une VM dans local-lvm, Proxmox ne réserve pas immédiatement 32 Go d'espace physique. C'est le principe du thin provisioning — ou provisioning à la demande. Le volume LVM-thin n'occupe de l'espace réel que pour les données effectivement écrites.
Une VM fraîchement installée avec un OS de 3 Go n'utilisera que 3 à 4 Go sur le stockage physique, même si son disque virtuel est configuré à 32 Go. Cette différence est fondamentale pour l'utilisation de l'espace disque dans un atelier ou une infrastructure partagée.
Le thin-provisioning rend également les snapshots instantanés. Plutôt que de copier 32 Go de données, un snapshot LVM-thin ne note que les différences entre l'état actuel et le point de sauvegarde — une opération qui prend quelques millisecondes.
Dans le Shell du nœud (interface web → votre nœud → Shell) :
# Voir l'utilisation réelle du pool LVM-thin
lvs -o name,size,data_percent,lv_attr pve/data
Résultat attendu (nœud pxe211, Proxmox VE 9.2.21, en fin de chapitre 2) :
root@pxe211:~# lvs -o name,size,data_percent,lv_attr pve/data
LV LSize Data% Attr
data 61.87g 64.62 twi-aotz--
La colonne Data% est celle qu'il faut surveiller, et elle n'est visible que là. df -h ne montre rien du pool thin : il affiche l'espace vu par chaque VM, non l'espace réellement consommé dans le pool.
Les attributs twi-aotz-- se décodent : t pour thin pool, w pour accessible en écriture, i pour l'allocation héritée. Puis a pour actif, o pour ouvert, z pour la remise à zéro des blocs neufs.
Un pool thin saturé met en pause toutes les VM qui s'y trouvent, d'un coup. Le sur-provisionnement autorise la somme des disques virtuels à dépasser la taille du pool ; tant que les VM n'écrivent pas tout, cela tient. Le jour où elles le font, LVM n'a plus de blocs à distribuer et gèle les écritures — les systèmes invités passent en lecture seule ou plantent.
Au-delà de 80 % de Data%, il faut agir : étendre le pool, supprimer des snapshots, ou déplacer des disques. Une supervision de cette valeur est indispensable en production, et c'est la première valeur à confier à un outil de supervision.
Revenons à la définition de base : allouer un disque, c'est réserver de l'espace sur le stockage. Le thin provisioning réserve sur le papier, et n'occupe qu'au moment où l'on écrit. Les termes, du plus large au plus fin :
| Terme | Ce que c'est | Dans le laboratoire |
|---|---|---|
| Groupe de volumes | L'espace disque physique géré par LVM | pve, créé à l'installation |
| Pool thin | Le volume qui distribue des blocs aux disques des VM, au fil des écritures | data, vu par Proxmox comme le stockage local-lvm |
| Disque thin | Le disque d'une VM, de taille promise | vm-100-disk-0 |
Taille virtuelle (LSize) |
Ce que la VM croit avoir | 20 Go pour la VM 105 (atelier 2) |
Données écrites (Data%) |
Les blocs occupés pour de vrai | La colonne Data% de lvs |
| Sur-provisionnement | La somme des disques promis dépasse la taille du pool | Possible sur local-lvm |
| Pool saturé | Plus de bloc libre : les écritures gèlent, les VM se figent | À éviter : agir au-delà de 80 % |
Le sur-provisionnement tient tant que les VM n'écrivent pas toutes leur taille promise en même temps. Le jour où elles le font, le pool sature et fige toutes ses VM.
Où voir l'espace sur votre nœud :
| Notion | Où la voir |
|---|---|
| Le pool et son remplissage | Le stockage local-lvm → Summary (Usage) ; pvesm status |
| La part réellement écrite, pool et disques | lvs -o name,size,data_percent,lv_attr pve : colonnes LSize et Data% |
| La taille promise à une VM | La VM → Hardware → Hard Disk (size=32G) |
Ce que df -h ne montre pas |
df -h sur le nœud ignore le pool thin : seul lvs en donne le vrai remplissage |
6.2 Surveiller et gérer l'espace
Le danger du thin-provisioning est le over-provisioning. La somme des disques virtuels dépasse l'espace physique disponible. Si toutes les VMs commencent à écrire intensivement, le pool peut se remplir.
La jauge du stockage passe à l'orange puis au rouge dans l'interface, mais aucune alerte n'est envoyée. Sans supervision, on découvre le problème quand les VM se figent en erreur d'écriture.
Dans le Shell du nœud (interface web → votre nœud → Shell) :
# Vérifier l'espace disponible dans le groupe de volumes LVM
# (vgdisplay écrit « Free PE » avec deux espaces : d'où le « + » du motif)
vgdisplay pve # à lire : VG Size, Alloc PE, Free PE
Résultat attendu (les lignes qui comptent) :
root@pxe211:~# vgdisplay pve | grep -E 'VG Size|Alloc +PE|Free +PE'
VG Size <91.50 GiB
Alloc PE / Size 23423 / <91.50 GiB
Free PE / Size 0 / 0
Ces trois lignes indiquent la taille totale du groupe de volumes (VG Size), l'espace alloué aux volumes existants, et l'espace libre restant.
Deux espaces à ne pas confondre. Data% (de lvs) mesure le remplissage du thin pool : c'est lui qui gèle les VM quand il atteint 100 %.
Free PE mesure ce qui reste hors du pool dans le groupe de volumes, disponible pour agrandir le pool par lvextend. Ici, 0 : l'installateur a donné tout le groupe au système et au pool. Pour agrandir ce pool, il faut d'abord agrandir le disque.
Erreur fréquente : créer trop de VMs avec des disques surdimensionnés, puis oublier de surveiller le taux d'utilisation du pool. Mettez en place une alerte à 70 % en production.
6.3 Formats de disque et migration entre stockages
Proxmox supporte plusieurs formats de disques virtuels. Le choix du format dépend du type de stockage et du cas d'usage.
Et il n'est pas toujours libre : le type de stockage impose souvent le format. C'est la première chose à comprendre.
Un stockage bloc — LVM-thin, ZFS, RBD Ceph — ne connaît que le format raw. Il gère lui-même les instantanés et le provisionnement fin au niveau du volume.
La VM n'a donc pas besoin d'un format qui les apporte. Un stockage fichier — un répertoire, du NFS — accepte au contraire qcow2, parce qu'il faut bien un fichier capable de porter seul ses métadonnées et ses snapshots.
D'où la conséquence pratique qui surprend : sur LVM-thin, on ne perd rien à être en raw. Les snapshots existent quand même, mais ils sont assurés par la couche de stockage, pas par le format du disque. Croire qu'il faut du qcow2 pour avoir des snapshots est une erreur fréquente venue du monde du fichier.
Et c'est ce qui rend la migration entre stockages intéressante : déplacer un disque d'un stockage fichier vers un stockage bloc convertit automatiquement qcow2 en raw, et inversement. Proxmox gère la conversion de façon transparente pendant le Move Disk, y compris à chaud sur une VM allumée.
| Format | Description | Cas d'usage |
|---|---|---|
| raw | Format brut, pas de métadonnées. Performances maximales, accès direct aux blocs. | LVM-thin, ZFS — les snapshots sont gérés par la couche sous-jacente |
| qcow2 | Format QEMU natif avec snapshots internes, compression, sparsification. Légèrement moins performant en IOPS (opérations d'entrée-sortie par seconde). | Stockages répertoire (NFS, local) et images cloud |
| vmdk | Format VMware. Utile pour les migrations depuis VMware. Non recommandé pour de nouvelles VMs. | Migration VMware → Proxmox uniquement |
Dans le Shell du nœud (interface web → votre nœud → Shell) :
# Voir le format et l'occupation réelle d'un disque virtuel
qemu-img info /dev/pve/vm-100-disk-0
# → file format: raw
# → virtual size: 20 GiB
# → disk size: 20 GiB
qemu-img info /var/lib/vz/images/100/vm-100-disk-0.qcow2
# → file format: qcow2
# → virtual size: 20 GiB
# → disk size: 3.2 GiB ← espace réellement utilisé (sparsification)
Résultat attendu : les valeurs notées en commentaire (→) — raw pour le volume LVM, qcow2 pour le fichier, chacun avec sa taille virtuelle et sa taille sur disque.
qemu-img info distingue deux tailles, et c'est tout l'intérêt de la commande. La taille virtuelle est celle que voit la VM : le disque de 20 Gio annoncé à son système. La taille sur disque est ce que le fichier occupe réellement sur le stockage.
Pour un fichier qcow2, l'écart est le provisionnement fin en action : seuls les blocs écrits existent. Pour un volume LVM brut, qemu-img ne sait pas ce que le pool thin a réellement alloué — c'est lvs et sa colonne Data% qu'il faut lire, comme au §6.1.
Le déplacement d'un disque, enfin, se fait sans arrêter la VM :
Dans l'interface : la VM → Hardware → le disque → Disk Action → Move Storage. Le stockage cible doit accepter le contenu Disk image. Sur un nœud neuf, seul local-lvm l'accepte : pour l'essai ci-dessous, un stockage de type répertoire, disques-dir, a été déclaré avec ce contenu.
Dans le Shell du nœud (interface web → votre nœud → Shell) :
# Vers local : refusé, local n'accepte pas les disques
qm disk move 105 scsi1 local --format qcow2 --delete 1
# Vers un répertoire qui accepte les disques, VM allumée, au format qcow2
qm disk move 105 scsi1 disques-dir --format qcow2
qm config 105 # le disque déplacé, et l'ancien resté en unused ; à lire : scsi1, unused
Résultat attendu (les lignes qui comptent) :
400 Parameter verification failed.
storage: storage 'local' does not support vm images
copying volume 'local-lvm:vm-105-disk-1' from current storage 'local-lvm' to target storage 'disques-dir'
moving disk with snapshots, snapshots will not be moved!
create full clone of drive scsi1 (local-lvm:vm-105-disk-1)
Formatting '/var/lib/disques-dir/images/105/vm-105-disk-0.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off preallocation=metadata compression_type=zlib size=6442450944 lazy_refcounts=off refcount_bits=16
allocated target volume 'disques-dir:105/vm-105-disk-0.qcow2'
drive mirror is starting for drive-scsi1
mirror-scsi1: transferred 2.3 GiB of 6.0 GiB (38.02%) in 1s
mirror-scsi1: transferred 6.0 GiB of 6.0 GiB (100.00%) in 2s, ready
all 'mirror' jobs are ready
mirror-scsi1: Completing block job...
mirror-scsi1: Completed successfully.
mirror-scsi1: mirror-job finished
adding source volume 'local-lvm:vm-105-disk-1' as 'unused0' to config
scsi1: disques-dir:105/vm-105-disk-0.qcow2,discard=on,iothread=1,size=6G
unused0: local-lvm:vm-105-disk-1
La sortie raconte la méthode. Sur une VM allumée, Proxmox crée le nouveau disque, puis lance un miroir.
QEMU recopie les blocs en arrière-plan, et répercute sur la copie les écritures qui arrivent pendant ce temps. Quand les deux côtés sont identiques (ready), il bascule la VM sur le nouveau disque, sans interruption.
Trois points à retenir :
- L'ancien disque reste, détaché, en
unused0: il occupe toujours sa place. On le supprime ensuite (Hardware →unused0→ Remove). --delete 1supprimerait la source dans la foulée. Mais il est refusé sur une VM qui a des snapshots :you can't move a disk with snapshots and delete the source(sur la VM 105, qui porte le snapshotavant-essai).- Les snapshots ne suivent pas le disque :
moving disk with snapshots, snapshots will not be moved!.
qm move-disk, l'ancien nom de la commande, fonctionne encore.
6.4 Redimensionner un disque à chaud
Un besoin très courant en exploitation est d'agrandir le disque d'une VM déjà en production, sans devoir l'éteindre ni recréer son volume. Proxmox permet ce redimensionnement à chaud grâce au support du hot-plug de la plupart des bus de disque modernes (VirtIO SCSI, VirtIO Block).
L'opération se déroule en deux temps. Proxmox agrandit d'abord le volume logique côté hyperviseur : LVM-thin, ZFS ou fichier qcow2 selon le stockage. Puis il informe la VM en cours d'exécution que la nouvelle taille est disponible.
C'est ensuite au système de fichiers à l'intérieur de la VM d'utiliser cet espace. Proxmox ne redimensionne jamais la partition ni le système de fichiers du guest : seulement le disque virtuel vu depuis l'hyperviseur.
Dans le Shell du nœud (interface web → votre nœud → Shell) :
# Agrandir le disque scsi0 de la VM 100 de 10 Go supplémentaires
qm resize 100 scsi0 +10G
# Vérifier la nouvelle taille côté hyperviseur
qm config 100 | grep scsi0
Résultat attendu de qm resize 100 scsi0 +10G :
Size of logical volume pve/vm-100-disk-0 changed from 32.00 GiB (8192 extents) to 42.00 GiB (10752 extents).
Logical volume pve/vm-100-disk-0 successfully resized.
Le + compte : +10G ajoute 10 Gio, alors que 10G sans signe demanderait une taille finale de 10 Gio — plus petite que l'actuelle, ce que Proxmox refuse (shrinking disks is not supported).
Une fois le disque agrandi côté hyperviseur, il reste à agrandir la partition et le système de fichiers depuis l'intérieur de la VM. Sur une distribution Linux moderne utilisant growpart et un système de fichiers ext4 ou XFS, la procédure est directe :
Dans la VM (sa Console, ou en SSH) :
# Depuis l'intérieur de la VM Debian
growpart /dev/sda 1 # agrandit la partition 1 sur le disque /dev/sda
resize2fs /dev/sda1 # agrandit le système de fichiers ext4 (xfs_growfs pour XFS)
# Vérifier l'espace disponible après agrandissement
df -h /
Résultat attendu :
CHANGED: partition=1 start=2048 old: size=67106816 end=67108863 new: size=88078303 end=88080350
resize2fs 1.47.2 (1-Jan-2025)
Filesystem at /dev/sda1 is mounted on /; on-line resizing required
The filesystem on /dev/sda1 is now 11009787 (4k) blocks long.
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 42G 2.1G 38G 6% /
Les trois étapes se lisent dans l'ordre. growpart déplace la fin de la partition (CHANGED, avec l'ancienne et la nouvelle taille en secteurs). resize2fs étend le système de fichiers à chaud (on-line resizing).
df confirme la nouvelle taille vue par le système. Une partition agrandie sans resize2fs ne sert à rien : df afficherait toujours l'ancienne taille.
Sur une Debian installée depuis l'ISO, growpart /dev/sda 1 échoue souvent. Le partitionnement assisté place la racine en sda1, puis une partition étendue sda2 qui contient le swap sda5 — après la racine. La partition 1 est donc coincée : l'espace ajouté se trouve au-delà du swap, et growpart répond NOCHANGE: partition 1 ... could only be grown by 0.
lsblk montre la situation d'un coup d'œil. Il faut alors désactiver le swap (swapoff -a), supprimer puis recréer les partitions 2 et 5 en fin de disque, ou tout simplement ajouter un second disque plutôt qu'agrandir le premier. Les images cloud, elles, placent la racine en dernier et ne connaissent pas ce problème.
Attention : growpart n'est pas installé par défaut. Il vient du paquet cloud-guest-utils, que ni l'ISO netinst ni la tâche « utilitaires usuels du système » n'installent. Sur une VM installée depuis une ISO, il faut donc commencer par :
Dans la VM (sa Console, ou en SSH) :
apt update # la liste des paquets
apt install -y cloud-guest-utils # installer growpart (paquet cloud-guest-utils)
which growpart # où il est installé
Résultat attendu (la fin) : /usr/bin/growpart.
Sur les images cloud-init, en revanche, cloud-guest-utils est préinstallé : ces images sont conçues pour s'adapter au disque qu'on leur attribue. L'opération y est transparente, ce qui explique que beaucoup de tutoriels omettent cette étape. La commande qm resize ne permet que d'agrandir un disque.
Il n'existe volontairement aucune commande pour réduire un disque virtuel à chaud. Cette opération risquerait de tronquer des données déjà écrites en fin de volume. Pour réduire un disque, il faut passer par une réinstallation ou une migration vers un nouveau disque plus petit après avoir vérifié que l'espace occupé le permet.
▶ À pratiquer maintenant — Atelier 4
Mesurer le provisionnement fin, ajouter un second disque, puis l'agrandir sans arrêter la VM.
Proxmox VE — TSSR