Hva har LVM og Matryoshka til felles?

God dag.
Jeg vil gjerne dele min praktiske erfaring med å bygge et datalagringssystem for KVM med md RAID + LVM med fellesskapet.

Programmet vil inneholde:

  • Bygger md RAID 1 fra NVMe SSD.
  • Montering av md RAID 6 fra SATA SSD og vanlige stasjoner.
  • Funksjoner ved TRIM/DISCARD-drift på SSD RAID 1/6.
  • Opprette en oppstartbar md RAID 1/6-array på et vanlig sett med disker.
  • Installere systemet på NVMe RAID 1 når det ikke er NVMe-støtte i BIOS.
  • Bruker LVM cache og LVM thin.
  • Bruke BTRFS-øyeblikksbilder og send/motta for sikkerhetskopiering.
  • Bruke LVM tynne øyeblikksbilder og thin_delta for sikkerhetskopiering i BTRFS-stil.

Hvis du er interessert, se katt.

Uttalelse

Forfatteren bærer ikke noe ansvar for konsekvensene av å bruke eller ikke bruke materialer/eksempler/kode/tips/data fra denne artikkelen. Ved å lese eller bruke dette materialet på noen måte, påtar du deg ansvaret for alle konsekvenser av disse handlingene. Mulige konsekvenser inkluderer:

  • Sprøstekte NVMe SSD-er.
  • Fullstendig oppbrukt opptaksressurs og feil på SSD-stasjoner.
  • Fullstendig tap av alle data på alle stasjoner, inkludert sikkerhetskopier.
  • Defekt maskinvare.
  • Bortkastet tid, nerver og penger.
  • Eventuelle andre konsekvenser som ikke er oppført ovenfor.

jern

Tilgjengelige var:

Hovedkort fra rundt 2013 med Z87 brikkesett, komplett med Intel Core i7 / Haswell.

  • Prosessor 4 kjerner, 8 tråder
  • 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-kontakter

SAS-adapter LSI SAS9211-8I blinket til IT / HBA-modus. RAID-aktivert fastvare er med vilje erstattet med HBA-fastvare for å:

  1. Du kan når som helst kaste ut denne adapteren og erstatte den med en hvilken som helst annen du kommer over.
  2. TRIM/Discard fungerte normalt på disker, fordi... i RAID-fastvare støttes ikke disse kommandoene i det hele tatt, og HBA, generelt sett, bryr seg ikke om hvilke kommandoer som overføres over bussen.

Harddisker - 8 stykker HGST Travelstar 7K1000 med en kapasitet på 1 TB i en formfaktor på 2.5, som for bærbare datamaskiner. Disse stasjonene var tidligere i et RAID 6-array. De vil også ha bruk i det nye systemet. For å lagre lokale sikkerhetskopier.

I tillegg lagt til:

6 stk SATA SSD modell Samsung 860 QVO 2TB. Disse SSD-ene krevde et stort volum, tilstedeværelsen av en SLC-cache, pålitelighet og en lav pris var ønsket. Støtte for forkast/null var nødvendig, som kontrolleres av linjen i dmesg:

kernel: ata1.00: Enabling discard_zeroes_data

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

For disse SSD-ene er tilfeldig lese-/skrivehastighet og ressurskapasitet for dine behov viktig. Radiator for dem. Nødvendigvis. Absolutt. Ellers stek dem til de er sprø under den første RAID-synkroniseringen.

StarTech PEX8M2E2-adapter for 2 x NVMe SSD installert i PCIe 3.0 8x spor. Dette er igjen bare en HBA, men for NVMe. Det skiller seg fra billige adaptere ved at det ikke krever PCIe-bifurkasjonsstøtte fra hovedkortet på grunn av tilstedeværelsen av en innebygd PCIe-svitsj. Det vil fungere selv i det eldste systemet med PCIe, selv om det er et x1 PCIe 1.0-spor. Naturligvis med passende hastighet. Det er ingen RAIDer der. Det er ingen innebygd BIOS ombord. Så systemet ditt vil ikke på magisk vis lære å starte opp med NVMe, langt mindre gjør NVMe RAID takket være denne enheten.

Denne komponenten var utelukkende på grunn av tilstedeværelsen av bare én gratis 8x PCIe 3.0 i systemet, og hvis det er 2 ledige spor, kan den enkelt erstattes med to penny PEX4M2E1 eller analoger, som kan kjøpes hvor som helst til en pris av 600 rubler.

Avvisningen av all slags maskinvare eller innebygde brikkesett/BIOS RAIDer ble gjort bevisst, for å kunne erstatte hele systemet fullstendig, med unntak av SSD/HDD selv, samtidig som alle dataene ble bevart. Ideelt sett, slik at du kan lagre selv det installerte operativsystemet når du flytter til helt ny/annen maskinvare. Hovedsaken er at det er SATA- og PCIe-porter. Det er som en live CD eller oppstartbar flash-stasjon, bare veldig rask og litt klumpete.

ЮморEllers vet du hva som skjer - noen ganger må du raskt ta med deg hele matrisen for å ta bort. Men jeg vil ikke miste data. For å gjøre dette er alle de nevnte mediene beleilig plassert på lysbildene i 5.25-brønnene til standardkofferten.

Vel, og selvfølgelig for å eksperimentere med forskjellige metoder for SSD-bufring i Linux.

Maskinvareangrep er kjedelige. Slå den på. Enten fungerer det eller så gjør det ikke. Og med mdadm er det alltid alternativer.

Soft

Tidligere ble Debian 8 Jessie installert på maskinvaren, som er nær EOL. RAID 6 ble satt sammen fra de ovennevnte harddiskene sammen med LVM. Den kjørte virtuelle maskiner i kvm/libvirt.

Fordi Forfatteren har passende erfaring med å lage bærbare oppstartbare SATA/NVMe-flash-stasjoner, og også, for ikke å bryte den vanlige apt-malen, ble Ubuntu 18.04 valgt som målsystem, som allerede er tilstrekkelig stabilisert, men fortsatt har 3 år med støtte i fremtiden.

Det nevnte systemet inneholder alle maskinvaredriverne vi trenger ut av esken. Vi trenger ingen tredjeparts programvare eller drivere.

Klargjør for installasjon

For å installere systemet trenger vi Ubuntu Desktop Image. Serversystemet har en slags kraftig installasjonsprogram, som viser overdreven uavhengighet som ikke kan deaktiveres ved å skyve UEFI-systempartisjonen inn på en av diskene, og ødelegge all skjønnheten. Følgelig er den kun installert i UEFI-modus. Tilbyr ingen alternativer.

Dette er vi ikke fornøyd med.

Hvorfor?Dessverre er UEFI-oppstart ekstremt dårlig kompatibel med oppstartsprogramvare RAID, fordi... Ingen tilbyr oss reservasjoner for UEFI ESP-partisjonen. Det finnes oppskrifter på nettet som foreslår å plassere ESP-partisjonen på en flash-stasjon i en USB-port, men dette er et feilpunkt. Det er oppskrifter som bruker programvaren mdadm RAID 1 med metadata versjon 0.9 som ikke hindrer UEFI BIOS fra å se denne partisjonen, men denne lever til det lykkelige øyeblikket når BIOS eller et annet maskinvare-OS skriver noe til ESP og glemmer å synkronisere det til andre. speilene.

I tillegg avhenger UEFI-oppstart av NVRAM, som ikke vil flytte sammen med diskene til det nye systemet, fordi er en del av hovedkortet.

Så vi vil ikke finne opp et nytt hjul på nytt. Vi har allerede en ferdig, tidstestet bestefarssykkel, nå kalt Legacy/BIOS-støvel, som bærer det stolte navnet CSM på UEFI-kompatible systemer. Vi skal bare ta den av hylla, smøre den, pumpe opp dekkene og tørke av den med en fuktig klut.

Desktop-versjonen av Ubuntu kan heller ikke installeres riktig med Legacy bootloader, men her, som de sier, er det i det minste alternativer.

Og så samler vi inn maskinvaren og laster systemet fra den oppstartbare Ubuntu Live flash-stasjonen. Vi må laste ned pakker, så vi setter opp nettverket som fungerer for deg. Hvis det ikke fungerer, kan du laste de nødvendige pakkene på en flash-stasjon på forhånd.

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

#sudo bash

Hvordan…?Linjen over er den kanoniske utløseren for holiwars om sudo. C bоstørre muligheter kommer ogоstørre ansvar. Spørsmålet er om du kan ta det på deg selv. Mange tror at det å bruke sudo på denne måten i hvert fall ikke er forsiktig. Derimot:

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

Hvorfor ikke ZFS...?Når vi installerer programvare på datamaskinen vår, låner vi i hovedsak ut maskinvaren vår til utviklerne av denne programvaren å drive.
Når vi stoler på denne programvaren med sikkerheten til dataene våre, tar vi opp et lån tilsvarende kostnaden for å gjenopprette disse dataene, som vi må betale ned en dag.

Fra dette synspunktet er ZFS en Ferrari, og mdadm+lvm er mer som en sykkel.

Subjektivt foretrekker forfatteren å låne ut en sykkel på kreditt til ukjente personer i stedet for en Ferrari. Der er ikke prisen på emisjonen høy. Ikke behov for rettigheter. Enklere enn trafikkreglene. Parkering er gratis. Langrennsevnen er bedre. Du kan alltid feste ben til en sykkel, og du kan reparere en sykkel med egne hender.

Hvorfor da BTRFS...?For å starte opp operativsystemet trenger vi et filsystem som støttes i Legacy/BIOS GRUB ut av esken, og som samtidig støtter live snapshots. Vi vil bruke den for /boot-partisjonen. I tillegg foretrekker forfatteren å bruke denne FS for / (root), og ikke glem å merke seg at for annen programvare kan du opprette separate partisjoner på LVM og montere dem i de nødvendige katalogene.

Vi vil ikke lagre noen bilder av virtuelle maskiner eller databaser på denne FS.
Denne FS vil kun bli brukt til å lage øyeblikksbilder av systemet uten å slå det av og deretter overføre disse øyeblikksbildene til en sikkerhetskopidisk ved hjelp av send/recieve.

I tillegg foretrekker forfatteren generelt å beholde et minimum av programvare direkte på maskinvaren og kjøre all annen programvare i virtuelle maskiner ved å bruke ting som å videresende GPUer og PCI-USB Host-kontrollere til KVM via IOMMU.

Det eneste som er igjen på maskinvaren er datalagring, virtualisering og backup.

Hvis du stoler mer på ZFS, er de i prinsippet utskiftbare for den angitte applikasjonen.

Forfatteren ignorerer imidlertid bevisst de innebygde speil-/RAID- og redundansfunksjonene som ZFS, BRTFS og LVM har.

Som et tilleggsargument har BTRFS muligheten til å gjøre tilfeldige skrivinger til sekvensielle, noe som har en ekstremt positiv effekt på hastigheten på synkronisering av øyeblikksbilder/sikkerhetskopier på harddisken.

La oss skanne alle enheter på nytt:

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

La oss ta en titt 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

Diskoppsett

NVMe SSD-er

Men vi vil ikke merke dem på noen måte. Uansett, BIOS ser ikke disse stasjonene. Så de vil gå helt til programvare RAID. Vi vil ikke engang lage seksjoner der. Hvis du vil følge "kanonen" eller "prinsipielt", lag én stor partisjon, som en HDD.

SATA HDD

Det er ingen grunn til å finne på noe spesielt her. Vi lager én seksjon for alt. Vi vil opprette en partisjon fordi BIOS ser disse diskene og kan til og med prøve å starte opp fra dem. Vi vil til og med installere GRUB på disse diskene senere slik at systemet plutselig kan gjø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 ting blir interessant for oss.

For det første er stasjonene våre 2 TB store. Dette er innenfor det akseptable området for MBR, som er det vi vil bruke. Kan om nødvendig erstattes med GPT. GPT-disker har et kompatibilitetslag som lar MBR-kompatible systemer se de første 4 partisjonene hvis de er plassert innenfor de første 2 terabytene. Hovedsaken er at oppstartspartisjonen og bios_grub-partisjonen på disse diskene skal være i begynnelsen. Dette lar deg til og med starte opp fra GPT Legacy/BIOS-stasjoner.

Men dette er ikke vår sak.

Her skal vi lage to seksjoner. Den første vil være på 1 GB og brukes til RAID 1 /boot.

Den andre vil bli brukt for RAID 6 og vil ta opp all gjenværende ledig plass bortsett fra et lite ikke-allokert område på slutten av stasjonen.

Hva er dette umerkede området?Ifølge kilder på nettverket har våre SATA SSD-er ombord en dynamisk utvidbar SLC-cache som varierer i størrelse fra 6 til 78 gigabyte. Vi får 6 gigabyte "gratis" på grunn av forskjellen mellom "gigabyte" og "gibibyte" i dataarket til stasjonen. De resterende 72 gigabyte tildeles fra ubrukt plass.

Her skal det bemerkes at vi har en SLC-cache, og plassen er opptatt i 4 bit MLC-modus. Noe som for oss i praksis betyr at for hver 4 gigabyte ledig plass vil vi bare få 1 gigabyte med SLC-cache.

Multipliser 72 gigabyte med 4 og få 288 gigabyte. Dette er den ledige plassen som vi ikke vil merke ut for å la stasjonene utnytte SLC-cachen fullt ut.

Dermed vil vi effektivt få opptil 312 gigabyte med SLC-cache fra totalt seks stasjoner. Av alle stasjonene vil 2 bli brukt i RAID for redundans.

Denne mengden cache vil tillate oss ekstremt sjelden i det virkelige liv å møte en situasjon der en skriving ikke går til cachen. Dette kompenserer ekstremt godt for den tristeste ulempen med QLC-minne - den ekstremt lave skrivehastigheten når data skrives utenom cachen. Hvis belastningene dine ikke samsvarer med dette, så anbefaler jeg at du tenker godt over hvor lenge SSD-en din vil vare under en slik belastning, tatt i betraktning 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

Opprette matriser

Først må vi gi nytt navn til maskinen. Dette er nødvendig fordi vertsnavnet er en del av array-navnet et sted inne i mdadm og påvirker noe et sted. Selvfølgelig kan arrays gis nytt navn senere, men dette er et unødvendig trinn.

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

NVMe SSD-er

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

Hvorfor -anta-rent...?For å unngå initialisering av arrays. For både RAID nivå 1 og 6 er dette gyldig. Alt kan fungere uten initialisering hvis det er en ny matrise. Dessuten er initialisering av SSD-arrayen ved opprettelse en sløsing med TBW-ressurser. Vi bruker TRIM/DISCARD der det er mulig på sammensatte SSD-matriser for å "initialisere" dem.

For SSD-matriser støttes RAID 1 DISCARD ut av esken.

For SSD RAID 6 DISCARD-matriser må du aktivere det i kjernemodulens parametere.

Dette bør bare gjøres hvis alle SSD-er som brukes i nivå 4/5/6-matriser i dette systemet har fungerende støtte for discard_zeroes_data. Noen ganger kommer du over merkelige stasjoner som forteller kjernen at denne funksjonen støttes, men faktisk er den ikke der, eller funksjonen fungerer ikke alltid. For øyeblikket er støtte tilgjengelig nesten overalt, men det er gamle stasjoner og fastvare med feil. Av denne grunn er DISCARD-støtte deaktivert som standard for RAID 6.

Merk, følgende kommando vil ødelegge alle data på NVMe-stasjoner ved å "initialisere" arrayet med "null".

#blkdiscard /dev/md0

Hvis noe går galt, prøv å spesifisere et trinn.

#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...?Å øke chunk-størrelsen har en positiv effekt på hastigheten på tilfeldig lesing i blokker opp til chunk-størrelse inkludert. Dette skjer fordi én operasjon av passende størrelse eller mindre kan fullføres helt på en enkelt enhet. Derfor er IOPS fra alle enheter oppsummert. I følge statistikk overstiger ikke 99% av IO 512K.

RAID 6 IOPS per skriving alltid mindre enn eller lik IOPS for én stasjon. Når, som en tilfeldig lesning, IOPS kan være flere ganger større enn for én stasjon, og her er blokkstørrelsen av sentral betydning.
Forfatteren ser ikke poenget med å prøve å optimalisere en parameter som er dårlig i RAID 6 by-design og i stedet optimalisere det RAID 6 er god på.
Vi vil kompensere for den dårlige tilfeldige skrivingen av RAID 6 med en NVMe-cache og tynne triks for forsyning.

Vi har ennå ikke aktivert DISCARD for RAID 6. Så vi vil ikke "initialisere" denne matrisen foreløpig. Vi gjør dette senere, etter å ha installert operativsystemet.

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 hastighet ønsker vi å plassere rotfilsystemet på NVMe RAID 1 som er /dev/md0.
Imidlertid vil vi fortsatt trenge denne raske matrisen for andre behov, for eksempel swap, metadata og LVM-cache og LVM-tynne metadata, så vi vil lage en LVM VG på denne matrisen.

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

La oss lage en partisjon for rotfilsystemet.

#lvcreate -L 128G --name root root

La oss lage en partisjon for å bytte i henhold til størrelsen på RAM.

#lvcreate -L 32G --name swap root

OS installasjon

Totalt har vi alt som er nødvendig for å installere systemet.

Start systeminstallasjonsveiviseren fra Ubuntu Live-miljøet. Normal installasjon. Bare på stadiet for å velge disker for installasjon, må du spesifisere følgende:

  • /dev/md1, - monteringspunkt /boot, FS - BTRFS
  • /dev/root/root (aka /dev/mapper/root-root), - monteringspunkt / (root), FS - BTRFS
  • /dev/root/swap (aka /dev/mapper/root-swap), - bruk som byttepartisjon
  • Installer oppstartslasteren på /dev/sda

Når du velger BTRFS som rotfilsystem, vil installasjonsprogrammet automatisk opprette to BTRFS-volumer kalt "@" for / (root), og "@home" for /home.

La oss starte installasjonen...

Installasjonen vil avsluttes med en modal dialogboks som indikerer en feil ved installasjon av oppstartslasteren. Dessverre vil du ikke kunne gå ut av denne dialogen med standard midler og fortsette installasjonen. Vi logger ut av systemet og logger på igjen, og ender opp i et rent Ubuntu Live-skrivebord. Åpne terminalen, og igjen:

#sudo bash

Opprett et chroot-miljø for å fortsette installasjonen:

#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

La oss konfigurere nettverket og vertsnavnet 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

La oss gå inn i chroot-miljøet:

#chroot /mnt/chroot

Først og fremst vil vi levere pakkene:

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

La oss sjekke og fikse alle pakker som ble installert skjevt på grunn av ufullstendig systeminstallasjon:

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

Hvis noe ikke fungerer, må du kanskje redigere /etc/apt/sources.list først

La oss justere parametrene for RAID 6-modulen for å aktivere TRIM/DISCARD:

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

La oss justere arrayene våre litt:

#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

Hva var det..?Vi har laget et sett med udev-regler som vil gjøre følgende:

  • Angi blokkbufferstørrelsen for RAID 2020 til å være tilstrekkelig for 6. Standardverdien, ser det ut til, har ikke endret seg siden opprettelsen av Linux, og har ikke vært tilstrekkelig på lenge.
  • Reserver et minimum av IO for varigheten av array-sjekker/-synkroniseringer. Dette er for å forhindre at arrayene dine blir sittende fast i en tilstand av evig synkronisering under belastning.
  • Begrens maksimal IO under kontroller/synkronisering av arrays. Dette er nødvendig for at synkronisering/sjekking av SSD-RAIDer ikke skal steke diskene dine til en skarphet. Dette gjelder spesielt for NVMe. (Husker du om radiatoren? Jeg spøkte ikke.)
  • Forby disker fra å stoppe spindelrotasjon (HDD) via APM og still inn dvaletidsavbruddet for diskkontrollere til 7 timer. Du kan deaktivere APM fullstendig hvis stasjonene dine kan gjøre det (-B 255). Med standardverdien vil stasjonene stoppe etter fem sekunder. Da vil operativsystemet tilbakestille diskbufferen, diskene vil snurre opp igjen, og alt vil starte på nytt. Plater har et begrenset maksimalt antall spindelrotasjoner. En slik enkel standardsyklus kan lett drepe diskene dine i løpet av et par år. Ikke alle disker lider av dette, men våre er «bærbare», med de riktige standardinnstillingene, som får RAID til å se ut som en mini-MAID.
  • Installer readahead på disker (roterende) 1 megabyte - to påfølgende blokker/chunk RAID 6
  • Deaktiver readahead på selve matrisene.

La oss 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øke etter /boot-partisjonen med UUID. Array-navngivning kan teoretisk endres.

Vi vil søke etter de resterende delene etter LVM-navn i /dev/mapper/vg-lv-notasjonen, fordi de identifiserer partisjoner ganske unikt.

Vi bruker ikke UUID for LVM pga UUID-en til LVM-volumer og deres øyeblikksbilder kan være den samme.Monter /dev/mapper/root-root.. to ganger?Ja. Nøyaktig. Funksjon av BTRFS. Dette filsystemet kan monteres flere ganger med forskjellige undervolum.

På grunn av denne samme funksjonen anbefaler jeg at du aldri lager LVM-øyeblikksbilder av aktive BTRFS-volumer. Du kan få en overraskelse når du starter på nytt.

La oss gjenskape mdadm-konfigurasjonen:

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

La oss justere LVM-innstillingene:

#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

Hva var det..?Vi har aktivert automatisk utvidelse av tynne LVM-bassenger ved å nå 90 % av den okkuperte plassen med 5 % av volumet.

Vi har økt det maksimale antallet cacheblokker for LVM-cache.

Vi har forhindret LVM i å søke etter LVM-volumer (PV) på:

  • enheter som inneholder LVM-buffer (cdata)
  • enheter bufret ved hjelp av LVM-hurtigbuffer, og omgår hurtigbufferen ( _corig). I dette tilfellet vil selve den bufrede enheten fortsatt skannes gjennom hurtigbufferen (bare ).
  • enheter som inneholder LVM-cache-metadata (cmeta)
  • alle enheter i VG med navnebildene. Her vil vi ha diskbilder av virtuelle maskiner, og vi ønsker ikke at LVM på verten skal aktivere volumer som tilhører gjeste-OS.
  • alle enheter i VG med navnet backup. Her vil vi ha sikkerhetskopier av virtuelle maskinbilder.
  • alle enheter hvis navn slutter med "gpv" (fysisk gjestevolum)

Vi har aktivert DISCARD-støtte når du frigjør ledig plass på LVM VG. Vær forsiktig. Dette vil gjøre sletting av LV-er på SSD-en ganske tidkrevende. Dette gjelder spesielt SSD RAID 6. Men etter planen skal vi bruke tynn provisjonering, så dette vil ikke hindre oss i det hele tatt.

La oss oppdatere initramfs-bildet:

#update-initramfs -u -k all

Installer og konfigurer grub:

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

Hvilke disker å velge?Alle som er sd*. Systemet må kunne starte opp fra en hvilken som helst fungerende SATA-stasjon eller SSD.

Hvorfor la de til os-prober..?For overdreven uavhengighet og lekne hender.

Det fungerer ikke riktig hvis en av RAID-ene er i en degradert tilstand. Den prøver å søke etter operativsystemet på partisjoner som brukes i virtuelle maskiner som kjører på denne maskinvaren.

Hvis du trenger det, kan du la det stå, men husk alt det ovennevnte. Jeg anbefaler å se etter oppskrifter for å bli kvitt slemme hender på nettet.

Med dette har vi fullført den første installasjonen. Det er på tide å starte på nytt til det nylig installerte operativsystemet. Ikke glem å fjerne den oppstartbare Live CD/USB.

#exit
#reboot

Velg en av SATA SSD-ene som oppstartsenhet.

LVM på SATA SSD

På dette tidspunktet har vi allerede startet opp i det nye operativsystemet, konfigurert nettverket, apt, åpnet terminalemulatoren og kjørt:

#sudo bash

La oss fortsette.

"Initialiser" arrayet fra SATA SSD:

#blkdiscard /dev/md2

Hvis det ikke fungerer, prøv:

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

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

Hvorfor en annen VG..?Faktisk har vi allerede en VG som heter root. Hvorfor ikke legge alt inn i én VG?

Hvis det er flere PV-er i en VG, så for at VG-en skal aktiveres riktig, må alle PV-er være tilstede (online). Unntaket er LVM RAID, som vi bevisst ikke bruker.

Vi ønsker virkelig at hvis det er en feil (les datatap) på noen av RAID 6-arrayene, vil operativsystemet starte opp normalt og gi oss muligheten til å løse problemet.

For å gjøre dette, vil vi på det første abstraksjonsnivået isolere hver type fysiske «medier» til et eget VG.

Vitenskapelig sett tilhører forskjellige RAID-arrayer forskjellige "pålitelighetsdomener". Du bør ikke skape et ekstra vanlig feilpunkt for dem ved å stappe dem inn i én VG.

Tilstedeværelsen av LVM på "maskinvare"-nivå vil tillate oss å vilkårlig kutte deler av forskjellige RAID-arrayer ved å kombinere dem på forskjellige måter. For eksempel - løp samtidig bcache + LVM tynn, bcache + BTRFS, LVM cache + LVM tynn, en kompleks ZFS-konfigurasjon med cacher, eller en annen helvetes blanding for å prøve å sammenligne det hele.

På "maskinvare"-nivået vil vi ikke bruke noe annet enn gode gamle "tykke" LVM-volumer. Unntaket fra denne regelen kan være backup-partisjonen.

Jeg tror på dette tidspunktet hadde mange lesere allerede begynt å mistenke noe om hekkende dukke.

LVM på SATA HDD

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

Ny VG igjen..?Vi ønsker virkelig at hvis diskarrayen som vi skal bruke til sikkerhetskopiering av data mislykkes, vil operativsystemet vårt fortsette å fungere normalt, samtidig som det opprettholder tilgang til ikke-backup-data som vanlig. Derfor, for å unngå VG-aktiveringsproblemer, oppretter vi en egen VG.

Sette opp LVM cache

La oss lage en LV på NVMe RAID 1 for å bruke den som en bufringsenhet.

#lvcreate -L 70871154688B --name cache root

Hvorfor er det så lite...?Faktum er at våre NVMe SSD-er også har en SLC-cache. 4 gigabyte "gratis" og 18 gigabyte dynamisk på grunn av ledig plass som er okkupert i 3-bit MLC. Når denne cachen er oppbrukt, vil ikke NVMe SSD-er være mye raskere enn vår SATA SSD med cache. Av denne grunn gir det faktisk ingen mening for oss å gjøre LVM-cache-partisjonen mye større enn dobbelt så stor som SLC-cachen til NVMe-stasjonen. For NVMe-stasjonene som brukes, anser forfatteren det som rimelig å lage 32-64 gigabyte cache.

Den gitte partisjonsstørrelsen er nødvendig for å organisere 64 gigabyte cache, cache-metadata og metadata-sikkerhetskopi.

I tillegg legger jeg merke til at etter en skitten systemavslutning, vil LVM merke hele hurtigbufferen som skitten og vil synkronisere igjen. Dessuten vil dette gjentas hver gang lvchange brukes på denne enheten til systemet startes på nytt. Derfor anbefaler jeg å gjenskape hurtigbufferen umiddelbart ved å bruke riktig skript.

La oss lage en LV på SATA RAID 6 for å bruke den som en bufret enhet.

#lvcreate -L 3298543271936B --name cache data

Hvorfor bare tre terabyte..?Slik at du om nødvendig kan bruke SATA SSD RAID 6 til andre behov. Størrelsen på den hurtigbufrede plassen kan økes dynamisk, på flukt, uten å stoppe systemet. For å gjøre dette må du midlertidig stoppe og aktivere cachen på nytt, men den særegne fordelen med LVM-cache fremfor for eksempel bcache er at dette kan gjøres umiddelbart.

La oss lage en ny VG for caching.

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

La oss lage en LV på den bufrede enheten.

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

Her tok vi umiddelbart opp all ledig plass på /dev/data/cache slik at alle andre nødvendige partisjoner ble opprettet umiddelbart på /dev/root/cache. Hvis du opprettet noe på feil sted, kan du flytte det ved å bruke pvmove.

La oss opprette og aktivere hurtigbufferen:

#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 slike biter..?Gjennom praktiske eksperimenter kunne forfatteren finne ut at det beste resultatet oppnås hvis størrelsen på LVM-cacheblokken sammenfaller med størrelsen på den tynne LVM-blokken. Dessuten, jo mindre størrelsen er, desto bedre presterer konfigurasjonen i et tilfeldig opptak.

64k er minste blokkstørrelse tillatt for LVM tynn.

Vær forsiktig med tilbakeskrivning..!Ja. Denne typen hurtigbuffer utsetter skrivesynkronisering til den bufrede enheten. Dette betyr at hvis hurtigbufferen går tapt, kan du miste data på den bufrede enheten. Senere vil forfatteren fortelle deg hvilke tiltak, i tillegg til NVMe RAID 1, som kan tas for å kompensere for denne risikoen.

Denne hurtigbuffertypen ble valgt med vilje for å kompensere for den dårlige tilfeldige skriveytelsen til RAID 6.

La oss sjekke hva 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)

Bare [cachedata_corig] skal være plassert på /dev/data/cache. Hvis noe er galt, bruk pvmove.

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

#lvconvert -y --uncache cache/cachedata

Dette gjøres på nett. LVM vil ganske enkelt synkronisere cachen til disken, fjerne den og gi nytt navn til cachedata_corig tilbake til cachedata.

Sette opp LVM tynn

La oss grovt anslå hvor mye plass vi trenger for tynne LVM-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"

Rund opp til 4 gigabyte: 4294967296B

Multipliser med to og legg til 4194304B for LVM PV-metadata: 8594128896B
La oss lage en egen partisjon på NVMe RAID 1 for å plassere tynne LVM-metadata og deres sikkerhetskopi på den:

#lvcreate -L 8594128896B --name images root

For hva..?Her kan spørsmålet oppstå: hvorfor plassere LVM tynne metadata separat hvis de fortsatt vil være bufret på NVMe og vil fungere raskt.

Selv om fart er viktig her, er det langt fra hovedårsaken. Saken er at cachen er et feilpunkt. Noe kan skje med den, og hvis de tynne LVM-metadataene er bufret, vil det føre til at alt går tapt. Uten komplette metadata vil det være nesten umulig å sette sammen tynne volumer.

Ved å flytte metadataene til et eget ikke-bufret, men raskt, volum garanterer vi sikkerheten til metadataene i tilfelle cache-tap eller korrupsjon. I dette tilfellet vil all skade forårsaket av cache-tap lokaliseres i tynne volumer, noe som vil forenkle gjenopprettingsprosedyren i størrelsesordener. Med stor sannsynlighet vil disse skadene bli gjenopprettet ved hjelp av FS-logger.

Videre, hvis et øyeblikksbilde av et tynt volum tidligere ble tatt, og etter det ble cachen fullstendig synkronisert minst én gang, vil integriteten til øyeblikksbildet være garantert på grunn av den interne utformingen av LVM thin i tilfelle cache-tap .

La oss lage et nytt VG som skal være ansvarlig for thin-provisioning:

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

La oss lage et basseng:

#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T images/thin-pool
Hvorfor -Z yI tillegg til det denne modusen egentlig er ment for – for å forhindre at data fra én virtuell maskin lekker til en annen virtuell maskin ved omfordeling av plass – brukes nullstilling i tillegg for å øke hastigheten på tilfeldig skriving i blokker mindre enn 64k. Enhver skriving mindre enn 64k til et tidligere ikke-allokert område av det tynne volumet vil bli 64K kantjustert i hurtigbufferen. Dette vil tillate operasjonen å utføres helt gjennom hurtigbufferen, og omgå den bufrede enheten.

La oss flytte LV-ene til de tilsvarende PV-ene:

#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

La oss sjekke:

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

La oss lage et tynt volum for tester:

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

Vi vil installere pakker for tester og overvåking:

#apt-get install sysstat fio

Slik kan du observere oppførselen til lagringskonfigurasjonen vår i sanntid:

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

Slik kan vi teste konfigurasjonen vår:

#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

Forsiktig! Ressurs!Denne koden vil kjøre 36 forskjellige tester, som hver kjører i 4 sekunder. Halvparten av testene er for opptak. Du kan ta opp mye på NVMe på 4 sekunder. Opptil 3 gigabyte per sekund. Så hver kjøring av skrivetester kan spise opptil 216 gigabyte med SSD-ressurs fra deg.

Lesing og skriving blandet?Ja. Det er fornuftig å kjøre lese- og skrivetestene separat. Dessuten er det fornuftig å sikre at alle cacher er synkronisert slik at en tidligere skrevet skriving ikke påvirker lesingen.

Resultatene vil variere sterkt under den første lanseringen og påfølgende etter hvert som cachen og det tynne volumet fylles opp, og også avhengig av om systemet klarte å synkronisere cachene som ble fylt under siste lansering.

Jeg anbefaler blant annet å måle hastigheten på et allerede fullt tynt volum som et øyeblikksbilde nettopp ble tatt fra. Forfatteren hadde muligheten til å observere hvordan tilfeldige skrivinger akselererer kraftig umiddelbart etter å ha laget det første øyeblikksbildet, spesielt når cachen ennå ikke er helt full. Dette skjer på grunn av kopi-på-skriv skrivesemantikk, justering av cache og tynne volumblokker, og det faktum at en tilfeldig skriving til RAID 6 blir til en tilfeldig lesing fra RAID 6 etterfulgt av en skriving til cachen. I vår konfigurasjon er tilfeldig lesing fra RAID 6 opptil 6 ganger (antall SATA SSD-er i arrayet) raskere enn å skrive. Fordi blokker for CoW tildeles sekvensielt fra en tynn pool, deretter blir opptaket for det meste også til sekvensiell.

Begge disse funksjonene kan brukes til din fordel.

Buffer "sammenhengende" øyeblikksbilder

For å redusere risikoen for tap av data i tilfelle cacheskade/tap, foreslår forfatteren å introdusere praksisen med å rotere øyeblikksbilder for å garantere deres integritet i dette tilfellet.

For det første, fordi metadata med tynne volum ligger på en ubufret enhet, vil metadataene være konsistente og mulige tap vil bli isolert innenfor datablokker.

Følgende øyeblikksbildrotasjonssyklus garanterer integriteten til dataene inne i øyeblikksbildene i tilfelle cache-tap:

  1. For hvert tynt volum med navnet <navn>, lag et øyeblikksbilde med navnet <navn>.cached
  2. La oss sette migreringsterskelen til en rimelig høy verdi: #lvchange --quiet --cachesettings "migration_threshold=16384" cache/cachedata
  3. I løkken sjekker vi antall skitne blokker i cachen: #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' til vi får null. Hvis nullen mangler for lenge, kan den opprettes ved midlertidig å bytte cachen til skrivemodus. Men med tanke på hastighetsegenskapene til SATA- og NVMe SSD-arrayene våre, samt deres TBW-ressurs, vil du enten raskt kunne fange øyeblikket uten å endre hurtigbuffermodus, eller så vil maskinvaren din spise opp hele ressursen i noen få dager. På grunn av ressursbegrensninger kan systemet i prinsippet ikke være under 100 % skrivebelastning hele tiden. Våre NVMe SSD-er under 100 % skrivebelastning vil fullstendig tømme ressursen inn 3-4 dager. SATA SSD-er vil bare vare dobbelt så lenge. Derfor vil vi anta at det meste av belastningen går til lesing, og vi har relativt kortvarige utbrudd av ekstremt høy aktivitet kombinert med lav belastning i gjennomsnitt for skriving.
  4. Så snart vi fanget (eller laget) en null, omdøper vi <navn>.cached til <navn>.committed. Den gamle <navn>.committed er slettet.
  5. Eventuelt, hvis hurtigbufferen er 100 % full, kan den gjenskapes med et skript, og dermed tømme den. Med en halvtom cache fungerer systemet mye raskere når du skriver.
  6. Sett migreringsterskelen til null: #lvchange --quiet --cachesettings "migration_threshold=0" cache/cachedata Dette vil midlertidig forhindre at hurtigbufferen synkroniseres til hovedmediet.
  7. Vi venter til ganske mange endringer samler seg i cachen #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' eller timeren går av.
  8. Vi gjentar igjen.

Hvorfor problemer med migrasjonsterskel...?Saken er at i praksis er et "tilfeldig" opptak faktisk ikke helt tilfeldig. Hvis vi skrev noe til en sektor på 4 kilobyte i størrelse, er det stor sannsynlighet for at det i løpet av de neste par minuttene vil bli gjort en registrering til samme eller en av nabosektorene (+- 32K).

Ved å sette migrasjonsterskelen til null, utsetter vi skrivesynkronisering på SATA SSD-en og samler flere endringer til én 64K-blokk i hurtigbufferen. Dette sparer betydelig ressursen til SATA SSD.

Hvor er koden..?Dessverre anser forfatteren seg selv som utilstrekkelig kompetent i utviklingen av bash-manus fordi han er 100% selvlært og praktiserer "google"-drevet utvikling, derfor mener han at den forferdelige koden som kommer ut av hendene hans ikke bør brukes av noen ellers.

Jeg tror at fagfolk på dette feltet selvstendig vil kunne skildre all logikken beskrevet ovenfor, om nødvendig, og kanskje til og med vakkert designe den som en systemtjeneste, slik forfatteren prøvde å gjøre.

Et slikt enkelt rotasjonsskjema for øyeblikksbilder vil tillate oss ikke bare å konstant ha ett øyeblikksbilde fullt synkronisert på SATA SSD-en, men vil også tillate oss, ved å bruke thin_delta-verktøyet, å finne ut hvilke blokker som ble endret etter opprettelsen, og dermed lokalisere skade på hovedvolumene, noe som forenkler gjenopprettingen betydelig.

TRIM/KASSERT i libvirt/KVM

Fordi datalagringen vil bli brukt til KVM som kjører libvirt, da vil det være en god idé å lære våre VM-er ikke bare å ta opp ledig plass, men også å frigjøre det som ikke lenger trengs.

Dette gjøres ved å emulere TRIM/DISCARD-støtte på virtuelle disker. For å gjøre dette må du endre kontrollertypen 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>

Slike DISCARDs fra gjeste-OSer blir korrekt behandlet av LVM, og blokker frigjøres riktig både i hurtigbufferen og i den tynne bassenget. I vårt tilfelle skjer dette hovedsakelig på en forsinket måte, når du sletter neste øyeblikksbilde.

BTRFS sikkerhetskopi

Bruk ferdige skript med ekstrem forsiktighet og på egen risiko. Forfatteren skrev denne koden selv og utelukkende for seg selv. Jeg er sikker på at mange erfarne Linux-brukere har lignende verktøy, og det er ikke nødvendig å kopiere noen andres.

La oss lage et volum på sikkerhetskopienheten:

#lvcreate -L 256G --name backup backup

La oss formatere det i BTRFS:

#mkfs.btrfs /dev/backup/backup

La oss lage monteringspunkter og montere rotunderdelene av 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

La oss lage kataloger for sikkerhetskopiering:

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

La oss lage en katalog for sikkerhetskopieringsskript:

#mkdir /root/btrfs-backup

La oss kopiere skriptet:

Mye skummel bash-kode. Bruk på eget ansvar. Ikke skriv sinte brev 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

Hva gjør det til og med..?Inneholder et sett med enkle kommandoer for å lage BTRFS-øyeblikksbilder og kopiere dem til en annen FS ved å bruke BTRFS send/motta.

Den første lanseringen kan være relativt lang, fordi... I begynnelsen vil alle data bli kopiert. Videre lanseringer vil være veldig raske, fordi... Kun endringer vil bli kopiert.

Et annet skript som vi legger inn i cron:

Litt mer 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

Hva gjør den..?Oppretter og synkroniserer inkrementelle øyeblikksbilder av de oppførte BTRFS-volumene på backup-FS. Etter dette sletter den alle bilder laget for 60 dager siden. Etter lansering vil daterte øyeblikksbilder av de oppførte volumene vises i /backup/btrfs/back/remote/ underkatalogene.

La oss gi koden utførelsesrettigheter:

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

La oss sjekke det og legge 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 tynn backup

La oss lage en tynn pool på sikkerhetskopienheten:

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

La oss installere ddrescue, fordi... skript vil bruke dette verktøyet:

#apt-get install gddrescue

La oss lage en katalog for skript:

#mkdir /root/lvm-thin-backup

La oss kopiere skriptene:

Mye bash inni...#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

Hva gjør den...?Inneholder et sett med kommandoer for å manipulere tynne øyeblikksbilder og synkronisere forskjellen mellom to tynne øyeblikksbilder mottatt via thin_delta til en annen blokkenhet ved bruk av ddrescue og blkdiscard.

Et annet skript som vi vil legge inn i cron:

Litt mer 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

Hva gjør den...?Bruker det forrige skriptet til å lage og synkronisere sikkerhetskopier av de oppførte tynne volumene. Skriptet vil etterlate inaktive øyeblikksbilder av de oppførte volumene, som er nødvendige for å spore endringer siden siste synkronisering.

Dette skriptet må redigeres, og spesifisere listen over tynne volumer som det skal lages sikkerhetskopier av. Navnene som er oppgitt er kun for illustrasjonsformål. Hvis du ønsker det, kan du skrive et skript som vil synkronisere alle volumer.

La oss gi rettighetene:

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

La oss sjekke det og legge 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 lanseringen blir lang, fordi... tynne volumer vil bli fullstendig synkronisert ved å kopiere all brukt plass. Takket være tynne LVM-metadata vet vi hvilke blokker som faktisk er i bruk, så bare faktisk brukte tynne volumblokker vil bli kopiert.

Påfølgende kjøringer vil kopiere dataene trinnvis takket være endringssporing via tynne LVM-metadata.

La oss se hva som skjedde:

#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

Hva har dette med hekkende dukker å gjøre?

Mest sannsynlig, gitt at LVM LV logiske volumer kan være LVM PV fysiske volumer for andre VG-er. LVM kan være rekursivt, som hekkende dukker. Dette gir LVM ekstrem fleksibilitet.

PS

I neste artikkel vil vi prøve å bruke flere lignende mobile lagringssystemer/KVM som grunnlag for å lage en geo-distribuert lagring/vm-klynge med redundans på flere kontinenter ved bruk av hjemmestasjonære datamaskiner, hjemmenettverket og P2P-nettverk.

Kilde: www.habr.com

Legg til en kommentar