Cosa hanno in comune LVM e Matrioska?

Buongiorno.
Vorrei condividere con la comunità la mia esperienza pratica nella creazione di un sistema di archiviazione dati per KVM utilizzando md RAID + LVM.

Il programma includerà:

  • Creazione di md RAID 1 da SSD NVMe.
  • Assemblaggio di md RAID 6 da SSD SATA e unità normali.
  • Caratteristiche dell'operazione TRIM/DISCARD su SSD RAID 1/6.
  • Creazione di un array md RAID 1/6 avviabile su un set comune di dischi.
  • Installazione del sistema su NVMe RAID 1 quando non è presente il supporto NVMe nel BIOS.
  • Utilizzando la cache LVM e LVM thin.
  • Utilizzo di snapshot BTRFS e invio/ricezione per il backup.
  • Utilizzo di thin snapshot LVM e thin_delta per backup in stile BTRFS.

Se sei interessato, guarda il gatto.

dichiarazione

L'autore non si assume alcuna responsabilità per le conseguenze derivanti dall'utilizzo o dal mancato utilizzo di materiali/esempi/codici/suggerimenti/dati contenuti in questo articolo. Leggendo o utilizzando questo materiale in qualsiasi modo, ti assumi la responsabilità di tutte le conseguenze di queste azioni. Le possibili conseguenze includono:

  • SSD NVMe croccanti.
  • Risorse di registrazione completamente esaurite e guasto delle unità SSD.
  • Perdita completa di tutti i dati su tutte le unità, comprese le copie di backup.
  • Hardware del computer difettoso.
  • Tempo sprecato, nervi e denaro.
  • Qualsiasi altra conseguenza non elencata sopra.

ferro

Erano disponibili:

Scheda madre del 2013 circa con chipset Z87, completa di Intel Core i7 / Haswell.

  • Processore 4 core, 8 thread
  • RAM DDR32 da 3GB
  • 1x16 o 2x8 PCIe 3.0
  • 1x4 + 1x1 PCIe 2.0
  • 6 connettori SATA 6 da 3 Gbps

L'adattatore SAS LSI SAS9211-8I è passato alla modalità IT / HBA. Il firmware abilitato per RAID è stato intenzionalmente sostituito con il firmware HBA per:

  1. Potresti buttare via questo adattatore in qualsiasi momento e sostituirlo con qualsiasi altro tu abbia incontrato.
  2. TRIM/Discard ha funzionato normalmente sui dischi, perché... nel firmware RAID questi comandi non sono affatto supportati e all'HBA, in generale, non interessa quali comandi vengono trasmessi sul bus.

Dischi rigidi: 8 pezzi di HGST Travelstar 7K1000 con una capacità di 1 TB in un fattore di forma 2.5, come per i laptop. Queste unità erano precedentemente in un array RAID 6. Avranno un utilizzo anche nel nuovo sistema. Per archiviare i backup locali.

Aggiunto inoltre:

SSD SATA da 6 pezzi modello Samsung 860 QVO 2TB. Questi SSD richiedevano un volume elevato, si desiderava la presenza di una cache SLC, affidabilità e un prezzo basso. Era richiesto il supporto per scarto/zero, che viene controllato dalla riga in dmesg:

kernel: ata1.00: Enabling discard_zeroes_data

2 pezzi di SSD NVMe modello Samsung SSD 970 EVO 500GB.

Per questi SSD, la velocità di lettura/scrittura casuale e la capacità delle risorse per le tue esigenze sono importanti. Radiatore per loro. Necessariamente. Assolutamente. Altrimenti friggeteli fino a renderli croccanti durante la prima sincronizzazione RAID.

Adattatore StarTech PEX8M2E2 per 2 SSD NVMe installati nello slot PCIe 3.0 8x. Questo, ancora una volta, è solo un HBA, ma per NVMe. Si differenzia dagli adattatori economici in quanto non richiede il supporto della biforcazione PCIe dalla scheda madre a causa della presenza di uno switch PCIe integrato. Funzionerà anche nel sistema più vecchio con PCIe, anche se si tratta di uno slot x1 PCIe 1.0. Naturalmente, alla velocità appropriata. Non ci sono RAID lì. Non è presente un BIOS integrato a bordo. Quindi, il tuo sistema non imparerà magicamente ad avviarsi con NVMe, tanto meno a fare RAID NVMe grazie a questo dispositivo.

Questo componente è dovuto esclusivamente alla presenza di un solo 8x PCIe 3.0 libero nel sistema e, se ci sono 2 slot liberi, può essere facilmente sostituito con due penny PEX4M2E1 o analoghi, che possono essere acquistati ovunque al prezzo di 600 rubli.

Il rifiuto di tutti i tipi di hardware o chipset/BIOS RAID integrati è stato fatto deliberatamente, per poter sostituire completamente l'intero sistema, ad eccezione degli stessi SSD/HDD, preservando tutti i dati. Idealmente, in modo da poter salvare anche il sistema operativo installato quando si passa a un hardware completamente nuovo/diverso. La cosa principale è che ci sono porte SATA e PCIe. È come un CD live o un'unità flash avviabile, solo molto veloce e un po' ingombrante.

UmorismoAltrimenti, sai cosa succede: a volte hai urgentemente bisogno di portare con te l'intero array da portare via. Ma non voglio perdere dati. Per fare ciò, tutti i supporti menzionati sono comodamente posizionati sulle guide negli alloggiamenti da 5.25 del case standard.

Bene, e naturalmente, per sperimentare diversi metodi di caching SSD in Linux.

I raid hardware sono noiosi. Accendilo. O funziona oppure no. E con mdadm ci sono sempre delle opzioni.

morbido

In precedenza, l'hardware è stato installato Debian 8 Jessie, che è vicino alla fine del ciclo di vita. È stato creato un RAID 6 utilizzando gli HDD menzionati in precedenza abbinati a LVM. Era in esecuzione macchine virtuali in kvm/libvirt.

Poiché l'autore ha una rilevante esperienza nella creazione di unità flash SATA/NVMe avviabili portatili, e anche per non rompere il solito modello apt, è stato scelto il sistema di destinazione Ubuntu 18.04 che si è già stabilizzato a sufficienza, ma ha ancora 3 anni di supporto in prospettiva.

Il sistema menzionato contiene tutti i driver hardware di cui abbiamo bisogno e pronti all'uso. Non abbiamo bisogno di software o driver di terze parti.

Preparazione per l'installazione

Per installare il sistema avremo bisogno Ubuntu Immagine del desktop. Il sistema server ha una sorta di programma di installazione basato sul kernel che mostra un'autonomia eccessiva e non disabilitante, inserendo sempre la partizione di sistema UEFI su una delle unità, rovinando tutto. Di conseguenza, si installa solo in modalità UEFI. Non offre alcuna opzione.

Non siamo contenti di questo.

Perché?Sfortunatamente, l'avvio UEFI è estremamente poco compatibile con il software di avvio RAID, perché... Nessuno ci offre prenotazioni per la partizione UEFI ESP. Esistono ricette su Internet che suggeriscono di posizionare la partizione ESP su un'unità flash in una porta USB, ma questo è un punto di fallimento. Esistono ricette che utilizzano il software mdadm RAID 1 con metadati versione 0.9 che non impediscono al BIOS UEFI di vedere questa partizione, ma questo sopravvive fino al momento felice in cui il BIOS o un altro sistema operativo hardware scrive qualcosa sull'ESP e si dimentica di sincronizzarlo con altri specchi.

Inoltre, l'avvio UEFI dipende dalla NVRAM, che non si sposterà insieme ai dischi nel nuovo sistema, perché fa parte della scheda madre.

Quindi non reinventeremo una nuova ruota. Abbiamo già una bici del nonno già pronta e collaudata nel tempo, ora chiamata Legacy/BIOS boot, che porta l'orgoglioso nome di CSM sui sistemi compatibili con UEFI. Lo toglieremo semplicemente dallo scaffale, lo lubrificheremo, gonfieremo le gomme e lo puliremo con un panno umido.

Versione desktop Ubuntu Inoltre, non si installa correttamente con il bootloader Legacy, ma almeno ci sono delle alternative, come si suol dire.

Quindi, assembliamo l'hardware e avviamo il sistema da un'unità flash avviabile. Ubuntu In diretta. Dovremo scaricare dei pacchetti, quindi configuriamo la rete che hai a disposizione. Se non funziona, puoi scaricare i pacchetti necessari su una chiavetta USB in anticipo.

Entriamo nell'ambiente Desktop, lanciamo l'emulatore di terminale e partiamo:

#sudo bash

Come…?La riga sopra è l'innesco canonico per gli holiwar su sudo. C bоarrivano maggiori opportunità eоmaggiore responsabilità. La domanda è se puoi affrontarlo da solo. Molte persone pensano che usare sudo in questo modo almeno non sia prudente. Tuttavia:

Guarda il video

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

Perché non ZFS...?Quando installiamo un software sul nostro computer, essenzialmente prestiamo il nostro hardware agli sviluppatori di questo software per guidarlo.
Quando affidiamo a questo software la sicurezza dei nostri dati, prendiamo un prestito pari al costo del ripristino di questi dati, che un giorno dovremo ripagare.

Da questo punto di vista ZFS è una Ferrari, mentre mdadm+lvm è più simile a una bicicletta.

Soggettivamente l'autore preferisce prestare una moto a credito a sconosciuti anziché una Ferrari. Lì il prezzo della questione non è alto. Non c'è bisogno di diritti. Più semplice delle regole del traffico. Il parcheggio è gratuito. La capacità di attraversare il paese è migliore. Puoi sempre attaccare le gambe a una bicicletta e puoi ripararla con le tue mani.

Perché allora BTRFS...?Per avviare il sistema operativo, abbiamo bisogno di un file system che sia supportato in Legacy/BIOS GRUB e che allo stesso tempo supporti le istantanee live. Lo useremo per la partizione /boot. Inoltre, l'autore preferisce utilizzare questo FS per / (root), senza dimenticare di notare che per qualsiasi altro software è possibile creare partizioni separate su LVM e montarle nelle directory necessarie.

Nessuna delle due immagini macchine virtuali, non memorizzeremo alcun database su questo file system.
Questo FS verrà utilizzato solo per creare istantanee del sistema senza spegnerlo e quindi trasferire queste istantanee su un disco di backup utilizzando invio/ricezione.

Inoltre, l'autore generalmente preferisce mantenere un minimo di software direttamente sull'hardware ed eseguire tutto il resto del software su macchine virtuali utilizzando cose come l'inoltro di GPU e controller host PCI-USB a KVM tramite IOMMU.

Le uniche cose rimaste sull'hardware sono l'archiviazione dei dati, la virtualizzazione e il backup.

Se ti fidi di più di ZFS, in linea di principio sono intercambiabili per l'applicazione specificata.

Tuttavia, l'autore ignora deliberatamente le funzionalità di mirroring/RAID integrate e di ridondanza di ZFS, BRTFS e LVM.

Come ulteriore argomento, BTRFS ha la capacità di trasformare le scritture casuali in sequenziali, il che ha un effetto estremamente positivo sulla velocità di sincronizzazione di istantanee/backup sull'HDD.

Eseguiamo nuovamente la scansione di tutti i dispositivi:

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

Diamo un'occhiata in giro:

#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

Disposizione del disco

NVMe SSD

Ma non li contrassegneremo in alcun modo. Tuttavia, il nostro BIOS non vede queste unità. Quindi passeranno interamente al RAID software. Non creeremo nemmeno sezioni lì. Se vuoi seguire il "canone" o "principalmente", crea una partizione grande, come un HDD.

SATA HDD

Non è necessario inventare nulla di speciale qui. Creeremo una sezione per tutto. Creeremo una partizione perché il BIOS vede questi dischi e potrebbe anche provare ad avviarsi da essi. Successivamente installeremo GRUB su questi dischi in modo che il sistema possa farlo all'improvviso.

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

/dev/sdg1: inizio= 2048, dimensione= 1953523120, tipo=fd, avviabile
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

È qui che le cose si fanno interessanti per noi.

Innanzitutto, le nostre unità hanno una dimensione di 2 TB. Questo rientra nell'intervallo accettabile per l'MBR, che è quello che utilizzeremo. Se necessario, può essere sostituito con GPT. I dischi GPT hanno un livello di compatibilità che consente ai sistemi compatibili con MBR di vedere le prime 4 partizioni se si trovano entro i primi 2 terabyte. La cosa principale è che la partizione di avvio e la partizione bios_grub su questi dischi dovrebbero essere all'inizio. Ciò ti consente anche di eseguire l'avvio da unità GPT Legacy/BIOS.

Ma questo non è il nostro caso.

Qui creeremo due sezioni. Il primo avrà una dimensione di 1 GB e verrà utilizzato per RAID 1 /boot.

Il secondo verrà utilizzato per RAID 6 e occuperà tutto lo spazio libero rimanente tranne una piccola area non allocata alla fine del disco.

Cos'è quest'area non contrassegnata?Secondo fonti in rete, i nostri SSD SATA hanno a bordo una cache SLC espandibile dinamicamente di dimensioni comprese tra 6 e 78 gigabyte. Otteniamo 6 gigabyte “gratuitamente” a causa della differenza tra “gigabyte” e “gibibyte” nella scheda tecnica dell'unità. I restanti 72 gigabyte vengono allocati dallo spazio inutilizzato.

Qui va notato che abbiamo una cache SLC e lo spazio è occupato in modalità MLC a 4 bit. Ciò per noi significa effettivamente che per ogni 4 gigabyte di spazio libero otterremo solo 1 gigabyte di cache SLC.

Moltiplica 72 gigabyte per 4 e ottieni 288 gigabyte. Questo è lo spazio libero che non delegheremo per consentire alle unità di sfruttare appieno la cache SLC.

Pertanto, otterremo effettivamente fino a 312 gigabyte di cache SLC da sei unità in totale. Di tutte le unità, 2 verranno utilizzate in RAID per la ridondanza.

Questa quantità di cache ci consentirà estremamente raramente nella vita reale di incontrare una situazione in cui una scrittura non va alla cache. Ciò compensa molto bene lo svantaggio più triste della memoria QLC: la velocità di scrittura estremamente bassa quando i dati vengono scritti bypassando la cache. Se i tuoi carichi non corrispondono a questo, ti consiglio di riflettere attentamente su quanto tempo durerà il tuo SSD con un tale carico, tenendo conto del TBW dalla scheda tecnica.

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

/dev/sda1: inizio= 2048, dimensione= 2097152, tipo=fd, avviabile
/dev/sda2: inizio= 2099200, dimensione= 3300950016, tipo=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

Creazione di array

Per prima cosa dobbiamo rinominare la macchina. Ciò è necessario perché il nome host fa parte del nome dell'array da qualche parte all'interno di mdadm e influisce su qualcosa da qualche parte. Naturalmente, gli array possono essere rinominati in seguito, ma questo è un passaggio non necessario.

#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

Perché -assumere-pulito...?Per evitare di inizializzare gli array. Ciò è valido per entrambi i livelli RAID 1 e 6. Tutto può funzionare senza inizializzazione se si tratta di un nuovo array. Inoltre, inizializzare l'array SSD al momento della creazione è uno spreco di risorse TBW. Utilizziamo TRIM/DISCARD ove possibile sugli array SSD assemblati per "inizializzarli".

Per gli array SSD, RAID 1 DISCARD è supportato immediatamente.

Per gli array SSD RAID 6 DISCARD, è necessario abilitarlo nei parametri del modulo kernel.

Questa operazione dovrebbe essere eseguita solo se tutti gli SSD utilizzati negli array di livello 4/5/6 in questo sistema dispongono del supporto funzionante per scarget_zeroes_data. A volte ti imbatti in strani drive che dicono al kernel che questa funzione è supportata, ma in realtà non c'è, oppure la funzione non sempre funziona. Al momento il supporto è disponibile quasi ovunque, tuttavia ci sono vecchie unità e firmware con errori. Per questo motivo, il supporto DISCARD è disabilitato per impostazione predefinita per RAID 6.

Attenzione, il seguente comando distruggerà tutti i dati sulle unità NVMe “inizializzando” l'array con “zero”.

#blkdiscard /dev/md0

Se qualcosa va storto, prova a specificare un passaggio.

#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

Perché così grande...?L'aumento della dimensione del blocco ha un effetto positivo sulla velocità di lettura casuale in blocchi fino alla dimensione del blocco inclusa. Ciò accade perché un'operazione di dimensioni adeguate o inferiori può essere completata interamente su un singolo dispositivo. Pertanto, vengono sommati gli IOPS di tutti i dispositivi. Secondo le statistiche, il 99% dell'IO non supera i 512K.

Il RAID ha 6 IOPS per scrittura sempre inferiore o uguale agli IOPS di un'unità. Quando, come lettura casuale, gli IOPS possono essere molte volte maggiori di quelli di un'unità, e qui la dimensione del blocco è di fondamentale importanza.
L'autore non vede il motivo di cercare di ottimizzare un parametro che non è corretto in RAID 6 in base alla progettazione e ottimizza invece ciò in cui RAID 6 è bravo.
Compenseremo la scarsa scrittura casuale di RAID 6 con una cache NVMe e trucchi di thin-provisioning.

Non abbiamo ancora abilitato DISCARD per RAID 6. Quindi per ora non "inizializzeremo" questo array. Lo faremo più tardi, dopo aver installato il sistema operativo.

SATA HDD

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

LVM su RAID NVMe

Per velocità, vogliamo posizionare il file system root su NVMe RAID 1 che è /dev/md0.
Tuttavia, avremo ancora bisogno di questo array veloce per altre esigenze, come swap, metadati, cache LVM e metadati LVM-thin, quindi creeremo un VG LVM su questo array.

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

Creiamo una partizione per il file system root.

#lvcreate -L 128G --name root root

Creiamo una partizione per lo swap in base alla dimensione della RAM.

#lvcreate -L 32G --name swap root

Installazione del sistema operativo

In totale, abbiamo tutto il necessario per installare il sistema.

Avviare la procedura guidata di installazione del sistema dall'ambiente Ubuntu Live. Installazione standard. Solo nella fase di selezione del disco è necessario specificare quanto segue:

  • /dev/md1, - punto di montaggio /boot, FS - BTRFS
  • /dev/root/root (noto anche come /dev/mapper/root-root), - punto di montaggio / (root), FS - BTRFS
  • /dev/root/swap (noto anche come /dev/mapper/root-swap), - utilizzare come partizione di swap
  • Installa il bootloader su /dev/sda

Quando selezioni BTRFS come file system root, il programma di installazione creerà automaticamente due volumi BTRFS denominati "@" per / (root) e "@home" per /home.

Iniziamo l'installazione...

L'installazione terminerà con una finestra di dialogo modale che ti informerà di un errore durante l'installazione del bootloader. Purtroppo, non è possibile chiudere questa finestra di dialogo utilizzando gli strumenti predefiniti e continuare l'installazione. Disconnettiti dal sistema e accedi nuovamente, tornando a un desktop pulito. Ubuntu In diretta. Apri il terminale e di nuovo:

#sudo bash

Crea un ambiente chroot per continuare l'installazione:

#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

Configuriamo la rete e il nome host in chroot:

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

Andiamo nell'ambiente chroot:

#chroot /mnt/chroot

Innanzitutto consegneremo i pacchi:

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

Controlliamo e sistemiamo tutti i pacchetti che sono stati installati in modo storto a causa di un'installazione incompleta del sistema:

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

Se qualcosa non funziona, potrebbe essere necessario prima modificare /etc/apt/sources.list

Regoliamo i parametri del modulo RAID 6 per abilitare TRIM/DISCARD:

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

Modifichiamo un po' i nostri array:

#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

Cos'era..?Abbiamo creato una serie di regole udev che faranno quanto segue:

  • Imposta la dimensione della cache a blocchi RAID 6 su un valore appropriato per il 2020. Il valore predefinito sembra essere rimasto invariato dalla sua creazione. Linuxe da tempo non è più adeguato.
  • Riservare un minimo di I/O per la durata dei controlli/sincronizzazione degli array. Questo per evitare che i tuoi array rimangano bloccati in uno stato di eterna sincronizzazione sotto carico.
  • Limita l'IO massimo durante i controlli/sincronizzazione degli array. Ciò è necessario affinché la sincronizzazione/controllo dei RAID SSD non rovini le tue unità. Ciò è particolarmente vero per NVMe. (Ricordi il radiatore? Non stavo scherzando.)
  • Impedisci ai dischi di interrompere la rotazione del mandrino (HDD) tramite APM e imposta il timeout di sospensione per i controller del disco su 7 ore. Puoi disabilitare completamente l'APM se le tue unità possono farlo (-B 255). Con il valore predefinito, gli azionamenti si fermeranno dopo cinque secondi. Quindi il sistema operativo vuole reimpostare la cache del disco, i dischi gireranno di nuovo e tutto ricomincerà da capo. I dischi hanno un numero massimo limitato di rotazioni del mandrino. Un ciclo predefinito così semplice può facilmente uccidere i tuoi dischi in un paio d'anni. Non tutti i dischi ne soffrono, ma i nostri sono “laptop”, con le opportune impostazioni predefinite, che fanno sembrare il RAID un mini-MAID.
  • Installa readahead su dischi (rotanti) da 1 megabyte - due blocchi/chunk consecutivi RAID 6
  • Disabilita il readahead sugli array stessi.

Modifichiamo /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

Perché..?Cercheremo la partizione /boot in base all'UUID. La denominazione degli array potrebbe teoricamente cambiare.

Cercheremo le restanti sezioni in base ai nomi LVM nella notazione /dev/mapper/vg-lv, perché identificano le partizioni in modo abbastanza univoco.

Non utilizziamo l'UUID per LVM perché L'UUID dei volumi LVM e le relative istantanee possono essere gli stessi.Montare /dev/mapper/root-root... due volte?SÌ. Esattamente. Caratteristica di BTRFS. Questo file system può essere montato più volte con diversi sottovolti.

A causa di questa stessa funzionalità, consiglio di non creare mai istantanee LVM di volumi BTRFS attivi. Potresti ricevere una sorpresa al riavvio.

Rigeneriamo la configurazione di mdadm:

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

Regoliamo le impostazioni LVM:

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

attivazione {
thin_pool_autoextend_threshold=90
thin_pool_autoextend_percent=5
}
allocazione {
cache_pool_max_chunks=2097152
}
dispositivi {
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

Cos'era..?Abbiamo abilitato l'espansione automatica dei thin pool LVM al raggiungimento del 90% dello spazio occupato del 5% del volume.

Abbiamo aumentato il numero massimo di blocchi di cache per la cache LVM.

Abbiamo impedito a LVM di cercare volumi LVM (PV) su:

  • dispositivi contenenti cache LVM (cdata)
  • dispositivi memorizzati nella cache utilizzando la cache LVM, ignorando la cache ( _corig). In questo caso, il dispositivo memorizzato nella cache verrà comunque scansionato attraverso la cache (solo ).
  • dispositivi contenenti metadati della cache LVM (cmeta)
  • tutti i dispositivi in ​​VG con il nome immagini. Qui avremo immagini disco di macchine virtuali e non vogliamo che LVM sull'host attivi i volumi appartenenti al sistema operativo guest.
  • tutti i dispositivi in ​​VG con il nome backup. Qui avremo copie di backup delle immagini della macchina virtuale.
  • tutti i dispositivi il cui nome termina con “gpv” (volume fisico guest)

Abbiamo abilitato il supporto DISCARD quando si libera spazio libero su LVM VG. Stai attento. Ciò renderà l'eliminazione dei LV sull'SSD piuttosto dispendiosa in termini di tempo. Ciò vale soprattutto per SSD RAID 6. Tuttavia, secondo il piano, utilizzeremo il thin provisioning, quindi questo non ci ostacolerà affatto.

Aggiorniamo l'immagine initramfs:

#update-initramfs -u -k all

Installa e configura grub:

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

Quali dischi scegliere?Tutti coloro che sono SD*. Il sistema deve essere in grado di avviarsi da qualsiasi unità SATA o SSD funzionante.

Perché hanno aggiunto os-prober...?Per un'eccessiva indipendenza e mani giocose.

Non funziona correttamente se uno dei RAID è in uno stato degradato. Tenta di cercare il sistema operativo sulle partizioni utilizzate nelle macchine virtuali in esecuzione su questo hardware.

Se ne hai bisogno, puoi lasciarlo, ma tieni presente tutto quanto sopra. Consiglio di cercare ricette online per sbarazzarsi delle mani cattive.

Con questo abbiamo completato l'installazione iniziale. È ora di riavviare il sistema operativo appena installato. Non dimenticare di rimuovere il Live CD/USB avviabile.

#exit
#reboot

Seleziona uno qualsiasi degli SSD SATA come dispositivo di avvio.

LVM su SSD SATA

A questo punto, abbiamo già avviato il nuovo sistema operativo, configurato la rete, apt, aperto l'emulatore di terminale ed eseguito:

#sudo bash

Noi continuiamo

"Inizializza" l'array da SSD SATA:

#blkdiscard /dev/md2

Se non funziona, prova:

#blkdiscard --step 65536 /dev/md2
Crea LVM VG su SSD SATA:

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

Perché un altro VG...?In effetti, abbiamo già un VG chiamato root. Perché non aggiungere tutto in un VG?

Se sono presenti più PV in un VG, affinché il VG venga attivato correttamente è necessario che tutti i PV siano presenti (online). L'eccezione è LVM RAID, che deliberatamente non utilizziamo.

Vogliamo davvero che se si verifica un guasto (leggi perdita di dati) su uno qualsiasi degli array RAID 6, il sistema operativo si avvierà normalmente e ci darà l'opportunità di risolvere il problema.

Per fare ciò, al primo livello di astrazione isoleremo ciascun tipo di “media” fisico in un VG separato.

Dal punto di vista scientifico, diversi array RAID appartengono a diversi “domini di affidabilità”. Non dovresti creare un ulteriore punto comune di fallimento per loro stipandoli in un unico VG.

La presenza di LVM a livello “hardware” ci consentirà di tagliare arbitrariamente pezzi di diversi array RAID combinandoli in modi diversi. Ad esempio: corri contemporaneamente bcache + LVM thin, bcache + BTRFS, cache LVM + LVM thin, una configurazione ZFS complessa con cache o qualsiasi altra combinazione infernale per provare a confrontare il tutto.

A livello "hardware", non utilizzeremo altro che i buoni vecchi volumi LVM "spessi". L'eccezione a questa regola potrebbe essere la partizione di backup.

Penso che a questo punto molti lettori avessero già iniziato a sospettare qualcosa sulla bambola da nidificazione.

LVM su HDD SATA

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

Di nuovo nuovo VG..?Vogliamo davvero che, se l'array di dischi che utilizzeremo per il backup dei dati si guasta, il nostro sistema operativo continui a funzionare normalmente, pur mantenendo l'accesso ai dati non di backup come al solito. Pertanto, per evitare problemi di attivazione del VG, creiamo un VG separato.

Configurazione della cache LVM

Creiamo un LV su NVMe RAID 1 per utilizzarlo come dispositivo di caching.

#lvcreate -L 70871154688B --name cache root

Perché c'è così poco...?Il fatto è che i nostri SSD NVMe hanno anche una cache SLC. 4 gigabyte di “libero” e 18 gigabyte di dinamico grazie allo spazio libero occupato nell'MLC a 3 bit. Una volta esaurita questa cache, gli SSD NVMe non saranno molto più veloci dei nostri SSD SATA con cache. In realtà, per questo motivo, non ha senso per noi rendere la partizione della cache LVM molto più grande del doppio della dimensione della cache SLC dell'unità NVMe. Per le unità NVMe utilizzate, l'autore ritiene ragionevole creare 32-64 gigabyte di cache.

La dimensione della partizione specificata è necessaria per organizzare 64 gigabyte di cache, metadati della cache e backup dei metadati.

Inoltre, noto che dopo un arresto anomalo del sistema, LVM contrassegnerà l'intera cache come sporca e si sincronizzerà nuovamente. Inoltre, ciò verrà ripetuto ogni volta che lvchange viene utilizzato su questo dispositivo fino al riavvio successivo del sistema. Pertanto consiglio di ricreare immediatamente la cache utilizzando lo script appropriato.

Creiamo un LV su SATA RAID 6 per utilizzarlo come dispositivo memorizzato nella cache.

#lvcreate -L 3298543271936B --name cache data

Perché solo tre terabyte...?In modo che, se necessario, sia possibile utilizzare SATA SSD RAID 6 per altre esigenze. La dimensione dello spazio memorizzato nella cache può essere aumentata dinamicamente, al volo, senza arrestare il sistema. Per fare ciò, è necessario arrestare e riattivare temporaneamente la cache, ma il vantaggio distintivo di LVM-cache rispetto, ad esempio, a bcache è che ciò può essere fatto al volo.

Creiamo un nuovo VG per la memorizzazione nella cache.

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

Creiamo un LV sul dispositivo memorizzato nella cache.

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

Qui abbiamo subito occupato tutto lo spazio libero su /dev/data/cache in modo che tutte le altre partizioni necessarie siano state create immediatamente su /dev/root/cache. Se hai creato qualcosa nel posto sbagliato, puoi spostarlo usando pvmove.

Creiamo e abilitiamo la 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

Perché pezzi così grandi...?Attraverso esperimenti pratici, l'autore è riuscito a scoprire che il risultato migliore si ottiene se la dimensione del blocco della cache LVM coincide con la dimensione del thin block LVM. Inoltre, minore è la dimensione, migliore sarà la prestazione della configurazione in una registrazione casuale.

64k è la dimensione minima del blocco consentita per LVM thin.

Fai attenzione alla riscrittura..!SÌ. Questo tipo di cache rinvia la sincronizzazione in scrittura al dispositivo memorizzato nella cache. Ciò significa che se la cache viene persa, potresti perdere i dati sul dispositivo memorizzato nella cache. Successivamente l'autore vi dirà quali misure si possono adottare, oltre a NVMe RAID 1, per compensare questo rischio.

Questo tipo di cache è stato scelto intenzionalmente per compensare le scarse prestazioni di scrittura casuale di RAID 6.

Controlliamo cosa abbiamo ottenuto:

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

Solo [cachedata_corig] dovrebbe trovarsi in /dev/data/cache. Se qualcosa non va, usa pvmove.

Puoi disabilitare la cache se necessario con un comando:

#lvconvert -y --uncache cache/cachedata

Questo viene fatto on-line. LVM sincronizzerà semplicemente la cache sul disco, la rimuoverà e rinominerà cachedata_corig in cachedata.

Configurazione di LVM sottile

Stimiamo approssimativamente la quantità di spazio necessaria per i metadati thin 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"

Arrotondare per eccesso a 4 gigabyte: 4294967296B

Moltiplicare per due e aggiungere 4194304B per i metadati LVM PV: 8594128896B
Creiamo una partizione separata su NVMe RAID 1 per posizionare su di esso i metadati thin LVM e la relativa copia di backup:

#lvcreate -L 8594128896B --name images root

Per quello..?Qui potrebbe sorgere la domanda: perché posizionare i metadati thin LVM separatamente se verranno comunque memorizzati nella cache su NVMe e funzioneranno rapidamente.

Anche se qui la velocità è importante, non è la ragione principale. Il fatto è che la cache è un punto di fallimento. Potrebbe succedergli qualcosa e, se i metadati thin di LVM vengono memorizzati nella cache, ciò causerà la completa perdita di tutto. Senza metadati completi, sarà quasi impossibile assemblare piccoli volumi.

Spostando i metadati in un volume separato, non memorizzato nella cache, ma veloce, garantiamo la sicurezza dei metadati in caso di perdita o danneggiamento della cache. In questo caso, tutti i danni causati dalla perdita della cache saranno localizzati all'interno di volumi sottili, il che semplificherà la procedura di ripristino di diversi ordini di grandezza. Con un'alta probabilità, questi danni verranno ripristinati utilizzando i registri FS.

Inoltre, se in precedenza è stata eseguita un'istantanea del volume thin e successivamente la cache è stata completamente sincronizzata almeno una volta, a causa della progettazione interna di LVM thin, l'integrità dell'istantanea sarà garantita in caso di perdita della cache .

Creiamo un nuovo VG che sarà responsabile del thin-provisioning:

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

Creiamo una piscina:

#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T images/thin-pool
Perché -Z yOltre allo scopo effettivo di questa modalità, ovvero impedire che i dati di una macchina virtuale si diffondano su un'altra macchina virtuale durante la ridistribuzione dello spazio, l'azzeramento viene utilizzato anche per aumentare la velocità di scrittura casuale in blocchi inferiori a 64k. Qualsiasi scrittura inferiore a 64K su un'area precedentemente non allocata del volume thin verrà allineata ai bordi a 64K nella cache. Ciò consentirà di eseguire l'operazione interamente tramite la cache, ignorando il dispositivo memorizzato nella cache.

Spostiamo i LV nei PV corrispondenti:

#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

Dai un'occhiata:

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

Creiamo un volume sottile per i test:

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

Installeremo i pacchetti per test e monitoraggio:

#apt-get install sysstat fio

Ecco come puoi osservare il comportamento della nostra configurazione di archiviazione in tempo reale:

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

Ecco come possiamo testare la nostra configurazione:

#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

Accuratamente! Risorsa!Questo codice eseguirà 36 test diversi, ciascuno della durata di 4 secondi. La metà dei test sono destinati alla registrazione. Puoi registrare molto su NVMe in 4 secondi. Fino a 3 gigabyte al secondo. Pertanto, ogni esecuzione di test di scrittura può consumare fino a 216 gigabyte di risorse SSD.

Leggere e scrivere misti?SÌ. È opportuno eseguire i test di lettura e scrittura separatamente. Inoltre, è opportuno assicurarsi che tutte le cache siano sincronizzate in modo che una scrittura effettuata in precedenza non influisca sulla lettura.

I risultati varieranno notevolmente durante il primo avvio e quelli successivi man mano che la cache e il volume sottile si riempiono e anche a seconda che il sistema sia riuscito a sincronizzare le cache riempite durante l'ultimo avvio.

Tra l'altro consiglio di misurare la velocità su un volume sottile già pieno da cui è stata appena scattata un'istantanea. L'autore ha avuto l'opportunità di osservare come le scritture casuali accelerano bruscamente subito dopo aver creato il primo snapshot, soprattutto quando la cache non è ancora completamente piena. Ciò accade a causa della semantica di scrittura copy-on-write, dell'allineamento della cache e dei blocchi di volume thin e del fatto che una scrittura casuale su RAID 6 si trasforma in una lettura casuale da RAID 6 seguita da una scrittura nella cache. Nella nostra configurazione, la lettura casuale da RAID 6 è fino a 6 volte (il numero di SSD SATA nell'array) più veloce della scrittura. Perché i blocchi per CoW vengono allocati in sequenza da un pool sottile, quindi anche la registrazione, per la maggior parte, diventa sequenziale.

Entrambe queste funzionalità possono essere utilizzate a tuo vantaggio.

Memorizza nella cache istantanee “coerenti”.

Per ridurre il rischio di perdita di dati in caso di danneggiamento/perdita della cache, l'autore propone di introdurre la pratica della rotazione degli snapshot per garantirne l'integrità in questo caso.

Innanzitutto, poiché i metadati del volume sottile risiedono su un dispositivo non memorizzato nella cache, i metadati saranno coerenti e le possibili perdite verranno isolate all'interno dei blocchi di dati.

Il seguente ciclo di rotazione degli snapshot garantisce l'integrità dei dati all'interno degli snapshot in caso di perdita della cache:

  1. Per ogni volume thin con il nome <nome>, creare uno snapshot con il nome <nome>.cached
  2. Impostiamo la soglia di migrazione su un valore ragionevolmente alto: #lvchange --quiet --cachesettings "migration_threshold=16384" cache/cachedata
  3. Nel ciclo controlliamo il numero di blocchi sporchi nella cache: #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' finché non arriviamo a zero. Se lo zero manca per troppo tempo, può essere creato commutando temporaneamente la cache in modalità writethrough. Tuttavia, tenendo conto delle caratteristiche di velocità dei nostri array SSD SATA e NVMe, nonché della loro risorsa TBW, sarai in grado di cogliere rapidamente l'attimo senza modificare la modalità cache, oppure il tuo hardware consumerà completamente l'intera risorsa in pochi giorni. A causa delle limitazioni delle risorse, il sistema, in linea di principio, non può essere sempre sotto il 100% del carico di scrittura. I nostri SSD NVMe con un carico di scrittura del 100% esauriranno completamente la risorsa 3-4 giorni. Gli SSD SATA dureranno solo il doppio del tempo. Pertanto, supponiamo che la maggior parte del carico sia destinata alla lettura e che abbiamo esplosioni di attività estremamente elevata a breve termine combinate con un carico medio basso per la scrittura.
  4. Non appena catturiamo (o realizziamo) uno zero, rinominiamo <name>.cached in <name>.comtched. Il vecchio <nome>.comtched viene eliminato.
  5. Facoltativamente, se la cache è piena al 100%, può essere ricreata da uno script, cancellandola. Con una cache semivuota, il sistema funziona molto più velocemente durante la scrittura.
  6. Imposta la soglia di migrazione su zero: #lvchange --quiet --cachesettings "migration_threshold=0" cache/cachedata Ciò impedirà temporaneamente la sincronizzazione della cache con il supporto principale.
  7. Aspettiamo che nella cache si accumulino parecchie modifiche #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' oppure il timer si spegnerà.
  8. Ripetiamo ancora.

Perché difficoltà con la soglia di migrazione...?Il fatto è che nella pratica reale una registrazione “casuale” non è del tutto casuale. Se scriviamo qualcosa su un settore di 4 kilobyte, c'è un'alta probabilità che nei prossimi due minuti verrà effettuata una registrazione sullo stesso o su uno dei settori adiacenti (+- 32K).

Impostando la soglia di migrazione su zero, posticipiamo la sincronizzazione in scrittura sull'SSD SATA e aggreghiamo diverse modifiche a un blocco da 64 KB nella cache. Ciò consente di risparmiare in modo significativo la risorsa dell'SSD SATA.

Dov'è il codice...?Purtroppo l'autore si ritiene non sufficientemente competente nello sviluppo di script bash perché è autodidatta al 100% e pratica lo sviluppo guidato da "google", pertanto ritiene che il terribile codice che gli esce dalle mani non dovrebbe essere utilizzato da nessuno altro.

Penso che i professionisti in questo campo saranno in grado di rappresentare autonomamente tutta la logica sopra descritta, se necessario, e, forse, anche di progettarla magnificamente come un servizio systemd, come ha cercato di fare l'autore.

Uno schema di rotazione degli snapshot così semplice ci consentirà non solo di avere costantemente uno snapshot completamente sincronizzato sull'SSD SATA, ma ci consentirà anche, utilizzando l'utilità thin_delta, di scoprire quali blocchi sono stati modificati dopo la sua creazione e quindi localizzare il danno su i volumi principali, semplificando notevolmente il ripristino.

TAGLIA/SCARTA in libvirt/KVM

Perché l'archiviazione dei dati verrà utilizzata per KVM che esegue libvirt, quindi sarebbe una buona idea insegnare alle nostre VM non solo a occupare spazio libero, ma anche a liberare quello che non è più necessario.

Questo viene fatto emulando il supporto TRIM/DISCARD sui dischi virtuali. Per fare ciò, è necessario cambiare il tipo di controller in virtio-scsi e modificare il file 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' modello='virtio-scsi'>




Tali DISCARD dai sistemi operativi guest vengono elaborati correttamente da LVM e i blocchi vengono liberati correttamente sia nella cache che nel thin pool. Nel nostro caso, ciò avviene principalmente in modo ritardato, quando si elimina lo snapshot successivo.

Backup BTRFS

Utilizza script già pronti con estremo cautela e a proprio rischioL'autore ha scritto questo codice da solo ed esclusivamente per sé stesso. Sono sicuro che molti utenti esperti Linux Esistono soluzioni e sviluppi simili, e non c'è bisogno di copiare gli altri.

Creiamo un volume sul dispositivo di backup:

#lvcreate -L 256G --name backup backup

Formattiamolo in BTRFS:

#mkfs.btrfs /dev/backup/backup

Creiamo punti di montaggio e montiamo le sottosezioni root del file system:

#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
#aggiornamento-grub

Creiamo le directory per i backup:

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

Creiamo una directory per gli script di backup:

#mkdir /root/btrfs-backup

Copiamo lo script:

Un sacco di codice bash spaventoso. Utilizzare a proprio rischio. Non scrivere lettere arrabbiate all'autore...#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="$(percorso reale $0)"
SCRIPT_DIR="$(nome_dir $SCRIPT_FILE)"
SCRIPT_NAME="$(nomebase -s .sh $SCRIPT_FILE)"

LOCK_FILE="/dev/shm/$SCRIPT_NAME.lock"
DATE_PREFIX='%Y-%m-%d'
FORMATO_DATA=$PREFISSO_DATA'-%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/"
BACKUP="/backup/btrfs/back/remote/"

funzione terminate()
{
echo "$1" >&2
esci da 1
}

funzione wait_lock()
{
gregge 98
}

funzione wait_lock_or_terminate()
{
echo "In attesa del blocco..."
wait_lock || terminate "Impossibile ottenere il blocco. Uscita..."
echo "Bloccato..."
}

funzione suffisso()
{
DATA_FORMATTATA=$(data +"$DATA_FORMATO")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}

funzione filtro()
{
DATA_FORMATTATA=$(data --data="$1" +"$DATA_PREFISSO")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}

funzione 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="$(nome_dir $TARGET_BASE_PATH)"
SOURCE_PEND_PATH="$MOUNTS$1$PEND_SUFFIX"
TARGET_PEND_PATH="$BACKUPS$1$PEND_SUFFIX"
se [ -d "$SOURCE_BASE_PATH" ]
poi
echo "$SOURCE_BASE_PATH trovato"
altro
echo "File $SOURCE_BASE_PATH non trovato. Creazione dello snapshot di $SOURCE_PATH in $SOURCE_BASE_PATH"
snapshot del sottovolume btrfs -r $SOURCE_PATH $SOURCE_BASE_PATH
sync
se [ -d "$TARGET_BASE_PATH" ]
poi
echo "$TARGET_BASE_PATH trovato fuori sincrono con la sorgente... rimozione..."
btrfs sottovolume elimina -c $TARGET_BASE_PATH
sync
fi
fi
se [ -d "$TARGET_BASE_PATH" ]
poi
echo "$TARGET_BASE_PATH trovato"
altro
echo "$TARGET_BASE_PATH non trovato. Sincronizzazione con $TARGET_BASE_DIR"
btrfs invia $SOURCE_BASE_PATH | btrfs riceve $TARGET_BASE_DIR
sync
fi
se [ -d "$SOURCE_PEND_PATH" ]
poi
echo "$SOURCE_PEND_PATH trovato durante la rimozione..."
btrfs sottovolume elimina -c $SOURCE_PEND_PATH
sync
fi
snapshot del sottovolume btrfs -r $SOURCE_PATH $SOURCE_PEND_PATH
sync
se [ -d "$TARGET_PEND_PATH" ]
poi
echo "$TARGET_PEND_PATH trovato durante la rimozione..."
btrfs sottovolume elimina -c $TARGET_PEND_PATH
sync
fi
echo "Invio di $SOURCE_PEND_PATH a $TARGET_PEND_PATH"
btrfs invia -p $SOURCE_BASE_PATH $SOURCE_PEND_PATH | btrfs riceve $TARGET_BASE_DIR
sync
TARGET_DATE_SUFFIX=$(suffisso)
snapshot del sottovolume btrfs -r $TARGET_PEND_PATH "$TARGET_PATH$TARGET_DATE_SUFFIX"
sync
btrfs sottovolume delete -c $SOURCE_BASE_PATH
sync
btrfs sottovolume elimina -c $TARGET_BASE_PATH
sync
mv $SOURCE_PEND_PATH $SOURCE_BASE_PATH
mv $TARGET_PEND_PATH $TARGET_BASE_PATH
sync
}

funzione list()
{
LIST_TARGET_BASE_PATH="$BACKUPS$1$BASE_SUFFIX"
LIST_TARGET_BASE_DIR="$(nome_dir $LIST_TARGET_BASE_PATH)"
LIST_TARGET_BASE_NAME="$(nomebase -s .$BASE_SUFFIX $LIST_TARGET_BASE_PATH)"
trova "$LIST_TARGET_BASE_DIR" -maxdepth 1 -mindepth 1 -type d -printf "%fn" | grep "${LIST_TARGET_BASE_NAME/$BASE_SUFFIX/$SNAP_SUFFIX}.$DATE_REGEX"
}

funzione remove()
{
REMOVE_TARGET_BASE_PATH="$BACKUPS$1$BASE_SUFFIX"
REMOVE_TARGET_BASE_DIR="$(nome_dir $REMOVE_TARGET_BASE_PATH)"
Eliminazione del sottovolume btrfs -c $REMOVE_TARGET_BASE_DIR/$2
sync
}

funzione removeall()
{
DATA_OFFSET="$2"
FILTRO="$(filtro "$DATE_OFFSET")"
durante la lettura -r SNAPSHOT ; fai
rimuovi "$1" "$SNAPSHOT"
fatto < <(elenco "$1" | grep "$FILTER")

}

(
COMANDO="$1"
spostamento

caso "$COMANDO" in
"--aiuto")
echo "Aiuto"
;;
"suffisso")
suffisso
;;
"filtro")
filtro "$1"
;;
"backup")
attendi_blocco_o_termina
riserva "$1"
;;
"lista")
lista "$1"
;;
"rimuovere")
attendi_blocco_o_termina
rimuovi "$1" "$2"
;;
"rimuovi tutto")
attendi_blocco_o_termina
rimuovi tutto "$1" "$2"
;;
*)
echo "Nessuno.."
;;
che C
) 98>$LOCK_FILE

EOF

Cosa fa...?Contiene una serie di semplici comandi per creare istantanee BTRFS e copiarle su un altro FS utilizzando l'invio/ricezione BTRFS.

Il primo lancio può essere relativamente lungo, perché... All'inizio, tutti i dati verranno copiati. Gli ulteriori lanci saranno molto rapidi, perché... Verranno copiate solo le modifiche.

Un altro script che inseriremo in cron:

Ancora qualche codice 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="$(percorso reale $0)"
SCRIPT_DIR="$(nome_dir $SCRIPT_FILE)"
SCRIPT_NAME="$(nomebase -s .sh $SCRIPT_FILE)"

BACKUP_SCRIPT="$SCRIPT_DIR/btrfs-backup.sh"
RITENZIONE="-60 giorni"
$BACKUP_SCRIPT backup radice/@
$BACKUP_SCRIPT rimuovi tutto root/@ "$RETENTION"
$BACKUP_SCRIPT backup root/@home
$BACKUP_SCRIPT rimuovi tutto root/@home "$RETENTION"
$BACKUP_SCRIPT backup avvio/
$BACKUP_SCRIPT rimuove tutto l'avvio/ "$RETENTION"
EOF

Che cosa fa..?Crea e sincronizza istantanee incrementali dei volumi BTRFS elencati sull'FS di backup. Successivamente, verranno eliminate tutte le immagini create 60 giorni fa. Dopo l'avvio, le istantanee datate dei volumi elencati verranno visualizzate nelle sottodirectory /backup/btrfs/back/remote/.

Diamo i diritti di esecuzione del codice:

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

Controlliamolo e inseriamolo nel 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 sottile LVM

Creiamo un thin pool sul dispositivo di backup:

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

Installiamo ddrescue, perché... gli script utilizzeranno questo strumento:

#apt-get install gddrescue

Creiamo una directory per gli script:

#mkdir /root/lvm-thin-backup

Copiamo gli script:

Un sacco di divertimento dentro...#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="$(percorso reale $0)"
SCRIPT_DIR="$(nome_dir $SCRIPT_FILE)"
SCRIPT_NAME="$(nomebase -s .sh $SCRIPT_FILE)"

LOCK_FILE="/dev/shm/$SCRIPT_NAME.lock"
DATE_PREFIX='%Y-%m-%d'
FORMATO_DATA=$PREFISSO_DATA'-%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"
BACKUP="backup"
BACKUPS_POOL="thin-pool"

esporta LVM_SUPPRESS_FD_WARNINGS=1

funzione terminate()
{
echo "$1" >&2
esci da 1
}

funzione wait_lock()
{
gregge 98
}

funzione wait_lock_or_terminate()
{
echo "In attesa del blocco..."
wait_lock || terminate "Impossibile ottenere il blocco. Uscita..."
echo "Bloccato..."
}

funzione suffisso()
{
DATA_FORMATTATA=$(data +"$DATA_FORMATO")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}

funzione filtro()
{
DATA_FORMATTATA=$(data --data="$1" +"$DATA_PREFISSO")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}

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

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

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

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

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

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

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

funzione deactivate_volume {
lvchange -un "$1/$2"
}

funzione read_thin_metadata_snap {
stato dmsetup "$1" | awk '{print $7}'
}

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

se [ "$DIFF_SOURCE_POOL" == "" ]
poi
(>&2 echo "Il LV sorgente non è sottile.")
esci da 1
fi

se [ "$DIFF_TARGET_POOL" == "" ]
poi
(>&2 echo "Il target LV non è sottile.")
esci da 1
fi

se [ "$DIFF_SOURCE_POOL" != "$DIFF_TARGET_POOL" ]
poi
(>&2 echo "I LV di origine e di destinazione appartengono a pool sottili diversi.")
esci da 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=$(lettura_snap_metadati_sottili $DIFF_POOL_PATH_TPOOL)

se [ "$DIFF_POOL_METADATA_SNAP" != "-" ]
poi
(>&2 echo "Lo snapshot dei metadati del pool sottile esiste già. Si presume che sia obsoleto. Lo snapshot dei metadati verrà rilasciato tra 5 secondi.")
dormi 5
messaggio dmsetup $DIFF_POOL_PATH_TPOOL 0 release_metadata_snap
fi

messaggio dmsetup $DIFF_POOL_PATH_TPOOL 0 reserve_metadata_snap
DIFF_POOL_METADATA_SNAP=$(lettura_snap_metadati_sottili $DIFF_POOL_PATH_TPOOL)

se [ "$DIFF_POOL_METADATA_SNAP" == "-" ]
poi
(>&2 echo "Impossibile creare lo snapshot dei metadati del pool sottile.")
esci da 1
fi

#Manteniamo l'output nella variabile perché lo snapshot dei metadati deve essere rilasciato in anticipo.
DIFF_DATA=$(thin_delta -m$DIFF_POOL_METADATA_SNAP --snap1 $DIFF_SOURCE_ID --snap2 $DIFF_TARGET_ID $DIFF_POOL_PATH_TMETA)

messaggio dmsetup $DIFF_POOL_PATH_TPOOL 0 release_metadata_snap

echo $"$DIFF_DATA" | grep -E 'diverso|solo_sinistra|solo_destra' | sed's/

}

funzione 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=$(dimensione_blocco_lettura_blocco $SYNC_VG $SYNC_PEND_POOL)
SYNC_PEND_PATH=$(read_lv_dm_path $SYNC_VG $SYNC_PEND)

attiva_volume $SYNC_VG $SYNC_PEND

durante la lettura -r SYNC_ACTION SYNC_OFFSET SYNC_LENGTH ; fai
SYNC_OFFSET_BYTES=$((SYNC_OFFSET * SYNC_BLOCK_SIZE))
LUNGHEZZA_SINCRONIZZAZIONE_BYTE=$((LUNGHEZZA_SINCRONIZZAZIONE * DIMENSIONE_BLOCCO_SINCRONIZZAZIONE))
se [ "$SYNC_ACTION" == "copy" ]
poi
ddrescue --quiet --force --posizione-input=$SYNC_OFFSET_BYTES --posizione-output=$SYNC_OFFSET_BYTES --dimensione=$SYNC_LENGTH_BYTES "$SYNC_PEND_PATH" "$SYNC_TARGET"
fi

se [ "$SYNC_ACTION" == "scarta" ]
poi
blkdiscard -o $SYNC_OFFSET_BYTES -l $SYNC_LENGTH_BYTES "$SYNC_TARGET"
fi
fatto < <(thindiff "$SYNC_VG" "$SYNC_PEND" "$SYNC_BASE")
}

funzione discard_volume()
{
DISCARD_VG="$1"
DISCARD_LV="$2"
DISCARD_LV_PATH=$(read_lv_dm_path "$DISCARD_VG" "$DISCARD_LV")
se [ "$DISCARD_LV_PATH" != "" ]
poi
echo "$DISCARD_LV_PATH trovato"
altro
echo "$DISCARD_LV non trovato in $DISCARD_VG"
esci da 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" || uscita 1
lvcreate --thin-pool "$DISCARD_LV_POOL" -V "$DISCARD_LV_SIZE"B --name "$DISCARD_LV" "$DISCARD_VG" || uscita 1
}

funzione backup()
{
SOURCE_VG="$1"
SOURCE_LV="$2"
TARGET_VG="$BACKUP"
TARGET_LV="$SOURCE_VG-$SOURCE_LV"
SOURCE_BASE_LV="$SOURCE_LV$BASE_SUFFISSO"
TARGET_BASE_LV="$TARGET_LV$BASE_SUFFISSO"
SOURCE_PEND_LV="$SOURCE_LV$PEND_SUFFIX"
TARGET_PEND_LV="$TARGET_LV$PEND_SUFFIX"
PERCORSO_LV_BASE_ORIGINE=$(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")

se [ "$SOURCE_BASE_LV_PATH" != "" ]
poi
echo "$SOURCE_BASE_LV_PATH trovato"
altro
echo "Base di origine non trovata. Creazione dello snapshot di $SOURCE_VG/$SOURCE_LV in $SOURCE_VG/$SOURCE_BASE_LV"
lvcreate --quiet --snapshot --name "$SOURCE_BASE_LV" "$SOURCE_VG/$SOURCE_LV" || uscita 1
PERCORSO_LV_BASE_ORIGINE=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_BASE_LV")
attiva_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
echo "Scacceremo $SOURCE_BASE_LV_PATH perché dobbiamo effettuare il bootstrap."
POOL_BASE_ORIGINALE=$(read_pool_lv $SOURCE_VG $SOURCE_BASE_LV)
DIMENSIONE_CHUNK_BASE_ORIGINE=$(dimensione_chunk_livello_lettura $SOURCE_VG $SOURCE_BASE_POOL)
volume_di_scarto "$SOURCE_VG" "$SOURCE_BASE_LV"
sync
se [ "$TARGET_BASE_LV_PATH" != "" ]
poi
echo "$TARGET_BASE_LV_PATH trovato fuori sincrono con la sorgente... rimozione..."
lvremove -y --quiet $TARGET_BASE_LV_PATH || uscita 1
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
sync
fi
fi
DIMENSIONE_BASE_ORIGINE=$(dimensione_livello_lettura "$SOURCE_VG" "$SOURCE_BASE_LV")
se [ "$TARGET_BASE_LV_PATH" != "" ]
poi
echo "$TARGET_BASE_LV_PATH trovato"
altro
echo "$TARGET_VG/$TARGET_LV non trovati. Creazione di un volume vuoto."
lvcreate --thin-pool "$BACKUPS_POOL" -V "$SOURCE_BASE_SIZE"B --name "$TARGET_BASE_LV" "$TARGET_VG" || uscita 1
echo "Devo riavviare lo snap. Scarto la sorgente in $SOURCE_BASE_LV_PATH"
attiva_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
POOL_BASE_ORIGINALE=$(read_pool_lv $SOURCE_VG $SOURCE_BASE_LV)
DIMENSIONE_CHUNK_BASE_ORIGINE=$(dimensione_chunk_livello_lettura $SOURCE_VG $SOURCE_BASE_POOL)
volume_di_scarto "$SOURCE_VG" "$SOURCE_BASE_LV"
POOL_BASE_TARGET=$(read_pool_lv $TARGET_VG $TARGET_BASE_LV)
DIMENSIONE_CHUNK_BASE_TARGET=$(dimensione_chunk_livello_lettura $TARGET_VG $TARGET_BASE_POOL)
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
echo "Scartamento del target in $TARGET_BASE_LV_PATH"
volume_di_scarto "$TARGET_VG" "$TARGET_BASE_LV"
sync
fi
se [ "$SOURCE_PEND_LV_PATH" != "" ]
poi
echo "$SOURCE_PEND_LV_PATH trovato durante la rimozione..."
lvremove -y --quiet "$SOURCE_PEND_LV_PATH" || uscita 1
sync
fi
lvcreate --quiet --snapshot --name "$SOURCE_PEND_LV" "$SOURCE_VG/$SOURCE_LV" || uscita 1
SOURCE_PEND_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_PEND_LV")
sync
se [ "$TARGET_PEND_LV_PATH" != "" ]
poi
echo "$TARGET_PEND_LV_PATH trovato durante la rimozione..."
lvremove -y --quiet $TARGET_PEND_LV_PATH
sync
fi
lvcreate --quiet --snapshot --name "$TARGET_PEND_LV" "$TARGET_VG/$TARGET_BASE_LV" || uscita 1
TARGET_PEND_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_PEND_LV")
DIMENSIONE_PEND_LV_ORIGINE=$(dimensione_livello_lettura "$SOURCE_VG" "$SOURCE_PEND_LV")
lvresize -L "$SOURCE_PEND_LV_SIZE"B "$TARGET_PEND_LV_PATH"
attiva_volume "$TARGET_VG" "$TARGET_PEND_LV"
echo "Sincronizzazione di $SOURCE_PEND_LV_PATH con $TARGET_PEND_LV_PATH"
thinsync "$SOURCE_VG" "$SOURCE_PEND_LV" "$SOURCE_BASE_LV" "$TARGET_PEND_LV_PATH" || uscita 1
sync

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

funzione verify()
{
SOURCE_VG="$1"
SOURCE_LV="$2"
TARGET_VG="$BACKUP"
TARGET_LV="$SOURCE_VG-$SOURCE_LV"
SOURCE_BASE_LV="$SOURCE_LV$BASE_SUFFISSO"
TARGET_BASE_LV="$TARGET_LV$BASE_SUFFISSO"
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
PERCORSO_LV_BASE_ORIGINE=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_BASE_LV")

se [ "$SOURCE_BASE_LV_PATH" != "" ]
poi
echo "$SOURCE_BASE_LV_PATH trovato"
altro
echo "$SOURCE_BASE_LV_PATH non trovato"
esci da 1
fi
se [ "$TARGET_BASE_LV_PATH" != "" ]
poi
echo "$TARGET_BASE_LV_PATH trovato"
altro
echo "$TARGET_BASE_LV_PATH non trovato"
esci da 1
fi
attiva_volume "$TARGET_VG" "$TARGET_BASE_LV"
attiva_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
echo Confronto tra "$SOURCE_BASE_LV_PATH" e "$TARGET_BASE_LV_PATH"
cmp "$SOURCE_BASE_LV_PATH" "$TARGET_BASE_LV_PATH"
eco Fatto...
disattiva_volume "$TARGET_VG" "$TARGET_BASE_LV"
disattiva_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
}

funzione resync()
{
SOURCE_VG="$1"
SOURCE_LV="$2"
TARGET_VG="$BACKUP"
TARGET_LV="$SOURCE_VG-$SOURCE_LV"
SOURCE_BASE_LV="$SOURCE_LV$BASE_SUFFISSO"
TARGET_BASE_LV="$TARGET_LV$BASE_SUFFISSO"
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
PERCORSO_LV_BASE_ORIGINE=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_BASE_LV")

se [ "$SOURCE_BASE_LV_PATH" != "" ]
poi
echo "$SOURCE_BASE_LV_PATH trovato"
altro
echo "$SOURCE_BASE_LV_PATH non trovato"
esci da 1
fi
se [ "$TARGET_BASE_LV_PATH" != "" ]
poi
echo "$TARGET_BASE_LV_PATH trovato"
altro
echo "$TARGET_BASE_LV_PATH non trovato"
esci da 1
fi
attiva_volume "$TARGET_VG" "$TARGET_BASE_LV"
attiva_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
POOL_BASE_ORIGINALE=$(read_pool_lv $SOURCE_VG $SOURCE_BASE_LV)
SYNC_BLOCK_SIZE=$(dimensione_blocco_livello_lettura $SOURCE_VG $SOURCE_BASE_POOL)

echo Sincronizzazione di "$SOURCE_BASE_LV_PATH" con "$TARGET_BASE_LV_PATH"

CMP_OFFSET=0
mentre [[ "$CMP_OFFSET" != "" ]] ; fai
CMP_MISMATCH=$(cmp -i "$CMP_OFFSET" "$SOURCE_BASE_LV_PATH" "$TARGET_BASE_LV_PATH" | grep differ | awk '{print $5}' | sed 's/,//g' )
se [[ "$CMP_MISMATCH" != "" ]] ; allora
CMP_OFFSET=$(( CMP_MISMATCH + CMP_OFFSET ))
SYNC_OFFSET_BYTES=$(( ( CMP_OFFSET / SYNC_BLOCK_SIZE ) * SYNC_BLOCK_SIZE ))
LUNGHEZZA_SYNC_BYTES=$((DIMENSIONE_BLOCCO_SYNC))
echo "Sincronizzazione di $SYNC_LENGTH_BYTES byte in $SYNC_OFFSET_BYTES da $SOURCE_BASE_LV_PATH a $TARGET_BASE_LV_PATH"
ddrescue --quiet --force --posizione-input=$SYNC_OFFSET_BYTES --posizione-output=$SYNC_OFFSET_BYTES --dimensione=$SYNC_LENGTH_BYTES "$SOURCE_BASE_LV_PATH" "$TARGET_BASE_LV_PATH"
altro
CMP_OFFSET=""
fi
fatto
eco Fatto...
disattiva_volume "$TARGET_VG" "$TARGET_BASE_LV"
disattiva_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
}

funzione list()
{
LIST_SOURCE_VG="$1"
LIST_SOURCE_LV="$2"
LIST_TARGET_VG="$BACKUP"
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"
}

funzione remove()
{
REMOVE_TARGET_VG="$BACKUP"
RIMUOVI_TARGET_LV="$1"
lvremove -y "$REMOVE_TARGET_VG/$REMOVE_TARGET_LV"
sync
}

funzione removeall()
{
DATA_OFFSET="$3"
FILTRO="$(filtro "$DATE_OFFSET")"
durante la lettura -r SNAPSHOT ; fai
rimuovere "$SNAPSHOT"
fatto < <(elenco "$1" "$2" | grep "$FILTER")

}

(
COMANDO="$1"
spostamento

caso "$COMANDO" in
"--aiuto")
echo "Aiuto"
;;
"suffisso")
suffisso
;;
"filtro")
filtro "$1"
;;
"backup")
attendi_blocco_o_termina
riserva "$1" "$2"
;;
"lista")
elenco "$1" "$2"
;;
"diff sottile")
thindiff "$1" "$2" "$3"
;;
"sincronizzazione sottile")
thinsync "$1" "$2" "$3" "$4"
;;
"verificare")
attendi_blocco_o_termina
verifica "$1" "$2"
;;
"risincronizzare")
attendi_blocco_o_termina
risincronizza "$1" "$2"
;;
"rimuovere")
attendi_blocco_o_termina
rimuovi "$1"
;;
"rimuovi tutto")
attendi_blocco_o_termina
rimuovi tutto "$1" "$2" "$3"
;;
*)
echo "Nessuno.."
;;
che C
) 98>$LOCK_FILE

EOF

Che cosa fa...?Contiene una serie di comandi per manipolare thin snapshot e sincronizzare la differenza tra due thin snapshot ricevuti tramite thin_delta su un altro dispositivo a blocchi utilizzando ddrescue e blkdiscard.

Un altro script che inseriremo in cron:

Un po' più di sfacciataggine#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="$(percorso reale $0)"
SCRIPT_DIR="$(nome_dir $SCRIPT_FILE)"
SCRIPT_NAME="$(nomebase -s .sh $SCRIPT_FILE)"

BACKUP_SCRIPT="$SCRIPT_DIR/lvm-thin-backup.sh"
RITENZIONE="-60 giorni"

$BACKUP_SCRIPT immagini di backup linux-dev
$BACKUP_SCRIPT immagini di backup win8
$BACKUP_SCRIPT immagini di backup win8-data
#eccetera

$BACKUP_SCRIPT rimuove tutte le immagini linux-dev "$RETENTION"
$BACKUP_SCRIPT rimuove tutte le immagini win8 "$RETENTION"
$BACKUP_SCRIPT rimuove tutte le immagini win8-data "$RETENTION"
#eccetera

EOF

Che cosa fa...?Utilizza lo script precedente per creare e sincronizzare i backup dei volumi thin elencati. Lo script lascerà istantanee inattive dei volumi elencati, necessarie per tenere traccia delle modifiche dall'ultima sincronizzazione.

Questo script deve essere modificato, specificando l'elenco dei volumi thin per i quali devono essere effettuate le copie di backup. I nomi indicati sono solo a scopo illustrativo. Se lo desideri, puoi scrivere uno script che sincronizzerà tutti i volumi.

Diamo i diritti:

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

Controlliamolo e inseriamolo nel 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

Il primo lancio sarà lungo, perché... i volumi thin verranno completamente sincronizzati copiando tutto lo spazio utilizzato. Grazie ai metadati thin di LVM, sappiamo quali blocchi sono effettivamente in uso, quindi verranno copiati solo i blocchi di volume thin effettivamente utilizzati.

Le esecuzioni successive copieranno i dati in modo incrementale grazie al rilevamento delle modifiche tramite metadati sottili LVM.

Vediamo cosa è successo:

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

#tempo /root/lvm-thin-backup/cron-daily.sh
1 m reali 2,710 s
utente 0m12,721s
sistema 0m6,671s

#ls -al /backup/btrfs/back/remote/*
/backup/btrfs/back/remote/boot:
Totale 0
drwxr-xr-x 1 radice radice 1260 26 mar 09:11 .
drwxr-xr-x 1 radice radice 16 mar 6 09:30 ..
drwxr-xr-x 1 radice radice 322 26 mar 02:00 .@base
drwxr-xr-x 1 radice radice 516 6 mar 09:39 .@snap.2020-03-06-09-39-37
drwxr-xr-x 1 radice radice 516 6 mar 09:39 .@snap.2020-03-06-09-39-57
...
/backup/btrfs/back/remote/root:
Totale 0
drwxr-xr-x 1 radice radice 2820 26 mar 09:11 .
drwxr-xr-x 1 radice radice 16 mar 6 09:30 ..
drwxr-xr-x 1 radice radice 240 26 mar 09:11 @.@base
drwxr-xr-x 1 root root 22 mar 26 09:11 @home.@base
drwxr-xr-x 1 radice radice 22 mar 6 09:39 @home.@snap.2020-03-06-09-39-35
drwxr-xr-x 1 radice radice 22 mar 6 09:39 @home.@snap.2020-03-06-09-39-57
...
drwxr-xr-x 1 radice radice 240 mar 6 09:39 @.@snap.2020-03-06-09-39-26
drwxr-xr-x 1 radice radice 240 mar 6 09:39 @.@snap.2020-03-06-09-39-56
...

#lvs -olv_name,lv_size immagini e lvs -olv_name,lv_size backup
LV LSIze
sviluppo linux 128,00g
linux-dev.base 128,00g
piscina sottile 1,38t
win8 128,00g
win8-data 2,00t
win8-database 2,00t
win8.base 128,00g
LV LSIze
riserva 256,00g
immagini-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
...
immagini-win8-database.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
...
immagini-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
...
piscina sottile <2,09t

Cosa c'entra questo con le bambole che nidificano?

Molto probabilmente, dato che i volumi logici LVM LV possono essere volumi fisici LVM PV per altri VG. LVM può essere ricorsivo, come le bambole che nidificano. Ciò conferisce a LVM un'estrema flessibilità.

PS

Nel prossimo articolo, proveremo a utilizzare diversi sistemi di archiviazione mobile/KVM simili come base per creare un cluster di archiviazione/VM geo-distribuito con ridondanza in diversi continenti utilizzando desktop domestici, Internet domestico e reti P2P.

Fonte: habr.com

Acquista hosting affidabile per siti con protezione DDoS, server VPS VDS 🔥 Acquista un hosting web affidabile con protezione DDoS, server VPS e VDS | ProHoster