Journal Repartitionnement d'un disque distant à chaud

Posté par  (site web personnel) . Licence CC By‑SA.
20
30
avr.
2020

Sommaire

Quelques outils pratiques pour repartitionner à distance un disque dur en minimisant l'interruption de service : losetup, rsync --copy-devices et dd.

Le problème

  1. j'ai récemment dû installer un serveur dans le cloud un peu rapidement.
  2. du coup, j'ai foiré le partitionnement et j'ai utilisé tout un disque pour une partition (en faisant mkfs.ext4 /dev/sdb) : pas de table de partition, pas de LVM donc aucune flexibilité, pas de sauvegarde "propre" possible (instantanés lvm ou btrfs ou autre).

Partant de là, comment repartitionner ça de façon la plus sûre possible ? Et, si possible, sans interruption de service ? Sachant, bien évidemment, que ma connexion n'est pas très performante, donc que la solution consistant à tout récupérer en local, travailler en local et retransférer les données est exclue (trop long).

J'ai découvert quelques options et outils bien pratiques pour cela, l'objet de ce journal est de les partager avec vous.

Pour l'exemple, le disque qu'on souhaite redimensionner s'appelle /dev/sdb et pèse 50 Go.

Étape 1 : redimensionner la partition

Par défaut, la partition occupe toute la place. La première chose à faire, c'est de la redimensionner pour garder la place disponible en fin de disque pour faire les opérations voulues. Ici, on limite la place occupée à 15 Go (on pourrait aller jusqu'à 25 Go pour un disque de 50 Go). Ça nécessite de démonter la partition avant de la redimensionner (il y a donc une petite interruption de service à ce moment-là).

# umount /dev/sdb
# e2fsck -f /dev/sdb
# resize2fs /dev/sdb 15Go
# mount /dev/sdb

Étape 2 : sauvegarder la partition à distance

J'ai fait une bourde une fois, je veux pas en faire une seconde fois : avant toute chose, je veux sauvegarder la partition chez moi pour récupérer les données en cas d'erreur.

Savez-vous que rsync peut synchroniser des périphériques blocs ? Différents avis sur internet disent qu'il faut pour cela recompiler rsync avec un patch dédié, mais ces avis sont assez anciens et c'est aujourd'hui possible au moins sur Debian stable avec l'option --copy-devices (option non documentée). Rsync permet ainsi de transférer le disque en 2 passes :

  1. une première fois avec le disque monté pour récupérer l'essentiel des données (sauvegarde lente, mais le service continue à fonctionner)
  2. une seconde fois avec le disque démonté pour récupérer l'image disque dans un état cohérent (le service est interrompu mais ça va plus vite, on a seulement à transférer la différence entre la première sauvegarder et l'état à l'instant présent)

Problèmes :

  1. comment ne sauvegarder que la partition (les 15 Go) et pas tout le disque ?
  2. comment donner accès à ce périphérique à l'utilisateur sous le nom duquel on se connecte via SSH ? (étant entendu que la connexion à root via SSH est désactivée, bien évidemment)

À ces deux questions, losetup apporte une réponse élégante :

# losetup  /dev/sdb --sizelimit 15G --read-only --find --show
/dev/loop0
# chmod 640 /dev/loop0
# chgrp mon-utilisateur-ssh /dev/loop0

Maintenant, tout est prêt pour récupérer une première image de la partition distante :

mon-pc$ rsync --copy-devices mon-utilisateur-ssh@mon-serveur-tres-loin:/dev/loop0 disk.raw

Attention, cette partition n'est pas une sauvegarde "propre" : le disque étant toujours en cours d'utilisation sur le serveur, les données qui y sont stockées ne sont pas forcément cohérentes. Il faut ensuite démonter le disque (donc interrompre le service) et relancer la commande précédente pour avoir l'image propre :

# umount /dev/sdb
mon-pc$ rsync --copy-devices mon-utilisateur-ssh@mon-serveur-tres-loin:/dev/loop0 disk.raw
mon-pc$ sha1sum disk.raw
abcdefabcdefabcdefabcdefabcdefabcdefabcd disk.raw

La dernière commande calcule la somme de contrôle de l'image, permettant plus bas de vérifier que le transfert s'est bien passé vers la nouvelle partition.

Étape 3 : repartitionner le disque

Je ne sais pas quelle est la taille précise d'un en-tête de table de partition GUID, mais en lisant en diagonale l'article Wikipedia, ça ne doit pas peser bien lourd : on se contente de déplacer le premier Mo à l'abri avant de reformater le disque, ce qui écrira une table de partitions sur les premiers octets.

Ensuite, avec fdisk on crée une table de partition GUID (g) et une partition que l'on fait commencer après la fin de la partition qu'on souhaite transférer (ici : on la fait commencer au milieu du disque).

# dd if=/dev/sdb of=/root/disk-header bs=1M count=1
# fdisk /dev/sdb
Command (m for help): g
Created a new GPT disklabel (GUID: ABCDEFAB-CDEF-ABCD-EFAB-ABCDEFABCDEF).

The old ext4 signature will be removed by a write command.

Command (m for help): n
Partition number (1-128, default 1): 2
First sector (2048-104857566, default 2048): 52428800
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-2097118, default 104857566): 

Created a new partition 2 of type 'Linux filesystem' and of size 25 GiB.

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

Maintenant, vous faites ce que vous voulez avec la partition /dev/sdb2 (créer un volume LVM, ouvrir un espace chiffré avec LUKS, …).

Dans mon cas, j'ai un nouvel espace de stockage accessible à l'adresse /dev/mapper/donnees. Il faut transférer les données vers ce nouvel espace. dd permet de copier finement, d'abord depuis le fichier d'en-tête préalablement sauvegardé, ensuite depuis le disque dur /dev/loop0 pour tout le contenu au-delà du premier Mo. Utiliser /dev/loop0 comme source de copie nous évite d'avoir à préciser à dd la longueur précise des données à copier puisque ce périphérique est aligné sur la partition initiale.

# dd if=/root/disk-header of=/dev/mapper/donnees
# dd if=/dev/loop0 bs=1M seek=1 skip=1 of=/dev/mapper/donnees
# sha1sum /dev/mapper/donnees
abcdefabcdefabcdefabcdefabcdefabcdefabcd /dev/mapper/donnees

La dernière commande a pour objet de vérifier que tout s'est bien passé. Si la somme de contrôle ne correspondait pas, il faudrait renvoyer disk.raw depuis l'ordinateur local avec rsync aussi, en ajoutant de la même façon un périphérique boucle avec losetup visant le nouvel espace de stockage. Ça allongerait notablement l'interruption de service.

Si tout est bon, on peut relancer le service, après avoir corrigé le fichier /etc/fstab :

# mount /dev/mapper/donnees

Conclusion

L'interruption de service a duré le temps nécessaire redimensionner la partition (très rapide), puis le temps pour lire une fois les données (le second rsync) et pour les écrire une fois (dd). Il est sans doute possible de mener ces 2 opérations en parallèle.

Je suis surtout content de réaliser qu'avec rsync --copy-devices on a une solution très basique de sauvegarde incrémentale de périphériques blocs.

  • # C'est dans ce genre de situation que je me dis que j'ai bien raison d'utiliser lvm systématiquement.

    Posté par  . Évalué à 6. Dernière modification le 01 mai 2020 à 10:05.

    Et pour info, un fdisk sur un device lvm est inutile sauf pour un disque de boot. C'est même problématique lorsque tu le fais sur des LUNS d'un SAN que tu veux agrandir.

    Sinon, vu que tu es sur un cloud, pourquoi ne pas avoir ajouté un second disque durant la manip ?

    • [^] # Re: C'est dans ce genre de situation que[...]

      Posté par  . Évalué à 3.

      Salut,

      pourquoi ne pas avoir ajouté un second disque durant la manip ?

      Je me suis posé exactement la même question, mais j'ai eu peur de dire une bêtise.

      J'en étais arrivé à la réponse que c'est une forme de cloud privé d'entreprise (si on peut dire ça comme ça), et que le "scaling" n'est pas possible/facile.

      Sinon, merci pour le journal !

      PS : Désolé, j'ai dû tronquer le sujet ;)

      Matricule 23415

    • [^] # Re: C'est dans ce genre de situation que je me dis que j'ai bien raison d'utiliser lvm

      Posté par  (site web personnel) . Évalué à 3. Dernière modification le 01 mai 2020 à 12:27.

      Sinon, vu que tu es sur un cloud, pourquoi ne pas avoir ajouté un second disque durant la manip ?

      Je n'ai pas été très précis : c'était pas un cloud, mais un serveur privé virtuel. Il était possible de commander un disque supplémentaire, mais l'idée était aussi de faire l'opération à moindre coût (sans achat supplémentaire).

      Et pour info, un fdisk sur un device lvm est inutile sauf pour un disque de boot. C'est même problématique lorsque tu le fais sur des LUNS d'un SAN que tu veux agrandir.

      Peux-tu développer ? Ici, c'est un disque virtuel que l'hébergeur met à ma disposition, donc j'imagine bien qu'il y a un LVM en-dessous (ou toute autre techno du genre). C'est "gênant" dans ce cas ?

      Est-ce que tu es simplement en train de dire que la fonction "table de partition" est redondante avec les fonction de gestion d'espace avec LVM ?

      Mais ici comme j'ai voulu mettre du LVM: la table de partition m'a permis de créer l'espace LVM sur la deuxième moitié du disque (vgcreate vg-data /dev/sdb2), copier les données sur le volume créé sur cet espace, ajouter la partition sur la première moitié du disque (fdisk) et l'ajouter à l'espace LVM (vgextend vg-data /dev/sdb1) pour pouvoir utiliser les 50Go. Donc j'empile effectivement LVM sur une table de partition. Aurait-il été possible de faire la même opération sans table de partition ?

      • [^] # Re: C'est dans ce genre de situation que je me dis que j'ai bien raison d'utiliser lvm

        Posté par  . Évalué à 5.

        Est-ce que tu es simplement en train de dire que la fonction "table de partition" est redondante avec les fonction de gestion d'espace avec LVM ?

        Oui. Une partition (sdb1, sdb2, etc …) vu de ton système c'est la même chose que sdb. Tu peux faire les mêmes actions dessus: créer un filesystem sans partition, l'inclure dans un volume LVM … Je n'ai pas essayé mais il est probable qu'il soit même possible de faire un fdisk dessus. Une partition faite par fdisk ou équivalent n'est qu'un découpage en disques virtuels plus petit, et lvm fait la même chose. Ton périphérique physique ext découpé en plusieurs tranches (pv composé de Physical Extends) a la différence près qu'il y a ensuite une couche qui agrège ces PE pour en faire un volume logique (lv).

        fdisk n'est utile avec lvm que pour marquer le disque comme bootable. Si ton disque est un disque autre que le disque de boot, tu peux directement faire un pvcreate dessus (pvcreate /dev/sdb - de la même façon que tu as créé ton filesystem sur ton disque sans le partitionner).

        Ici, c'est un disque virtuel que l'hébergeur met à ma disposition, donc j'imagine bien qu'il y a un LVM en-dessous (ou toute autre techno du genre). C'est "gênant" dans ce cas ?

        C'est gênant si tu as par dessus repartitionné ton disque. Si tu fais agrandir le volume, tu devras supprimer ta partition pour en recréer une autre (fia fdisk par exemple). Avec LVM, tu as juste à faire un pvextend (de la même façon que, dans l'état initial de ton disque, où tu n'avais pas créé de partitions, tu n'aurais eu qu'à faire un resize2fs pour étendre ta partition sur ton volume étendu). Il me semble qu'il y a une manip avant à faire pour que ton système voit que ton disque a été agrandi mais je ne m'en rappelle plus (ça fait quelques années que je n'ai plus eu à faire ce genre de truc).

        Note: je viens de relire la fin de ton message. Il y aurait moyen de s'en sortir avec des partitions sur le disque en créant une nouvelle partition sur le disque agrandi et en ajoutant cette partition au lvm avec un pvcreate. Mais je trouve ça moche de saucissonner son volume à plusieurs niveaux comme ça.

        Maintenant, si tu veux jouer un peu, tu peux faire en sorte d'avoir une seule partition et un seul volume sur ton disque. Il suffit de faire un pvreduce de ta seconde partition, ensuite retirer ta deuxieme partition de ton volume LVM (vgreduce), détruire ta partition sdb2, puis détruire sdb1 et la recréer sur tout le disque (ça ne se fait pas a chaud mais ça dure pas des plombes non plus), faire un pvextend de sdb1 et tu auras un volume correspondant à un disque physique.

        la table de partition m'a permis de créer l'espace LVM sur la deuxième moitié du disque (vgcreate vg-data /dev/sdb2), copier les données sur le volume créé sur cet espace, ajouter la partition sur la première moitié du disque (fdisk) et l'ajouter à l'espace LVM (vgextend vg-data /dev/sdb1)

        C'est moche :(

        Aurait-il été possible de faire la même opération sans table de partition ?

        Clairement, non. Pas a ma connaissance.

      • [^] # Re: C'est dans ce genre de situation que je me dis que j'ai bien raison d'utiliser lvm

        Posté par  . Évalué à 2.

        Aurait-il été possible de faire la même opération sans table de partition ?

        Il y a moyen, mais c'est tordu risqué.

        pvcreate a une option --setphysicalvolumesize qui permet de forcer la taille utilisée.
        On veut qu'il soit au début du volume, donc il faut décaler ton système de fichier à la fin.
        Ça peut se faire à coup de dd (il y a peut-être des outils pour faire ça mieux/plus simplement), c'est la partie la plus risqué de l'opération.
        Tu crée alors ton volume physique dans l'espace libéré, puis les goupes et volumes logiques normalement.
        Après, tu as deux options.
        Soit tu recopie ton FS qui traine encore à a fin de sdb dans un fichier, puis tu étends (avec pvresize) ton volume physique pour récupérer l'espace, et tu monte le fichier en loopback our recopier les fichiers (si tu n'a pas fait d'erreur avant).
        Ou alors tu utilise losetup avec l'option --offset pour accéder directement à la fin de sdb en loopback, tu recopie les fichiers, et ensuite tu récupère l'espace avec pvresize.

        Par contre, je ne comprend pas pourquoi tu te complique la vie à essayer de copier le système de fichier brut, ce sont les fichiers qui sont dedans qui sont importants, et ils prendront forcément moins de place que le FS complet.

        • [^] # Re: C'est dans ce genre de situation que je me dis que j'ai bien raison d'utiliser lvm

          Posté par  . Évalué à 2. Dernière modification le 01 mai 2020 à 14:14.

          Effectivement, c'est une solution à laquelle je n'avais pas pensé.

          Par contre, je ne comprend pas pourquoi tu te complique la vie à essayer de copier le système de fichier brut, ce sont les fichiers qui sont dedans qui sont importants, et ils prendront forcément moins de place que le FS complet.

          C'est aussi ce que je me disais. Cela dit je ne me souviens plus si rsync par exemple prend aussi les fichiers cachés. Mais bon, si le but est de déplacer les blocs du début à la fin, ça peut aider à déplacer la partition d'origine non ?

          Un truc du style réduire la partition initiale au max, puis créer une nouvelle partition sdbN a la fin du disque, déplacer les données par blocs sur cette partition, ensuite pvcreate sur sdb en spécifiant la bonne taile, création du vg/lv/fs puis reprendre les données (en bloc) de la fin vers le début.

          Ensuite détruire la partition et resizer le PV.

          • [^] # Re: C'est dans ce genre de situation que je me dis que j'ai bien raison d'utiliser lvm

            Posté par  . Évalué à 7.

            Son problème d'origine est qu'il n'avait pas partitionné sdb, donc il n'y avait pas de partition d'origine et il ne pouvait pas en créer une nouvelle.

            Sauf que …

            Comme sont système de fichier est ext4, d'après https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Layout les 1024 premiers octets sont inutilisé.
            Donc il aurait été également possible de créer une table de partition de type MBR (qui est placée dans les premiers 512 octets), pour ensuite créer une vrai partition au-delà de la zone où se trouve encore le FS, puis à coup de dd copier le FS dans la partition ainsi créée.
            Ça évite de jouer avec losetup, mais c'est tout aussi tordu puisqu'on se retrouve avec un sdb "hybride" partitionné/non-partitionné.

            • [^] # Re: C'est dans ce genre de situation que je me dis que j'ai bien raison d'utiliser lvm

              Posté par  . Évalué à 1.

              J'ai récemment appris l'existence de cette "feature" car elle est indispensable pour pouvoir booter un sparc. L'openboot firmware ne pourrait démarrer que sur la partition qui commence au secteur zéro, partie qui contient aussi la table de partition (a.k.a. "sun disklabel") et, a fortiori, le bootblock. Ne me demandez pas comment grub s'en sort pour s'insérer dans ce bazard, ça marche juste.

  • # Autre solution

    Posté par  . Évalué à 2.

    J'ai eu la même problématique : partionnement mal fait, disque 1TB plein à ~60%, pas envie de 3 mois pour reuploader.

    L'hébergeur proposait une solution d'archivage en ligne. On crée une archive, on est facturé au mois et au GB. J'avais calculé un coût de quelques euros, que j'acceptais.

    Au final, je n'ai rien payé : l'hébergeur compte les GB archivés, et l'archive se prépare sur un espace disque temporaire. J'ai bêtement utilisé l'espace disque temporaire pour stocker mes données le temps de réinstaller / reformater / repartitionner mon serveur. Cet espace, utilisé 1 journée environ, n'a pas été facturé.

    OK, ta solution est + fun :-)

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.