Ce au în comun LVM și Matryoshka?

Bună ziua.
Aș dori să împărtășesc comunității experiența mea practică de construire a unui sistem de stocare a datelor pentru KVM folosind md RAID + LVM.

Programul va include:

  • Construirea md RAID 1 de pe SSD NVMe.
  • Asamblarea md RAID 6 de pe SATA SSD și unități obișnuite.
  • Caracteristici ale operațiunii TRIM/DISCARD pe SSD RAID 1/6.
  • Crearea unui array md RAID 1/6 bootabil pe un set comun de discuri.
  • Instalarea sistemului pe NVMe RAID 1 când nu există suport NVMe în BIOS.
  • Folosind cache LVM și LVM thin.
  • Folosind instantanee BTRFS și trimiteți/primiți pentru backup.
  • Folosind instantanee subțiri LVM și thin_delta pentru backup-uri în stil BTRFS.

Dacă sunteți interesat, vă rugăm să vedeți cat.

Declarație

Autorul nu poartă nicio responsabilitate pentru consecințele utilizării sau neutilizarii materialelor/exemplelor/codului/sfaturii/date din acest articol. Citind sau folosind acest material în orice mod, vă asumați responsabilitatea pentru toate consecințele acestor acțiuni. Consecințele posibile includ:

  • SSD-uri NVMe prăjite.
  • Resursa de înregistrare epuizată complet și defecțiunea unităților SSD.
  • Pierderea completă a tuturor datelor de pe toate unitățile, inclusiv a copiilor de rezervă.
  • Hardware defect de calculator.
  • Timp pierdut, nervi și bani.
  • Orice alte consecințe care nu sunt enumerate mai sus.

fier

Disponibili au fost:

Placa de baza din jurul anului 2013 cu chipset Z87, completata cu Intel Core i7 / Haswell.

  • Procesor 4 nuclee, 8 fire
  • 32 GB RAM DDR3
  • 1 x 16 sau 2 x 8 PCIe 3.0
  • 1 x 4 + 1 x 1 PCIe 2.0
  • 6 x 6 GBps conectori SATA 3

Adaptorul SAS LSI SAS9211-8I a trecut la modul IT/HBA. Firmware-ul compatibil RAID a fost înlocuit intenționat cu firmware-ul HBA pentru:

  1. Puteți arunca oricând acest adaptor și să-l înlocuiți cu oricare altul pe care l-ați întâlnit.
  2. TRIM/Discard a funcționat normal pe discuri, deoarece... în firmware-ul RAID aceste comenzi nu sunt acceptate deloc, iar HBA-ului, în general, nu îi pasă ce comenzi sunt transmise prin magistrală.

Hard disk-uri - 8 bucăți HGST Travelstar 7K1000 cu o capacitate de 1 TB într-un factor de formă de 2.5, ca și pentru laptopuri. Aceste unități erau anterior într-o matrice RAID 6. Ele vor avea, de asemenea, o utilizare în noul sistem. Pentru a stoca copii de rezervă locale.

Adăugat suplimentar:

6 piese SATA SSD model Samsung 860 QVO 2TB. Aceste SSD-uri necesitau un volum mare, prezența unui cache SLC, fiabilitate și un preț scăzut. A fost necesar suport pentru aruncare/zero, care este verificat de linia din dmesg:

kernel: ata1.00: Enabling discard_zeroes_data

2 piese SSD NVMe model Samsung SSD 970 EVO 500GB.

Pentru aceste SSD-uri, viteza de citire/scriere aleatorie și capacitatea de resurse pentru nevoile dvs. sunt importante. Radiator pentru ei. Neapărat. Absolut. În caz contrar, prăjiți-le până devin crocante în timpul primei sincronizări RAID.

Adaptor StarTech PEX8M2E2 pentru 2 x SSD NVMe instalat în slot PCIe 3.0 8x. Acesta, din nou, este doar un HBA, dar pentru NVMe. Diferă de adaptoarele ieftine prin faptul că nu necesită suport pentru bifurcare PCIe de la placa de bază datorită prezenței unui comutator PCIe încorporat. Va funcționa chiar și în cel mai vechi sistem cu PCIe, chiar dacă este un slot x1 PCIe 1.0. Desigur, la viteza potrivită. Nu există RAID-uri acolo. Nu există un BIOS încorporat la bord. Deci, sistemul dumneavoastră nu va învăța în mod magic să pornească cu NVMe, cu atât mai puțin să facă NVMe RAID datorită acestui dispozitiv.

Această componentă s-a datorat exclusiv prezenței unui singur PCIe 8 3.0x gratuit în sistem și, dacă există 2 sloturi libere, poate fi înlocuită cu ușurință cu doi bănuți PEX4M2E1 sau analogi, care pot fi cumpărați oriunde la prețul de 600 ruble.

Respingerea tuturor tipurilor de hardware sau chipset-uri/BIOS RAID-uri încorporate a fost făcută în mod deliberat, pentru a putea înlocui complet întregul sistem, cu excepția SSD-ului/HDD-urilor înșiși, păstrând în același timp toate datele. Ideal, astfel încât să puteți salva chiar și sistemul de operare instalat atunci când treceți la un hardware complet nou/diferit. Principalul lucru este că există porturi SATA și PCIe. Este ca un CD live sau o unitate flash bootabilă, doar foarte rapidă și puțin voluminoasă.

ЮморÎn caz contrar, știți ce se întâmplă - uneori trebuie urgent să luați cu dvs. întreaga matrice pentru a lua. Dar nu vreau să pierd date. Pentru a face acest lucru, toate mediile menționate sunt amplasate convenabil pe diapozitivele din locașurile 5.25 ale carcasei standard.

Ei bine, și, desigur, pentru a experimenta diferite metode de stocare în cache SSD în Linux.

Raidurile hardware sunt plictisitoare. Porniți-l. Ori funcționează, ori nu. Și cu mdadm există întotdeauna opțiuni.

moale

Anterior, Debian 8 Jessie a fost instalat pe hardware, care este aproape de EOL. RAID 6 a fost asamblat din HDD-urile menționate mai sus asociate cu LVM. A rulat mașini virtuale în kvm/libvirt.

Deoarece Autorul are o experiență adecvată în crearea de unități flash SATA/NVMe bootabile portabile și, de asemenea, pentru a nu rupe șablonul apt obișnuit, a fost ales ca sistem țintă Ubuntu 18.04, care a fost deja suficient de stabilizat, dar mai are 3 ani de funcționare. sprijin în viitor.

Sistemul menționat conține toate driverele hardware de care avem nevoie din cutie. Nu avem nevoie de software sau drivere de la terți.

Pregătirea pentru instalare

Pentru a instala sistemul avem nevoie de Ubuntu Desktop Image. Sistemul server are un fel de instalare viguroasă, care arată o independență excesivă care nu poate fi dezactivată prin împingerea partiției sistemului UEFI pe unul dintre discuri, stricând toată frumusețea. În consecință, este instalat numai în modul UEFI. Nu oferă nicio opțiune.

Nu suntem mulțumiți de asta.

De ce?Din păcate, încărcarea UEFI este extrem de slab compatibilă cu software-ul de pornire RAID, deoarece... Nimeni nu ne oferă rezervări pentru partiția UEFI ESP. Există rețete online care sugerează plasarea partiției ESP pe o unitate flash într-un port USB, dar acesta este un punct de eșec. Există rețete care folosesc software-ul mdadm RAID 1 cu metadate versiunea 0.9 care nu împiedică UEFI BIOS să vadă această partiție, dar aceasta trăiește până la momentul fericit când BIOS-ul sau un alt SO hardware scrie ceva în ESP și uită să-l sincronizeze cu alte oglinzi.

În plus, încărcarea UEFI depinde de NVRAM, care nu se va muta împreună cu discurile în noul sistem, deoarece face parte din placa de baza.

Deci, nu vom reinventa o nouă roată. Avem deja o bicicletă a bunicului gata făcută, testată în timp, numită acum Legacy/BIOS boot, purtând mândrul nume de CSM pe sisteme compatibile cu UEFI. Îl vom scoate de pe raft, îl vom lubrifia, vom pompa anvelopele și îl vom șterge cu o cârpă umedă.

De asemenea, versiunea de desktop a Ubuntu nu poate fi instalată corect cu bootloader-ul Legacy, dar aici, după cum se spune, cel puțin există opțiuni.

Și astfel, colectăm hardware-ul și încărcăm sistemul de pe unitatea flash bootabilă Ubuntu Live. Va trebui să descarcăm pachete, așa că vom configura rețeaua care funcționează pentru dvs. Dacă nu funcționează, puteți încărca în avans pachetele necesare pe o unitate flash.

Intrăm în mediul Desktop, lansăm emulatorul de terminal și pornim:

#sudo bash

Cum…?Linia de mai sus este declanșarea canonică pentru holiwars despre sudo. C bоvin oportunități mai mari șiоresponsabilitate mai mare. Întrebarea este dacă o poți lua singur. Mulți oameni cred că folosirea sudo în acest fel este cel puțin nu atentă. In orice caz:

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

De ce nu ZFS...?Când instalăm software pe computerul nostru, în esență, împrumutăm hardware-ul nostru dezvoltatorilor acestui software pentru a conduce.
Când avem încredere în acest software cu siguranța datelor noastre, contractăm un împrumut egal cu costul restabilirii acestor date, pe care va trebui să-l plătim cândva.

Din acest punct de vedere, ZFS este un Ferrari, iar mdadm+lvm seamănă mai mult cu o bicicletă.

Subiectiv, autorul preferă să împrumute o bicicletă pe credit unor persoane necunoscute în loc de un Ferrari. Acolo, prețul problemei nu este mare. Nu este nevoie de drepturi. Mai simplu decât regulile de circulație. Parcarea este gratuită. Abilitatea de cross-country este mai bună. Puteți atașa oricând picioare la o bicicletă și puteți repara o bicicletă cu propriile mâini.

De ce atunci BTRFS...?Pentru a porni sistemul de operare, avem nevoie de un sistem de fișiere care este acceptat în Legacy/BIOS GRUB și, în același timp, acceptă instantanee live. O vom folosi pentru partiția /boot. În plus, autorul preferă să folosească acest FS pentru / (rădăcină), fără a uita să rețineți că pentru orice alt software puteți crea partiții separate pe LVM și le puteți monta în directoarele necesare.

Nu vom stoca nicio imagine cu mașini virtuale sau baze de date pe acest FS.
Acest FS va fi folosit doar pentru a crea instantanee ale sistemului fără a-l opri și apoi transfera aceste instantanee pe un disc de rezervă folosind trimitere/primire.

În plus, autorul preferă în general să păstreze un minim de software direct pe hardware și să ruleze toate celelalte software în mașinile virtuale folosind lucruri precum redirecționarea GPU-urilor și controlerelor PCI-USB Host către KVM prin IOMMU.

Singurele lucruri rămase pe hardware sunt stocarea datelor, virtualizarea și backup-ul.

Dacă aveți mai multă încredere în ZFS, atunci, în principiu, pentru aplicația specificată, acestea sunt interschimbabile.

Cu toate acestea, autorul ignoră în mod deliberat caracteristicile încorporate de oglindire/RAID și redundanță pe care le au ZFS, BRTFS și LVM.

Ca un argument suplimentar, BTRFS are capacitatea de a transforma scrierile aleatoare în cele secvențiale, ceea ce are un efect extrem de pozitiv asupra vitezei de sincronizare a instantaneelor/backup-urilor de pe HDD.

Să rescanăm toate dispozitivele:

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

Să aruncăm o privire în jur:

#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

Dispunerea discului

SSD NVMe

Dar nu le vom marca în niciun fel. Totuși, BIOS-ul nostru nu vede aceste unități. Deci, vor merge în întregime la software RAID. Nici măcar nu vom crea secțiuni acolo. Dacă doriți să urmați „canonul” sau „în principal”, creați o partiție mare, cum ar fi un HDD.

SATA HDD

Nu este nevoie să inventezi ceva special aici. Vom crea o secțiune pentru tot. Vom crea o partiție deoarece BIOS-ul vede aceste discuri și poate chiar încerca să pornească de pe ele. Vom instala chiar și GRUB pe aceste discuri mai târziu, astfel încât sistemul să poată face acest lucru brusc.

#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

Aici lucrurile devin interesante pentru noi.

În primul rând, unitățile noastre au o dimensiune de 2 TB. Acesta este în intervalul acceptabil pentru MBR, ceea ce vom folosi. Dacă este necesar, poate fi înlocuit cu GPT. Discurile GPT au un strat de compatibilitate care permite sistemelor compatibile cu MBR să vadă primele 4 partiții dacă acestea sunt situate în primii 2 terabytes. Principalul lucru este că partiția de boot și partiția bios_grub de pe aceste discuri ar trebui să fie la început. Acest lucru vă permite chiar să porniți de pe unitățile GPT Legacy/BIOS.

Dar acesta nu este cazul nostru.

Aici vom crea două secțiuni. Primul va avea o dimensiune de 1 GB și va fi folosit pentru RAID 1 /boot.

Al doilea va fi folosit pentru RAID 6 și va ocupa tot spațiul liber rămas, cu excepția unei mici zone nealocate la sfârșitul unității.

Ce este această zonă nemarcată?Potrivit surselor din rețea, SSD-urile noastre SATA au la bord un cache SLC extensibil dinamic, cu dimensiuni cuprinse între 6 și 78 de gigaocteți. Primim 6 gigaocteți „gratuit” datorită diferenței dintre „gigaocteți” și „gibiocteți” din fișa de date a unității. Restul de 72 de gigaocteți sunt alocați din spațiul neutilizat.

Aici trebuie menționat că avem un cache SLC, iar spațiul este ocupat în modul MLC pe 4 biți. Ceea ce pentru noi înseamnă efectiv că pentru fiecare 4 gigaocteți de spațiu liber vom primi doar 1 gigaoctet de cache SLC.

Înmulțiți 72 de gigaocteți cu 4 și obțineți 288 de gigaocteți. Acesta este spațiul liber pe care nu îl vom marca pentru a permite unităților să utilizeze pe deplin memoria cache SLC.

Astfel, vom obține efectiv până la 312 gigabytes de cache SLC dintr-un total de șase unități. Dintre toate unitățile, 2 vor fi folosite în RAID pentru redundanță.

Această cantitate de cache ne va permite extrem de rar în viața reală să întâlnim o situație în care o scriere nu ajunge în cache. Acest lucru compensează extrem de bine cel mai trist dezavantaj al memoriei QLC - viteza de scriere extrem de scăzută atunci când datele sunt scrise ocolind memoria cache. Dacă încărcările dvs. nu corespund cu aceasta, atunci vă recomand să vă gândiți bine cât timp va rezista SSD-ul dvs. sub o astfel de încărcare, ținând cont de TBW din fișa de date.

#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

Crearea de tablouri

În primul rând, trebuie să redenumim mașina. Acest lucru este necesar deoarece numele gazdei face parte din numele matricei undeva în mdadm și afectează ceva undeva. Desigur, matricele pot fi redenumite mai târziu, dar acesta este un pas inutil.

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

SSD NVMe

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

De ce -asuma-curat...?Pentru a evita inițializarea matricelor. Pentru ambele niveluri RAID 1 și 6 acest lucru este valabil. Totul poate funcționa fără inițializare dacă este o matrice nouă. Mai mult, inițializarea matricei SSD la creare este o risipă de resurse TBW. Folosim TRIM/DISCARD acolo unde este posibil pe matricele SSD asamblate pentru a le „inițializa”.

Pentru matricele SSD, RAID 1 DISCARD este acceptat imediat.

Pentru matricele SSD RAID 6 DISCARD, trebuie să-l activați în parametrii modulului kernel.

Acest lucru ar trebui făcut numai dacă toate SSD-urile utilizate în matricele de nivel 4/5/6 din acest sistem au suport funcțional pentru discard_zeroes_data. Uneori întâlniți unități ciudate care spun nucleului că această funcție este acceptată, dar de fapt nu există sau funcția nu funcționează întotdeauna. În acest moment, suportul este disponibil aproape peste tot, cu toate acestea, există unități vechi și firmware cu erori. Din acest motiv, suportul DISCARD este dezactivat implicit pentru RAID 6.

Atenție, următoarea comandă va distruge toate datele de pe unitățile NVMe prin „inițializarea” matricei cu „zero”.

#blkdiscard /dev/md0

Dacă ceva nu merge bine, încercați să specificați un pas.

#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

De ce atât de mare...?Creșterea dimensiunii bucăților are un efect pozitiv asupra vitezei de citire aleatorie în blocuri până la dimensiunea bucăților inclusiv. Acest lucru se întâmplă deoarece o operație de dimensiunea adecvată sau mai mică poate fi finalizată în întregime pe un singur dispozitiv. Prin urmare, IOPS-ul de la toate dispozitivele este rezumat. Conform statisticilor, 99% din IO nu depășește 512K.

RAID 6 IOPS per scriere mereu mai mic sau egal cu IOPS-ul unei unități. Când, ca citire aleatorie, IOPS poate fi de câteva ori mai mare decât cea a unei unități, iar aici dimensiunea blocului este de o importanță cheie.
Autorul nu vede rostul în a încerca să optimizeze un parametru care este rău în RAID 6 prin proiectare și, în schimb, optimizează la ce este bun RAID 6.
Vom compensa scrierea aleatorie slabă a RAID 6 cu un cache NVMe și trucuri de thin-provisioning.

Nu am activat încă DISCARD pentru RAID 6. Deci nu vom „inițializa” această matrice deocamdată. Vom face asta mai târziu, după instalarea sistemului de operare.

SATA HDD

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

LVM pe RAID NVMe

Pentru viteză, dorim să plasăm sistemul de fișiere rădăcină pe NVMe RAID 1, care este /dev/md0.
Cu toate acestea, vom avea în continuare nevoie de această matrice rapidă pentru alte nevoi, cum ar fi swap, metadate și LVM-cache și LVM-thin metadate, așa că vom crea un LVM VG pe această matrice.

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

Să creăm o partiție pentru sistemul de fișiere rădăcină.

#lvcreate -L 128G --name root root

Să creăm o partiție pentru schimbare în funcție de dimensiunea memoriei RAM.

#lvcreate -L 32G --name swap root

Instalare OS

În total, avem tot ce este necesar pentru a instala sistemul.

Lansați asistentul de instalare a sistemului din mediul Ubuntu Live. Instalare normală. Numai în etapa de selectare a discurilor pentru instalare, trebuie să specificați următoarele:

  • /dev/md1, - punct de montare /boot, FS - BTRFS
  • /dev/root/root (alias /dev/mapper/root-root), - punct de montare / (rădăcină), FS - BTRFS
  • /dev/root/swap (alias /dev/mapper/root-swap), - utilizați ca partiție de swap
  • Instalați bootloader-ul pe /dev/sda

Când selectați BTRFS ca sistem de fișiere rădăcină, programul de instalare va crea automat două volume BTRFS numite „@” pentru / (rădăcină) și „@home” pentru /home.

Să începem instalarea...

Instalarea se va încheia cu o casetă de dialog modală care indică o eroare la instalarea bootloader-ului. Din păcate, nu veți putea să părăsiți acest dialog folosind mijloace standard și să continuați instalarea. Ne deconectam din sistem și ne conectăm din nou, ajungând într-un desktop Ubuntu Live curat. Deschideți terminalul și din nou:

#sudo bash

Creați un mediu chroot pentru a continua instalarea:

#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

Să configuram rețeaua și numele de gazdă în chroot:

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

Să intrăm în mediul chroot:

#chroot /mnt/chroot

În primul rând, vom livra pachetele:

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

Să verificăm și să reparăm toate pachetele care au fost instalate greșit din cauza instalării incomplete a sistemului:

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

Dacă ceva nu merge, poate fi necesar să editați mai întâi /etc/apt/sources.list

Să ajustăm parametrii pentru modulul RAID 6 pentru a activa TRIM/DISCARD:

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

Să ne modificăm puțin matricele:

#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

Ce-a fost asta..?Am creat un set de reguli udev care vor face următoarele:

  • Setați dimensiunea cache a blocurilor pentru RAID 2020 să fie adecvată pentru 6. Se pare că valoarea implicită nu s-a schimbat de la crearea Linux și nu a fost adecvată de mult timp.
  • Rezervați un minim de IO pe durata verificărilor/sincronizărilor matricei. Acest lucru este pentru a preveni blocarea matricelor dvs. într-o stare de sincronizare eternă sub sarcină.
  • Limitați IO maximă în timpul verificărilor/sincronizării matricelor. Acest lucru este necesar pentru ca sincronizarea/verificarea RAID-urilor SSD să nu vă prăjească unitățile la un nivel clar. Acest lucru este valabil mai ales pentru NVMe. (Îți amintești de calorifer? Nu glumeam.)
  • Interziceți discurilor să oprească rotația axului (HDD) prin APM și setați durata de repaus pentru controlerele de disc la 7 ore. Puteți dezactiva complet APM dacă unitățile dvs. pot face acest lucru (-B 255). Cu valoarea implicită, unitățile se vor opri după cinci secunde. Apoi sistemul de operare dorește să resetați memoria cache a discului, discurile se vor învârti din nou și totul va începe din nou. Discurile au un număr maxim limitat de rotații ale axului. Un astfel de ciclu implicit simplu vă poate ucide cu ușurință discurile în câțiva ani. Nu toate discurile suferă de asta, dar ale noastre sunt „laptop”, cu setările implicite corespunzătoare, care fac ca RAID-ul să arate ca un mini-MAID.
  • Instalați readahead pe discuri (rotire) 1 megaoctet - două blocuri consecutive/bucăți RAID 6
  • Dezactivați readahead pe matricele în sine.

Să edităm /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

De ce este asta..?Vom căuta partiția /boot după UUID. Numele matricei s-ar putea schimba teoretic.

Vom căuta secțiunile rămase după numele LVM în notația /dev/mapper/vg-lv, deoarece ele identifică partițiile destul de unic.

Nu folosim UUID pentru LVM deoarece UUID-ul volumelor LVM și instantaneele lor pot fi aceleași.Montați /dev/mapper/root-root.. de două ori?Da. Exact. Caracteristica BTRFS. Acest sistem de fișiere poate fi montat de mai multe ori cu subvoluri diferite.

Datorită aceleiași caracteristici, vă recomand să nu creați niciodată instantanee LVM ale volumelor BTRFS active. Este posibil să aveți o surpriză când reporniți.

Să regenerăm configurația mdadm:

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

Să ajustăm setările 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

Ce-a fost asta..?Am activat extinderea automată a piscinelor subțiri LVM la atingerea a 90% din spațiul ocupat cu 5% din volum.

Am crescut numărul maxim de blocuri cache pentru cache LVM.

Am împiedicat LVM să caute volume LVM (PV) pe:

  • dispozitive care conțin cache LVM (cdata)
  • dispozitivele stocate în cache folosind cache LVM, ocolind memoria cache ( _corig). În acest caz, dispozitivul stocat în cache va fi în continuare scanat prin cache (doar ).
  • dispozitive care conțin metadate cache LVM (cmeta)
  • toate dispozitivele în VG cu numele imaginilor. Aici vom avea imagini de disc ale mașinilor virtuale și nu dorim ca LVM pe gazdă să activeze volume care aparțin sistemului de operare invitat.
  • toate dispozitivele din VG cu numele de rezervă. Aici vom avea copii de rezervă ale imaginilor mașinii virtuale.
  • toate dispozitivele al căror nume se termină cu „gpv” (volum fizic al oaspeților)

Am activat suportul DISCARD atunci când eliberăm spațiu liber pe LVM VG. Atenție. Acest lucru va face ca ștergerea LV-urilor de pe SSD să fie destul de consumatoare de timp. Acest lucru este valabil mai ales pentru SSD RAID 6. Cu toate acestea, conform planului, vom folosi thin provisioning, așa că acest lucru nu ne va împiedica deloc.

Să actualizăm imaginea initramfs:

#update-initramfs -u -k all

Instalați și configurați grub:

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

Ce discuri ar trebui să alegi?Toți cei care sunt sd*. Sistemul trebuie să poată porni de pe orice unitate SATA sau SSD care funcționează.

De ce au adăugat os-prober..?Pentru independență excesivă și mâini jucăușe.

Nu funcționează corect dacă unul dintre RAID-uri este într-o stare degradată. Încearcă să caute sistemul de operare pe partițiile care sunt utilizate în mașinile virtuale care rulează pe acest hardware.

Dacă aveți nevoie, îl puteți lăsa, dar țineți cont de toate cele de mai sus. Recomand sa cautati retete pentru a scapa de mainile obraznice online.

Cu aceasta am finalizat instalarea inițială. Este timpul să reporniți sistemul de operare nou instalat. Nu uitați să eliminați live CD/USB bootabil.

#exit
#reboot

Selectați oricare dintre SSD-urile SATA ca dispozitiv de pornire.

LVM pe SSD SATA

În acest moment, am pornit deja în noul sistem de operare, am configurat rețeaua, am apt, am deschis emulatorul de terminal și am rulat:

#sudo bash

Hai sa continuăm.

„Inițializați” matricea de pe SSD SATA:

#blkdiscard /dev/md2

Dacă nu funcționează, atunci încercați:

#blkdiscard --step 65536 /dev/md2
Creați LVM VG pe SSD SATA:

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

De ce alt VG..?De fapt, avem deja un VG numit root. De ce nu adăugați totul într-un singur VG?

Dacă există mai multe PV într-un VG, atunci pentru ca VG să fie activat corect, toate PV-urile trebuie să fie prezente (online). Excepția este LVM RAID, pe care nu îl folosim în mod deliberat.

Ne dorim foarte mult ca în cazul în care există o defecțiune (pierdere de date de citire) pe oricare dintre matricele RAID 6, sistemul de operare să pornească normal și să ne ofere posibilitatea de a rezolva problema.

Pentru a face acest lucru, la primul nivel de abstractizare vom izola fiecare tip de „media” fizică într-un VG separat.

Din punct de vedere științific, diferitele matrice RAID aparțin unor „domenii de fiabilitate” diferite. Nu ar trebui să creați un punct de eșec comun suplimentar pentru ei prin înghesuirea lor într-un singur VG.

Prezența LVM la nivel „hardware” ne va permite să tăiem în mod arbitrar bucăți din diferite matrice RAID combinându-le în moduri diferite. De exemplu - fugi simultan bcache + LVM thin, bcache + BTRFS, LVM cache + LVM thin, o configurație ZFS complexă cu cache sau orice alt amestec infernal pentru a încerca să le comparăm pe toate.

La nivel „hardware”, nu vom folosi altceva decât volume LVM „groase” vechi. Excepția de la această regulă poate fi partiția de rezervă.

Cred că în acest moment, mulți cititori începuseră deja să suspecteze ceva despre păpușa de cuib.

LVM pe HDD SATA

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

VG nou din nou...?Ne dorim foarte mult ca, dacă matricea de discuri pe care o vom folosi pentru backupul datelor eșuează, sistemul nostru de operare va continua să funcționeze normal, menținând în același timp accesul la datele care nu fac backup ca de obicei. Prin urmare, pentru a evita problemele de activare a VG, creăm un VG separat.

Configurarea memoriei cache LVM

Să creăm un LV pe NVMe RAID 1 pentru a-l folosi ca dispozitiv de cache.

#lvcreate -L 70871154688B --name cache root

De ce este atât de puțin...?Cert este că SSD-urile noastre NVMe au și un cache SLC. 4 gigabytes de „liber” și 18 gigabytes de dinamică datorită spațiului liber ocupat în MLC-ul pe 3 biți. Odată ce acest cache este epuizat, SSD-urile NVMe nu vor fi cu mult mai rapide decât SSD-ul nostru SATA cu cache. De fapt, din acest motiv, nu are sens pentru noi să facem partiția cache-ului LVM mult mai mare decât de două ori dimensiunea cache-ului SLC al unității NVMe. Pentru unitățile NVMe utilizate, autorul consideră rezonabil să se creeze 32-64 gigaocteți de cache.

Dimensiunea dată de partiție este necesară pentru a organiza 64 de gigaocteți de cache, metadate din cache și backup pentru metadate.

În plus, observ că după o închidere murdară a sistemului, LVM va marca întregul cache ca murdar și se va sincroniza din nou. Mai mult, acest lucru se va repeta de fiecare dată când lvchange este utilizat pe acest dispozitiv până când sistemul este repornit din nou. Prin urmare, recomand să recreați imediat memoria cache folosind scriptul corespunzător.

Să creăm un LV pe SATA RAID 6 pentru a-l folosi ca dispozitiv în cache.

#lvcreate -L 3298543271936B --name cache data

De ce doar trei terabytes..?Astfel încât, dacă este necesar, puteți utiliza SATA SSD RAID 6 pentru alte nevoi. Dimensiunea spațiului din cache poate fi mărită dinamic, din mers, fără a opri sistemul. Pentru a face acest lucru, trebuie să opriți temporar și să reactivați memoria cache, dar avantajul distinctiv al LVM-cache față de, de exemplu, bcache este că acest lucru se poate face din mers.

Să creăm un nou VG pentru stocarea în cache.

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

Să creăm un LV pe dispozitivul din cache.

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

Aici am ocupat imediat tot spațiul liber pe /dev/data/cache, astfel încât toate celelalte partiții necesare au fost create imediat pe /dev/root/cache. Dacă ați creat ceva în locul greșit, îl puteți muta folosind pvmove.

Să creăm și să activăm memoria 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

De ce o astfel de bucată..?Prin experimente practice, autorul a putut afla că cel mai bun rezultat este obținut dacă dimensiunea blocului cache LVM coincide cu dimensiunea blocului subțire LVM. Mai mult, cu cât dimensiunea este mai mică, cu atât configurația funcționează mai bine într-o înregistrare aleatorie.

64k este dimensiunea minimă a blocului permisă pentru LVM thin.

Fii atent la rescriere...!Da. Acest tip de cache amână sincronizarea scrierii pe dispozitivul din cache. Aceasta înseamnă că, dacă memoria cache este pierdută, este posibil să pierdeți date de pe dispozitivul din cache. Mai târziu, autorul vă va spune ce măsuri, pe lângă NVMe RAID 1, pot fi luate pentru a compensa acest risc.

Acest tip de cache a fost ales în mod intenționat pentru a compensa performanța slabă de scriere aleatorie a RAID 6.

Să verificăm ce avem:

#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)

Doar [cachedata_corig] ar trebui să fie localizat pe /dev/data/cache. Dacă ceva nu este în regulă, atunci utilizați pvmove.

Puteți dezactiva memoria cache dacă este necesar cu o singură comandă:

#lvconvert -y --uncache cache/cachedata

Acest lucru se face online. LVM va sincroniza pur și simplu memoria cache pe disc, o va elimina și va redenumi cachedata_corig înapoi la cachedata.

Configurarea LVM thin

Să estimăm aproximativ de cât spațiu avem nevoie pentru metadatele subțiri 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"

Rotunjiți până la 4 gigaocteți: 4294967296B

Înmulțiți cu doi și adăugați 4194304B pentru metadatele LVM PV: 8594128896B
Să creăm o partiție separată pe NVMe RAID 1 pentru a plasa metadatele subțiri LVM și copia lor de rezervă pe ea:

#lvcreate -L 8594128896B --name images root

Pentru ce..?Aici poate apărea întrebarea: de ce să plasați metadatele subțiri LVM separat dacă vor fi încă stocate în cache pe NVMe și vor funcționa rapid.

Deși viteza este importantă aici, este departe de motivul principal. Chestia este că memoria cache este un punct de eșec. S-ar putea întâmpla ceva cu el și, dacă metadatele subțiri LVM sunt stocate în cache, totul va fi pierdut complet. Fără metadate complete, va fi aproape imposibil să asamblați volume subțiri.

Prin mutarea metadatelor într-un volum separat, fără cache, dar rapid, garantăm siguranța metadatelor în cazul pierderii sau corupției memoriei cache. În acest caz, toate daunele cauzate de pierderea memoriei cache vor fi localizate în volume subțiri, ceea ce va simplifica procedura de recuperare cu ordine de mărime. Cu o mare probabilitate, aceste daune vor fi restaurate folosind jurnalele FS.

Mai mult decât atât, dacă a fost făcut anterior un instantaneu al unui volum subțire, iar după aceea cache-ul a fost complet sincronizat cel puțin o dată, atunci, datorită designului intern al LVM thin, integritatea instantaneului va fi garantată în cazul pierderii memoriei cache. .

Să creăm un nou VG care va fi responsabil pentru thin-provisioning:

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

Să creăm o piscină:

#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T images/thin-pool
De ce -Z yÎn plus față de ceea ce este destinat de fapt acest mod - pentru a preveni scurgerea datelor de la o mașină virtuală către o altă mașină virtuală atunci când se redistribuie spațiul - zero este folosită suplimentar pentru a crește viteza de scriere aleatorie în blocuri mai mici de 64k. Orice scriere mai mică de 64k într-o zonă nealocată anterior a volumului subțire va deveni 64K aliniată la margine în cache. Acest lucru va permite ca operația să fie efectuată în întregime prin cache, ocolind dispozitivul din cache.

Să mutăm LV-urile la PV-urile corespunzătoare:

#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

Sa verificam:

#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)

Să creăm un volum subțire pentru teste:

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

Vom instala pachete pentru teste și monitorizare:

#apt-get install sysstat fio

Iată cum puteți observa comportamentul configurației noastre de stocare în timp real:

#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)'

Iată cum ne putem testa configurația:

#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

Cu grija! Resursă!Acest cod va rula 36 de teste diferite, fiecare rulând timp de 4 secunde. Jumătate dintre teste sunt pentru înregistrare. Puteți înregistra multe pe NVMe în 4 secunde. Până la 3 gigaocteți pe secundă. Deci, fiecare rulare de teste de scriere poate consuma până la 216 gigaocteți de resurse SSD de la dvs.

Citit și scris amestecat?Da. Este logic să rulați separat testele de citire și scriere. Mai mult, este logic să vă asigurați că toate cache-urile sunt sincronizate, astfel încât o scriere făcută anterior să nu afecteze citirea.

Rezultatele vor varia foarte mult în timpul primei lansări și în cele ulterioare, pe măsură ce memoria cache și volumul subțire se umplu și, de asemenea, în funcție de dacă sistemul a reușit să sincronizeze cache-urile umplute în timpul ultimei lansări.

Printre altele, recomand măsurarea vitezei pe un volum subțire deja plin din care tocmai a fost luat un instantaneu. Autorul a avut ocazia să observe cum scrierile aleatoare accelerează brusc imediat după crearea primului instantaneu, mai ales când memoria cache nu este încă complet plină. Acest lucru se întâmplă din cauza semanticii de scriere a copierii la scriere, a alinierii cache-ului și a blocurilor de volum subțire și a faptului că o scriere aleatorie în RAID 6 se transformă într-o citire aleatorie din RAID 6 urmată de o scriere în cache. În configurația noastră, citirea aleatorie din RAID 6 este de până la 6 ori (numărul de SSD-uri SATA din matrice) mai rapidă decât scrierea. Deoarece blocurile pentru CoW sunt alocate secvenţial dintr-un pool subţire, apoi înregistrarea, în cea mai mare parte, se transformă, de asemenea, în secvenţial.

Ambele caracteristici pot fi folosite în avantajul dumneavoastră.

Memorați în cache instantanee „coerente”.

Pentru a reduce riscul de pierdere a datelor în caz de deteriorare/pierdere a cache-ului, autorul propune introducerea practicii de rotație a instantaneelor ​​pentru a garanta integritatea acestora în acest caz.

În primul rând, deoarece metadatele de volum subțire se află pe un dispozitiv necache, metadatele vor fi consistente și posibilele pierderi vor fi izolate în blocurile de date.

Următorul ciclu de rotație a instantaneelor ​​garantează integritatea datelor din interiorul instantaneelor ​​în caz de pierdere a memoriei cache:

  1. Pentru fiecare volum subțire cu numele <nume>, creați un instantaneu cu numele <nume>.cached
  2. Să setăm pragul de migrare la o valoare rezonabil ridicată: #lvchange --quiet --cachesettings "migration_threshold=16384" cache/cachedata
  3. În buclă verificăm numărul de blocuri murdare din cache: #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' până ajungem la zero. Dacă zero lipsește prea mult timp, acesta poate fi creat prin comutarea temporară a memoriei cache în modul writethrough. Cu toate acestea, ținând cont de caracteristicile de viteză ale matricelor noastre SSD SATA și NVMe, precum și de resursa lor TBW, fie vei putea surprinde rapid momentul fără a schimba modul cache, fie hardware-ul tău își va consuma complet întreaga resursă în cateva zile. Din cauza limitărilor de resurse, sistemul este, în principiu, incapabil să fie sub 100% încărcare de scriere tot timpul. SSD-urile noastre NVMe sub sarcina de scriere de 100% vor epuiza complet resursele zile 3-4. SSD-urile SATA vor dura doar de două ori mai mult. Prin urmare, vom presupune că cea mai mare parte a încărcăturii se duce la citire și avem explozii de activitate extrem de mare pe termen relativ scurt, combinate cu o sarcină scăzută în medie pentru scriere.
  4. De îndată ce am prins (sau am făcut) un zero, redenumim <name>.cached în <name>.committed. Vechiul <nume>.committed este șters.
  5. Opțional, dacă memoria cache este plină 100%, aceasta poate fi recreată printr-un script, ștergându-l astfel. Cu un cache pe jumătate gol, sistemul funcționează mult mai rapid la scriere.
  6. Setați pragul de migrare la zero: #lvchange --quiet --cachesettings "migration_threshold=0" cache/cachedata Acest lucru va împiedica temporar sincronizarea cache-ului cu media principală.
  7. Așteptăm până când se acumulează destul de multe modificări în cache #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' sau cronometrul se va opri.
  8. Repetăm ​​din nou.

De ce dificultăți cu pragul de migrație...?Chestia este că, în practică reală, o înregistrare „aleatorie” nu este de fapt complet aleatorie. Dacă am scris ceva într-un sector de 4 kiloocteți, există o mare probabilitate ca în următoarele două minute să se înregistreze același sector sau unul dintre sectoarele vecine (+- 32K).

Setând pragul de migrare la zero, amânăm sincronizarea scrierii pe SSD-ul SATA și cumulăm mai multe modificări la un bloc de 64K din cache. Acest lucru economisește semnificativ resursele SSD-ului SATA.

Unde este codul..?Din păcate, autorul se consideră insuficient de competent în dezvoltarea scripturilor bash deoarece este 100% autodidact și practică dezvoltarea condusă de „google”, prin urmare crede că codul teribil care iese din mâinile lui nu ar trebui să fie folosit de nimeni. altfel.

Cred că profesioniștii din acest domeniu vor putea să descrie în mod independent toată logica descrisă mai sus, dacă este necesar, și, poate, chiar să o proiecteze frumos ca un serviciu systemd, așa cum a încercat să facă autorul.

O astfel de schemă simplă de rotație a instantaneelor ​​ne va permite nu numai să avem în mod constant un instantaneu complet sincronizat pe SSD-ul SATA, dar ne va permite și, folosind utilitarul thin_delta, să aflăm ce blocuri au fost schimbate după crearea sa și, astfel, să localizăm daunele pe volumele principale, simplificând foarte mult recuperarea .

TRIM/DISCARD în libvirt/KVM

Deoarece stocarea datelor va fi folosită pentru KVM care rulează libvirt, atunci ar fi o idee bună să învățăm VM-urile noastre nu numai să ocupe spațiu liber, ci și să elibereze ceea ce nu mai este necesar.

Acest lucru se face prin emularea suportului TRIM/DISCARD pe discurile virtuale. Pentru a face acest lucru, trebuie să schimbați tipul controlerului în virtio-scsi și să editați fișierul 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>

Astfel de DISCARD de la sistemele de operare invitate sunt procesate corect de LVM, iar blocurile sunt eliberate corect atât în ​​cache, cât și în pool-ul subțire. În cazul nostru, acest lucru se întâmplă în principal într-o manieră întârziată, atunci când ștergeți următorul instantaneu.

BTRFS Backup

Utilizați scripturi gata făcute cu extrem prudență și pe propriul risc. Autorul a scris acest cod el însuși și exclusiv pentru el însuși. Sunt sigur că mulți utilizatori Linux experimentați au instrumente similare și nu este nevoie să le copiați pe ale altcuiva.

Să creăm un volum pe dispozitivul de rezervă:

#lvcreate -L 256G --name backup backup

Să-l formatăm în BTRFS:

#mkfs.btrfs /dev/backup/backup

Să creăm puncte de montare și să montăm subsecțiunile rădăcină ale sistemului de fișiere:

#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

Să creăm directoare pentru copii de rezervă:

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

Să creăm un director pentru scripturile de rezervă:

#mkdir /root/btrfs-backup

Să copiem scriptul:

O mulțime de cod bash înfricoșător. Folosiți pe propria răspundere. Nu scrie scrisori supărate autorului...#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

Ce face chiar..?Conține un set de comenzi simple pentru crearea de instantanee BTRFS și copierea lor într-un alt FS folosind trimitere/primire BTRFS.

Prima lansare poate fi relativ lungă, pentru că... La început, toate datele vor fi copiate. Lansările ulterioare vor fi foarte rapide, deoarece... Doar modificările vor fi copiate.

Un alt script pe care îl vom pune în cron:

Mai mult cod 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

Ce face..?Creează și sincronizează instantanee incrementale ale volumelor BTRFS listate pe FS de rezervă. După aceasta, șterge toate imaginile create acum 60 de zile. După lansare, în subdirectoarele /backup/btrfs/back/remote/ vor apărea instantanee datate ale volumelor listate.

Să dăm drepturi de executare a codului:

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

Să-l verificăm și să-l punem în 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

Backup subțire LVM

Să creăm un pool subțire pe dispozitivul de rezervă:

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

Hai să instalăm ddrescue, pentru că... scripturile vor folosi acest instrument:

#apt-get install gddrescue

Să creăm un director pentru scripturi:

#mkdir /root/lvm-thin-backup

Să copiem scripturile:

Multă bătaie înăuntru...#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

Ce face...?Conține un set de comenzi pentru manipularea instantaneelor ​​subțiri și sincronizarea diferenței dintre două instantanee subțiri primite prin thin_delta la un alt dispozitiv bloc folosind ddrescue și blkdiscard.

Un alt script pe care îl vom pune în cron:

Mai multă bătaie#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

Ce face...?Utilizează scriptul anterior pentru a crea și sincroniza copii de rezervă ale volumelor subțiri enumerate. Scriptul va lăsa instantanee inactive ale volumelor listate, care sunt necesare pentru a urmări modificările de la ultima sincronizare.

Acest script trebuie editat, specificând lista de volume subțiri pentru care ar trebui făcute copii de rezervă. Numele date sunt doar cu titlu ilustrativ. Dacă doriți, puteți scrie un script care va sincroniza toate volumele.

Să dăm drepturile:

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

Să-l verificăm și să-l punem în 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

Prima lansare va fi lungă, pentru că... volumele subțiri vor fi complet sincronizate prin copierea întregului spațiu utilizat. Datorită metadatelor subțiri LVM, știm ce blocuri sunt de fapt utilizate, așa că vor fi copiate numai blocurile de volum subțire utilizate efectiv.

Execuțiile ulterioare vor copia datele în mod incremental datorită urmăririi modificărilor prin metadatele subțiri LVM.

Să vedem ce s-a întâmplat:

#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

Ce legătură are asta cu păpușile de cuib?

Cel mai probabil, având în vedere că volumele logice LVM LV pot fi volume fizice LVM PV pentru alte VG-uri. LVM poate fi recursiv, precum păpușile de cuib. Acest lucru oferă LVM o flexibilitate extremă.

PS

În articolul următor, vom încerca să folosim mai multe sisteme similare de stocare mobilă/KVM ca bază pentru crearea unui cluster de stocare/vm geo-distribuit cu redundanță pe mai multe continente folosind desktop-uri de acasă, internetul de acasă și rețelele P2P.

Sursa: www.habr.com

Adauga un comentariu