Hvad har LVM og Matryoshka til fælles?

Goddag.
Jeg vil gerne dele min praktiske erfaring med at bygge et datalagringssystem til KVM med md RAID + LVM med fællesskabet.

Programmet vil indeholde:

  • Opbygning af md RAID 1 fra NVMe SSD.
  • Samling af md RAID 6 fra SATA SSD og almindelige drev.
  • Funktioner ved TRIM/DISCARD-drift på SSD RAID 1/6.
  • Oprettelse af et bootbart md RAID 1/6-array på et fælles sæt diske.
  • Installation af systemet på NVMe RAID 1, når der ikke er NVMe-understøttelse i BIOS.
  • Brug af LVM-cache og LVM tynd.
  • Brug af BTRFS snapshots og send/modtag til backup.
  • Brug af tynde LVM-snapshots og thin_delta til sikkerhedskopiering i BTRFS-stil.

Hvis du er interesseret, så se kat.

Statement

Forfatteren påtager sig intet ansvar for konsekvenserne af at bruge eller ikke bruge materialer/eksempler/kode/tips/data fra denne artikel. Ved at læse eller bruge dette materiale på nogen måde, påtager du dig ansvaret for alle konsekvenser af disse handlinger. Mulige konsekvenser omfatter:

  • Sprødstegte NVMe SSD'er.
  • Fuldstændig brugt optagelsesressource og fejl på SSD-drev.
  • Fuldstændig tab af alle data på alle drev, inklusive sikkerhedskopier.
  • Defekt computerhardware.
  • Spildte tid, nerver og penge.
  • Eventuelle andre konsekvenser, der ikke er anført ovenfor.

jern

Tilgængelige var:

Bundkort fra omkring 2013 med Z87 chipset, komplet med Intel Core i7 / Haswell.

  • Processor 4 kerner, 8 tråde
  • 32 GB DDR3 RAM
  • 1 x 16 eller 2 x 8 PCIe 3.0
  • 1 x 4 + 1 x 1 PCIe 2.0
  • 6 x 6 GBps SATA 3-stik

SAS-adapter LSI SAS9211-8I blinkede til IT/HBA-tilstand. RAID-aktiveret firmware er bevidst blevet erstattet med HBA-firmware for at:

  1. Du kan til enhver tid smide denne adapter ud og erstatte den med en hvilken som helst anden, du støder på.
  2. TRIM/Discard fungerede normalt på diske, fordi... i RAID-firmware understøttes disse kommandoer slet ikke, og HBA er generelt ligeglad med, hvilke kommandoer der transmitteres over bussen.

Harddiske - 8 stykker HGST Travelstar 7K1000 med en kapacitet på 1 TB i en 2.5 formfaktor, som for bærbare computere. Disse drev var tidligere i et RAID 6-array. De vil også have brug for det nye system. For at gemme lokale sikkerhedskopier.

Yderligere tilføjet:

6 stk SATA SSD model Samsung 860 QVO 2TB. Disse SSD'er krævede et stort volumen, tilstedeværelsen af ​​en SLC-cache, pålidelighed og en lav pris var ønsket. Understøttelse af discard/nul var påkrævet, hvilket kontrolleres af linjen i dmesg:

kernel: ata1.00: Enabling discard_zeroes_data

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

For disse SSD'er er tilfældig læse-/skrivehastighed og ressourcekapacitet til dine behov vigtige. Radiator til dem. Nødvendigvis. Absolut. Ellers steges de, indtil de er sprøde under den første RAID-synkronisering.

StarTech PEX8M2E2 adapter til 2 x NVMe SSD installeret i PCIe 3.0 8x slot. Dette er igen kun en HBA, men for NVMe. Den adskiller sig fra billige adaptere ved, at den ikke kræver PCIe-bifurkationsunderstøttelse fra bundkortet på grund af tilstedeværelsen af ​​en indbygget PCIe-switch. Det vil fungere selv i det ældste system med PCIe, selvom det er et x1 PCIe 1.0 slot. Naturligvis med passende hastighed. Der er ingen RAID'er der. Der er ingen indbygget BIOS ombord. Så dit system vil ikke på magisk vis lære at starte med NVMe, meget mindre gør NVMe RAID takket være denne enhed.

Denne komponent skyldtes udelukkende tilstedeværelsen af ​​kun én gratis 8x PCIe 3.0 i systemet, og hvis der er 2 ledige slots, kan den nemt udskiftes med to penny PEX4M2E1 eller analoger, som kan købes hvor som helst til en pris af 600 rubler.

Afvisningen af ​​alle former for hardware eller indbyggede chipset/BIOS RAID'er blev foretaget bevidst, for helt at kunne erstatte hele systemet, med undtagelse af selve SSD'en/HDD'en, samtidig med at alle data blev bevaret. Ideelt set, så du kan gemme selv det installerede styresystem, når du flytter til helt ny/anden hardware. Det vigtigste er, at der er SATA- og PCIe-porte. Det er ligesom en live-cd eller et bootbart flashdrev, kun meget hurtigt og lidt omfangsrigt.

HumorEllers ved du, hvad der sker - nogle gange har du akut brug for at tage hele arrayet med dig for at tage væk. Men jeg vil ikke miste data. For at gøre dette er alle de nævnte medier bekvemt placeret på diasene i standardkassens 5.25 båse.

Nå, og selvfølgelig for at eksperimentere med forskellige metoder til SSD-cache i Linux.

Hardware raids er kedelige. Tænde den. Enten virker det, eller også gør det ikke. Og med mdadm er der altid muligheder.

Software

Tidligere var Debian 8 Jessie installeret på hardwaren, som er tæt på EOL. RAID 6 blev samlet fra de ovennævnte HDD'er parret med LVM. Den kørte virtuelle maskiner i kvm/libvirt.

Fordi Forfatteren har passende erfaring med at skabe bærbare bootable SATA/NVMe-flashdrev, og også, for ikke at bryde den sædvanlige apt-skabelon, blev Ubuntu 18.04 valgt som målsystem, som allerede er tilstrækkeligt stabiliseret, men stadig har 3 års støtte i fremtiden.

Det nævnte system indeholder alle de hardware-drivere, vi skal bruge ud af kassen. Vi har ikke brug for nogen tredjepartssoftware eller -drivere.

Forberedelse til installation

For at installere systemet har vi brug for Ubuntu Desktop Image. Serversystemet har en form for kraftfuldt installationsprogram, som viser overdreven uafhængighed, som ikke kan deaktiveres ved at skubbe UEFI-systempartitionen ind på en af ​​diskene, hvilket ødelægger al skønheden. Derfor installeres det kun i UEFI-tilstand. Tilbyder ingen muligheder.

Det er vi ikke tilfredse med.

Hvorfor?Desværre er UEFI boot ekstremt dårligt kompatibel med boot software RAID, fordi... Ingen tilbyder os reservationer til UEFI ESP-partitionen. Der er opskrifter på internettet, der foreslår at placere ESP-partitionen på et flashdrev i en USB-port, men dette er et fejlpunkt. Der er opskrifter, der bruger software mdadm RAID 1 med metadata version 0.9, som ikke forhindrer UEFI BIOS i at se denne partition, men denne lever indtil det lykkelige øjeblik, hvor BIOS'en eller et andet hardware OS skriver noget til ESP'en og glemmer at synkronisere det til andre spejle.

Derudover afhænger UEFI-start af NVRAM, som ikke vil flytte sammen med diskene til det nye system, fordi er en del af bundkortet.

Så vi vil ikke genopfinde et nyt hjul. Vi har allerede en færdiglavet, gennemtestet bedstefarscykel, nu kaldet Legacy/BIOS-støvle, der bærer det stolte navn CSM på UEFI-kompatible systemer. Vi tager det bare ned fra hylden, smører det, pumper dækkene op og tørrer det af med en fugtig klud.

Desktopversionen af ​​Ubuntu kan heller ikke installeres korrekt med Legacy bootloader, men her er der, som de siger, i det mindste muligheder.

Og så samler vi hardwaren og indlæser systemet fra Ubuntu Live bootable flashdrev. Vi bliver nødt til at downloade pakker, så vi opsætter det netværk, der fungerer for dig. Hvis det ikke virker, kan du indlæse de nødvendige pakker på et flashdrev på forhånd.

Vi går ind i skrivebordsmiljøet, starter terminalemulatoren, og så går vi:

#sudo bash

Hvordan…?Linjen ovenfor er den kanoniske udløser for holiwars om sudo. C bоstørre muligheder kommer ogоstørre ansvar. Spørgsmålet er, om du selv kan tage det på dig. Mange mennesker tror, ​​at det i hvert fald ikke er forsigtigt at bruge sudo på denne måde. Imidlertid:

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

Hvorfor ikke ZFS...?Når vi installerer software på vores computer, udlåner vi i det væsentlige vores hardware til udviklerne af denne software at køre.
Når vi stoler på denne software med sikkerheden af ​​vores data, optager vi et lån svarende til omkostningerne ved at gendanne disse data, som vi skal betale af en dag.

Fra dette synspunkt er ZFS en Ferrari, og mdadm+lvm er mere som en cykel.

Subjektivt foretrækker forfatteren at låne en cykel på kredit til ukendte personer i stedet for en Ferrari. Der er prisen på spørgsmålet ikke høj. Intet behov for rettigheder. Enklere end færdselsregler. Parkering er gratis. Cross-country evner er bedre. Du kan altid sætte ben på en cykel, og du kan reparere en cykel med dine egne hænder.

Hvorfor så BTRFS...?For at kunne starte operativsystemet, har vi brug for et filsystem, der understøttes i Legacy/BIOS GRUB ud af boksen, og som samtidig understøtter live snapshots. Vi vil bruge det til /boot-partitionen. Derudover foretrækker forfatteren at bruge denne FS til / (rod), og glem ikke at bemærke, at for enhver anden software kan du oprette separate partitioner på LVM og montere dem i de nødvendige mapper.

Vi vil ikke gemme nogen billeder af virtuelle maskiner eller databaser på denne FS.
Denne FS vil kun blive brugt til at oprette snapshots af systemet uden at slå det fra og derefter overføre disse snapshots til en backup disk ved hjælp af send/recieve.

Derudover foretrækker forfatteren generelt at beholde et minimum af software direkte på hardwaren og køre al den anden software i virtuelle maskiner ved hjælp af ting som at videresende GPU'er og PCI-USB Host-controllere til KVM via IOMMU.

De eneste ting, der er tilbage på hardwaren, er datalagring, virtualisering og backup.

Hvis du stoler mere på ZFS, så er de i princippet udskiftelige for den angivne applikation.

Forfatteren ignorerer dog bevidst de indbyggede spejling/RAID og redundansfunktioner, som ZFS, BRTFS og LVM har.

Som et yderligere argument har BTRFS muligheden for at omdanne tilfældige skrivninger til sekventielle, hvilket har en yderst positiv effekt på hastigheden af ​​synkronisering af snapshots/backups på HDD'en.

Lad os genscanne alle enheder:

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

Lad os tage et kig rundt:

#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

Disklayout

NVMe SSD

Men vi vil ikke markere dem på nogen måde. Alligevel ser vores BIOS ikke disse drev. Så de går udelukkende til software RAID. Vi vil ikke engang oprette sektioner der. Hvis du vil følge "kanon" eller "principielt", skal du oprette en stor partition, som en HDD.

SATA HDD

Der er ingen grund til at opfinde noget særligt her. Vi opretter én sektion til alt. Vi vil oprette en partition, fordi BIOS ser disse diske og måske endda prøve at starte fra dem. Vi vil endda installere GRUB på disse diske senere, så systemet pludselig kan gøre dette.

#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

SATA SSD

Det er her, tingene bliver interessante for os.

For det første er vores drev 2 TB store. Dette er inden for det acceptable område for MBR, hvilket er det, vi vil bruge. Kan om nødvendigt udskiftes med GPT. GPT-diske har et kompatibilitetslag, der gør det muligt for MBR-kompatible systemer at se de første 4 partitioner, hvis de er placeret inden for de første 2 terabyte. Det vigtigste er, at boot-partitionen og bios_grub-partitionen på disse diske skal være i begyndelsen. Dette giver dig endda mulighed for at starte fra GPT Legacy/BIOS-drev.

Men dette er ikke vores tilfælde.

Her vil vi oprette to sektioner. Den første vil være på 1 GB og bruges til RAID 1 /boot.

Den anden vil blive brugt til RAID 6 og vil optage al den resterende ledige plads med undtagelse af et lille ikke-allokeret område for enden af ​​drevet.

Hvad er dette umarkerede område?Ifølge kilder på netværket har vores SATA SSD'er ombord en dynamisk udvidelig SLC-cache, der varierer i størrelse fra 6 til 78 gigabyte. Vi får 6 gigabyte "gratis" på grund af forskellen mellem "gigabyte" og "gibibyte" i drevets datablad. De resterende 72 gigabyte er allokeret fra ubrugt plads.

Her skal det bemærkes, at vi har en SLC-cache, og pladsen er optaget i 4 bit MLC-tilstand. Hvilket for os reelt betyder, at vi for hver 4 gigabyte ledig plads kun får 1 gigabyte SLC-cache.

Gang 72 gigabyte med 4 og få 288 gigabyte. Dette er den ledige plads, som vi ikke vil markere ud for at tillade drevene at gøre fuld brug af SLC-cachen.

Således vil vi reelt få op til 312 gigabyte SLC-cache fra i alt seks drev. Af alle drevene vil 2 blive brugt i RAID til redundans.

Denne mængde cache vil give os mulighed for ekstremt sjældent i det virkelige liv at støde på en situation, hvor en skrivning ikke går til cachen. Dette kompenserer ekstremt godt for den tristeste ulempe ved QLC-hukommelse - den ekstremt lave skrivehastighed, når data skrives uden om cachen. Hvis dine belastninger ikke svarer til dette, så anbefaler jeg, at du tænker grundigt over, hvor længe din SSD holder under en sådan belastning, under hensyntagen til TBW fra databladet.

#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

Oprettelse af arrays

Først skal vi omdøbe maskinen. Dette er nødvendigt, fordi værtsnavnet er en del af array-navnet et sted inde i mdadm og påvirker noget et eller andet sted. Selvfølgelig kan arrays omdøbes senere, men dette er et unødvendigt skridt.

#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

Hvorfor -antage-ren...?For at undgå initialisering af arrays. For både RAID niveau 1 og 6 er dette gyldigt. Alt kan fungere uden initialisering, hvis det er et nyt array. Desuden er initialisering af SSD-arrayet ved oprettelse spild af TBW-ressourcer. Vi bruger TRIM/DISCARD hvor det er muligt på samlede SSD-arrays for at "initialisere" dem.

For SSD-arrays understøttes RAID 1 DISCARD ud af æsken.

For SSD RAID 6 DISCARD-arrays skal du aktivere det i kernemodulets parametre.

Dette bør kun gøres, hvis alle SSD'er, der bruges i niveau 4/5/6-arrays i dette system, har fungerende understøttelse af discard_zeroes_data. Nogle gange støder du på mærkelige drev, der fortæller kernen, at denne funktion er understøttet, men faktisk er den der ikke, eller funktionen virker ikke altid. I øjeblikket er support tilgængelig næsten overalt, dog er der gamle drev og firmware med fejl. Af denne grund er DISCARD-understøttelse deaktiveret som standard for RAID 6.

Bemærk, følgende kommando vil ødelægge alle data på NVMe-drev ved at "initialisere" arrayet med "nuller".

#blkdiscard /dev/md0

Hvis noget går galt, kan du prøve at angive et trin.

#blkdiscard --step 65536 /dev/md0

SATA SSD

#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

Hvorfor så stor...?Forøgelse af chunk-størrelsen har en positiv effekt på hastigheden af ​​tilfældig læsning i blokke op til chunk-size inklusive. Dette sker, fordi en operation af den passende størrelse eller mindre kan udføres helt på en enkelt enhed. Derfor er IOPS fra alle enheder opsummeret. Ifølge statistikker overstiger 99% af IO ikke 512K.

RAID 6 IOPS pr. skrivning altid mindre end eller lig med IOPS for et drev. Når IOPS som tilfældig læsning kan være flere gange større end et drevs, og her er blokstørrelsen af ​​afgørende betydning.
Forfatteren kan ikke se meningen med at forsøge at optimere en parameter, der er dårlig i RAID 6 by-design og i stedet optimere, hvad RAID 6 er god til.
Vi vil kompensere for den dårlige tilfældige skrivning af RAID 6 med en NVMe-cache og tynde-provisioning-tricks.

Vi har endnu ikke aktiveret DISCARD for RAID 6. Så vi vil ikke "initialisere" dette array for nu. Vi gør dette senere, efter at have installeret OS.

SATA HDD

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

LVM på NVMe RAID

For hastighed ønsker vi at placere rodfilsystemet på NVMe RAID 1, som er /dev/md0.
Vi har dog stadig brug for dette hurtige array til andre behov, såsom swap, metadata og LVM-cache og LVM-tynde metadata, så vi vil oprette en LVM VG på dette array.

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

Lad os oprette en partition til rodfilsystemet.

#lvcreate -L 128G --name root root

Lad os oprette en partition til at bytte i henhold til størrelsen af ​​RAM.

#lvcreate -L 32G --name swap root

OS installation

I alt har vi alt det nødvendige for at installere systemet.

Start systeminstallationsguiden fra Ubuntu Live-miljøet. Normal installation. Kun på tidspunktet for valg af diske til installation skal du angive følgende:

  • /dev/md1, - mount point /boot, FS - BTRFS
  • /dev/root/root (aka /dev/mapper/root-root), - mount point / (root), FS - BTRFS
  • /dev/root/swap (aka /dev/mapper/root-swap), - brug som swap-partition
  • Installer bootloaderen på /dev/sda

Når du vælger BTRFS som rodfilsystem, vil installationsprogrammet automatisk oprette to BTRFS-volumener med navnet "@" for / (root) og "@home" for /home.

Lad os starte installationen...

Installationen afsluttes med en modal dialogboks, der indikerer en fejl under installation af bootloaderen. Desværre vil du ikke være i stand til at afslutte denne dialog ved hjælp af standardmidler og fortsætte installationen. Vi logger ud af systemet og logger ind igen, og ender på et rent Ubuntu Live-skrivebord. Åbn terminalen og igen:

#sudo bash

Opret et chroot-miljø for at fortsætte installationen:

#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

Lad os konfigurere netværket og værtsnavnet i chroot:

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

Lad os gå ind i chroot-miljøet:

#chroot /mnt/chroot

Først og fremmest leverer vi pakkerne:

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

Lad os tjekke og rette alle pakker, der blev installeret skævt på grund af ufuldstændig systeminstallation:

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

Hvis noget ikke fungerer, skal du muligvis redigere /etc/apt/sources.list først

Lad os justere parametrene for RAID 6-modulet for at aktivere TRIM/DISCARD:

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

Lad os justere vores arrays lidt:

#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

Hvad var det..?Vi har oprettet et sæt udev-regler, der gør følgende:

  • Indstil blokcache-størrelsen for RAID 2020 til at være tilstrækkelig til 6. Standardværdien har tilsyneladende ikke ændret sig siden oprettelsen af ​​Linux og har ikke været tilstrækkelig i lang tid.
  • Reserver et minimum af IO for varigheden af ​​array-tjek/synkroniseringer. Dette er for at forhindre dine arrays i at sidde fast i en tilstand af evig synkronisering under belastning.
  • Begræns den maksimale IO under kontrol/synkronisering af arrays. Dette er nødvendigt, så synkronisering/kontrol af SSD RAID'er ikke steger dine drev til en sprødhed. Dette gælder især for NVMe. (Husker du om radiatoren? Jeg lavede ikke sjov.)
  • Forbyd diske i at stoppe spindelrotation (HDD) via APM, og indstil sleep-timeout for diskcontrollere til 7 timer. Du kan deaktivere APM fuldstændigt, hvis dine drev kan gøre det (-B 255). Med standardværdien stopper drevene efter fem sekunder. Så vil operativsystemet nulstille diskcachen, diskene vil snurre op igen, og alt vil starte forfra. Diske har et begrænset maksimalt antal spindelrotationer. Sådan en simpel standardcyklus kan nemt dræbe dine diske om et par år. Ikke alle diske lider under dette, men vores er "bærbare" dem, med de passende standardindstillinger, som får RAID til at ligne en mini-MAID.
  • Installer readahead på diske (roterende) 1 megabyte - to på hinanden følgende blokke/chunk RAID 6
  • Deaktiver readahead på selve arrays.

Lad os redigere /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

Hvorfor det..?Vi vil søge efter /boot-partitionen med UUID. Array-navngivning kan teoretisk ændres.

Vi vil søge efter de resterende sektioner efter LVM-navne i /dev/mapper/vg-lv-notationen, fordi de identificerer partitioner helt unikt.

Vi bruger ikke UUID til LVM pga UUID'et for LVM-volumener og deres snapshots kan være det samme.Mount /dev/mapper/root-root.. to gange?Ja. Nemlig. Funktion af BTRFS. Dette filsystem kan monteres flere gange med forskellige subvols.

På grund af den samme funktion anbefaler jeg aldrig at oprette LVM-snapshots af aktive BTRFS-volumener. Du kan få en overraskelse, når du genstarter.

Lad os genskabe mdadm-konfigurationen:

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

Lad os justere LVM-indstillingerne:

#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

Hvad var det..?Vi har aktiveret automatisk udvidelse af tynde LVM-bassiner, når de når 90 % af den besatte plads med 5 % af volumen.

Vi har øget det maksimale antal cacheblokke for LVM-cache.

Vi har forhindret LVM i at søge efter LVM-volumener (PV) på:

  • enheder, der indeholder LVM-cache (cdata)
  • enheder, der er cachelagret ved hjælp af LVM-cache, uden om cachen ( _corig). I dette tilfælde vil selve den cachelagrede enhed stadig blive scannet gennem cachen (kun ).
  • enheder, der indeholder LVM-cache-metadata (cmeta)
  • alle enheder i VG med navnebillederne. Her vil vi have diskbilleder af virtuelle maskiner, og vi ønsker ikke, at LVM på værten skal aktivere volumener, der tilhører gæsteoperativsystemet.
  • alle enheder i VG med navnet backup. Her vil vi have sikkerhedskopier af virtuelle maskinbilleder.
  • alle enheder, hvis navn ender med "gpv" (fysisk gæstevolumen)

Vi har aktiveret DISCARD-understøttelse, når du frigør ledig plads på LVM VG. Vær forsigtig. Dette vil gøre sletning af LV'er på SSD'en ret tidskrævende. Det gælder især SSD RAID 6. Vi vil dog efter planen bruge tynd provisionering, så det vil overhovedet ikke hindre os.

Lad os opdatere initramfs-billedet:

#update-initramfs -u -k all

Installer og konfigurer grub:

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

Hvilke diske skal man vælge?Alle der er sd*. Systemet skal kunne starte fra ethvert fungerende SATA-drev eller SSD.

Hvorfor tilføjede de os-prober..?Til overdreven uafhængighed og legende hænder.

Det virker ikke korrekt, hvis en af ​​RAID'erne er i en forringet tilstand. Den forsøger at søge efter OS på partitioner, der bruges i virtuelle maskiner, der kører på denne hardware.

Hvis du har brug for det, kan du lade det være, men husk alt ovenstående. Jeg anbefaler at lede efter opskrifter til at slippe af med frække hænder online.

Hermed har vi afsluttet den indledende installation. Det er tid til at genstarte i det nyligt installerede OS. Glem ikke at fjerne den bootbare Live CD/USB.

#exit
#reboot

Vælg en af ​​SATA SSD'erne som opstartsenhed.

LVM på SATA SSD

På dette tidspunkt har vi allerede startet op i det nye OS, konfigureret netværket, apt, åbnet terminalemulatoren og kørt:

#sudo bash

Lad os fortsætte.

"Initialiser" arrayet fra SATA SSD:

#blkdiscard /dev/md2

Hvis det ikke virker, så prøv:

#blkdiscard --step 65536 /dev/md2
Opret LVM VG på SATA SSD:

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

Hvorfor endnu en VG..?Faktisk har vi allerede en VG ved navn root. Hvorfor ikke tilføje alt til én VG?

Hvis der er flere PV'er i en VG, så skal alle PV'er være til stede (online), for at VG'en kan aktiveres korrekt. Undtagelsen er LVM RAID, som vi bevidst ikke bruger.

Vi ønsker virkelig, at hvis der er en fejl (læs datatab) på nogen af ​​RAID 6-arrayerne, vil operativsystemet starte normalt og give os mulighed for at løse problemet.

For at gøre dette vil vi på det første abstraktionsniveau isolere hver type fysisk "medie" i en separat VG.

Videnskabeligt set hører forskellige RAID-arrays til forskellige "pålidelighedsdomæner". Du bør ikke skabe et ekstra fælles fejlpunkt for dem ved at proppe dem ind i én VG.

Tilstedeværelsen af ​​LVM på "hardware"-niveau vil give os mulighed for vilkårligt at skære stykker af forskellige RAID-arrays ved at kombinere dem på forskellige måder. For eksempel - løb samtidigt bcache + LVM tynd, bcache + BTRFS, LVM cache + LVM tynd, en kompleks ZFS-konfiguration med caches eller enhver anden helvedes blanding for at prøve at sammenligne det hele.

På "hardware"-niveau vil vi ikke bruge andet end gode gamle "tykke" LVM-volumener. Undtagelsen fra denne regel kan være backup-partitionen.

Jeg tror på dette tidspunkt, at mange læsere allerede var begyndt at mistænke noget om rededukken.

LVM på SATA HDD

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

Ny VG igen..?Vi ønsker virkelig, at hvis det diskarray, som vi vil bruge til sikkerhedskopiering af data, fejler, vil vores operativsystem fortsætte med at fungere normalt, mens vi bevarer adgangen til ikke-backup-data som normalt. Derfor, for at undgå VG-aktiveringsproblemer, opretter vi en separat VG.

Opsætning af LVM-cache

Lad os oprette en LV på NVMe RAID 1 for at bruge den som en caching-enhed.

#lvcreate -L 70871154688B --name cache root

Hvorfor er der så lidt...?Faktum er, at vores NVMe SSD'er også har en SLC-cache. 4 gigabyte "gratis" og 18 gigabyte dynamisk på grund af den ledige plads, der er optaget i 3-bit MLC. Når først denne cache er opbrugt, vil NVMe SSD'er ikke være meget hurtigere end vores SATA SSD med cache. Af denne grund giver det faktisk ingen mening for os at gøre LVM-cache-partitionen meget større end dobbelt så stor som SLC-cachen på NVMe-drevet. For de anvendte NVMe-drev anser forfatteren det for rimeligt at lave 32-64 gigabyte cache.

Den givne partitionsstørrelse er påkrævet for at organisere 64 gigabyte cache, cache-metadata og metadata-backup.

Derudover bemærker jeg, at efter en beskidt systemnedlukning, vil LVM markere hele cachen som beskidt og vil synkronisere igen. Desuden vil dette blive gentaget hver gang lvchange bruges på denne enhed, indtil systemet genstartes igen. Derfor anbefaler jeg straks at genskabe cachen ved hjælp af det relevante script.

Lad os oprette en LV på SATA RAID 6 for at bruge den som en cache-enhed.

#lvcreate -L 3298543271936B --name cache data

Hvorfor kun tre terabyte..?Så du om nødvendigt kan bruge SATA SSD RAID 6 til nogle andre behov. Størrelsen af ​​det cachelagrede rum kan øges dynamisk, på farten, uden at stoppe systemet. For at gøre dette skal du midlertidigt stoppe og genaktivere cachen, men den karakteristiske fordel ved LVM-cache i forhold til for eksempel bcache er, at dette kan gøres med det samme.

Lad os oprette en ny VG til caching.

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

Lad os oprette en LV på den cachelagrede enhed.

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

Her optog vi straks al den ledige plads på /dev/data/cache, så alle andre nødvendige partitioner blev oprettet med det samme på /dev/root/cache. Hvis du har oprettet noget det forkerte sted, kan du flytte det ved hjælp af pvmove.

Lad os oprette og aktivere cachen:

#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

Hvorfor sådan chunksize..?Gennem praktiske eksperimenter var forfatteren i stand til at finde ud af, at det bedste resultat opnås, hvis størrelsen på LVM-cacheblokken falder sammen med størrelsen på den tynde LVM-blok. Desuden, jo mindre størrelsen er, jo bedre fungerer konfigurationen i en tilfældig optagelse.

64k er den mindst tilladte blokstørrelse for LVM tynd.

Vær forsigtig med tilbageskrivning..!Ja. Denne type cache udskyder skrivesynkronisering til den cachelagrede enhed. Det betyder, at hvis cachen går tabt, kan du miste data på den cachelagrede enhed. Senere vil forfatteren fortælle dig, hvilke foranstaltninger der udover NVMe RAID 1 kan tages for at kompensere for denne risiko.

Denne cachetype blev valgt med vilje for at kompensere for den dårlige tilfældige skriveydeevne af RAID 6.

Lad os tjekke, hvad vi har:

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

Kun [cachedata_corig] skal være placeret på /dev/data/cache. Hvis noget er galt, så brug pvmove.

Du kan om nødvendigt deaktivere cachen med én kommando:

#lvconvert -y --uncache cache/cachedata

Dette gøres online. LVM vil simpelthen synkronisere cachen til disken, fjerne den og omdøbe cachedata_corig tilbage til cachedata.

Opsætning af LVM tynd

Lad os groft vurdere, hvor meget plads vi har brug for til LVM tynde metadata:

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

Afrund op til 4 gigabyte: 4294967296B

Multiplicer med to og tilføj 4194304B for LVM PV-metadata: 8594128896B
Lad os oprette en separat partition på NVMe RAID 1 for at placere tynde LVM-metadata og deres sikkerhedskopi på den:

#lvcreate -L 8594128896B --name images root

For hvad..?Her kan spørgsmålet opstå: hvorfor placere tynde LVM-metadata separat, hvis de stadig vil være cachelagret på NVMe og vil fungere hurtigt.

Selvom hastighed er vigtig her, er det langt fra hovedårsagen. Sagen er, at cachen er et fejlpunkt. Der kan ske noget med det, og hvis de tynde LVM-metadata er cachelagret, vil det medføre, at alt går fuldstændig tabt. Uden komplette metadata vil det være næsten umuligt at samle tynde volumener.

Ved at flytte metadataene til en separat ikke-cache, men hurtig, volumen, garanterer vi sikkerheden af ​​metadataene i tilfælde af cachetab eller korruption. I dette tilfælde vil al skade forårsaget af cachetab være lokaliseret i tynde volumener, hvilket vil forenkle gendannelsesproceduren i størrelsesordener. Med stor sandsynlighed vil disse skader blive genoprettet ved hjælp af FS-logfiler.

Desuden, hvis et øjebliksbillede af et tyndt volumen tidligere blev taget, og derefter cachen blev fuldt synkroniseret mindst én gang, så vil integriteten af ​​snapshottet være garanteret i tilfælde af cachetab på grund af det interne design af LVM thin .

Lad os oprette en ny VG, der vil være ansvarlig for thin-provisioning:

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

Lad os skabe en pool:

#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T images/thin-pool
Hvorfor -Z yUd over hvad denne tilstand faktisk er beregnet til - for at forhindre data fra en virtuel maskine i at lække til en anden virtuel maskine, når der omfordeles plads - bruges nulstilling desuden til at øge hastigheden af ​​tilfældig skrivning i blokke mindre end 64k. Enhver skrivning på mindre end 64k til et tidligere ikke-allokeret område af det tynde volumen vil blive 64K kantjusteret i cachen. Dette vil gøre det muligt at udføre handlingen helt gennem cachen, uden at den cachelagrede enhed.

Lad os flytte LV'erne til de tilsvarende PV'er:

#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

Lad os tjekke:

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

Lad os skabe et tyndt volumen til test:

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

Vi installerer pakker til test og overvågning:

#apt-get install sysstat fio

Sådan kan du observere adfærden af ​​vores lagerkonfiguration i realtid:

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

Sådan kan vi teste vores konfiguration:

#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

Forsigtigt! Ressource!Denne kode vil køre 36 forskellige test, som hver kører i 4 sekunder. Halvdelen af ​​testene er til optagelse. Du kan optage meget på NVMe på 4 sekunder. Op til 3 gigabyte i sekundet. Så hver kørsel af skrivetest kan spise op til 216 gigabyte SSD-ressource fra dig.

Læsning og skrivning blandet?Ja. Det giver mening at køre læse- og skrivetestene separat. Desuden giver det mening at sikre, at alle caches er synkroniseret, så en tidligere lavet skrivning ikke påvirker læsningen.

Resultaterne vil variere meget under den første lancering og de efterfølgende, efterhånden som cachen og den tynde volumen fyldes op, og også afhængigt af om systemet formåede at synkronisere de caches, der blev fyldt under den sidste lancering.

Jeg anbefaler blandt andet at måle hastigheden på en i forvejen fuld tynd volumen, hvorfra der lige er taget et snapshot. Forfatteren havde mulighed for at observere, hvordan tilfældige skrivninger accelererer kraftigt umiddelbart efter oprettelse af det første snapshot, især når cachen endnu ikke er helt fuld. Dette sker på grund af copy-on-write skrivesemantik, justering af cache og tynde volumenblokke, og det faktum, at en tilfældig skrivning til RAID 6 bliver til en tilfældig læsning fra RAID 6 efterfulgt af en skrivning til cachen. I vores konfiguration er tilfældig læsning fra RAID 6 op til 6 gange (antallet af SATA SSD'er i arrayet) hurtigere end at skrive. Fordi blokke for CoW allokeres sekventielt fra en tynd pulje, så bliver optagelsen for det meste også til sekventiel.

Begge disse funktioner kan bruges til din fordel.

Cache "sammenhængende" snapshots

For at reducere risikoen for tab af data i tilfælde af cache-skade/tab, foreslår forfatteren at indføre praksis med at rotere snapshots for at garantere deres integritet i dette tilfælde.

For det første, fordi tynde volumen metadata findes på en ikke-cachelagret enhed, vil metadataene være konsistente, og mulige tab vil blive isoleret inden for datablokke.

Følgende snapshot-rotationscyklus garanterer integriteten af ​​dataene inde i snapshots i tilfælde af cachetab:

  1. For hvert tyndt bind med navnet <navn> skal du oprette et snapshot med navnet <navn>.cached
  2. Lad os sætte migrationstærsklen til en rimelig høj værdi: #lvchange --quiet --cachesettings "migration_threshold=16384" cache/cachedata
  3. I løkken tjekker vi antallet af beskidte blokke i cachen: #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' indtil vi får nul. Hvis nullet mangler for længe, ​​kan det oprettes ved midlertidigt at skifte cachen til gennemskrivningstilstand. Men under hensyntagen til hastighedsegenskaberne for vores SATA- og NVMe SSD-arrays, såvel som deres TBW-ressource, vil du enten hurtigt kunne fange øjeblikket uden at ændre cache-tilstanden, eller også vil din hardware helt æde hele sin ressource i et par dage. På grund af ressourcebegrænsninger er systemet i princippet ikke i stand til at være under 100% skrivebelastning hele tiden. Vores NVMe SSD'er under 100 % skrivebelastning vil helt udtømme ressourcen ind 3-4 dage. SATA SSD'er holder kun dobbelt så længe. Derfor vil vi antage, at det meste af belastningen går til læsning, og vi har relativt kortvarige udbrud af ekstrem høj aktivitet kombineret med en lav belastning i gennemsnit til skrivning.
  4. Så snart vi fangede (eller lavede) et nul, omdøber vi <navn>.cached til <navn>.committed. Det gamle <navn>.committed er slettet.
  5. Hvis cachen er 100 % fuld, kan den eventuelt genskabes med et script og dermed rydde den. Med en halvtom cache arbejder systemet meget hurtigere, når man skriver.
  6. Indstil migrationstærsklen til nul: #lvchange --quiet --cachesettings "migration_threshold=0" cache/cachedata Dette vil midlertidigt forhindre cachen i at synkronisere med hovedmediet.
  7. Vi venter, indtil der samler sig ret mange ændringer i cachen #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' eller timeren slukker.
  8. Vi gentager igen.

Hvorfor problemer med migrationstærsklen...?Sagen er, at i virkelig praksis er en "tilfældig" optagelse faktisk ikke helt tilfældig. Hvis vi skrev noget til en sektor på 4 kilobytes i størrelse, er der stor sandsynlighed for, at der i løbet af de næste par minutter vil blive lavet en registrering til den samme eller en af ​​de tilstødende (+- 32K) sektorer.

Ved at sætte migrationstærsklen til nul, udskyder vi skrivesynkronisering på SATA SSD'en og samler flere ændringer til en 64K blok i cachen. Dette sparer betydeligt ressourcen til SATA SSD.

Hvor er koden..?Desværre anser forfatteren sig selv for utilstrækkelig kompetent i udviklingen af ​​bash-scripts, fordi han er 100% autodidakt og praktiserer "google"-drevet udvikling, derfor mener han, at den forfærdelige kode, der kommer ud af hans hænder, ikke bør bruges af nogen andet.

Jeg tror, ​​at fagfolk på dette område selvstændigt vil være i stand til at skildre al den logik, der er beskrevet ovenfor, om nødvendigt, og måske endda smukt designe den som en systemd service, som forfatteren forsøgte at gøre.

Et sådant simpelt snapshot-rotationsskema vil tillade os ikke kun konstant at have ét snapshot fuldt synkroniseret på SATA SSD'en, men vil også give os mulighed for ved hjælp af thin_delta-værktøjet at finde ud af, hvilke blokke der blev ændret efter dets oprettelse, og dermed lokalisere skader på de vigtigste mængder, hvilket i høj grad forenkler inddrivelsen.

TRIM/DISCARD i libvirt/KVM

Fordi datalageret vil blive brugt til KVM, der kører libvirt, så vil det være en god idé at lære vores VM'er ikke kun at optage ledig plads, men også at frigøre det, der ikke længere er nødvendigt.

Dette gøres ved at emulere TRIM/DISCARD-understøttelse på virtuelle diske. For at gøre dette skal du ændre controllertypen til virtio-scsi og redigere 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>

Sådanne DISCARDs fra gæsteoperativsystemer behandles korrekt af LVM, og blokke frigives korrekt både i cachen og i den tynde pulje. I vores tilfælde sker dette hovedsageligt på en forsinket måde, når du sletter det næste snapshot.

BTRFS backup

Brug færdige scripts med крайней forsigtighed og på egen risiko. Forfatteren skrev denne kode selv og udelukkende for sig selv. Jeg er sikker på, at mange erfarne Linux-brugere har lignende værktøjer, og der er ingen grund til at kopiere andres.

Lad os oprette en volumen på backup-enheden:

#lvcreate -L 256G --name backup backup

Lad os formatere det i BTRFS:

#mkfs.btrfs /dev/backup/backup

Lad os oprette monteringspunkter og montere rodundersektionerne af filsystemet:

#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

Lad os oprette mapper til sikkerhedskopier:

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

Lad os oprette en mappe til backup-scripts:

#mkdir /root/btrfs-backup

Lad os kopiere scriptet:

Masser af skræmmende bash-kode. Brug på eget ansvar. Skriv ikke vrede breve til forfatteren...#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

Hvad gør det overhovedet..?Indeholder et sæt simple kommandoer til at skabe BTRFS-snapshots og kopiere dem til en anden FS ved hjælp af BTRFS send/modtag.

Den første lancering kan være forholdsvis lang, fordi... I begyndelsen vil alle data blive kopieret. Yderligere lanceringer vil være meget hurtige, fordi... Kun ændringer vil blive kopieret.

Et andet script, som vi vil sætte ind i cron:

Lidt mere bash-kode#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

Hvad gør den..?Opretter og synkroniserer trinvise snapshots af de angivne BTRFS-volumener på backup-FS'en. Herefter sletter den alle billeder oprettet for 60 dage siden. Efter lanceringen vil daterede snapshots af de angivne volumener blive vist i /backup/btrfs/back/remote/ undermapper.

Lad os give koden eksekveringsrettigheder:

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

Lad os tjekke det og sætte det i 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

LVM tynd backup

Lad os skabe en tynd pulje på backup-enheden:

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

Lad os installere ddrescue, fordi... scripts vil bruge dette værktøj:

#apt-get install gddrescue

Lad os oprette en mappe til scripts:

#mkdir /root/lvm-thin-backup

Lad os kopiere scripts:

En masse bash indeni...#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

Hvad gør den...?Indeholder et sæt kommandoer til at manipulere tynde snapshots og synkronisere forskellen mellem to tynde snapshots modtaget via thin_delta til en anden blokenhed ved hjælp af ddrescue og blkdiscard.

Et andet script, som vi vil sætte i cron:

Lidt mere bash#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

Hvad gør den...?Bruger det forrige script til at oprette og synkronisere sikkerhedskopier af de anførte tynde enheder. Scriptet vil efterlade inaktive snapshots af de angivne volumener, som er nødvendige for at spore ændringer siden sidste synkronisering.

Dette script skal redigeres og angive listen over tynde bind, som der skal laves sikkerhedskopier af. De angivne navne er kun til illustrative formål. Hvis du ønsker det, kan du skrive et script, der vil synkronisere alle bind.

Lad os give rettighederne:

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

Lad os tjekke det og sætte det i 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

Den første lancering bliver lang, fordi... tynde volumener vil blive fuldt synkroniseret ved at kopiere al brugt plads. Takket være tynde LVM-metadata ved vi, hvilke blokke der faktisk er i brug, så kun faktisk brugte tynde volumenblokke vil blive kopieret.

Efterfølgende kørsler kopierer dataene trinvist takket være ændringssporing via tynde LVM-metadata.

Lad os se, hvad der skete:

#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

Hvad har det at gøre med yngledukker?

Mest sandsynligt, givet at LVM LV logiske volumener kan være LVM PV fysiske volumener for andre VG'er. LVM kan være rekursiv, ligesom dukker, der indlejrer sig. Dette giver LVM ekstrem fleksibilitet.

PS

I den næste artikel vil vi forsøge at bruge flere lignende mobile storage-systemer/KVM som grundlag for at skabe en geo-distribueret storage/vm-klynge med redundans på flere kontinenter ved hjælp af hjemmecomputere, hjemmeinternettet og P2P-netværk.

Kilde: www.habr.com

Tilføj en kommentar