Qu'ont en commun LVM et Matriochka ?

Bonne journée.
J'aimerais partager avec la communauté mon expérience pratique dans la création d'un système de stockage de données pour KVM utilisant md RAID + LVM.

Le programme comprendra :

  • Création de md RAID 1 à partir d'un SSD NVMe.
  • Assemblage de md RAID 6 à partir d'un SSD SATA et de disques standards.
  • Caractéristiques du fonctionnement TRIM/DISCARD sur SSD RAID 1/6.
  • Création d'une matrice md RAID 1/6 amorçable sur un ensemble commun de disques.
  • Installation du système sur NVMe RAID 1 lorsqu'il n'y a pas de support NVMe dans le BIOS.
  • Utilisation du cache LVM et de LVM Thin.
  • Utilisation d'instantanés BTRFS et envoi/réception pour la sauvegarde.
  • Utilisation d'instantanés légers LVM et Thin_delta pour les sauvegardes de style BTRFS.

Si vous êtes intéressé, veuillez consulter le chat.

déclaration

L'auteur n'assume aucune responsabilité pour les conséquences de l'utilisation ou de la non-utilisation des matériaux/exemples/codes/astuces/données de cet article. En lisant ou en utilisant ce matériel de quelque manière que ce soit, vous assumez la responsabilité de toutes les conséquences de ces actions. Les conséquences possibles incluent :

  • SSD NVMe croustillants.
  • Ressources d'enregistrement complètement épuisées et panne des disques SSD.
  • Perte totale de toutes les données sur tous les disques, y compris les copies de sauvegarde.
  • Matériel informatique défectueux.
  • Perte de temps, de nerfs et d'argent.
  • Toute autre conséquence non répertoriée ci-dessus.

Fer

Disponibles étaient :

Carte mère d'environ 2013 avec chipset Z87, complète avec Intel Core i7 / Haswell.

  • Processeur 4 cœurs, 8 threads
  • 32 Go de RAM DDR3
  • 1 x 16 ou 2 x 8 PCIe 3.0
  • 1 x 4 + 1 x 1 PCIe 2.0
  • 6 connecteurs SATA 6 à 3 Gbit/s

L'adaptateur SAS LSI SAS9211-8I a flashé en mode IT/HBA. Le micrologiciel compatible RAID a été intentionnellement remplacé par le micrologiciel HBA pour :

  1. Vous pouvez jeter cet adaptateur à tout moment et le remplacer par n'importe quel autre que vous rencontrez.
  2. TRIM/Discard fonctionnait normalement sur les disques, car... dans le micrologiciel RAID, ces commandes ne sont pas du tout prises en charge et le HBA, en général, ne se soucie pas des commandes transmises sur le bus.

Disques durs - 8 morceaux de HGST Travelstar 7K1000 d'une capacité de 1 To dans un facteur de forme 2.5, comme pour les ordinateurs portables. Ces disques appartenaient auparavant à une matrice RAID 6. Ils seront également utiles dans le nouveau système. Pour stocker des sauvegardes locales.

Ajout supplémentaire :

6 pièces SATA SSD modèle Samsung 860 QVO 2 To. Ces SSD nécessitaient un volume important, la présence d'un cache SLC, de la fiabilité et un prix bas étaient souhaités. La prise en charge de throw/zero était requise, ce qui est vérifié par la ligne dans dmesg :

kernel: ata1.00: Enabling discard_zeroes_data

2 morceaux de SSD NVMe modèle Samsung SSD 970 EVO 500 Go.

Pour ces SSD, la vitesse de lecture/écriture aléatoire et la capacité des ressources adaptées à vos besoins sont importantes. Radiateur pour eux. Nécessairement. Absolument. Sinon, faites-les frire jusqu'à ce qu'ils soient croustillants lors de la première synchronisation RAID.

Adaptateur StarTech PEX8M2E2 pour 2 x SSD NVMe installé dans un emplacement PCIe 3.0 8x. Encore une fois, il ne s'agit que d'un HBA, mais pour NVMe. Il diffère des adaptateurs bon marché en ce sens qu'il ne nécessite pas de prise en charge de la bifurcation PCIe de la carte mère en raison de la présence d'un commutateur PCIe intégré. Il fonctionnera même dans le système le plus ancien avec PCIe, même s'il s'agit d'un emplacement x1 PCIe 1.0. Naturellement, à la vitesse appropriée. Il n'y a pas de RAID là-bas. Il n'y a pas de BIOS intégré à bord. Ainsi, votre système n'apprendra pas comme par magie à démarrer avec NVMe, et encore moins à faire du NVMe RAID grâce à ce périphérique.

Ce composant était uniquement dû à la présence d'un seul 8x PCIe 3.0 gratuit dans le système et, s'il y a 2 emplacements libres, il peut être facilement remplacé par deux centimes PEX4M2E1 ou analogues, qui peuvent être achetés n'importe où au prix de 600 roubles.

Le rejet de tout type de matériel ou de chipset/BIOS RAID intégrés a été fait délibérément, afin de pouvoir remplacer complètement l'ensemble du système, à l'exception des SSD/HDD eux-mêmes, tout en conservant toutes les données. Idéalement, afin que vous puissiez même sauvegarder le système d'exploitation installé lors du passage à un matériel complètement nouveau/différent. L'essentiel est qu'il existe des ports SATA et PCIe. C'est comme un live CD ou une clé USB bootable, mais très rapide et un peu encombrant.

HumourSinon, vous savez ce qui se passe - vous devez parfois emporter de toute urgence l'ensemble du tableau avec vous. Mais je ne veux pas perdre de données. Pour ce faire, tous les supports mentionnés sont idéalement situés sur les diapositives dans les baies 5.25 du boîtier standard.

Eh bien, et bien sûr, pour expérimenter différentes méthodes de mise en cache SSD sous Linux.

Les raids matériels sont ennuyeux. Allume ça. Soit ça marche, soit ça ne marche pas. Et avec mdadm, il y a toujours des options.

Logiciel

Auparavant, Debian 8 Jessie était installée sur le matériel, proche d'EOL. RAID 6 a été assemblé à partir des disques durs mentionnés ci-dessus associés à LVM. Il exécutait des machines virtuelles dans kvm/libvirt.

Parce que L'auteur a une expérience appropriée dans la création de lecteurs flash SATA/NVMe amorçables portables, et aussi, afin de ne pas casser le modèle apt habituel, Ubuntu 18.04 a été choisi comme système cible, qui a déjà été suffisamment stabilisé, mais a encore 3 ans de soutien à l'avenir.

Le système mentionné contient tous les pilotes matériels dont nous avons besoin, prêts à l'emploi. Nous n'avons besoin d'aucun logiciel ou pilote tiers.

Préparation à l'installation

Pour installer le système, nous avons besoin d'Ubuntu Desktop Image. Le système serveur dispose d'une sorte d'installateur vigoureux, qui montre une indépendance excessive qui ne peut pas être désactivée en plaçant la partition système UEFI sur l'un des disques, gâchant ainsi toute la beauté. Par conséquent, il est installé uniquement en mode UEFI. N'offre aucune option.

Cela ne nous satisfait pas.

Pourquoi?Malheureusement, le démarrage UEFI est extrêmement mal compatible avec le logiciel de démarrage RAID, car... Personne ne nous propose de réservations pour la partition UEFI ESP. Il existe des recettes sur Internet qui suggèrent de placer la partition ESP sur une clé USB dans un port USB, mais c'est un point d'échec. Il existe des recettes utilisant le logiciel mdadm RAID 1 avec la version de métadonnées 0.9 qui n'empêchent pas le BIOS UEFI de voir cette partition, mais cela dure jusqu'au moment heureux où le BIOS ou un autre système d'exploitation matériel écrit quelque chose sur l'ESP et oublie de le synchroniser avec un autre. miroirs.

De plus, le démarrage UEFI dépend de la NVRAM, qui ne sera pas déplacée avec les disques vers le nouveau système, car fait partie de la carte mère.

Nous ne réinventerons donc pas une nouvelle roue. Nous disposons déjà d'un vélo de grand-père prêt à l'emploi et éprouvé, désormais appelé démarrage Legacy/BIOS, portant le fier nom de CSM sur les systèmes compatibles UEFI. Nous allons simplement le retirer du commerce, le lubrifier, gonfler les pneus et l'essuyer avec un chiffon humide.

La version de bureau d'Ubuntu ne peut pas non plus être installée correctement avec le chargeur de démarrage Legacy, mais ici, comme on dit, il existe au moins des options.

Et ainsi, nous collectons le matériel et chargeons le système à partir du lecteur flash amorçable Ubuntu Live. Nous devrons télécharger des packages afin de configurer le réseau qui vous convient. Si cela ne fonctionne pas, vous pouvez charger à l'avance les packages nécessaires sur une clé USB.

On passe dans l'environnement Desktop, on lance l'émulateur de terminal, et c'est parti :

#sudo bash

Comment…?La ligne ci-dessus est le déclencheur canonique des vacances à propos de sudo. Cbоde plus grandes opportunités se présentent etоune plus grande responsabilité. La question est de savoir si vous pouvez vous en charger vous-même. Beaucoup de gens pensent qu'utiliser sudo de cette manière n'est pour le moins pas prudent. Cependant:

#apt-get install mdadm lvm2 thin-provisioning-tools btrfs-tools util-linux lsscsi nvme-cli mc

Pourquoi pas ZFS... ?Lorsque nous installons un logiciel sur notre ordinateur, nous prêtons essentiellement notre matériel aux développeurs de ce logiciel pour qu'il le pilote.
Lorsque nous confions à ce logiciel la sécurité de nos données, nous contractons un emprunt égal au coût de restauration de ces données, que nous devrons un jour rembourser.

De ce point de vue, ZFS est une Ferrari, et mdadm+lvm ressemble plus à un vélo.

Subjectivement, l'auteur préfère prêter une moto à crédit à des inconnus plutôt qu'une Ferrari. Là, le prix de l'émission n'est pas élevé. Pas besoin de droits. Plus simple que les règles de circulation. Le stationnement est gratuit. La capacité de cross-country est meilleure. Vous pouvez toujours attacher des jambes à un vélo et réparer un vélo de vos propres mains.

Pourquoi alors BTRFS... ?Afin de démarrer le système d'exploitation, nous avons besoin d'un système de fichiers pris en charge par Legacy/BIOS GRUB et prenant en même temps en charge les instantanés en direct. Nous l'utiliserons pour la partition /boot. De plus, l'auteur préfère utiliser ce FS pour / (root), sans oublier de noter que pour tout autre logiciel vous pouvez créer des partitions distinctes sur LVM et les monter dans les répertoires nécessaires.

Nous ne stockerons aucune image de machines virtuelles ou de bases de données sur ce FS.
Ce FS ne sera utilisé que pour créer des instantanés du système sans l'éteindre, puis transférer ces instantanés sur un disque de sauvegarde par envoi/réception.

De plus, l'auteur préfère généralement conserver un minimum de logiciels directement sur le matériel et exécuter tous les autres logiciels dans des machines virtuelles en utilisant des éléments tels que le transfert des GPU et des contrôleurs hôtes PCI-USB vers KVM via IOMMU.

Les seules choses qui restent sur le matériel sont le stockage des données, la virtualisation et la sauvegarde.

Si vous faites davantage confiance à ZFS, alors, en principe, pour l'application spécifiée, ils sont interchangeables.

Cependant, l'auteur ignore délibérément les fonctionnalités intégrées de mise en miroir/RAID et de redondance dont disposent ZFS, BRTFS et LVM.

Comme argument supplémentaire, BTRFS a la capacité de transformer des écritures aléatoires en écritures séquentielles, ce qui a un effet extrêmement positif sur la vitesse de synchronisation des instantanés/sauvegardes sur le disque dur.

Analysons à nouveau tous les appareils :

#udevadm control --reload-rules && udevadm trigger

Jetons un coup d'œil autour de vous :

#lsscsi && nvme list
[0:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sda
[1:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sdb
[2:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sdc
[3:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sdd
[4:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sde
[5:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sdf
[6:0:0:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdg
[6:0:1:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdh
[6:0:2:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdi
[6:0:3:0] disk ATA HGST HTS721010A9 A3B0 /dev/sdj
[6:0:4:0] disk ATA HGST HTS721010A9 A3B0 /dev/sdk
[6:0:5:0] disk ATA HGST HTS721010A9 A3B0 /dev/sdl
[6:0:6:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdm
[6:0:7:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdn
Node SN Model Namespace Usage Format FW Rev
---------------- -------------------- ---------------------------------------- --------- -------------------------- ---------------- --------
/dev/nvme0n1 S466NXXXXXXX15L Samsung SSD 970 EVO 500GB 1 0,00 GB / 500,11 GB 512 B + 0 B 2B2QEXE7
/dev/nvme1n1 S5H7NXXXXXXX48N Samsung SSD 970 EVO 500GB 1 0,00 GB / 500,11 GB 512 B + 0 B 2B2QEXE7

Disposition du disque

NVMe SSD

Mais nous ne les marquerons en aucun cas. Néanmoins, notre BIOS ne voit pas ces lecteurs. Ils passeront donc entièrement au RAID logiciel. Nous n’y créerons même pas de sections. Si vous souhaitez suivre le « canon » ou « principalement », créez une grande partition, comme un disque dur.

Disque dur SATA

Il n'est pas nécessaire d'inventer quelque chose de spécial ici. Nous allons créer une section pour tout. Nous allons créer une partition car le BIOS voit ces disques et peut même essayer de démarrer à partir d'eux. Nous installerons même GRUB sur ces disques plus tard pour que le système puisse soudainement le faire.

#cat >hdd.part << EOF
label: dos
label-id: 0x00000000
device: /dev/sdg
unit: sectors

/dev/sdg1 : start= 2048, size= 1953523120, type=fd, bootable
EOF
#sfdisk /dev/sdg < hdd.part
#sfdisk /dev/sdh < hdd.part
#sfdisk /dev/sdi < hdd.part
#sfdisk /dev/sdj < hdd.part
#sfdisk /dev/sdk < hdd.part
#sfdisk /dev/sdl < hdd.part
#sfdisk /dev/sdm < hdd.part
#sfdisk /dev/sdn < hdd.part

SSD SATA

C'est là que les choses deviennent intéressantes pour nous.

Premièrement, nos disques ont une taille de 2 To. C'est dans la plage acceptable pour le MBR, c'est ce que nous utiliserons. Si nécessaire, peut être remplacé par GPT. Les disques GPT ont une couche de compatibilité qui permet aux systèmes compatibles MBR de voir les 4 premières partitions si elles se trouvent dans les 2 premiers téraoctets. L'essentiel est que la partition de démarrage et la partition bios_grub sur ces disques soient au début. Cela vous permet même de démarrer à partir de lecteurs GPT Legacy/BIOS.

Mais ce n'est pas notre cas.

Ici, nous allons créer deux sections. Le premier aura une taille de 1 Go et sera utilisé pour RAID 1/boot.

Le second sera utilisé pour le RAID 6 et occupera tout l'espace libre restant à l'exception d'une petite zone non allouée à l'extrémité du disque.

Quelle est cette zone non marquée ?Selon des sources sur le réseau, nos SSD SATA intègrent un cache SLC extensible de manière dynamique dont la taille varie de 6 à 78 gigaoctets. Nous obtenons 6 gigaoctets « gratuitement » en raison de la différence entre « gigaoctets » et « gibioctets » dans la fiche technique du lecteur. Les 72 gigaoctets restants sont alloués à partir de l'espace inutilisé.

Ici, il convient de noter que nous avons un cache SLC, et que l'espace est occupé en mode MLC 4 bits. Ce qui pour nous signifie effectivement que pour 4 Go d'espace libre, nous n'obtiendrons que 1 Go de cache SLC.

Multipliez 72 gigaoctets par 4 et obtenez 288 gigaoctets. Il s'agit de l'espace libre que nous ne baliserons pas afin de permettre aux disques d'utiliser pleinement le cache SLC.

Ainsi, nous obtiendrons effectivement jusqu'à 312 Go de cache SLC à partir de six disques au total. Parmi tous les disques, 2 seront utilisés en RAID pour la redondance.

Cette quantité de cache nous permettra de rencontrer extrêmement rarement dans la vie réelle une situation où une écriture ne va pas dans le cache. Cela compense extrêmement bien l'inconvénient le plus triste de la mémoire QLC : la vitesse d'écriture extrêmement faible lorsque les données sont écrites en contournant le cache. Si vos charges ne correspondent pas à cela, alors je vous recommande de bien réfléchir à la durée de vie de votre SSD sous une telle charge, en tenant compte du TBW de la fiche technique.

#cat >ssd.part << EOF
label: dos
label-id: 0x00000000
device: /dev/sda
unit: sectors

/dev/sda1 : start= 2048, size= 2097152, type=fd, bootable
/dev/sda2 : start= 2099200, size= 3300950016, type=fd
EOF
#sfdisk /dev/sda < ssd.part
#sfdisk /dev/sdb < ssd.part
#sfdisk /dev/sdc < ssd.part
#sfdisk /dev/sdd < ssd.part
#sfdisk /dev/sde < ssd.part
#sfdisk /dev/sdf < ssd.part

Création de tableaux

Tout d’abord, nous devons renommer la machine. Cela est nécessaire car le nom d'hôte fait partie du nom du tableau quelque part dans mdadm et affecte quelque chose quelque part. Bien sûr, les tableaux peuvent être renommés ultérieurement, mais c'est une étape inutile.

#mcedit /etc/hostname
#mcedit /etc/hosts
#hostname
vdesk0

NVMe SSD

#mdadm --create --verbose --assume-clean /dev/md0 --level=1 --raid-devices=2 /dev/nvme[0-1]n1

Pourquoi - supposer - propre... ?Pour éviter d'initialiser les tableaux. Ceci est valable pour les niveaux RAID 1 et 6. Tout peut fonctionner sans initialisation s'il s'agit d'un nouveau tableau. De plus, l'initialisation de la baie SSD lors de la création est un gaspillage de ressources TBW. Nous utilisons TRIM/DISCARD lorsque cela est possible sur les baies SSD assemblées pour les « initialiser ».

Pour les baies SSD, RAID 1 DISCARD est pris en charge dès le départ.

Pour les baies SSD RAID 6 DISCARD, vous devez l'activer dans les paramètres du module noyau.

Cela ne doit être fait que si tous les disques SSD utilisés dans les baies de niveau 4/5/6 de ce système disposent d'un support fonctionnel pour throw_zeroes_data. Parfois, vous rencontrez des lecteurs étranges qui indiquent au noyau que cette fonction est prise en charge, mais en fait elle n'est pas là, ou la fonction ne fonctionne pas toujours. À l'heure actuelle, l'assistance est disponible presque partout, cependant, il existe d'anciens disques et micrologiciels comportant des erreurs. Pour cette raison, la prise en charge de DISCARD est désactivée par défaut pour RAID 6.

Attention, la commande suivante détruira toutes les données sur les disques NVMe en « initialisant » la baie avec des « zéros ».

#blkdiscard /dev/md0

Si quelque chose ne va pas, essayez de spécifier une étape.

#blkdiscard --step 65536 /dev/md0

SSD SATA

#mdadm --create --verbose --assume-clean /dev/md1 --level=1 --raid-devices=6 /dev/sd[a-f]1
#blkdiscard /dev/md1
#mdadm --create --verbose --assume-clean /dev/md2 --chunk-size=512 --level=6 --raid-devices=6 /dev/sd[a-f]2

Pourquoi si gros...?L'augmentation de la taille du bloc a un effet positif sur la vitesse de lecture aléatoire dans les blocs jusqu'à la taille du bloc incluse. Cela se produit car une opération de taille appropriée ou inférieure peut être effectuée entièrement sur un seul appareil. Par conséquent, les IOPS de tous les appareils sont résumées. Selon les statistiques, 99 % des IO ne dépassent pas 512 Ko.

RAID 6 IOPS par écriture toujours inférieur ou égal aux IOPS d’un disque. Lorsque, en lecture aléatoire, les IOPS peuvent être plusieurs fois supérieures à celles d'un lecteur, et ici la taille du bloc est d'une importance capitale.
L'auteur ne voit pas l'intérêt d'essayer d'optimiser un paramètre qui est mauvais dans la conception de RAID 6 et optimise à la place ce pour quoi RAID 6 est bon.
Nous compenserons la mauvaise écriture aléatoire du RAID 6 avec un cache NVMe et des astuces de provisionnement fin.

Nous n'avons pas encore activé DISCARD pour RAID 6. Nous n'allons donc pas « initialiser » cette matrice pour l'instant. Nous le ferons plus tard, après avoir installé le système d'exploitation.

Disque dur SATA

#mdadm --create --verbose --assume-clean /dev/md3 --chunk-size=512 --level=6 --raid-devices=8 /dev/sd[g-n]1

LVM sur RAID NVMe

Pour plus de rapidité, nous souhaitons placer le système de fichiers racine sur NVMe RAID 1 qui est /dev/md0.
Cependant, nous aurons toujours besoin de ce tableau rapide pour d'autres besoins, tels que le swap, les métadonnées et le cache LVM et les métadonnées minces LVM, nous allons donc créer un VG LVM sur ce tableau.

#pvcreate /dev/md0
#vgcreate root /dev/md0

Créons une partition pour le système de fichiers racine.

#lvcreate -L 128G --name root root

Créons une partition à échanger en fonction de la taille de la RAM.

#lvcreate -L 32G --name swap root

Installation du système d'exploitation

Au total, nous avons tout le nécessaire pour installer le système.

Lancez l'assistant d'installation du système depuis l'environnement Ubuntu Live. Installation normale. Uniquement au stade de la sélection des disques à installer, vous devez spécifier les éléments suivants :

  • /dev/md1, - point de montage /boot, FS - BTRFS
  • /dev/root/root (alias /dev/mapper/root-root), - point de montage / (root), FS - BTRFS
  • /dev/root/swap (alias /dev/mapper/root-swap), - utiliser comme partition d'échange
  • Installez le bootloader sur /dev/sda

Lorsque vous sélectionnez BTRFS comme système de fichiers racine, le programme d'installation crée automatiquement deux volumes BTRFS nommés « @ » pour / (racine) et « @home » pour /home.

Commençons l'installation...

L'installation se terminera par une boîte de dialogue modale indiquant une erreur lors de l'installation du chargeur de démarrage. Malheureusement, vous ne pourrez pas quitter cette boîte de dialogue par les moyens standard et poursuivre l'installation. Nous nous déconnectons du système et nous nous reconnectons, pour aboutir à un bureau Ubuntu Live propre. Ouvrez le terminal, et encore :

#sudo bash

Créez un environnement chroot pour poursuivre l'installation :

#mkdir /mnt/chroot
#mount -o defaults,space_cache,noatime,nodiratime,discard,subvol=@ /dev/mapper/root-root /mnt/chroot
#mount -o defaults,space_cache,noatime,nodiratime,discard,subvol=@home /dev/mapper/root-root /mnt/chroot/home
#mount -o defaults,space_cache,noatime,nodiratime,discard /dev/md1 /mnt/chroot/boot
#mount --bind /proc /mnt/chroot/proc
#mount --bind /sys /mnt/chroot/sys
#mount --bind /dev /mnt/chroot/dev

Configurons le réseau et le nom d'hôte dans chroot :

#cat /etc/hostname >/mnt/chroot/etc/hostname
#cat /etc/hosts >/mnt/chroot/etc/hosts
#cat /etc/resolv.conf >/mnt/chroot/etc/resolv.conf

Passons à l'environnement chroot :

#chroot /mnt/chroot

Tout d’abord, nous livrerons les colis :

apt-get install --reinstall mdadm lvm2 thin-provisioning-tools btrfs-tools util-linux lsscsi nvme-cli mc debsums hdparm

Vérifions et corrigeons tous les packages qui ont été mal installés en raison d'une installation incomplète du système :

#CORRUPTED_PACKAGES=$(debsums -s 2>&1 | awk '{print $6}' | uniq)
#apt-get install --reinstall $CORRUPTED_PACKAGES

Si quelque chose ne fonctionne pas, vous devrez peut-être d'abord modifier /etc/apt/sources.list

Ajustons les paramètres du module RAID 6 pour activer TRIM/DISCARD :

#cat >/etc/modprobe.d/raid456.conf << EOF
options raid456 devices_handle_discard_safely=1
EOF

Modifions un peu nos tableaux :

#cat >/etc/udev/rules.d/60-md.rules << EOF
SUBSYSTEM=="block", KERNEL=="md*", ACTION=="change", TEST=="md/stripe_cache_size", ATTR{md/stripe_cache_size}="32768"
SUBSYSTEM=="block", KERNEL=="md*", ACTION=="change", TEST=="md/sync_speed_min", ATTR{md/sync_speed_min}="48000"
SUBSYSTEM=="block", KERNEL=="md*", ACTION=="change", TEST=="md/sync_speed_max", ATTR{md/sync_speed_max}="300000"
EOF
#cat >/etc/udev/rules.d/62-hdparm.rules << EOF
SUBSYSTEM=="block", ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", RUN+="/sbin/hdparm -B 254 /dev/%k"
EOF
#cat >/etc/udev/rules.d/63-blockdev.rules << EOF
SUBSYSTEM=="block", ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", RUN+="/sbin/blockdev --setra 1024 /dev/%k"
SUBSYSTEM=="block", ACTION=="add|change", KERNEL=="md*", RUN+="/sbin/blockdev --setra 0 /dev/%k"
EOF

Qu'est-ce que c'était..?Nous avons créé un ensemble de règles udev qui feront ce qui suit :

  • Définissez la taille du cache de blocs pour RAID 2020 pour qu'elle soit adéquate pour 6. La valeur par défaut, semble-t-il, n'a pas changé depuis la création de Linux et n'est plus adéquate depuis longtemps.
  • Réservez un minimum d’E/S pour la durée des vérifications/synchronisations de la baie. Cela permet d'éviter que vos baies ne restent bloquées dans un état de synchronisation éternelle sous charge.
  • Limitez le maximum d'E/S lors des contrôles/synchronisation des baies. Ceci est nécessaire pour que la synchronisation/vérification des RAID SSD ne fasse pas frire vos disques. Cela est particulièrement vrai pour NVMe. (Tu te souviens du radiateur ? Je ne plaisantais pas.)
  • Interdisez aux disques d'arrêter la rotation de la broche (HDD) via APM et définissez le délai d'expiration des contrôleurs de disque sur 7 heures. Vous pouvez désactiver complètement l'APM si vos lecteurs peuvent le faire (-B 255). Avec la valeur par défaut, les lecteurs s'arrêteront après cinq secondes. Ensuite, le système d'exploitation souhaite réinitialiser le cache disque, les disques redémarreront et tout recommencera. Les disques ont un nombre maximum limité de rotations de broche. Un cycle par défaut aussi simple peut facilement tuer vos disques en quelques années. Tous les disques n'en souffrent pas, mais les nôtres sont des disques « portables », avec les paramètres par défaut appropriés, qui font ressembler le RAID à un mini-MAID.
  • Installer la lecture anticipée sur des disques (rotatifs) 1 Mo - deux blocs/chunk consécutifs RAID 6
  • Désactivez la lecture anticipée sur les tableaux eux-mêmes.

Modifions /etc/fstab :

#cat >/etc/fstab << EOF
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
# file-system mount-point type options dump pass
/dev/mapper/root-root / btrfs defaults,space_cache,noatime,nodiratime,discard,subvol=@ 0 1
UUID=$(blkid -o value -s UUID /dev/md1) /boot btrfs defaults,space_cache,noatime,nodiratime,discard 0 2
/dev/mapper/root-root /home btrfs defaults,space_cache,noatime,nodiratime,discard,subvol=@home 0 2
/dev/mapper/root-swap none swap sw 0 0
EOF

Pourquoi donc..?Nous rechercherons la partition /boot par UUID. Le nom du tableau pourrait théoriquement changer.

Nous rechercherons les sections restantes par noms LVM dans la notation /dev/mapper/vg-lv, car ils identifient les partitions de manière tout à fait unique.

Nous n'utilisons pas l'UUID pour LVM car L'UUID des volumes LVM et de leurs instantanés peuvent être identiques.Monter /dev/mapper/root-root.. deux fois ?Oui. Exactement. Caractéristique de BTRFS. Ce système de fichiers peut être monté plusieurs fois avec différents sous-vols.

En raison de cette même fonctionnalité, je recommande de ne jamais créer d'instantanés LVM de volumes BTRFS actifs. Vous pourriez avoir une surprise au redémarrage.

Régénérons la configuration mdadm :

#/usr/share/mdadm/mkconf | sed 's/#DEVICE/DEVICE/g' >/etc/mdadm/mdadm.conf

Ajustons les paramètres LVM :

#cat >>/etc/lvm/lvmlocal.conf << EOF

activation {
thin_pool_autoextend_threshold=90
thin_pool_autoextend_percent=5
}
allocation {
cache_pool_max_chunks=2097152
}
devices {
global_filter=["r|^/dev/.*_corig$|","r|^/dev/.*_cdata$|","r|^/dev/.*_cmeta$|","r|^/dev/.*gpv$|","r|^/dev/images/.*$|","r|^/dev/mapper/images.*$|","r|^/dev/backup/.*$|","r|^/dev/mapper/backup.*$|"] issue_discards=1
}
EOF

Qu'est-ce que c'était..?Nous avons permis l'expansion automatique des pools légers LVM lorsqu'ils atteignent 90 % de l'espace occupé par 5 % du volume.

Nous avons augmenté le nombre maximum de blocs de cache pour le cache LVM.

Nous avons empêché LVM de rechercher des volumes LVM (PV) sur :

  • périphériques contenant du cache LVM (cdata)
  • périphériques mis en cache à l'aide du cache LVM, en contournant le cache ( _corig). Dans ce cas, le périphérique mis en cache lui-même sera toujours analysé via le cache (juste ).
  • périphériques contenant des métadonnées de cache LVM (cmeta)
  • tous les appareils en VG avec le nom images. Ici, nous aurons des images disque de machines virtuelles, et nous ne voulons pas que LVM sur l'hôte active les volumes appartenant au système d'exploitation invité.
  • tous les appareils en VG avec le nom de sauvegarde. Ici, nous aurons des copies de sauvegarde des images de machines virtuelles.
  • tous les appareils dont le nom se termine par « gpv » (volume physique invité)

Nous avons activé la prise en charge de DISCARD lors de la libération d'espace libre sur LVM VG. Sois prudent. Cela rendra la suppression des LV sur le SSD assez longue. Cela s'applique particulièrement au SSD RAID 6. Cependant, selon le plan, nous utiliserons le provisionnement fin, donc cela ne nous gênera pas du tout.

Mettons à jour l'image initramfs :

#update-initramfs -u -k all

Installez et configurez grub :

#apt-get install grub-pc
#apt-get purge os-prober
#dpkg-reconfigure grub-pc

Quels disques choisir ?Tous ceux qui sont SD*. Le système doit pouvoir démarrer à partir de n’importe quel disque SATA ou SSD fonctionnel.

Pourquoi ont-ils ajouté os-prober .. ?Pour une indépendance excessive et des mains ludiques.

Il ne fonctionne pas correctement si l'un des RAID est dans un état dégradé. Il essaie de rechercher le système d'exploitation sur les partitions utilisées dans les machines virtuelles exécutées sur ce matériel.

Si vous en avez besoin, vous pouvez le laisser, mais gardez à l'esprit tout ce qui précède. Je recommande de rechercher des recettes en ligne pour se débarrasser des mains coquines.

Avec cela, nous avons terminé l'installation initiale. Il est temps de redémarrer le système d'exploitation nouvellement installé. N'oubliez pas de retirer le Live CD/USB bootable.

#exit
#reboot

Sélectionnez l'un des SSD SATA comme périphérique de démarrage.

LVM sur SSD SATA

À ce stade, nous avons déjà démarré le nouveau système d'exploitation, configuré le réseau, apt, ouvert l'émulateur de terminal et lancé :

#sudo bash

Nous continuons.

« Initialisez » la baie à partir du SSD SATA :

#blkdiscard /dev/md2

Si cela ne fonctionne pas, essayez :

#blkdiscard --step 65536 /dev/md2
Créez un LVM VG sur un SSD SATA :

#pvcreate /dev/md2
#vgcreate data /dev/md2

Pourquoi un autre VG.. ?En fait, nous avons déjà un VG nommé root. Pourquoi ne pas tout regrouper dans un seul VG ?

S'il y a plusieurs PV dans un VG, alors pour que le VG soit activé correctement, il faut que tous les PV soient présents (en ligne). L'exception est LVM RAID, que nous n'utilisons délibérément pas.

Nous voulons vraiment qu'en cas de panne (perte de données de lecture) sur l'une des matrices RAID 6, le système d'exploitation démarre normalement et nous donne la possibilité de résoudre le problème.

Pour ce faire, au premier niveau d’abstraction nous isolerons chaque type de « média » physique dans un VG distinct.

Scientifiquement parlant, différentes matrices RAID appartiennent à différents « domaines de fiabilité ». Vous ne devriez pas créer un point d’échec commun supplémentaire pour eux en les regroupant dans un seul VG.

La présence de LVM au niveau « matériel » nous permettra de découper arbitrairement des morceaux de différentes matrices RAID en les combinant de différentes manières. Par exemple - exécutez simultanément bcache + LVM Thin, bcache + BTRFS, LVM cache + LVM Thin, une configuration ZFS complexe avec caches, ou tout autre mélange infernal pour essayer de tout comparer.

Au niveau « matériel », nous n’utiliserons rien d’autre que de bons vieux volumes LVM « épais ». L'exception à cette règle peut être la partition de sauvegarde.

Je pense qu’à ce moment-là, de nombreux lecteurs avaient déjà commencé à soupçonner quelque chose à propos de la poupée gigogne.

LVM sur disque dur SATA

#pvcreate /dev/md3
#vgcreate backup /dev/md3

Encore du nouveau VG..?Nous voulons vraiment que si la baie de disques que nous utiliserons pour la sauvegarde des données tombe en panne, notre système d'exploitation continuera à fonctionner normalement, tout en conservant l'accès aux données non sauvegardées comme d'habitude. Par conséquent, pour éviter les problèmes d’activation du VG, nous créons un VG distinct.

Configuration du cache LVM

Créons un LV sur NVMe RAID 1 pour l'utiliser comme périphérique de mise en cache.

#lvcreate -L 70871154688B --name cache root

Pourquoi y a-t-il si peu... ?Le fait est que nos SSD NVMe disposent également d'un cache SLC. 4 Go de « libre » et 18 Go de dynamique en raison de l'espace libre occupé dans le MLC 3 bits. Une fois ce cache épuisé, les SSD NVMe ne seront pas beaucoup plus rapides que nos SSD SATA avec cache. En fait, pour cette raison, cela n'a aucun sens pour nous de rendre la partition de cache LVM beaucoup plus grande que deux fois la taille du cache SLC du lecteur NVMe. Pour les disques NVMe utilisés, l'auteur considère qu'il est raisonnable de créer 32 à 64 Go de cache.

La taille de partition donnée est requise pour organiser 64 Go de cache, de métadonnées de cache et de sauvegarde de métadonnées.

De plus, je note qu'après un arrêt sale du système, LVM marquera l'intégralité du cache comme sale et se synchronisera à nouveau. De plus, cela sera répété à chaque fois que lvchange sera utilisé sur cet appareil jusqu'à ce que le système soit à nouveau redémarré. Par conséquent, je recommande de recréer immédiatement le cache à l'aide du script approprié.

Créons un LV sur SATA RAID 6 pour l'utiliser comme périphérique mis en cache.

#lvcreate -L 3298543271936B --name cache data

Pourquoi seulement trois téraoctets… ?Ainsi, si nécessaire, vous pouvez utiliser SATA SSD RAID 6 pour d'autres besoins. La taille de l'espace mis en cache peut être augmentée dynamiquement, à la volée, sans arrêter le système. Pour ce faire, vous devez arrêter temporairement et réactiver le cache, mais l'avantage distinctif du cache LVM par rapport, par exemple, à bcache est que cela peut être fait à la volée.

Créons un nouveau VG pour la mise en cache.

#pvcreate /dev/root/cache
#pvcreate /dev/data/cache
#vgcreate cache /dev/root/cache /dev/data/cache

Créons un LV sur le périphérique mis en cache.

#lvcreate -L 3298539077632B --name cachedata cache /dev/data/cache

Ici, nous avons immédiatement occupé tout l'espace libre sur /dev/data/cache afin que toutes les autres partitions nécessaires soient créées immédiatement sur /dev/root/cache. Si vous avez créé quelque chose au mauvais endroit, vous pouvez le déplacer en utilisant pvmove.

Créons et activons le cache :

#lvcreate -y -L 64G -n cache cache /dev/root/cache
#lvcreate -y -L 1G -n cachemeta cache /dev/root/cache
#lvconvert -y --type cache-pool --cachemode writeback --chunksize 64k --poolmetadata cache/cachemeta cache/cache
#lvconvert -y --type cache --cachepool cache/cache cache/cachedata

Pourquoi une telle taille de morceau... ?Grâce à des expériences pratiques, l'auteur a pu découvrir que le meilleur résultat est obtenu si la taille du bloc de cache LVM coïncide avec la taille du bloc mince LVM. De plus, plus la taille est petite, meilleures sont les performances de la configuration lors d’un enregistrement aléatoire.

64 Ko est la taille de bloc minimale autorisée pour LVM Thin.

Soyez prudent en réécriture..!Oui. Ce type de cache diffère la synchronisation d'écriture sur le périphérique mis en cache. Cela signifie que si le cache est perdu, vous risquez de perdre des données sur le périphérique mis en cache. Plus tard, l'auteur vous indiquera quelles mesures, en plus du NVMe RAID 1, peuvent être prises pour compenser ce risque.

Ce type de cache a été choisi intentionnellement pour compenser les mauvaises performances d'écriture aléatoire du RAID 6.

Vérifions ce que nous avons :

#lvs -a -o lv_name,lv_size,devices --units B cache
LV LSize Devices
[cache] 68719476736B cache_cdata(0)
[cache_cdata] 68719476736B /dev/root/cache(0)
[cache_cmeta] 1073741824B /dev/root/cache(16384)
cachedata 3298539077632B cachedata_corig(0)
[cachedata_corig] 3298539077632B /dev/data/cache(0)
[lvol0_pmspare] 1073741824B /dev/root/cache(16640)

Seul [cachedata_corig] doit être situé sur /dev/data/cache. Si quelque chose ne va pas, utilisez pvmove.

Vous pouvez désactiver le cache si nécessaire avec une seule commande :

#lvconvert -y --uncache cache/cachedata

Cela se fait en ligne. LVM synchronisera simplement le cache sur le disque, le supprimera et renommera cachedata_corig en cachedata.

Configuration de LVM Thin

Estimons approximativement la quantité d'espace dont nous avons besoin pour les métadonnées légères LVM :

#thin_metadata_size --block-size=64k --pool-size=6terabytes --max-thins=100000 -u bytes
thin_metadata_size - 3385794560 bytes estimated metadata area size for "--block-size=64kibibytes --pool-size=6terabytes --max-thins=100000"

Arrondir à 4 Go : 4294967296B

Multipliez par deux et ajoutez 4194304B pour les métadonnées LVM PV : 8594128896B
Créons une partition distincte sur NVMe RAID 1 pour y placer les métadonnées légères LVM et leur copie de sauvegarde :

#lvcreate -L 8594128896B --name images root

Pour quoi..?Ici, la question peut se poser : pourquoi placer les métadonnées légères LVM séparément si elles seront toujours mises en cache sur NVMe et fonctionneront rapidement.

Même si la vitesse est importante ici, elle est loin d’être la raison principale. Le fait est que le cache est un point de défaillance. Quelque chose pourrait lui arriver, et si les métadonnées légères LVM sont mises en cache, tout sera complètement perdu. Sans métadonnées complètes, il sera presque impossible d’assembler des volumes légers.

En déplaçant les métadonnées vers un volume distinct non mis en cache, mais rapide, nous garantissons la sécurité des métadonnées en cas de perte ou de corruption du cache. Dans ce cas, tous les dommages causés par la perte de cache seront localisés à l'intérieur de volumes minces, ce qui simplifiera considérablement la procédure de récupération. Avec une forte probabilité, ces dommages seront restaurés à l'aide des journaux FS.

De plus, si un instantané d'un volume léger a été précédemment pris et que le cache a ensuite été entièrement synchronisé au moins une fois, alors, en raison de la conception interne de LVM Thin, l'intégrité de l'instantané sera garantie en cas de perte de cache. .

Créons un nouveau VG qui sera responsable du provisionnement dynamique :

#pvcreate /dev/root/images
#pvcreate /dev/cache/cachedata
#vgcreate images /dev/root/images /dev/cache/cachedata

Créons un pool :

#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T images/thin-pool
Pourquoi -Z yEn plus de ce à quoi ce mode est réellement destiné - pour empêcher les données d'une machine virtuelle de fuir vers une autre machine virtuelle lors de la redistribution de l'espace - la mise à zéro est également utilisée pour augmenter la vitesse d'écriture aléatoire dans des blocs inférieurs à 64 Ko. Toute écriture inférieure à 64 Ko dans une zone précédemment non allouée du volume léger deviendra 64 Ko alignés sur les bords dans le cache. Cela permettra d'effectuer l'opération entièrement via le cache, en contournant le périphérique mis en cache.

Déplaçons les LV vers les PV correspondants :

#pvmove -n images/thin-pool_tdata /dev/root/images /dev/cache/cachedata
#pvmove -n images/lvol0_pmspare /dev/cache/cachedata /dev/root/images
#pvmove -n images/thin-pool_tmeta /dev/cache/cachedata /dev/root/images

Chèque:

#lvs -a -o lv_name,lv_size,devices --units B images
LV LSize Devices
[lvol0_pmspare] 4294967296B /dev/root/images(0)
thin-pool 274877906944B thin-pool_tdata(0)
[thin-pool_tdata] 274877906944B /dev/cache/cachedata(0)
[thin-pool_tmeta] 4294967296B /dev/root/images(1024)

Créons un volume fin pour les tests :

#lvcreate -V 64G --thin-pool thin-pool --name test images

Nous installerons des packages pour les tests et la surveillance :

#apt-get install sysstat fio

Voici comment observer le comportement de notre configuration de stockage en temps réel :

#watch 'lvs --rows --reportformat basic --quiet -ocache_dirty_blocks,cache_settings cache/cachedata && (lvdisplay cache/cachedata | grep Cache) && (sar -p -d 2 1 | grep -E "sd|nvme|DEV|md1|md2|md3|md0" | grep -v Average | sort)'

Voici comment nous pouvons tester notre configuration :

#fio --loops=1 --size=64G --runtime=4 --filename=/dev/images/test --stonewall --ioengine=libaio --direct=1
--name=4kQD32read --bs=4k --iodepth=32 --rw=randread
--name=8kQD32read --bs=8k --iodepth=32 --rw=randread
--name=16kQD32read --bs=16k --iodepth=32 --rw=randread
--name=32KQD32read --bs=32k --iodepth=32 --rw=randread
--name=64KQD32read --bs=64k --iodepth=32 --rw=randread
--name=128KQD32read --bs=128k --iodepth=32 --rw=randread
--name=256KQD32read --bs=256k --iodepth=32 --rw=randread
--name=512KQD32read --bs=512k --iodepth=32 --rw=randread
--name=4Kread --bs=4k --rw=read
--name=8Kread --bs=8k --rw=read
--name=16Kread --bs=16k --rw=read
--name=32Kread --bs=32k --rw=read
--name=64Kread --bs=64k --rw=read
--name=128Kread --bs=128k --rw=read
--name=256Kread --bs=256k --rw=read
--name=512Kread --bs=512k --rw=read
--name=Seqread --bs=1m --rw=read
--name=Longread --bs=8m --rw=read
--name=Longwrite --bs=8m --rw=write
--name=Seqwrite --bs=1m --rw=write
--name=512Kwrite --bs=512k --rw=write
--name=256write --bs=256k --rw=write
--name=128write --bs=128k --rw=write
--name=64write --bs=64k --rw=write
--name=32write --bs=32k --rw=write
--name=16write --bs=16k --rw=write
--name=8write --bs=8k --rw=write
--name=4write --bs=4k --rw=write
--name=512KQD32write --bs=512k --iodepth=32 --rw=randwrite
--name=256KQD32write --bs=256k --iodepth=32 --rw=randwrite
--name=128KQD32write --bs=128k --iodepth=32 --rw=randwrite
--name=64KQD32write --bs=64k --iodepth=32 --rw=randwrite
--name=32KQD32write --bs=32k --iodepth=32 --rw=randwrite
--name=16KQD32write --bs=16k --iodepth=32 --rw=randwrite
--name=8KQD32write --bs=8k --iodepth=32 --rw=randwrite
--name=4kQD32write --bs=4k --iodepth=32 --rw=randwrite
| grep -E 'read|write|test' | grep -v ioengine

Soigneusement! Ressource!Ce code exécutera 36 tests différents, chacun s'exécutant pendant 4 secondes. La moitié des tests sont destinés à l'enregistrement. Vous pouvez enregistrer beaucoup de choses sur NVMe en 4 secondes. Jusqu'à 3 gigaoctets par seconde. Ainsi, chaque exécution de tests d’écriture peut consommer jusqu’à 216 Go de ressources SSD.

Lecture et écriture mélangées ?Oui. Il est logique d'exécuter les tests de lecture et d'écriture séparément. De plus, il est logique de s'assurer que tous les caches sont synchronisés afin qu'une écriture effectuée précédemment n'affecte pas la lecture.

Les résultats varieront considérablement lors du premier lancement et des suivants, à mesure que le cache et le volume léger se remplissent, et également selon que le système a réussi à synchroniser les caches remplis lors du dernier lancement.

Entre autres choses, je recommande de mesurer la vitesse sur un volume mince déjà plein à partir duquel un instantané vient d'être pris. L'auteur a eu l'occasion d'observer comment les écritures aléatoires s'accélèrent fortement immédiatement après la création du premier instantané, notamment lorsque le cache n'est pas encore complètement plein. Cela se produit en raison de la sémantique d'écriture de copie sur écriture, de l'alignement du cache et des blocs de volume minces, et du fait qu'une écriture aléatoire sur RAID 6 se transforme en une lecture aléatoire à partir de RAID 6 suivie d'une écriture dans le cache. Dans notre configuration, la lecture aléatoire depuis RAID 6 est jusqu'à 6 fois (le nombre de SSD SATA dans la matrice) plus rapide que l'écriture. Parce que les blocs pour CoW sont alloués séquentiellement à partir d'un pool mince, puis l'enregistrement, pour la plupart, se transforme également en séquentiel.

Ces deux fonctionnalités peuvent être utilisées à votre avantage.

Cacher des instantanés « cohérents »

Pour réduire le risque de perte de données en cas de dommage/perte du cache, l'auteur propose d'introduire la pratique de la rotation des instantanés pour garantir leur intégrité dans ce cas.

Premièrement, étant donné que les métadonnées des volumes légers résident sur un appareil non mis en cache, les métadonnées seront cohérentes et les pertes possibles seront isolées dans des blocs de données.

Le cycle de rotation des instantanés suivant garantit l'intégrité des données contenues dans les instantanés en cas de perte de cache :

  1. Pour chaque volume léger portant le nom <nom>, créez un instantané portant le nom <nom>.cached
  2. Fixons le seuil de migration à une valeur raisonnablement élevée : #lvchange --quiet --cachesettings "migration_threshold=16384" cache/cachedata
  3. Dans la boucle, nous vérifions le nombre de blocs sales dans le cache : #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' jusqu'à ce que nous obtenions zéro. Si le zéro manque trop longtemps, il peut être créé en basculant temporairement le cache en mode écriture directe. Cependant, compte tenu des caractéristiques de vitesse de nos baies SSD SATA et NVMe, ainsi que de leur ressource TBW, soit vous pourrez saisir rapidement l'instant sans changer le mode cache, soit votre matériel consommera complètement toute sa ressource en quelques jours. En raison de ressources limitées, le système est, en principe, incapable de fonctionner en permanence sous une charge d'écriture de 100 %. Nos SSD NVMe sous une charge d'écriture de 100 % épuiseront complètement les ressources dans 3-4 jours. Les SSD SATA ne dureront que deux fois plus longtemps. Par conséquent, nous supposerons que la majeure partie de la charge est consacrée à la lecture, et nous avons des poussées d'activité extrêmement élevées à relativement court terme combinées à une faible charge en moyenne pour l'écriture.
  4. Dès que nous avons attrapé (ou fait) un zéro, nous renommons <name>.cached en <name>.commit. L'ancien <nom>.commit est supprimé.
  5. Eventuellement, si le cache est plein à 100 %, il peut être recréé par un script, le vidant ainsi. Avec un cache à moitié vide, le système fonctionne beaucoup plus rapidement lors de l'écriture.
  6. Définissez le seuil de migration sur zéro : #lvchange --quiet --cachesettings "migration_threshold=0" cache/cachedata Cela empêchera temporairement le cache de se synchroniser avec le média principal.
  7. Nous attendons que pas mal de modifications s'accumulent dans le cache #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' ou la minuterie sonnera.
  8. Nous répétons encore.

Pourquoi des difficultés avec le seuil de migration... ?Le fait est que dans la pratique réelle, un enregistrement « aléatoire » n’est en réalité pas complètement aléatoire. Si nous écrivons quelque chose dans un secteur de 4 kilo-octets, il y a une forte probabilité que dans les prochaines minutes, un enregistrement soit effectué dans le même secteur ou dans l'un des secteurs voisins (+- 32 Ko).

En fixant le seuil de migration à zéro, nous reportons la synchronisation d'écriture sur le SSD SATA et regroupons plusieurs modifications dans un bloc de 64 Ko dans le cache. Cela économise considérablement la ressource du SSD SATA.

Où est le code..?Malheureusement, l'auteur se considère insuffisamment compétent dans le développement de scripts bash car il est 100% autodidacte et pratique le développement piloté par "google", il estime donc que le terrible code qui sort de ses mains ne devrait être utilisé par personne. autre.

Je pense que les professionnels dans ce domaine seront capables de décrire de manière indépendante toute la logique décrite ci-dessus, si nécessaire, et peut-être même de la concevoir magnifiquement en tant que service systemd, comme l'auteur a essayé de le faire.

Un schéma de rotation d'instantanés aussi simple nous permettra non seulement d'avoir constamment un instantané entièrement synchronisé sur le SSD SATA, mais nous permettra également, à l'aide de l'utilitaire Thin_delta, de savoir quels blocs ont été modifiés après sa création, et ainsi de localiser les dommages sur les principaux volumes, simplifiant grandement la récupération.

TRIM/DISCARTE dans libvirt/KVM

Parce que le stockage des données sera utilisé pour KVM exécutant libvirt, alors ce serait une bonne idée d'apprendre à nos VM non seulement à occuper de l'espace libre, mais aussi à libérer ce qui n'est plus nécessaire.

Cela se fait en émulant la prise en charge de TRIM/DISCARD sur les disques virtuels. Pour ce faire, vous devez changer le type de contrôleur en virtio-scsi et modifier le fichier XML.

#virsh edit vmname
<disk type='block' device='disk'>
<driver name='qemu' type='raw' cache='writethrough' io='threads' discard='unmap'/>
<source dev='/dev/images/vmname'/>
<backingStore/>
<target dev='sda' bus='scsi'/>
<alias name='scsi0-0-0-0'/>
<address type='drive' controller='0' bus='0' target='0' unit='0'/>
</disk>

<controller type='scsi' index='0' model='virtio-scsi'>
<alias name='scsi0'/>
<address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
</controller>

Ces REJETS des systèmes d'exploitation invités sont correctement traités par LVM et les blocs sont correctement libérés à la fois dans le cache et dans le pool léger. Dans notre cas, cela se produit principalement de manière différée, lors de la suppression du prochain instantané.

Sauvegarde BTRFS

Utilisez des scripts prêts à l'emploi avec крайней prudence et à vos risques et périls. L'auteur a écrit ce code lui-même et exclusivement pour lui-même. Je suis sûr que de nombreux utilisateurs Linux expérimentés disposent d’outils similaires et qu’il n’est pas nécessaire de copier ceux de quelqu’un d’autre.

Créons un volume sur le périphérique de sauvegarde :

#lvcreate -L 256G --name backup backup

Formatons-le en BTRFS :

#mkfs.btrfs /dev/backup/backup

Créons des points de montage et montons les sous-sections racine du système de fichiers :

#mkdir /backup
#mkdir /backup/btrfs
#mkdir /backup/btrfs/root
#mkdir /backup/btrfs/back
#ln -s /boot /backup/btrfs
# cat >>/etc/fstab << EOF

/dev/mapper/root-root /backup/btrfs/root btrfs defaults,space_cache,noatime,nodiratime 0 2
/dev/mapper/backup-backup /backup/btrfs/back btrfs defaults,space_cache,noatime,nodiratime 0 2
EOF
#mount -a
#update-initramfs -u
#update-grub

Créons des répertoires pour les sauvegardes :

#mkdir /backup/btrfs/back/remote
#mkdir /backup/btrfs/back/remote/root
#mkdir /backup/btrfs/back/remote/boot

Créons un répertoire pour les scripts de sauvegarde :

#mkdir /root/btrfs-backup

Copieons le script :

Beaucoup de code bash effrayant. À utiliser à vos risques et périls. N'écrivez pas de lettres de colère à l'auteur...#cat >/root/btrfs-backup/btrfs-backup.sh << EOF
#!/bin/bash
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

SCRIPT_FILE="$(realpath $0)"
SCRIPT_DIR="$(dirname $SCRIPT_FILE)"
SCRIPT_NAME="$(basename -s .sh $SCRIPT_FILE)"

LOCK_FILE="/dev/shm/$SCRIPT_NAME.lock"
DATE_PREFIX='%Y-%m-%d'
DATE_FORMAT=$DATE_PREFIX'-%H-%M-%S'
DATE_REGEX='[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]'
BASE_SUFFIX=".@base"
PEND_SUFFIX=".@pend"
SNAP_SUFFIX=".@snap"
MOUNTS="/backup/btrfs/"
BACKUPS="/backup/btrfs/back/remote/"

function terminate ()
{
echo "$1" >&2
exit 1
}

function wait_lock()
{
flock 98
}

function wait_lock_or_terminate()
{
echo "Wating for lock..."
wait_lock || terminate "Failed to get lock. Exiting..."
echo "Got lock..."
}

function suffix()
{
FORMATTED_DATE=$(date +"$DATE_FORMAT")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}

function filter()
{
FORMATTED_DATE=$(date --date="$1" +"$DATE_PREFIX")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}

function backup()
{
SOURCE_PATH="$MOUNTS$1"
TARGET_PATH="$BACKUPS$1"
SOURCE_BASE_PATH="$MOUNTS$1$BASE_SUFFIX"
TARGET_BASE_PATH="$BACKUPS$1$BASE_SUFFIX"
TARGET_BASE_DIR="$(dirname $TARGET_BASE_PATH)"
SOURCE_PEND_PATH="$MOUNTS$1$PEND_SUFFIX"
TARGET_PEND_PATH="$BACKUPS$1$PEND_SUFFIX"
if [ -d "$SOURCE_BASE_PATH" ] then
echo "$SOURCE_BASE_PATH found"
else
echo "$SOURCE_BASE_PATH File not found creating snapshot of $SOURCE_PATH to $SOURCE_BASE_PATH"
btrfs subvolume snapshot -r $SOURCE_PATH $SOURCE_BASE_PATH
sync
if [ -d "$TARGET_BASE_PATH" ] then
echo "$TARGET_BASE_PATH found out of sync with source... removing..."
btrfs subvolume delete -c $TARGET_BASE_PATH
sync
fi
fi
if [ -d "$TARGET_BASE_PATH" ] then
echo "$TARGET_BASE_PATH found"
else
echo "$TARGET_BASE_PATH not found. Synching to $TARGET_BASE_DIR"
btrfs send $SOURCE_BASE_PATH | btrfs receive $TARGET_BASE_DIR
sync
fi
if [ -d "$SOURCE_PEND_PATH" ] then
echo "$SOURCE_PEND_PATH found removing..."
btrfs subvolume delete -c $SOURCE_PEND_PATH
sync
fi
btrfs subvolume snapshot -r $SOURCE_PATH $SOURCE_PEND_PATH
sync
if [ -d "$TARGET_PEND_PATH" ] then
echo "$TARGET_PEND_PATH found removing..."
btrfs subvolume delete -c $TARGET_PEND_PATH
sync
fi
echo "Sending $SOURCE_PEND_PATH to $TARGET_PEND_PATH"
btrfs send -p $SOURCE_BASE_PATH $SOURCE_PEND_PATH | btrfs receive $TARGET_BASE_DIR
sync
TARGET_DATE_SUFFIX=$(suffix)
btrfs subvolume snapshot -r $TARGET_PEND_PATH "$TARGET_PATH$TARGET_DATE_SUFFIX"
sync
btrfs subvolume delete -c $SOURCE_BASE_PATH
sync
btrfs subvolume delete -c $TARGET_BASE_PATH
sync
mv $SOURCE_PEND_PATH $SOURCE_BASE_PATH
mv $TARGET_PEND_PATH $TARGET_BASE_PATH
sync
}

function list()
{
LIST_TARGET_BASE_PATH="$BACKUPS$1$BASE_SUFFIX"
LIST_TARGET_BASE_DIR="$(dirname $LIST_TARGET_BASE_PATH)"
LIST_TARGET_BASE_NAME="$(basename -s .$BASE_SUFFIX $LIST_TARGET_BASE_PATH)"
find "$LIST_TARGET_BASE_DIR" -maxdepth 1 -mindepth 1 -type d -printf "%fn" | grep "${LIST_TARGET_BASE_NAME/$BASE_SUFFIX/$SNAP_SUFFIX}.$DATE_REGEX"
}

function remove()
{
REMOVE_TARGET_BASE_PATH="$BACKUPS$1$BASE_SUFFIX"
REMOVE_TARGET_BASE_DIR="$(dirname $REMOVE_TARGET_BASE_PATH)"
btrfs subvolume delete -c $REMOVE_TARGET_BASE_DIR/$2
sync
}

function removeall()
{
DATE_OFFSET="$2"
FILTER="$(filter "$DATE_OFFSET")"
while read -r SNAPSHOT ; do
remove "$1" "$SNAPSHOT"
done < <(list "$1" | grep "$FILTER")

}

(
COMMAND="$1"
shift

case "$COMMAND" in
"--help")
echo "Help"
;;
"suffix")
suffix
;;
"filter")
filter "$1"
;;
"backup")
wait_lock_or_terminate
backup "$1"
;;
"list")
list "$1"
;;
"remove")
wait_lock_or_terminate
remove "$1" "$2"
;;
"removeall")
wait_lock_or_terminate
removeall "$1" "$2"
;;
*)
echo "None.."
;;
esac
) 98>$LOCK_FILE

EOF

Qu'est-ce que ça fait même... ?Contient un ensemble de commandes simples pour créer des instantanés BTRFS et les copier vers un autre FS à l'aide de l'envoi/réception BTRFS.

Le premier lancement peut être relativement long, car... Au début, toutes les données seront copiées. Les prochains lancements seront très rapides, car... Seules les modifications seront copiées.

Un autre script que nous mettrons dans cron :

Un peu plus de code bash#cat >/root/btrfs-backup/cron-daily.sh << EOF
#!/bin/bash
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

SCRIPT_FILE="$(realpath $0)"
SCRIPT_DIR="$(dirname $SCRIPT_FILE)"
SCRIPT_NAME="$(basename -s .sh $SCRIPT_FILE)"

BACKUP_SCRIPT="$SCRIPT_DIR/btrfs-backup.sh"
RETENTION="-60 day"
$BACKUP_SCRIPT backup root/@
$BACKUP_SCRIPT removeall root/@ "$RETENTION"
$BACKUP_SCRIPT backup root/@home
$BACKUP_SCRIPT removeall root/@home "$RETENTION"
$BACKUP_SCRIPT backup boot/
$BACKUP_SCRIPT removeall boot/ "$RETENTION"
EOF

Qu'est ce que ça fait..?Crée et synchronise des instantanés incrémentiels des volumes BTRFS répertoriés sur le FS de sauvegarde. Après cela, toutes les images créées il y a 60 jours sont supprimées. Après le lancement, des instantanés datés des volumes répertoriés apparaîtront dans les sous-répertoires /backup/btrfs/back/remote/.

Donnons au code les droits d'exécution :

#chmod +x /root/btrfs-backup/cron-daily.sh
#chmod +x /root/btrfs-backup/btrfs-backup.sh

Vérifions-le et mettons-le dans le cron :

#/usr/bin/nice -n 19 /usr/bin/ionice -c 3 /root/btrfs-backup/cron-daily.sh 2>&1 | /usr/bin/logger -t btrfs-backup
#cat /var/log/syslog | grep btrfs-backup
#crontab -e
0 2 * * * /usr/bin/nice -n 19 /usr/bin/ionice -c 3 /root/btrfs-backup/cron-daily.sh 2>&1 | /usr/bin/logger -t btrfs-backup

Sauvegarde légère LVM

Créons un Thin Pool sur le périphérique de sauvegarde :

#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T backup/thin-pool

Installons ddrescue, parce que... les scripts utiliseront cet outil :

#apt-get install gddrescue

Créons un répertoire pour les scripts :

#mkdir /root/lvm-thin-backup

Copions les scripts :

Beaucoup de tapage à l'intérieur...#cat >/root/lvm-thin-backup/lvm-thin-backup.sh << EOF
#!/bin/bash
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

SCRIPT_FILE="$(realpath $0)"
SCRIPT_DIR="$(dirname $SCRIPT_FILE)"
SCRIPT_NAME="$(basename -s .sh $SCRIPT_FILE)"

LOCK_FILE="/dev/shm/$SCRIPT_NAME.lock"
DATE_PREFIX='%Y-%m-%d'
DATE_FORMAT=$DATE_PREFIX'-%H-%M-%S'
DATE_REGEX='[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]'
BASE_SUFFIX=".base"
PEND_SUFFIX=".pend"
SNAP_SUFFIX=".snap"
BACKUPS="backup"
BACKUPS_POOL="thin-pool"

export LVM_SUPPRESS_FD_WARNINGS=1

function terminate ()
{
echo "$1" >&2
exit 1
}

function wait_lock()
{
flock 98
}

function wait_lock_or_terminate()
{
echo "Wating for lock..."
wait_lock || terminate "Failed to get lock. Exiting..."
echo "Got lock..."
}

function suffix()
{
FORMATTED_DATE=$(date +"$DATE_FORMAT")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}

function filter()
{
FORMATTED_DATE=$(date --date="$1" +"$DATE_PREFIX")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}

function read_thin_id {
lvs --rows --reportformat basic --quiet -othin_id "$1/$2" | awk '{print $2}'
}

function read_pool_lv {
lvs --rows --reportformat basic --quiet -opool_lv "$1/$2" | awk '{print $2}'
}

function read_lv_dm_path {
lvs --rows --reportformat basic --quiet -olv_dm_path "$1/$2" | awk '{print $2}'
}

function read_lv_active {
lvs --rows --reportformat basic --quiet -olv_active "$1/$2" | awk '{print $2}'
}

function read_lv_chunk_size {
lvs --rows --reportformat basic --quiet --units b --nosuffix -ochunk_size "$1/$2" | awk '{print $2}'
}

function read_lv_size {
lvs --rows --reportformat basic --quiet --units b --nosuffix -olv_size "$1/$2" | awk '{print $2}'
}

function activate_volume {
lvchange -ay -Ky "$1/$2"
}

function deactivate_volume {
lvchange -an "$1/$2"
}

function read_thin_metadata_snap {
dmsetup status "$1" | awk '{print $7}'
}

function thindiff()
{
DIFF_VG="$1"
DIFF_SOURCE="$2"
DIFF_TARGET="$3"
DIFF_SOURCE_POOL=$(read_pool_lv $DIFF_VG $DIFF_SOURCE)
DIFF_TARGET_POOL=$(read_pool_lv $DIFF_VG $DIFF_TARGET)

if [ "$DIFF_SOURCE_POOL" == "" ] then
(>&2 echo "Source LV is not thin.")
exit 1
fi

if [ "$DIFF_TARGET_POOL" == "" ] then
(>&2 echo "Target LV is not thin.")
exit 1
fi

if [ "$DIFF_SOURCE_POOL" != "$DIFF_TARGET_POOL" ] then
(>&2 echo "Source and target LVs belong to different thin pools.")
exit 1
fi

DIFF_POOL_PATH=$(read_lv_dm_path $DIFF_VG $DIFF_SOURCE_POOL)
DIFF_SOURCE_ID=$(read_thin_id $DIFF_VG $DIFF_SOURCE)
DIFF_TARGET_ID=$(read_thin_id $DIFF_VG $DIFF_TARGET)
DIFF_POOL_PATH_TPOOL="$DIFF_POOL_PATH-tpool"
DIFF_POOL_PATH_TMETA="$DIFF_POOL_PATH"_tmeta
DIFF_POOL_METADATA_SNAP=$(read_thin_metadata_snap $DIFF_POOL_PATH_TPOOL)

if [ "$DIFF_POOL_METADATA_SNAP" != "-" ] then
(>&2 echo "Thin pool metadata snapshot already exist. Assuming stale one. Will release metadata snapshot in 5 seconds.")
sleep 5
dmsetup message $DIFF_POOL_PATH_TPOOL 0 release_metadata_snap
fi

dmsetup message $DIFF_POOL_PATH_TPOOL 0 reserve_metadata_snap
DIFF_POOL_METADATA_SNAP=$(read_thin_metadata_snap $DIFF_POOL_PATH_TPOOL)

if [ "$DIFF_POOL_METADATA_SNAP" == "-" ] then
(>&2 echo "Failed to create thin pool metadata snapshot.")
exit 1
fi

#We keep output in variable because metadata snapshot need to be released early.
DIFF_DATA=$(thin_delta -m$DIFF_POOL_METADATA_SNAP --snap1 $DIFF_SOURCE_ID --snap2 $DIFF_TARGET_ID $DIFF_POOL_PATH_TMETA)

dmsetup message $DIFF_POOL_PATH_TPOOL 0 release_metadata_snap

echo $"$DIFF_DATA" | grep -E 'different|left_only|right_only' | sed 's/</"/g' | sed 's/ /"/g' | awk -F'"' '{print $6 "t" $8 "t" $11}' | sed 's/different/copy/g' | sed 's/left_only/copy/g' | sed 's/right_only/discard/g'

}

function thinsync()
{
SYNC_VG="$1"
SYNC_PEND="$2"
SYNC_BASE="$3"
SYNC_TARGET="$4"
SYNC_PEND_POOL=$(read_pool_lv $SYNC_VG $SYNC_PEND)
SYNC_BLOCK_SIZE=$(read_lv_chunk_size $SYNC_VG $SYNC_PEND_POOL)
SYNC_PEND_PATH=$(read_lv_dm_path $SYNC_VG $SYNC_PEND)

activate_volume $SYNC_VG $SYNC_PEND

while read -r SYNC_ACTION SYNC_OFFSET SYNC_LENGTH ; do
SYNC_OFFSET_BYTES=$((SYNC_OFFSET * SYNC_BLOCK_SIZE))
SYNC_LENGTH_BYTES=$((SYNC_LENGTH * SYNC_BLOCK_SIZE))
if [ "$SYNC_ACTION" == "copy" ] then
ddrescue --quiet --force --input-position=$SYNC_OFFSET_BYTES --output-position=$SYNC_OFFSET_BYTES --size=$SYNC_LENGTH_BYTES "$SYNC_PEND_PATH" "$SYNC_TARGET"
fi

if [ "$SYNC_ACTION" == "discard" ] then
blkdiscard -o $SYNC_OFFSET_BYTES -l $SYNC_LENGTH_BYTES "$SYNC_TARGET"
fi
done < <(thindiff "$SYNC_VG" "$SYNC_PEND" "$SYNC_BASE")
}

function discard_volume()
{
DISCARD_VG="$1"
DISCARD_LV="$2"
DISCARD_LV_PATH=$(read_lv_dm_path "$DISCARD_VG" "$DISCARD_LV")
if [ "$DISCARD_LV_PATH" != "" ] then
echo "$DISCARD_LV_PATH found"
else
echo "$DISCARD_LV not found in $DISCARD_VG"
exit 1
fi
DISCARD_LV_POOL=$(read_pool_lv $DISCARD_VG $DISCARD_LV)
DISCARD_LV_SIZE=$(read_lv_size "$DISCARD_VG" "$DISCARD_LV")
lvremove -y --quiet "$DISCARD_LV_PATH" || exit 1
lvcreate --thin-pool "$DISCARD_LV_POOL" -V "$DISCARD_LV_SIZE"B --name "$DISCARD_LV" "$DISCARD_VG" || exit 1
}

function backup()
{
SOURCE_VG="$1"
SOURCE_LV="$2"
TARGET_VG="$BACKUPS"
TARGET_LV="$SOURCE_VG-$SOURCE_LV"
SOURCE_BASE_LV="$SOURCE_LV$BASE_SUFFIX"
TARGET_BASE_LV="$TARGET_LV$BASE_SUFFIX"
SOURCE_PEND_LV="$SOURCE_LV$PEND_SUFFIX"
TARGET_PEND_LV="$TARGET_LV$PEND_SUFFIX"
SOURCE_BASE_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_BASE_LV")
SOURCE_PEND_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_PEND_LV")
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
TARGET_PEND_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_PEND_LV")

if [ "$SOURCE_BASE_LV_PATH" != "" ] then
echo "$SOURCE_BASE_LV_PATH found"
else
echo "Source base not found creating snapshot of $SOURCE_VG/$SOURCE_LV to $SOURCE_VG/$SOURCE_BASE_LV"
lvcreate --quiet --snapshot --name "$SOURCE_BASE_LV" "$SOURCE_VG/$SOURCE_LV" || exit 1
SOURCE_BASE_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_BASE_LV")
activate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
echo "Discarding $SOURCE_BASE_LV_PATH as we need to bootstrap."
SOURCE_BASE_POOL=$(read_pool_lv $SOURCE_VG $SOURCE_BASE_LV)
SOURCE_BASE_CHUNK_SIZE=$(read_lv_chunk_size $SOURCE_VG $SOURCE_BASE_POOL)
discard_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
sync
if [ "$TARGET_BASE_LV_PATH" != "" ] then
echo "$TARGET_BASE_LV_PATH found out of sync with source... removing..."
lvremove -y --quiet $TARGET_BASE_LV_PATH || exit 1
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
sync
fi
fi
SOURCE_BASE_SIZE=$(read_lv_size "$SOURCE_VG" "$SOURCE_BASE_LV")
if [ "$TARGET_BASE_LV_PATH" != "" ] then
echo "$TARGET_BASE_LV_PATH found"
else
echo "$TARGET_VG/$TARGET_LV not found. Creating empty volume."
lvcreate --thin-pool "$BACKUPS_POOL" -V "$SOURCE_BASE_SIZE"B --name "$TARGET_BASE_LV" "$TARGET_VG" || exit 1
echo "Have to rebootstrap. Discarding source at $SOURCE_BASE_LV_PATH"
activate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
SOURCE_BASE_POOL=$(read_pool_lv $SOURCE_VG $SOURCE_BASE_LV)
SOURCE_BASE_CHUNK_SIZE=$(read_lv_chunk_size $SOURCE_VG $SOURCE_BASE_POOL)
discard_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
TARGET_BASE_POOL=$(read_pool_lv $TARGET_VG $TARGET_BASE_LV)
TARGET_BASE_CHUNK_SIZE=$(read_lv_chunk_size $TARGET_VG $TARGET_BASE_POOL)
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
echo "Discarding target at $TARGET_BASE_LV_PATH"
discard_volume "$TARGET_VG" "$TARGET_BASE_LV"
sync
fi
if [ "$SOURCE_PEND_LV_PATH" != "" ] then
echo "$SOURCE_PEND_LV_PATH found removing..."
lvremove -y --quiet "$SOURCE_PEND_LV_PATH" || exit 1
sync
fi
lvcreate --quiet --snapshot --name "$SOURCE_PEND_LV" "$SOURCE_VG/$SOURCE_LV" || exit 1
SOURCE_PEND_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_PEND_LV")
sync
if [ "$TARGET_PEND_LV_PATH" != "" ] then
echo "$TARGET_PEND_LV_PATH found removing..."
lvremove -y --quiet $TARGET_PEND_LV_PATH
sync
fi
lvcreate --quiet --snapshot --name "$TARGET_PEND_LV" "$TARGET_VG/$TARGET_BASE_LV" || exit 1
TARGET_PEND_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_PEND_LV")
SOURCE_PEND_LV_SIZE=$(read_lv_size "$SOURCE_VG" "$SOURCE_PEND_LV")
lvresize -L "$SOURCE_PEND_LV_SIZE"B "$TARGET_PEND_LV_PATH"
activate_volume "$TARGET_VG" "$TARGET_PEND_LV"
echo "Synching $SOURCE_PEND_LV_PATH to $TARGET_PEND_LV_PATH"
thinsync "$SOURCE_VG" "$SOURCE_PEND_LV" "$SOURCE_BASE_LV" "$TARGET_PEND_LV_PATH" || exit 1
sync

TARGET_DATE_SUFFIX=$(suffix)
lvcreate --quiet --snapshot --name "$TARGET_LV$TARGET_DATE_SUFFIX" "$TARGET_VG/$TARGET_PEND_LV" || exit 1
sync
lvremove --quiet -y "$SOURCE_BASE_LV_PATH" || exit 1
sync
lvremove --quiet -y "$TARGET_BASE_LV_PATH" || exit 1
sync
lvrename -y "$SOURCE_VG/$SOURCE_PEND_LV" "$SOURCE_BASE_LV" || exit 1
lvrename -y "$TARGET_VG/$TARGET_PEND_LV" "$TARGET_BASE_LV" || exit 1
sync
deactivate_volume "$TARGET_VG" "$TARGET_BASE_LV"
deactivate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
}

function verify()
{
SOURCE_VG="$1"
SOURCE_LV="$2"
TARGET_VG="$BACKUPS"
TARGET_LV="$SOURCE_VG-$SOURCE_LV"
SOURCE_BASE_LV="$SOURCE_LV$BASE_SUFFIX"
TARGET_BASE_LV="$TARGET_LV$BASE_SUFFIX"
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
SOURCE_BASE_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_BASE_LV")

if [ "$SOURCE_BASE_LV_PATH" != "" ] then
echo "$SOURCE_BASE_LV_PATH found"
else
echo "$SOURCE_BASE_LV_PATH not found"
exit 1
fi
if [ "$TARGET_BASE_LV_PATH" != "" ] then
echo "$TARGET_BASE_LV_PATH found"
else
echo "$TARGET_BASE_LV_PATH not found"
exit 1
fi
activate_volume "$TARGET_VG" "$TARGET_BASE_LV"
activate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
echo Comparing "$SOURCE_BASE_LV_PATH" with "$TARGET_BASE_LV_PATH"
cmp "$SOURCE_BASE_LV_PATH" "$TARGET_BASE_LV_PATH"
echo Done...
deactivate_volume "$TARGET_VG" "$TARGET_BASE_LV"
deactivate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
}

function resync()
{
SOURCE_VG="$1"
SOURCE_LV="$2"
TARGET_VG="$BACKUPS"
TARGET_LV="$SOURCE_VG-$SOURCE_LV"
SOURCE_BASE_LV="$SOURCE_LV$BASE_SUFFIX"
TARGET_BASE_LV="$TARGET_LV$BASE_SUFFIX"
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
SOURCE_BASE_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_BASE_LV")

if [ "$SOURCE_BASE_LV_PATH" != "" ] then
echo "$SOURCE_BASE_LV_PATH found"
else
echo "$SOURCE_BASE_LV_PATH not found"
exit 1
fi
if [ "$TARGET_BASE_LV_PATH" != "" ] then
echo "$TARGET_BASE_LV_PATH found"
else
echo "$TARGET_BASE_LV_PATH not found"
exit 1
fi
activate_volume "$TARGET_VG" "$TARGET_BASE_LV"
activate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
SOURCE_BASE_POOL=$(read_pool_lv $SOURCE_VG $SOURCE_BASE_LV)
SYNC_BLOCK_SIZE=$(read_lv_chunk_size $SOURCE_VG $SOURCE_BASE_POOL)

echo Syncronizing "$SOURCE_BASE_LV_PATH" to "$TARGET_BASE_LV_PATH"

CMP_OFFSET=0
while [[ "$CMP_OFFSET" != "" ]] ; do
CMP_MISMATCH=$(cmp -i "$CMP_OFFSET" "$SOURCE_BASE_LV_PATH" "$TARGET_BASE_LV_PATH" | grep differ | awk '{print $5}' | sed 's/,//g' )
if [[ "$CMP_MISMATCH" != "" ]] ; then
CMP_OFFSET=$(( CMP_MISMATCH + CMP_OFFSET ))
SYNC_OFFSET_BYTES=$(( ( CMP_OFFSET / SYNC_BLOCK_SIZE ) * SYNC_BLOCK_SIZE ))
SYNC_LENGTH_BYTES=$(( SYNC_BLOCK_SIZE ))
echo "Synching $SYNC_LENGTH_BYTES bytes at $SYNC_OFFSET_BYTES from $SOURCE_BASE_LV_PATH to $TARGET_BASE_LV_PATH"
ddrescue --quiet --force --input-position=$SYNC_OFFSET_BYTES --output-position=$SYNC_OFFSET_BYTES --size=$SYNC_LENGTH_BYTES "$SOURCE_BASE_LV_PATH" "$TARGET_BASE_LV_PATH"
else
CMP_OFFSET=""
fi
done
echo Done...
deactivate_volume "$TARGET_VG" "$TARGET_BASE_LV"
deactivate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
}

function list()
{
LIST_SOURCE_VG="$1"
LIST_SOURCE_LV="$2"
LIST_TARGET_VG="$BACKUPS"
LIST_TARGET_LV="$LIST_SOURCE_VG-$LIST_SOURCE_LV"
LIST_TARGET_BASE_LV="$LIST_TARGET_LV$SNAP_SUFFIX"
lvs -olv_name | grep "$LIST_TARGET_BASE_LV.$DATE_REGEX"
}

function remove()
{
REMOVE_TARGET_VG="$BACKUPS"
REMOVE_TARGET_LV="$1"
lvremove -y "$REMOVE_TARGET_VG/$REMOVE_TARGET_LV"
sync
}

function removeall()
{
DATE_OFFSET="$3"
FILTER="$(filter "$DATE_OFFSET")"
while read -r SNAPSHOT ; do
remove "$SNAPSHOT"
done < <(list "$1" "$2" | grep "$FILTER")

}

(
COMMAND="$1"
shift

case "$COMMAND" in
"--help")
echo "Help"
;;
"suffix")
suffix
;;
"filter")
filter "$1"
;;
"backup")
wait_lock_or_terminate
backup "$1" "$2"
;;
"list")
list "$1" "$2"
;;
"thindiff")
thindiff "$1" "$2" "$3"
;;
"thinsync")
thinsync "$1" "$2" "$3" "$4"
;;
"verify")
wait_lock_or_terminate
verify "$1" "$2"
;;
"resync")
wait_lock_or_terminate
resync "$1" "$2"
;;
"remove")
wait_lock_or_terminate
remove "$1"
;;
"removeall")
wait_lock_or_terminate
removeall "$1" "$2" "$3"
;;
*)
echo "None.."
;;
esac
) 98>$LOCK_FILE

EOF

Qu'est ce que ça fait...?Contient un ensemble de commandes permettant de manipuler les instantanés légers et de synchroniser la différence entre deux instantanés légers reçus via Thin_delta vers un autre périphérique bloc à l'aide de ddrescue et blkdiscard.

Un autre script que nous mettrons dans cron :

Un peu plus de coup#cat >/root/lvm-thin-backup/cron-daily.sh << EOF
#!/bin/bash
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

SCRIPT_FILE="$(realpath $0)"
SCRIPT_DIR="$(dirname $SCRIPT_FILE)"
SCRIPT_NAME="$(basename -s .sh $SCRIPT_FILE)"

BACKUP_SCRIPT="$SCRIPT_DIR/lvm-thin-backup.sh"
RETENTION="-60 days"

$BACKUP_SCRIPT backup images linux-dev
$BACKUP_SCRIPT backup images win8
$BACKUP_SCRIPT backup images win8-data
#etc

$BACKUP_SCRIPT removeall images linux-dev "$RETENTION"
$BACKUP_SCRIPT removeall images win8 "$RETENTION"
$BACKUP_SCRIPT removeall images win8-data "$RETENTION"
#etc

EOF

Qu'est ce que ça fait...?Utilise le script précédent pour créer et synchroniser les sauvegardes des volumes légers répertoriés. Le script laissera des instantanés inactifs des volumes répertoriés, nécessaires au suivi des modifications depuis la dernière synchronisation.

Ce script doit être édité en précisant la liste des volumes légers pour lesquels des copies de sauvegarde doivent être effectuées. Les noms donnés sont uniquement à titre indicatif. Si vous le souhaitez, vous pouvez écrire un script qui synchronisera tous les volumes.

Donnons les droits :

#chmod +x /root/lvm-thin-backup/cron-daily.sh
#chmod +x /root/lvm-thin-backup/lvm-thin-backup.sh

Vérifions-le et mettons-le dans le cron :

#/usr/bin/nice -n 19 /usr/bin/ionice -c 3 /root/lvm-thin-backup/cron-daily.sh 2>&1 | /usr/bin/logger -t lvm-thin-backup
#cat /var/log/syslog | grep lvm-thin-backup
#crontab -e
0 3 * * * /usr/bin/nice -n 19 /usr/bin/ionice -c 3 /root/lvm-thin-backup/cron-daily.sh 2>&1 | /usr/bin/logger -t lvm-thin-backup

Le premier lancement sera long, car... les volumes légers seront entièrement synchronisés en copiant tout l'espace utilisé. Grâce aux métadonnées fines LVM, nous savons quels blocs sont réellement utilisés, de sorte que seuls les blocs de volumes fins réellement utilisés seront copiés.

Les exécutions ultérieures copieront les données progressivement grâce au suivi des modifications via les métadonnées fines LVM.

Voyons ce qui se passe:

#time /root/btrfs-backup/cron-daily.sh
real 0m2,967s
user 0m0,225s
sys 0m0,353s

#time /root/lvm-thin-backup/cron-daily.sh
real 1m2,710s
user 0m12,721s
sys 0m6,671s

#ls -al /backup/btrfs/back/remote/*
/backup/btrfs/back/remote/boot:
total 0
drwxr-xr-x 1 root root 1260 мар 26 09:11 .
drwxr-xr-x 1 root root 16 мар 6 09:30 ..
drwxr-xr-x 1 root root 322 мар 26 02:00 .@base
drwxr-xr-x 1 root root 516 мар 6 09:39 [email protected]
drwxr-xr-x 1 root root 516 мар 6 09:39 [email protected]
...
/backup/btrfs/back/remote/root:
total 0
drwxr-xr-x 1 root root 2820 мар 26 09:11 .
drwxr-xr-x 1 root root 16 мар 6 09:30 ..
drwxr-xr-x 1 root root 240 мар 26 09:11 @.@base
drwxr-xr-x 1 root root 22 мар 26 09:11 @home.@base
drwxr-xr-x 1 root root 22 мар 6 09:39 @[email protected]
drwxr-xr-x 1 root root 22 мар 6 09:39 @[email protected]
...
drwxr-xr-x 1 root root 240 мар 6 09:39 @[email protected]
drwxr-xr-x 1 root root 240 мар 6 09:39 @[email protected]
...

#lvs -olv_name,lv_size images && lvs -olv_name,lv_size backup
LV LSize
linux-dev 128,00g
linux-dev.base 128,00g
thin-pool 1,38t
win8 128,00g
win8-data 2,00t
win8-data.base 2,00t
win8.base 128,00g
LV LSize
backup 256,00g
images-linux-dev.base 128,00g
images-linux-dev.snap.2020-03-08-10-09-11 128,00g
images-linux-dev.snap.2020-03-08-10-09-25 128,00g
...
images-win8-data.base 2,00t
images-win8-data.snap.2020-03-16-14-11-55 2,00t
images-win8-data.snap.2020-03-16-14-19-50 2,00t
...
images-win8.base 128,00g
images-win8.snap.2020-03-17-04-51-46 128,00g
images-win8.snap.2020-03-18-03-02-49 128,00g
...
thin-pool <2,09t

Qu'est-ce que cela a à voir avec les poupées gigognes ?

Très probablement, étant donné que les volumes logiques LVM LV peuvent être des volumes physiques LVM PV pour d'autres VG. LVM peut être récursif, comme les poupées gigognes. Cela donne à LVM une flexibilité extrême.

PS

Dans le prochain article, nous essaierons d'utiliser plusieurs systèmes de stockage mobile/KVM similaires comme base pour créer un cluster de stockage/VM géo-distribué avec redondance sur plusieurs continents en utilisant les ordinateurs de bureau à domicile, l'Internet domestique et les réseaux P2P.

Source: habr.com

Ajouter un commentaire