God eftermiddag.
Jeg vil dele min praktiske erfaring med samfundet om opbygning af et lagringssystem til KVM ved hjælp af md RAID + LVM.
Programmet vil indeholde:
- Sammenstilling af md RAID 1 med NVMe SSD.
- Sammenstilling af md RAID 6 med SATA SSD og almindelige diske.
- Egenskaber ved TRIM/DISCARD på SSD RAID 1/6.
- Oprettelse af et bootbart md RAID 1/6 array på et sættet af diske.
- Installation af systemet på NVMe RAID 1 uden NVMe-support i BIOS.
- Brug af LVM cache og LVM thin.
- Brug af BTRFS snapshots og send/receive til backup.
- Brug af LVM thin snapshots og thin_delta til BTRFS-lignende backup.
Hvis du er interesseret, så læs videre.
Erklæring
Forfatteren påtager sig intet ansvar for konsekvenserne af brugen eller ikke-brugen af materialer/eksempler/kode/råd/data fra denne artikel. Når du læser eller på nogen måde bruger dette materiale, påtager du dig ansvaret for alle konsekvenser af disse handlinger. Mulige konsekvenser inkluderer:
- Brændte NVMe SSD'er til en sprød overflade.
- Fuldt udslidt skrive ressourcer og fejl på SSD-lagringsenheder.
- Total tab af alle data på alle lagringsenheder, inklusive backups.
- Defekt computerhardware.
- Brugt tid, nerver og penge.
- Enhver anden konsekvens, som ikke er nævnt ovenfor.
Hardware
Der var tilgængeligt:
Et bundkort fra omkring 2013 med Z87 chipset sammen med Intel Core i7 / Haswell.
- Processor med 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 porte
SAS-adapter LSI SAS9211-8I flash'et til IT / HBA tilstand. RAID-firmwaren er med vilje blevet udskiftet med HBA-firmware for at:
- Man til enhver tid kunne smide denne adapter væk og erstatte den med en hvilken som helst anden tilfældig adapter.
- TRIM/Discard fungerede fint på diske, da disse kommandoer ikke understøttes i RAID-firmwaren, mens HBA i det store hele ikke er ligeglad med, hvilke kommandoer der sendes over bussen.
Harddiske — 8 stk. HGST Travelstar 7K1000 med 1 TB kapacitet i 2.5" formfaktor, som til bærbare. Disse diske var tidligere i et RAID 6 array. De vil også finde anvendelse i det nye system. Til opbevaring af lokale sikkerhedskopier.
Der blev desuden tilføjet:
6 stk. SATA SSD-modeller fra Samsung 860 QVO med 2TB. Disse SSD'er krævede høj kapacitet, tilstedeværelse af SLC-cache, ønskede pålidelighed og en rimelig pris. Support for discard/zero var obligatorisk, hvilket kan kontrolleres med en linje i dmesg:
kernel: ata1.00: Aktivere discard_zeroes_data
2 stk NVMe SSD model Samsung SSD 970 EVO 500GB.
For disse SSD'er er tilfældelæsning/skrivning og ressourcer vigtige for dine behov. Køleribber til dem. Nødvendigt. Absolut nødvendigt. Ellers vil de overophede til sprødhed ved den første RAID-synkronisering.
StarTech PEX8M2E2-adapter til 2 x NVMe SSD'er til installation i PCIe 3.0 8x slot. Dette er igen blot en HBA, men til NVMe. Den adskiller sig fra de billige adaptere ved ikke at kræve PCIe bifurcation fra moderbordet takket være den indbyggede PCIe-switch. Den vil fungere selv i det ældste system, hvor der er PCIe, selvom det er en x1 PCIe 1.0 slot. Selvfølgelig med passende hastighed. Der er ingen RAID her. Ingen indbygget BIOS om bord. Så din system vil magisk ikke lære at starte fra NVMe og slet ikke lave NVMe RAID på grund af denne enhed.
Denne komponent blev udelukkende valgt på grund af kun én ledig 8x PCIe 3.0 i systemet, og med 2 ledige slots kan den let ændres til to billige PEX4M2E1 eller lignende, som kan købes overalt til en pris fra 600 rubler.
Afvisningen af alle mulige hardware- eller integrerede chipset/BIOS RAID'er blev gjort bevidst for at have mulighed for helt at erstatte hele systemet, bortset fra selve SSD/HDD'erne, og bevare alle data. Ideelt set for at kunne bevare selv det installerede operativsystem ved skift til helt nyt/andre hardware. Det vigtigste er, at der er SATA- og PCIe-porte. Dette er som et live CD eller bootbart USB-drev, bare meget hurtigt og lidt større.
HumorFor det ved du, hvordan det er — nogle gange skal man hurtigt tage hele arrayet med sig. Og man vil ikke miste data. Til dette placeres alle nævnte medier praktisk på skinner i 5.25 standard chassis.
Og selvfølgelig til eksperimenter med forskellige metoder til SSD-caching i Linux.
Hardware RAID'er er kedelige. Du tænder dem. De fungerer eller ikke. Men med mdadm er der altid muligheder.
Software
Tidligere var Debian 8 Jessie installeret på hardwaren, som er tæt på EOL. Der blev oprettet en RAID 6 af de nævnte HDD'er i par med LVM. På den kørte virtuelle maskiner i kvm/libvirt.
Da forfatteren har passende erfaring med at oprette bærbare bootable SATA/NVMe USB-drev, og for ikke at bryde den velkendte apt-skabelon, blev Ubuntu 18.04 valgt som det primære system, som allerede er blevet tilstrækkeligt stabilt, men stadig har 3 års support i fremtiden.
Det nævnte system har alle de nødvendige hardwaredrivere fra starten. Vi har ikke brug for nogen tredjepartssoftware eller drivere.
Forberedelse til installation
For at installere systemet har vi brug for Ubuntu Desktop-billede. Serverversionen har en kernel-installationsprogram, der udviser en overdreven, ikke-deaktiverbar autonomi, som tvinger UEFI-systempartitionen ind på en af diskene og ødelægger hele æstetikken. Det installeres derfor kun i UEFI-tilstand. Der er ikke andre muligheder.
Dette er ikke acceptabelt for os.
Hvorfor?Desværre er UEFI-boot ekstremt dårligt kompatibelt med software RAID, da ingen tilbyder redundans for UEFI ESP-partitionen. Der er opskrifter online, som foreslår at placere ESP-partitionen på et USB-drev, men det er et svaghedspunkt. Der er opskrifter, der bruger software mdadm RAID 1 med metadata i version 0.9, som ikke hindrer UEFI BIOS i at se denne partition, men dette holder kun indtil det lykkelige øjeblik, hvor BIOS eller et andet operativsystem på hardwaren skriver noget til ESP uden at synkronisere med de andre spejle.
Derudover afhænger UEFI-boot af NVRAM, som ikke flytter med hardiskene til det nye system, da det er en del af bundkortet.
Så vi vil ikke opfinde et nyt hjul. Vi har allerede en klar, gennemprøvet oldschool-løsning, der nu kaldes Legacy/BIOS boot, der bærer det stolte navn CSM på UEFI-kompatible systemer. Vi vil bare tage det ned fra hylden, smøre det, pumpe dækkene op og tørre det af med en fugtig klud.
Desktop-versionen af Ubuntu kan heller ikke installeres korrekt med Legacy-bootloaderen, men som man siger, er der mindst nogle muligheder.
Så lad os samle hardwaren og boote systemet fra et bootable USB-drev med Ubuntu Live. Vi skal downloade pakker, så vi indstiller netværket til det, der fungerer. Hvis det ikke fungerer, kan de nødvendige pakker forhåndsinstalleres på USB-drevet.
Vi går ind i desktop-miljøet, åbner terminalemulatoren, og så kører vi:
#sudo bash
Hvordan…?Linjen ovenfor er den kanoniske trigger for debatter fra holivarer om sudo. Med bfleste afStørre muligheder kommer også med et større ansvar. Spørgsmålet er, om du kan tage det på dig. Mange mener, at brugen af sudo på denne måde er, om ikke uansvarligt, så i det mindste forsigtigt. Men:fleste afHvorfor ikke ZFS…?

#apt-get install mdadm lvm2 thin-provisioning-tools btrfs-tools util-linux lsscsi nvme-cli mc
Når vi installerer software på vores computer, låner vi i bund og grund vores hardware til udviklerne af denne software.Når vi stoler på, at denne software beskytter vores data, tager vi et lån svarende til omkostningerne ved at genetablere disse data, som vi en dag må betale tilbage.
Set fra denne vinkel er ZFS en Ferrari, mens mdadm+lvm mere ligner en cykel.
Subjektivt foretrækker forfatteren at låne ukendte personer den lånte cykel frem for Ferrarien. Omkostningerne er ikke høje. Der kræves ikke en license. Reglerne er enklere. Parkering er gratis. Terrænet er bedre. Til en cykel kan man altid skaffe ben, og det er muligt at reparere cyklen selv.
Hvad så med BTRFS…?
For at starte operativsystemet har vi brug for et filsystem, der understøttes af Legacy/BIOS GRUB out of the box og samtidig understøtter snapshots on-the-fly. Vi vil bruge det til /boot-partitionen. Derudover foretrækker forfatteren at bruge dette filsystem til / (roden), mens han bemærker, at der kan oprettes separate partitioner på LVM til enhver anden software og monteres i de nødvendige kataloger.Verken billeder
, eller databaser vil blive opbevaret på dette filsystem. virtual machinesDette filsystem vil kun blive brugt til at skabe øjeblikkelige snapshots af systemet uden at slukke det og efterfølgende overføre disse snapshots til en backupdisk ved hjælp af send/receive.
Derudover foretrækker forfatteren generelt at holde minimum af software direkte på hardwaren og køre al anden software i virtuelle maskiner ved hjælp af ting som GPU- og PCI-USB-hostcontrollere i KVM via IOMMU.
På hardwaren forbliver kun - datalagring, virtualisering og backup.
Hvis du stoler mere på ZFS, så er de grundlæggende set udskiftelige til det anførte formål.
Alligevel ignorerer forfatteren bevidst de indbyggede funktioner til spejling / RAID og redundans, som findes i ZFS, BTRFS og LVM.
Dog trods forfatteren bevidst de indbyggede funktioner til spejling / RAID og redundans, der findes i ZFS, BRTFS og LVM.
Som et ekstra argument har BTRFS egenskaben til at omdanne tilfældige skrivninger til sekventielle, hvilket har en ekstremt positiv indflydelse på hastigheden ved synkronisering af snapshots / sikkerhedskopier på HDD.
Lad os gen-scanne alle enheder:
#udevadm control --reload-rules && udevadm trigger
Lad os se os omkring:
#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
Partitionering af "diskene"
NVMe SSD
Vi vil slet ikke partitionere dem. Vores BIOS kan alligevel ikke se disse enheder. Så de vil helt gå ind i software RAID. Vi vil ikke engang oprette partitioner der. Hvis man ønsker at følge "kanonen" eller "principielt" - så opret en stor partition, ligesom med HDD.
SATA HDD
Her er der ikke meget at finde på. Vi opretter én partition til det hele. Vi opretter en partition, fordi disse diske kan ses af BIOS, og det kan endda forsøge at starte fra dem. Vi vil endda senere installere GRUB på disse diske, så systemet pludselig lykkes.
#cat >hdd.part << EOF
etiket: dos
etiket-id: 0x00000000
enhed: /dev/sdg
enhed: sektorer
/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
Her bliver det virkelig interessant.
For det første har vores lagringsenheder en størrelse på 2 TB. Det ligger inden for det tilladelige for MBR, som vi vil benytte. Om nødvendigt kan vi skifte til GPT. GPT-diske har et kompatibilitetslager, 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 både boot-partitionen og bios_grub-partitionen på disse diske skal være i starten. Dette gør det muligt at udføre Legacy/BIOS-opstart selv fra GPT-diske.
Men det er ikke vores tilfælde.
Her vil vi oprette to partitioner. Den første vil være 1 GB og bruges til RAID 1 /boot.
Den anden vil blive brugt til RAID 6 og tage alt det resterende frie plads undtagen et lille uoprettet område i slutningen af lagringsenheden.
Hvad er det for et uoprettet område?Ifølge kilder på nettet har vores SATA SSD'er en dynamisk udvidelig SLC-cache størrelse på mellem 6 og 78 gigabyte. De 6 gigabyte får vi "gratis" på grund af forskellen mellem "gigabytes" og "gibibytes" i det tekniske dokument for lagerenheden. De resterende 72 gigabyte tildeles fra det ubrugte rum.
Her må det bemærkes, at vores cache er SLC, mens pladsen bruges i 4 bit MLC-tilstand. Hvad det effektivt betyder for os er, at for hver 4 gigabyte ledig plads får vi kun 1 gigabyte SLC-cache.
Vi multiplicerer 72 gigabyte med 4 og får 288 gigabyte. Dette er det frie rum, som vi ikke vil partitionere for at lade lagringsenhederne bruge SLC-cachen fuldt ud.
Dermed får vi effektivt op til 312 GB SLC-cache samlet fra seks enheder. Af alle enhederne vil 2 blive brugt i RAID for redundans.
Denne mængde cache vil gøre, at vi sjældent vil opleve, at skrivninger ikke går i cache i praksis. Dette kompenserer ekstremt godt for den mest beklagelige ulempe ved QLC-hukommelse, nemlig den ekstremt lave skrivehastighed, når data skrives uden om cachen. Hvis dine belastninger ikke matcher dette, anbefaler jeg, at du tænker nøje over, hvor længe dine SSD'er vil holde under en sådan belastning, i betragtning af TBW fra databladet.
#cat >ssd.part << EOF
etiket: dos
etiket-id: 0x00000000
enhed: /dev/sda
enhed: sektorer
/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
For at begynde skal vi ændre navnet på maskinen. Dette er nødvendigt, da værtens navn er en del af arrayets navn et sted inde i mdadm, og det påvirker noget. Arrays kan selvfølgelig ændres senere, men det er unødvendige handlinger.
#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 —assume-clean…?For ikke at initialisere arrays. For begge RAID-niveauer 1 og 6 er dette acceptabelt. Det kan fungere uden initialisering, hvis det er et nyt array. Desuden er initialisering af SSD-array ved oprettelse en spildt ressource i forhold til TBW. Vi bruger TRIM/DISCARD, hvor det er muligt, på de samlede SSD-arrays for at 'initialisere' dem.
For SSD RAID 1 arrays understøttes DISCARD som standard.
For SSD RAID 6 arrays skal DISCARD aktiveres i kernelmodulens indstillinger.
Dette bør kun gøres, når alle SSD'er brugt i arrays på niveau 4/5/6 i dette system har fungerende support til discard_zeroes_data. Nogle gange støder man på mærkelige enheder, der rapporterer til kernen, at de understøtter denne funktion, men faktisk ikke gør det, eller funktionen ikke altid fungerer. På nuværende tidspunkt er der næsten altid støtte, men gamle enheder og fejlbehæftede firmware kan forekomme. Af denne grund er DISCARD support som standard deaktiveret for RAID 6.
Advarsel, den næste kommando vil destruere alle data på NVMe-enhederne ved at 'initialisere' arrayet til 'nuller'.
#blkdiscard /dev/md0
Hvis noget gik galt, så prøv at angive et skridt.
#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…?Større chunk-size har en positiv indvirkning på hastigheden af tilfældig læsning i blokke op til og med chunk-size. Dette skyldes, at en operation af det relevante størrelse eller mindre kan udføres fuldt ud på én enhed. Derfor summeres IOPS fra alle enheder. Statistikker viser, at 99% af IO'en ikke overstiger 512K.
Ved RAID 6 er IOPS ved skrivning altid mindre end eller lig med IOPS for en enkelt enhed. Derimod kan IOPS ved tilfældig læsning være mange gange højere end for en enkelt enhed, og her spiller blokstørrelsen en afgørende rolle.
Forfatteren ser ikke mening i at forsøge at optimere en parameter, der er dårlig ved RAID 6 fra starten, og optimerer i stedet det, hvor RAID 6 klarer sig godt.
Den dårlige tilfældige skrivning ved RAID 6 vil vi kompensere for med NVMe-cache og tricks med thin-provisioning.
Vi har endnu ikke aktiveret DISCARD for RAID 6. Så vi vil ikke "initialisere" denne array endnu. Det vil vi gøre senere, efter OS-installationen.
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 rod-FS på NVMe RAID 1, som er /dev/md0.
Alligevel vil vi have brug for denne hurtige array til andre behov, såsom swap, metadata og LVM-cache, samt metadata til LVM-thin, så vi vil oprette en LVM VG på denne array.
#pvcreate /dev/md0
#vgcreate root /dev/md0
Lad os oprette en partition til rod-FS.
#lvcreate -L 128G --name root root
Lad os oprette en partition til swap, der svarer til størrelsen på RAM.
#lvcreate -L 32G --name swap root
Installation af OS
I alt har vi alt, hvad vi behøver for at installere systemet.
Vi starter installationsguiden fra Ubuntu Live-miljøet. Almindelig installation. Kun på tidspunktet for valg af diske til installation skal vi angive følgende:
- /dev/md1, — точка монтирования /boot, ФС — BTRFS
- /dev/root/root (a.k.a /dev/mapper/root-root), — точка монтирования / (корень), ФС — BTRFS
- /dev/root/swap (a.k.a /dev/mapper/root-swap), — использовать как раздел подкачки
- Installer bootloader på /dev/sda
Når BTRFS vælges som rod-FS, vil installatoren automatisk oprette to BTRFS-volumener kaldet "@" til / (roden) og "@home" til /home.
Starter installationen…
Installationen vil afsluttes med en modal dialog, der informerer om en fejl under installationen af bootloaderen. Desværre kan vi ikke forlade denne dialog med standardmetoder og fortsætte installationen. Vi logger ud af systemet og logger ind igen, hvilket bringer os til et rent skrivebord i Ubuntu Live. Vi åbner terminalen og gør det igen:
#sudo bash
Opretter 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 hostname 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
Gå ind i chroot-miljøet:
#chroot /mnt/chroot
Først installer vi pakkerne:
apt-get install --reinstall mdadm lvm2 thin-provisioning-tools btrfs-tools util-linux lsscsi nvme-cli mc debsums hdparm
Lad os kontrollere og rette alle pakker, der er installeret forkert på grund af en ufuldstændig installation af systemet:
#CORRUPTED_PACKAGES=$(debsums -s 2>&1 | awk '{print $6}' | uniq)
#apt-get install --reinstall $CORRUPTED_PACKAGES
Hvis noget ikke fungerede, kan det være, at du skal redigere /etc/apt/sources.list først.
Lad os justere indstillingerne for RAID 6-modulet for at aktivere TRIM/DISCARD:
#cat >/etc/modprobe.d/raid456.conf << EOF
muligheder raid456 enheder_håndter_discard_sikkert=1
EOF
Lad os justere vores arrays en smule:
#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 en række udev-regler, der vil gøre følgende:
- Sætte en passende cache-størrelse for blokke til RAID 6 for 2020. Standardværdien ser ikke ud til at være ændret siden Linux blev oprettet, og er for længst ikke længere passende.
- Reserver minimum IO under kontrol/synkroniseringsperioder for arrayene. Dette er nødvendigt for at dine array ikke sidder fast i en tilstand af evig synkronisering under belastning.
- Begræns maksimum IO under kontrol/synkroniseringsperioder for arrayene. Dette er nødvendigt, så synkronisering/kontrol af SSD RAID ikke overopheder dine lagringsenheder. Især relevant for NVMe. (Husk på køleribben? Jeg mente det ikke som en joke.)
- Forbyde diske at standse spindle-drejning via APM og indstille timeout for diskcontrollerens søvn til 7 timer. Du kan helt deaktivere APM, hvis dine diske understøtter det (-B 255). Med standardværdien vil diskene stoppe efter fem sekunder. Så ønsker OS at tilbageføre diskcache, diskene vil dreje sig igen, og alt starter forfra. Diskene har et begrænset maksimum antal drejninger. Sådan en simpel cyklus som standard kan nemt ødelægge dine diske på et par år. Ikke alle diske lider under dette, men vores 'bærbare', med tilsvarende standardindstillinger, der gør RAID til en skæv version af mini-MAID.
- Indstille readahead på (roterende) diske til 1 megabyte - to sekventielle blokke/chunks RAID 6
- Forbyde readahead på selve arrayene.
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 sådanne..?Vi vil søge efter partitionen /boot via UUID, da navngivningen af arrayene teoretisk kan ændre sig.
De øvrige partitioner vil vi søge efter via LVM-navne i notationen /dev/mapper/vg-lv, da de unikt identificerer partitionerne.
Vi bruger ikke UUID til LVM, da UUID for LVM-volumener og deres snapshots kan være ens.Monterer vi /dev/mapper/root-root to gange..?Ja. Sådan er det. En funktion ved BTRFS. Dette filsystem kan monteres flere gange med forskellige subvol.
På grund af denne samme funktion anbefaler jeg aldrig at oprette LVM-snapshots af aktive BTRFS-volumener. Du kan få en overraskelse ved genstart.
Vi regenererer 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 LVM thin pools, når 90% af pladsen er brugt, med en stigning på 5% af kapaciteten.
Vi har øget det maksimale antal cacheblokke for LVM cache.
Vi har forbudt LVM at søge LVM volumener (PV) på:
- enheder indeholdende LVM cache (cdata)
- enheder, der er cachet ved hjælp af LVM cache uden om cachen (_corig). Det cachede enhed vil stadig blive scannet gennem cachen (bare ).
- enheder indeholdende metadata for LVM cache (cmeta)
- alle enheder i VG med navnet images. Her vil vi have billeder af virtuelle maskiners diske, og vi ønsker ikke, at LVM på værten aktiverer volumener, der tilhører gæste-OS.
- alle enheder i VG med navnet backup. Her vil vi have sikkerhedskopier af billeder af virtuelle maskiner.
- alle enheder, hvis navn slutter på «gpv» (guest physical volume)
Vi har aktiveret støtte til DISCARD ved frigivelse af ledig plads på LVM VG. Vær forsigtig. Dette vil gøre sletning af LV på SSD ret langvarig. Dette gælder især for SSD RAID 6. Men ifølge planen vil vi bruge thin provisioning, så dette vil ikke være et problem for os.
Vi opdaterer initramfs-billedet:
#update-initramfs -u -k all
Vi installerer og konfigurerer grub:
#apt-get install grub-pc
#apt-get purge os-prober
#dpkg-reconfigure grub-pc
Hvilke diske skal vælges?Alle dem, der hedder sd*. Systemet skal være i stand til at boote fra enhver fungerende SATA-disk eller SSD.
Hvorfor stoppede vi os-prober..?For overdrevet selvstændighed og nysgerrige hænder.
Det fungerer ikke korrekt, hvis en af RAID'erne er i en degraderet tilstand. Det prøver at finde OS på partitioner, der bruges i virtuelle maskiner, der kører på dette hardware.
Hvis du har brug for det, kan du lade det være, men vær opmærksom på alt ovenstående. Jeg anbefaler at søge efter opskrifter på at slippe af med nysgerrige hænder online.
Her slutter vi den indledende installation. Det er tid til at genstarte i det nyinstallerede OS. Husk at fjerne bootable Live CD/USB.
#exit
#reboot
Som boot-enhed vælger vi en hvilken som helst af SATA SSD.
LVM på SATA SSD
På dette tidspunkt er vi allerede bootet ind i det nye OS, har konfigureret netværket, apt, åbnet terminalemulatoren og kørt:
#sudo bash
Lad os fortsætte.
«Initialiserer» arrayet af SATA SSD:
#blkdiscard /dev/md2
Hvis det ikke virker, prøv:
#blkdiscard --step 65536 /dev/md2
Opretter LVM VG på SATA SSD:
#pvcreate /dev/md2
#vgcreate data /dev/md2
Hvorfor endnu en VG..?I virkeligheden har vi allerede en VG med navnet root. Hvorfor ikke tilføje alt til én VG?
Hvis der er flere PV'er i VG, skal alle PV'er være til stede (online) for korrekt aktivering af VG. Undtagelsen er LVM RAID, som vi med vilje ikke bruger.
Vi ønsker virkelig, at operativsystemet skal kunne starte normalt og give os mulighed for at løse problemet i tilfælde af nedbrud (læs datatab) på nogen af RAID 6-arrays.
Til dette formål vil vi på det første abstraheringsniveau isolere hver type fysisk "lager" i en separat VG.
Hvis vi taler teknisk, så relaterer forskellige RAID-arrays til forskellige "pålidelighedsdomaener". Det er ikke en god idé at skabe et ekstra fælles fejlpunk ved at putte dem i en VG.
Tilstedeværelsen af LVM på "hardware"-niveau giver os mulighed for frit at skære stykker fra forskellige RAID-arrays og kombinere dem på forskellige måder. For eksempel - at køre samtidigt bcache + LVM thin, bcache + BTRFS, LVM cache + LVM thin, en kompleks ZFS-konfiguration med caches eller enhver anden onde blanding, for at kunne undersøge og sammenligne det hele.
På "hardware"-niveau vil vi ikke bruge andet end de gamle gode "tykke" LVM-volumener. En mulig undtagelse fra denne regel kan være partitionen til backup.
Jeg tror, at mange læsere allerede er begyndt at mistænke noget angående matrjosjkaen.
LVM på SATA HDD
#pvcreate /dev/md3
#vgcreate backup /dev/md3
Igen en ny VG..?Vi ønsker virkelig, at vores operativsystem fortsat fungerer normalt, når diskarrayet, vi bruger til at sikkerhedskopiere data, fejler, samtidig med at vi bevarer adgangen til de ikke-sikkerhedskopierede data. Derfor, for at undgå aktiveringsproblemer med VG, opretter vi en separat VG.
Opsætning af LVM cache
Vi opretter en LV på NVMe RAID 1 for at bruge den som cache-enhed.
#lvcreate -L 70871154688B --name cache root
Hvorfor så lidt…?Det skyldes, at vores NVMe SSD'er også har SLC-cache. 4 gigabyte "gratis" og 18 gigabyte dynamisk takket være det ledige plads der bruges i 3-bit MLC. Når denne cache er opbrugt, vil NVMe SSD'erne ikke være meget hurtigere end vores SATA SSD med cache. Derfor giver det ikke mening at lave LVM cache-partitionen meget større end det dobbelte af SLC-cache-størrelsen på NVMe-lageret. For de anvendte NVMe-lagringer mener forfatteren, at en cache på 32-64 gigabyte er rimelig.
Den angivne partitionsstørrelse er nødvendig for at organisere 64 gigabyte cache, placere cachemetadata og tage backup af metadata.
Jeg vil også bemærke, at efter en hård nedlukning af systemet vil LVM markere hele cachen som beskadiget og synkronisere den igen. Desuden vil dette ske ved hver brug af lvchange på denne enhed indtil en ny genstart af systemet. Derfor anbefaler jeg at genskabe cachen straks ved hjælp af det relevante script.
Vi opretter 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å der, hvis nødvendigt, kan bruges SATA SSD RAID 6 til andre formål. Den cachede plads kan dynamisk øges i realtid uden at stoppe systemets drift. Til dette skal cachen midlertidigt stoppes og derefter genaktiveres, men en fremtrædende fordel ved LVM-cache i forhold til for eksempel bcache er, at dette kan gøres på farten.
Vi opretter et nyt VG til caching.
#pvcreate /dev/root/cache
#pvcreate /dev/data/cache
#vgcreate cache /dev/root/cache /dev/data/cache
Vi opretter en LV på den cachede enhed.
#lvcreate -L 3298539077632B --name cachedata cache /dev/data/cache
Her har vi straks taget alt det ledige plads på /dev/data/cache, så alle andre nødvendige partitioner kan oprettes direkte på /dev/root/cache. Hvis noget er blevet oprettet et andet sted, kan det flyttes ved hjælp af pvmove.
Vi opretter og aktiverer 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 en sådan chunksize..?Gennem praktiske eksperimenter har forfatteren fundet ud af, at det bedste resultat opnås, når blokkens størrelse for LVM cache matcher blokkens størrelse for LVM thin. Jo mindre størrelse, jo bedre præstation leverer konfigurationen ved tilfældig skrivning.
64k er den minimale blokkestørrelse, der er tilladt for LVM thin.
Pas på med writeback..!Ja. Denne type cache udsætter synkroniseringen af skrivningen til den cachede enhed. Dette kan føre til datatab på den cachede enhed, hvis cachen går tabt. Senere vil forfatteren beskrive, hvilke foranstaltninger, ud over NVMe RAID 1, der kan træffes for at kompensere for denne risiko.
Denne type cache er valgt specifikt for at kompensere for den lave ydeevne ved RAID 6 ved tilfældig skrivning.
Lad os tjekke, hvad vi har opnået:
#lvs -a -o lv_name,lv_size,devices --units B cache
LV LSize Enheder
[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)
På /dev/data/cache skal der kun være [cachedata_corig]. Hvis noget ikke ser rigtigt ud, skal du bruge pvmove.
Cachen kan deaktiveres ved behov med én kommando:
#lvconvert -y --uncache cache/cachedata
Dette gøres on-line. LVM synkroniserer blot cachen til disken, sletter den og omdøber cachedata_corig tilbage til cachedata.
Konfiguration af LVM thin
Lad os estimere, hvor meget plads vi får brug for til metadata for LVM thin:
#thin_metadata_size --block-size=64k --pool-size=6terabytes --max-thins=100000 -u bytes
thin_metadata_size - 3385794560 bytes estimeret metadata område størrelse for "--block-size=64kibibytes --pool-size=6terabytes --max-thins=100000"
Lad os runde op til 4 gigabyte: 4294967296B
Multiplicér med to og læg 4194304B til metadataene LVM PV: 8594128896B
Lad os oprette en separat partition på NVMe RAID 1 til at placere LVM thin metadataene og deres backup:
#lvcreate -L 8594128896B --name images root
Hvorfor..?Her kan spørgsmålet opstå, hvorfor man skal placere LVM thin metadataene separat, når de stadig vil blive cachet på NVMe og vil fungere hurtigt.
Selvom hastigheden her er vigtig, er det langt fra den primære årsag. Problemet er, at cache er et fejlpunk. Der kan ske noget med det, og hvis LVM thin metadataene er cachet, vil det føre til fuldstændigt tab af alt. Uden hele metadataene vil det være næsten umuligt at samle de tynde volumer.
Ved at flytte metadataene til en separat, ikke-cachet, men hurtig volum, sikrer vi bevarelsen af metadataene i tilfælde af tab eller beskadigelse af cachen. I dette tilfælde vil alle skader forårsaget af tab af cache blive lokaliseret inden for de tynde volumer, hvilket vil forenkle genopretningsproceduren betydeligt. Med stor sandsynlighed vil disse skader være genoprettelige ved hjælp af FS-logfiler.
Desuden, hvis der tidligere blev taget et øjebliksbillede af det tynde volum, og efter dette blev cachen fuldstændig synkroniseret mindst én gang, vil integriteten af øjebliksbilledet være garanteret i tilfælde af tab af cache, på grund af egenskaberne ved LVM thin.
Lad os oprette en ny VG, som vil stå for thin-provisioning:
#pvcreate /dev/root/images
#pvcreate /dev/cache/cachedata
#vgcreate images /dev/root/images /dev/cache/cachedata
Lad os oprette et pool:
#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T images/thin-pool
Hvorfor -Z yUdover hvad denne tilstand faktisk er beregnet til - at forhindre data fra én virtuel maskine i at lække til en anden virtuel maskine ved omfordeling af plads - bruges zeroing desuden til at øge hastigheden af tilfældige skrivninger med blokke mindre end 64k. Enhver skrivning mindre end 64k til et tidligere ikke tildelt område af det tynde volum vil blive omdannet til 64K justeret til cachegrænsen. Dette vil muliggøre at udføre operationen helt gennem cachen uden om det cachede enhed.
Lad os flytte LV til de relevante PV:
#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 Enheder
[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 oprette et tyndt volum til tests:
#lvcreate -V 64G --thin-pool thin-pool --name test images
Lad os installere pakker til test og overvågning:
#apt-get install sysstat fio
Sådan kan vi overvåge 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
Pas på! Ressource!Denne kode vil køre 36 forskellige tests, hvor hver test varer i 4 sekunder. Halvdelen af testene er skrive-tests. På 4 sekunder kan der skrives en hel del på NVMe. Op til 3 gigabyte i sekundet. Så hver gang testsene kører for skrivning, kan det tage op til 216 gigabyte af SSD-ressourcen.
Læsning og skrivning blandet?Ja. Tests for læsning og skrivning bør køre separat. Desuden er det vigtigt at sikre, at alle caches er synkroniseret, så tidligere foretaget skrivning ikke påvirker læsningen.
Resultaterne vil variere betydeligt ved første kørsel og i efterfølgende kørsel, efterhånden som cachen og det tynde volumen fyldes, samt afhængigt af om systemet har haft tid til at synkronisere cache fyldt ved tidligere kørsel.
Derudover anbefaler jeg at måle hastigheden på et allerede fyldt tyndt volumen, som netop er blevet taget snapshot af. Forfatteren har observeret, hvordan tilfældig skrivning pludselig accelererer straks efter at det første snapshot blev taget, især når cachen ikke er helt fyldt. Dette sker på grund af copy-on-write semantikken for skrivning, udjævningen af cacheblokke og det tynde volumen, samt fordi tilfældig skrivning på RAID 6 bliver til tilfældig læsning med RAID 6 efterfulgt af skrivning i cachen. I vores konfiguration er tilfældig læsning med RAID 6 op til 6 gange (antal SATA SSD'er i arrayet) hurtigere end skrivning. Da blokke til CoW tildeles sekventielt fra det tynde pool, bliver skrivning i høj grad også sekventiel.
Begge disse funktioner kan udnyttes fordelagtigt.
Cache-'koherente' snapshots
For at reducere risikoen for datatab i tilfælde af skade/tabs på cachen foreslår forfatteren at indføre en praksis med rotation af snapshots, der sikrer deres integritet i sådanne tilfælde.
For det første, på grund af det faktum, at metadata for tynde volumen er placeret på en ikke-cacheret enhed, vil metadataene være integrerede, og eventuelle tab vil være isolerede inden for datablokke.
Den næste cyklus af snapshotrotation giver garanti for dataintegritet i snapshots i tilfælde af tab af cache:
- For hver tynd volume med navnet <navn> opretter vi et snapshot med navnet <navn>.cached
- Vi sætter migrationsgrænsen til en rimeligt høj værdi:
#lvchange --quiet --cachesettings "migration_threshold=16384" cache/cachedata - I cyklen 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 der ikke er noget nul i for lang tid, kan det midlertidigt oprettes ved at omstille cachen til writethrough-tilstand. Men i betragtning af hastighedsforholdene på vores SATA- og NVMe-SSD'er samt deres TBW-levetid, vil du enten kunne fange øjeblikket hurtigt uden at ændre cache-tilstanden, eller også vil dit hardware helt have opbrugt sin kapacitet på få dage. På grund af ressourcens begrænsninger kan systemet ikke generelt være under 100% belastning kontinuerligt. Vore NVMe-SSD'er vil ved 100% skrivebelastning fuldstændigt udtømme ressourcen på 3-4 dage. SATA-SSD'er vil kun holde cirka dobbelt så længe. Derfor antager vi, at størstedelen af belastningen er på læsning, mens skrivningen er præget af forholdsvis kortvarige, ekstremt aktive spidsbelastninger kombineret med lav belastning i gennemsnit. - Så snart vi får fat i (eller laver) et nul — omdøber vi <navn>.cached til <navn>.committed. Den gamle <navn>.committed slettes herved.
- Valgfrit, hvis cachen er 100% fyldt, kan den genskabes med et script, hvilket fjerner indholdet. Med en delvist fyldt cache arbejder systemet meget hurtigere med skrivning.
- Vi sætter migrationsgrænsen til nul:
#lvchange --quiet --cachesettings "migration_threshold=0" cache/cachedataDette vil midlertidigt forhindre synkronisering af cachen til det primære medie. - Vi venter, indtil der opsamles tilstrækkeligt med ændringer i cachen
#lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}'eller timeren udløber. - Gentager forfra.
Hvorfor besværet med migrationsgrænsen…?Sagen er, at i praksis er 'tilfældig' skrivning faktisk ikke helt tilfældig. Hvis vi har skrevet noget i en sektor på 4 kilobyte, er der en stor sandsynlighed for, at der i de næste par minutter vil blive skrevet til denne eller en af de nærliggende (+- 32K) sektorer.
Ved at sætte migrationsgrænsen til nul udskyder vi synkroniseringen af skrivningen til SATA-SSD og aggregerer flere ændringer af en 64K blok i cachen. Dermed sparer vi betydeligt på ressourcen for SATA-SSD.
Og hvor er koden..?Desværre anser forfatteren sig selv for at være utilstrækkeligt kompetent til udvikling af bash-scripts, da han er 100% selvudlært og praktiserer 'google'-drevet udvikling, og mener derfor, at den skræmmende kode, der kommer fra hans hænder, ikke bør bruges af andre.
Jeg tror, at professionelle folk kan fremstille hele den beskrevne logik selv, hvis nødvendigt, og måske endda præsentere det pænt som en systemd-service, som forfatteren forsøgte at gøre.
Denne enkle rotation af snapshots vil ikke kun give os ét fuldt synkroniseret snapshot på SATA SSD, men også tillade os at bruge værktøjet thin_delta til at finde ud af, hvilke blokke der er blevet ændret efter oprettelsen, og dermed lokalisere skader på de primære volumener, hvilket gør gendannelsen betydeligt lettere.
TRIM/DISCARD i libvirt/KVM
Da datalageret vil blive brugt til KVM under libvirt, ville det være en god idé at lære vores VM'er ikke kun at optage frit plads, men også at frigive det, der ikke længere er nødvendigt.
Dette gøres ved at emulere støtte til TRIM/DISCARD på virtuelle drev. For at gøre dette skal controller-typen ændres til virtio-scsi og xml redigeres.
#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'/>
</disk>
<controller type='scsi' index='0' model='virtio-scsi'>
<alias name='scsi0'/>
</controller>
Sådanne DISCARDs fra gæste-OS'er håndteres korrekt af LVM, og blokke frigives korrekt både i cachen og i det tynde pulje. I vores tilfælde sker dette stort set udskudt ved sletning af det næste snapshot.
Backup af BTRFS
Brug af færdige scripts med yderste forsigtighed og på eget ansvar. Forfatteren har skrevet denne kode selv og udelukkende til sig selv. Jeg er sikker på, at mange erfarne Linux-brugere har lignende løsninger, så det er ikke nødvendigt at kopiere andres.
Lad os oprette et volumen på backup-enheden:
#lvcreate -L 256G --name backup backup
Formatér i BTRFS:
#mkfs.btrfs /dev/backup/backup
Opret monteringspunkter og monter rodunderkatalogerne i FS:
#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
Opret mapper til sikkerhedskopier:
#mkdir /backup/btrfs/back/remote
#mkdir /backup/btrfs/back/remote/root
#mkdir /backup/btrfs/back/remote/boot
Opret en mappe til sikkerhedskopieringsscripts:
#mkdir /root/btrfs-backup
Kopier scriptet:
Masser af skræmmende bash-kode. Brug på eget ansvar. Ingen vrede mails 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 "Venter på lås..."
wait_lock || terminate "Kunne ikke få lås. Afslutter..."
echo "Fik lås..."
}
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" ]
så
echo "$SOURCE_BASE_PATH blev fundet"
ellers
echo "$SOURCE_BASE_PATH Filen blev ikke fundet, opretter snapshot fra $SOURCE_PATH til $SOURCE_BASE_PATH"
btrfs subvolume snapshot -r $SOURCE_PATH $SOURCE_BASE_PATH
synk
if [ -d "$TARGET_BASE_PATH" ]
så
echo "$TARGET_BASE_PATH blev fundet, er ikke synkroniseret med kilden... fjerner..."
btrfs subvolume delete -c $TARGET_BASE_PATH
synk
fi
fi
if [ -d "$TARGET_BASE_PATH" ]
så
echo "$TARGET_BASE_PATH blev fundet"
ellers
echo "$TARGET_BASE_PATH ikke fundet. Synkroniserer til $TARGET_BASE_DIR"
btrfs send $SOURCE_BASE_PATH | btrfs receive $TARGET_BASE_DIR
synk
fi
if [ -d "$SOURCE_PEND_PATH" ]
så
echo "$SOURCE_PEND_PATH blev fundet, fjerner..."
btrfs subvolume delete -c $SOURCE_PEND_PATH
synk
fi
btrfs subvolume snapshot -r $SOURCE_PATH $SOURCE_PEND_PATH
synk
if [ -d "$TARGET_PEND_PATH" ]
så
echo "$TARGET_PEND_PATH blev fundet, fjerner..."
btrfs subvolume delete -c $TARGET_PEND_PATH
synk
fi
echo "Sender $SOURCE_PEND_PATH til $TARGET_PEND_PATH"
btrfs send -p $SOURCE_BASE_PATH $SOURCE_PEND_PATH | btrfs receive $TARGET_BASE_DIR
synk
TARGET_DATE_SUFFIX=$(suffix)
btrfs subvolume snapshot -r $TARGET_PEND_PATH "$TARGET_PATH$TARGET_DATE_SUFFIX"
synk
btrfs subvolume delete -c $SOURCE_BASE_PATH
synk
btrfs subvolume delete -c $TARGET_BASE_PATH
synk
mv $SOURCE_PEND_PATH $SOURCE_BASE_PATH
mv $TARGET_PEND_PATH $TARGET_BASE_PATH
synk
}
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
synk
}
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 "Hjælp"
;;
"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 "Ingen.."
;;
esac
) 98>$LOCK_FILE
EOF
Hvad gør det egentlig..?Den indeholder et sæt af enkle kommandoer til at oprette BTRFS snapshots og kopiere dem til en anden filsystem via BTRFS send/receive.
Den første kørsel kan tage relativt lang tid, da alle data først vil blive kopieret. Efterfølgende kørsel vil være meget hurtigere, da kun ændringerne vil blive kopieret.
Endnu et script, som vi vil indsætte i cron:
Endnu lidt 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 dage"
$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 det..?Opretter og synkroniserer inkrementelle snapshots af de angivne BTRFS-volumener på backup-filsystemet. Derefter fjerner den alle snapshots, der blev oprettet for 60 dage siden. Efter udførelse vil der i underkatalogerne /backup/btrfs/back/remote/ være daterede snapshots af de nævnte volumener.
Lad os give koden udførelsesrettigheder:
#chmod +x /root/btrfs-backup/cron-daily.sh
#chmod +x /root/btrfs-backup/btrfs-backup.sh
Lad os kontrollere og indsætte 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
Sikkerhedskopiering af LVM thin
Opret en thin pool på backup-enheden:
#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T backup/thin-pool
Installer ddrescue, da skripterne vil bruge dette værktøj:
#apt-get install gddrescue
Opret en mappe til skripterne:
#mkdir /root/lvm-thin-backup
Lad os kopiere skripterne:
Der er meget 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 "Venter på lås..."
wait_lock || terminate "Kunne ikke få lås. Afslutter..."
echo "Fik lås..."
}
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" == "" ]
så
(>&2 echo "Kilde LV er ikke tynd.")
exit 1
fi
if [ "$DIFF_TARGET_POOL" == "" ]
så
(>&2 echo "Mål LV er ikke tynd.")
exit 1
fi
if [ "$DIFF_SOURCE_POOL" != "$DIFF_TARGET_POOL" ]
så
(>&2 echo "Kilde- og mål LV'er tilhører forskellige tynd-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" != "-" ]
så
(>&2 echo "Tynd pool metadata snapshot eksisterer allerede. Antager, at den er forældet. Vil frigive metadata snapshot om 5 sekunder.")
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" == "-" ]
så
(>&2 echo "Fejl i oprettelse af tynd 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" ]
så
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" ]
så
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" != "" ]
så
echo "$DISCARD_LV_PATH fundet"
ellers
echo "$DISCARD_LV ikke fundet i $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" != "" ]
så
echo "$SOURCE_BASE_LV_PATH fundet"
ellers
echo "Kilde basis ikke fundet, opretter snapshot af $SOURCE_VG/$SOURCE_LV til $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 "Overskriver $SOURCE_BASE_LV_PATH, da vi skal opstartes."
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"
synk
if [ "$TARGET_BASE_LV_PATH" != "" ]
så
echo "$TARGET_BASE_LV_PATH fundet, ude af synkronisering med kilde... fjerner..."
lvremove -y --quiet $TARGET_BASE_LV_PATH || exit 1
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
synk
fi
fi
SOURCE_BASE_SIZE=$(read_lv_size "$SOURCE_VG" "$SOURCE_BASE_LV")
if [ "$TARGET_BASE_LV_PATH" != "" ]
så
echo "$TARGET_BASE_LV_PATH fundet"
ellers
echo "$TARGET_VG/$TARGET_LV ikke fundet. Opretter tom volumen."
lvcreate --thin-pool "$BACKUPS_POOL" -V "$SOURCE_BASE_SIZE"B --name "$TARGET_BASE_LV" "$TARGET_VG" || exit 1
echo "Vi skal genopstartes. Overskriver kilde ved $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 "Overskriver mål ved $TARGET_BASE_LV_PATH"
discard_volume "$TARGET_VG" "$TARGET_BASE_LV"
synk
fi
if [ "$SOURCE_PEND_LV_PATH" != "" ]
så
echo "$SOURCE_PEND_LV_PATH fundet, fjerner..."
lvremove -y --quiet "$SOURCE_PEND_LV_PATH" || exit 1
synk
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")
synk
if [ "$TARGET_PEND_LV_PATH" != "" ]
så
echo "$TARGET_PEND_LV_PATH fundet, fjerner..."
lvremove -y --quiet $TARGET_PEND_LV_PATH
synk
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 "Synkroniserer $SOURCE_PEND_LV_PATH til $TARGET_PEND_LV_PATH"
thinsync "$SOURCE_VG" "$SOURCE_PEND_LV" "$SOURCE_BASE_LV" "$TARGET_PEND_LV_PATH" || exit 1
synk
TARGET_DATE_SUFFIX=$(suffix)
lvcreate --quiet --snapshot --name "$TARGET_LV$TARGET_DATE_SUFFIX" "$TARGET_VG/$TARGET_PEND_LV" || exit 1
synk
lvremove --quiet -y "$SOURCE_BASE_LV_PATH" || exit 1
synk
lvremove --quiet -y "$TARGET_BASE_LV_PATH" || exit 1
synk
lvrename -y "$SOURCE_VG/$SOURCE_PEND_LV" "$SOURCE_BASE_LV" || exit 1
lvrename -y "$TARGET_VG/$TARGET_PEND_LV" "$TARGET_BASE_LV" || exit 1
synk
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" != "" ]
så
echo "$SOURCE_BASE_LV_PATH fundet"
ellers
echo "$SOURCE_BASE_LV_PATH fundet ikke"
exit 1
fi
if [ "$TARGET_BASE_LV_PATH" != "" ]
så
echo "$TARGET_BASE_LV_PATH fundet"
ellers
echo "$TARGET_BASE_LV_PATH fundet ikke"
exit 1
fi
activate_volume "$TARGET_VG" "$TARGET_BASE_LV"
activate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
echo Sammenligner "$SOURCE_BASE_LV_PATH" med "$TARGET_BASE_LV_PATH"
cmp "$SOURCE_BASE_LV_PATH" "$TARGET_BASE_LV_PATH"
echo Færdig...
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" != "" ]
så
echo "$SOURCE_BASE_LV_PATH fundet"
ellers
echo "$SOURCE_BASE_LV_PATH fundet ikke"
exit 1
fi
if [ "$TARGET_BASE_LV_PATH" != "" ]
så
echo "$TARGET_BASE_LV_PATH fundet"
ellers
echo "$TARGET_BASE_LV_PATH fundet ikke"
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 Synkroniserer "$SOURCE_BASE_LV_PATH" til "$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 "Synkroniserer $SYNC_LENGTH_BYTES bytes ved $SYNC_OFFSET_BYTES fra $SOURCE_BASE_LV_PATH til $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"
ellers
CMP_OFFSET=""
fi
done
echo Færdig...
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"
synk
}
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 "Hjælp"
;;
"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 "Ingen.."
;;
esac
) 98>$LOCK_FILE
EOF
Hvad gør det…?Indeholder et sæt kommandoer til at manipulere tynde snapshots og synkronisere forskelle mellem to tynde snapshots, der er opnået via thin_delta, til et andet blokdevice ved hjælp af ddrescue og blkdiscard.
Endnu et 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 dage"
$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 det…?Bruger det tidligere script til at oprette og synkronisere backup af de nævnte tynde volumener. Scriptet efterlader inaktive snapshots af de nævnte volumener, som er nødvendige for at spore ændringer siden sidste synkronisering.
Dette script skal redigeres ved at angive listen over tynde volumener, der skal tages backup af. De angivne navne er kun til eksempel. Man kan vælge at skrive et script, der synkroniserer alle volumener.
Lad os give tilladelser:
#chmod +x /root/lvm-thin-backup/cron-daily.sh
#chmod +x /root/lvm-thin-backup/lvm-thin-backup.sh
Lad os kontrollere og indsætte 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 kørsel vil være lang, da de tynde volumener vil blive fuldt synkroniseret ved at kopiere hele det anvendte rum. Takket være metadataene fra LVM thin ved vi, hvilke blokke der faktisk anvendes, så kun de reelt anvendte blokke fra de tynde volumener vil blive kopieret.
Efterfølgende kørsler vil kopiere data inkrementelt takket være sporing af ændringer gennem metadataene fra LVM thin.
Lad os se, hvad vi har fået:
#time /root/btrfs-backup/cron-daily.sh
real 0m2,967s
bruger 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 mar 26 09:11 .
drwxr-xr-x 1 root root 16 mar 6 09:30 ..
drwxr-xr-x 1 root root 322 mar 26 02:00 .@base
drwxr-xr-x 1 root root 516 mar 6 09:39 .@snap.2020-03-06-09-39-37
drwxr-xr-x 1 root root 516 mar 6 09:39 .@snap.2020-03-06-09-39-57
...
/backup/btrfs/back/remote/root:
total 0
drwxr-xr-x 1 root root 2820 mar 26 09:11 .
drwxr-xr-x 1 root root 16 mar 6 09:30 ..
drwxr-xr-x 1 root root 240 mar 26 09:11 @.@base
drwxr-xr-x 1 root root 22 mar 26 09:11 @home.@base
drwxr-xr-x 1 root root 22 mar 6 09:39 @home.@snap.2020-03-06-09-39-35
drwxr-xr-x 1 root root 22 mar 6 09:39 @home.@snap.2020-03-06-09-39-57
...
drwxr-xr-x 1 root root 240 mar 6 09:39 @.@snap.2020-03-06-09-39-26
drwxr-xr-x 1 root root 240 mar 6 09:39 @.@snap.2020-03-06-09-39-56
...
#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 matrjosjkaer med det at gøre?
Sandsynligvis fordi logiske LVM-volumener (LV) kan være fysiske LVM-volumener (PV) for andre VG. LVM kan være rekursiv, ligesom matrjosjkaer. Dette giver LVM en enestående fleksibilitet.
P.S.
I den næste artikel vil vi forsøge at bruge flere af de lignende mobile SAN/KVM som grundlag for at skabe en geofordelt storage/VM-klynge med backup på flere kontinenter ved hjælp af hjemmecomputer, hjemmeinternet og P2P-netværk.
Kilde: habr.com
