URL: https://linuxfr.org/users/samuel/journaux/repartitionnement-d-un-disque-distant-a-chaud Title: Repartitionnement d'un disque distant à chaud Authors: Samuel Date: 2020年04月30日T22:11:51+02:00 License: CC By-SA Tags: dd, rsync, disque_dur, partitionnement et debian Score: 20 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](https://fr.wikipedia.org/wiki/GUID_Partition_Table), ç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.

AltStyle によって変換されたページ (->オリジナル) /