# Déplacer des données de PV à PV ou de LV à LV Cet article nécessite de comprendre [les bases de LVM](https://doc.ubuntu-fr.org/lvm). ## Contexte Dans certains cas, il est souhaitable de bouger tout ou partie des données d'un volume logique ou physique dans un autre volume logique ou physique. Prenons un exemple réel qui nous suivra pour cette page. Sur les machines virtuelles, à l'heure de l'écriture de cet article (06 janvier 2020), la mémoire est organisée comme suit : ``` $ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 100G 0 disk ├─sda1 8:1 0 243M 0 part /boot └─sda5 8:5 0 99,8G 0 part ├─vg00-swap 254:1 0 1,9G 0 lvm [SWAP] ├─vg00-root 254:2 0 57,9G 0 lvm / sdb 8:16 0 30G 0 disk └─vg01-data 254:0 0 20G 0 lvm /DATA ``` En d'autres termes : * Un PV `/dev/sda5` et un PV `/dev/sdb`, dans des **VG différents**, et tous deux correspondant au SSD, * Sur `/` est monté un LV basé sur le PV `/dev/sda5` (on utilise cet abus de langage, car chaque PV a son VG unique), * Sur `/DATA` est monté un LV basé sur le PV `/dev/sdb`, * Un LV basé sur le PV `/dev/sda5` est utilisé pour le SWAP. L'existence de deux disques distincts (`/dev/sda`, `/dev/sdb`), bien qu'appartenant tous deux au stockage `local` (SSD) de Proxmox, vient de l'existence historique de GlusterFS, qui nécessitait un disque à part. Il n'est pas nécessaire de complexifier les choses : **un VG** par type de stockage suffit, on voudrait donc réunir les deux disques sur un seul VG. Aussi, le dossier `/DATA/docker` est historiquement utilisé pour les [bind mounts](https://docs.docker.com/storage/bind-mounts/) dans les conteneurs Docker. Picasoft a petit à petit fait la transition vers des [volumes Docker](https://docs.docker.com/storage/volumes/), qui occupent le dossier `/var/lib/docker/volumes`. L'espace occupé dans `/DATA/docker` diminue tandis que celui de `/var/lib/docker` augmente. Sachant que `/var/lib/docker` contient aussi les images et les conteneurs, il serait intéressant de le mettre dans un LV séparé, dont on maîtrise la taille, afin qu'une explosion de taille ne paralyse pas l'intégralité du système et que l'on puisse facilement le redimensionner. Enfin, le dossier `/DATA/BACKUP` contient les backup des bases de données. Il n'est pas nécessaire que ces backups soient stockés sur le SSD, comme c'est le cas actuellement, car nous disposons d'une place limitée sur le SSD et la vitesse d'accès n'est pas une prioritaire. ## Objectifs L'état attendu (au niveau LVM) des machines virtuelles (du moins `pica01` ou `pica02`) est donc le suivant : * Un PV basé sur le SSD et un PV basé sur le HDD, dans des VG différents, * Un LV basé sur le PV SSD monté sur la racine (`/`), * Un LV basé sur le PV SSD utilisé comme SWAP, * Un LV basé sur le PV SSD monté sur `/DATA/docker`, * Un LV basé sur le PV SSD monté sur `/var/lib/docker`, * Un LV basé sur le PV HDD monté sur `/DATA/BACKUP`. Les transformations à effectuer sont les suivantes : * Fusionner les deux PV (`/dev/sda5` et `/dev/sdb`) pour n'en garder qu'un (`/dev/sda5`), *i.e.* transférer toutes les données des LV de `/dev/sdb` sur `/dev/sda5`, * Créer un nouveau LV monté sur `/DATA/docker` (SSD) et y transférer les données existantes, * Créer un nouveau LV monté sur `/var/lib/docker` (SSD) et y transférer les données existantes, * Créer un nouveau LV monté sur `/DATA/backup` (HDD) et y transférer les données existantes. ## Déplacer les données d'un PV vers un autre PV On veut "déplacer" `/dev/sdb` dans `/dev/sda`, c'est-à-dire déplacer tous les LV de `/dev/sdb` dans `/dev/sda5`. La première étape est de vérifier que `/dev/sda5` contient assez d'espace disque pour absorber `/dev/sdb` : ```bash $ pvs PV VG Fmt Attr PSize PFree /dev/sda5 vg00 lvm2 a-- 99,76g 40,00g /dev/sdb vg01 lvm2 a-- 30,00g 0 ``` On voit dans notre cas que c'est ok. Dans le cas contraire, on suivra [[technique:infrastructure:resize_vm_disks|ce tutoriel jusqu'à la partie "Augmentation du Physical Volume"]]. Plusieurs options s'offrent à nous : ### pvmove La commande [pvmove(8)](https://linux.die.net/man/8/pvmove) permet de bouger l'ensemble des **PE** (physical extents) d'un PV ou d'un LV vers un autre PV. Les PE sont des morceaux continus de données qui segmentent les PV. Cette commande est très précautionneuse et permet le transfert même quand le LV source est actif. **Problème**, la commande `pvmove` requiert que les deux PV appartiennent au même VG, ce qui n'est pas le cas ici : `sdb` et `sda5` appartiennent à deux VG différents. Il faudrait donc fusionner ces VG (*e.g* avec `vgmerge`), ce qui nécessite de rendre inactif un des deux VG, ce qui nécessite de démonter tous ses LV : impossibles pour le LV monté sur `/`, et pareil pour le LV monté sur `/DATA`, qui a des bind mounts à l'intérieur des conteneurs Docker, il faudrait éteindre tous les conteneurs... Si les deux PV avaient été sur le même VG, on aurait fait : ``` $ pvmove # Enlève le PV du VG $ vgreduce $ pvremove ``` Comme on pourrait le vérifier avec la commande `pvdisplay` avant et après l'opération `pvmove`, son stockage n'est plus du tout utilisé. ### Copier un snapshot avec dd On pourrait tout simplement utiliser `dd` pour copier les anciens LV vers les nouveaux LV. Mais comme les anciens LV sont utilisés (en particulier par Docker), la consistance des données peut être altérée pendant la copie, ce qu'on veut éviter. Voici comment on procède : On crée un snapshot de l'ancien volume, [au sens LVM](https://www.theurbanpenguin.com/maning-lvm-snapshots/). Le snapshot utilise peu de place (par un mécanisme de COW, il n'écrit dans le snapshot que lorsque l'original a été modifié) et assure la consistance des données. La taille du snapshot doit permettre de contenir uniquement les **changements** faits pendant la copie (on la déterminera empiriquement). ```bash $ lvcreate -L 100m -s /dev/vg01/data -n data_snapshot $ lvs LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert root vg00 -wi-ao---- 57,90g swap vg00 -wi-ao---- 1,86g data vg01 owi-aos--- 30,00g data_snapshot vg01 swi-a-s--- 100,00m data 0,21 ``` On constate que `data_snapshot` a bien pour origine `data`. On crée un nouveau LV sur le VG `vg00` (dont le seul membre est le PV `/dev/sda5`), avec une taille basée sur l'ancien et on copie le contenu du snapshot dans le nouveau LV. ```bash $ lvcreate -n data -L 35G vg00 $ dd if=/dev/vg01/data_snapshot of=/dev/vg00/data bs=4M status=progress 7679+0 enregistrements lus 7679+0 enregistrements écrits 32208060416 bytes (32 GB, 30 GiB) copied, 324,287 s, 99,3 MB/s ``` À ce stade, le principal est copié. Le souci avec Docker est que des bind mounts de `/DATA/docker/[...]` sont faits dans les conteneurs. Il ne suffit pas de démonter `/DATA` pour rendre le LV inactif. On stoppe donc le démon Docker et on démonte `/DATA`. ```bash $ systemctl stop docker $ umount /DATA ``` On utilise l'outil [lvmsync](https://github.com/mpalmer/lvmsync) pour copier les éventuelles modifications depuis la création du snapshot : ```bash $ lvmsync /dev/vg01/data_snapshot /dev/vg00/data Transferred 1568768 bytes in 0.41 seconds You transferred your changes 20530.80x faster than a full dd! ``` On constate en effet que le nombre de données à transférer est très faible (1,5M) et la copie est très rapide ; c'est une copie delta "à la `rsync`". On peut profiter que le LV ne soit pas monté pour réduire la taille ainsi que celle du système de fichier si nécessaire. On peut immédiatement monter le nouveau LV et redémarrer le démon Docker. Le downtime est très faible. ```bash # Si besoin... la taille est déterminée par votre estimation $ lvresize -L 15g --resizefs /dev/vg00/data $ mount /dev/mapper/vg00-data /DATA $ systemctl start docker ``` On peut ensuite tranquillement supprimer le snapshot ainsi que l'ancien LV, puis le VG et enfin le PV (s'il n'y avait qu'un PV concerné comme dans notre cas). On oublie pas de mettre à jour le `fstab` pour refléter le changement de chemin du nouveau LV (pas dans le même VG). ```bash $ lvremove /dev/vg01/data_snapshot $ lvremove /dev/vg01/data $ vgremove vg01 $ pvremove /dev/sdb ``` Il semble exister des cas où remonter dans `/DATA` montre un dossier vide, alors que monter dans n'importe quel autre dossier fonctionne. Peut-être du à systemd qui gère les points de montage `fstab`. Dans ce cas un `systemctl daemon-reload` préalable semble fonctionner. Voici l'état des périphériques blocs à la fin de la manipulation : ```bash $ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 100G 0 disk ├─sda1 8:1 0 243M 0 part /boot └─sda5 8:5 0 99,8G 0 part ├─vg00-swap 254:1 0 1,9G 0 lvm [SWAP] ├─vg00-root 254:2 0 57,9G 0 lvm / └─vg00-data 254:6 0 35G 0 lvm /DATA ``` On voit bien que le LV monté sur `/DATA` est sur le PV `sda5`, on peut alors tranquillement supprimer le deuxième disque depuis l'interface Promox. ## Séparation d'un LV en plusieurs LV On se retrouve maintenant dans la situation où un LV est monté sur `/DATA`, or on voudrait un LV sur `/DATA/docker` et un LV sur `/DATA/BACKUP`, chacun sur un PV différent (SSD et HDD). On voudrait aussi `/var/lib/docker` dans un `LV`. Pour le coup je ne vois pas vraiment de moyens d'éviter l'interruption de service car il ne s'agit pas de copier des LV entier mais de copier une partie du système de fichiers dans un LV et l'autre partie dans un autre. On aura toujours le risque de copie inconsistante, et pour le coup on ne peut pas vraiment exploiter les snapshots LVM (sauf à faire un snapshot, le monter dans un dossier temporaire, rsync les dossiers `/DATA/docker`... dans le nouveau LV, éteindre Docker, utiliser `lvmsync`, puis rsync de nouveau les dossiers pour avoir une copie delta au niveau du système de fichiers - ça peut marcher, mais je n'ai pas essayé). Étant donné que tout ces dossiers ne sont écrits que par le démon Docker, on va procéder de manière très bourrine : * Créer les volumes logiques souhaités * Éteindre le démon * Copier les fichiers des "anciens" LV aux "nouveaux" LV * Supprimer les anciens fichiers * Monter correctement les nouveaux LV L'idée est donc essentiellement de faire quelque chose comme (exemple pour les backups) ```bash # /dev/sdc est le nom du disque correspondant au HDD $ pvcreate /dev/sdc # On crée un PV auquel on intègre le HDD $ vgcreate vm102-hdd /dev/sdc # On crée un LV qui prend 50% de l'espace disponible $ lvcreate -n backup -l 50%FREE backup # On crée le système de fichiers $ mkfs.ext4 /dev/mapper/vm102--hdd-backup # On le monte dans un dossier temporaire $ mkdir /tmp/backup $ mount /dev/mapper/vm102--hdd-backup /tmp/backup # On déplace les backups $ cd /DATA/BACKUP $ rsync -a --info=progress2 . /tmp/backup # On supprime les anciens backups et on démonte le LV $ rm -r BACKUP $ umount /tmp/backup ``` Ensuite, on n'oubliera pas d'ajouter la ligne suivante dans `/etc/fstab` : ``` /dev/mapper/vm102--hdd-backup /DATA/BACKUP ext4 defaults 0 2 ``` On fait de même pour `/DATA/docker` et `/var/lib/docker`, simplement on ne crée pas de nouveau PV/VG, juste des nouveaux LV sur le VG sur lequel le SSD (`/dev/sda5`) est branché.