Vad har LVM och Matryoshka gemensamt?

God dag.
Jag skulle vilja dela med mig av min praktiska erfarenhet av att bygga ett datalagringssystem för KVM med md RAID + LVM.

Programmet kommer att innehålla:

  • Bygger md RAID 1 från NVMe SSD.
  • Montering av md RAID 6 från SATA SSD och vanliga enheter.
  • Funktioner för TRIM/DISCARD-drift på SSD RAID 1/6.
  • Skapa en startbar md RAID 1/6-array på en vanlig uppsättning diskar.
  • Installera systemet på NVMe RAID 1 när det inte finns något NVMe-stöd i BIOS.
  • Använder LVM-cache och LVM thin.
  • Använda BTRFS ögonblicksbilder och skicka/ta emot för säkerhetskopiering.
  • Använda LVM tunna ögonblicksbilder och thin_delta för säkerhetskopieringar i BTRFS-stil.

Om du är intresserad, se katt.

Uttalande

Författaren tar inget ansvar för konsekvenserna av att använda eller inte använda material/exempel/kod/tips/data från denna artikel. Genom att läsa eller använda detta material på något sätt tar du ansvar för alla konsekvenser av dessa handlingar. Möjliga konsekvenser inkluderar:

  • Knäckfriterade NVMe SSD:er.
  • Fullständigt förbrukad inspelningsresurs och fel på SSD-enheter.
  • Fullständig förlust av all data på alla enheter, inklusive säkerhetskopior.
  • Felaktig datorhårdvara.
  • Slöseri med tid, nerver och pengar.
  • Eventuella andra konsekvenser som inte är listade ovan.

järn

Tillgängliga var:

Moderkort från omkring 2013 med Z87-chipset, komplett med Intel Core i7 / Haswell.

  • Processor 4 kärnor, 8 trådar
  • 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 blinkade till IT/HBA-läge. RAID-aktiverad firmware har avsiktligt ersatts med HBA firmware för att:

  1. Du kan kasta ut den här adaptern när som helst och ersätta den med vilken annan du stöter på.
  2. TRIM/Kassera fungerade normalt på diskar, eftersom... i RAID-firmware stöds inte dessa kommandon alls, och HBA bryr sig i allmänhet inte om vilka kommandon som sänds över bussen.

Hårddiskar - 8 stycken HGST Travelstar 7K1000 med en kapacitet på 1 TB i en 2.5 formfaktor, som för bärbara datorer. Dessa enheter var tidigare i en RAID 6-array. De kommer också att ha användning i det nya systemet. För att lagra lokala säkerhetskopior.

Dessutom lagt till:

6 stycken SATA SSD modell Samsung 860 QVO 2TB. Dessa SSD:er krävde en stor volym, närvaron av en SLC-cache, tillförlitlighet och ett lågt pris var önskvärt. Stöd för discard/zero krävdes, vilket kontrolleras av raden i dmesg:

kernel: ata1.00: Enabling discard_zeroes_data

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

För dessa SSD:er är slumpmässig läs/skrivhastighet och resurskapacitet för dina behov viktiga. Kylare för dem. Nödvändigtvis. Absolut. Annars, stek dem tills de är knapriga under den första RAID-synkroniseringen.

StarTech PEX8M2E2-adapter för 2 x NVMe SSD installerad i PCIe 3.0 8x-kortplats. Detta är återigen bara en HBA, men för NVMe. Den skiljer sig från billiga adaptrar genom att den inte kräver PCIe-bifurkationsstöd från moderkortet på grund av närvaron av en inbyggd PCIe-switch. Det kommer att fungera även i det äldsta systemet med PCIe, även om det är en x1 PCIe 1.0-plats. Naturligtvis i lämplig hastighet. Det finns inga RAID där. Det finns ingen inbyggd BIOS ombord. Så ditt system lär sig inte på magiskt sätt att starta med NVMe, än mindre gör NVMe RAID tack vare den här enheten.

Den här komponenten berodde enbart på närvaron av endast en gratis 8x PCIe 3.0 i systemet, och om det finns 2 lediga platser kan den enkelt ersättas med två penny PEX4M2E1 eller analoger, som kan köpas var som helst till ett pris av 600 rubel.

Avvisandet av all slags hårdvara eller inbyggd chipset/BIOS RAID gjordes medvetet, för att helt kunna ersätta hela systemet, med undantag för själva SSD/HDD, samtidigt som all data bevaras. Helst så att du kan spara även det installerade operativsystemet när du flyttar till helt ny/annan hårdvara. Huvudsaken är att det finns SATA- och PCIe-portar. Det är som en live-CD eller en startbar flashenhet, bara väldigt snabb och lite skrymmande.

ЮморAnnars vet du vad som händer - ibland behöver du akut ta med dig hela arrayen för att ta bort. Men jag vill inte förlora data. För att göra detta är alla nämnda media bekvämt placerade på bilderna i standardfodralets 5.25 fack.

Tja, och, naturligtvis, för att experimentera med olika metoder för SSD-cache i Linux.

Hårdvaruangrepp är tråkiga. Sätt på den. Antingen fungerar det eller så gör det inte. Och med mdadm finns det alltid alternativ.

Programvara

Tidigare installerades Debian 8 Jessie på hårdvaran, som är nära EOL. RAID 6 sattes ihop från ovan nämnda hårddiskar parade med LVM. Den körde virtuella maskiner i kvm/libvirt.

Därför att Författaren har lämplig erfarenhet av att skapa bärbara startbara SATA/NVMe-flashenheter, och även, för att inte bryta den vanliga apt-mallen, valdes Ubuntu 18.04 som målsystem, som redan har stabiliserats tillräckligt, men som fortfarande har 3 år av stöd i framtiden.

Det nämnda systemet innehåller alla hårdvarudrivrutiner vi behöver ur lådan. Vi behöver ingen programvara eller drivrutiner från tredje part.

Förbereder för installation

För att installera systemet behöver vi Ubuntu Desktop Image. Serversystemet har någon form av kraftfull installationsprogram, som visar överdrivet oberoende som inte kan inaktiveras genom att trycka in UEFI-systempartitionen på en av diskarna, vilket förstör all skönhet. Följaktligen installeras den endast i UEFI-läge. Erbjuder inga alternativ.

Vi är inte nöjda med detta.

Varför?Tyvärr är UEFI-start extremt dåligt kompatibel med startprogramvara RAID, eftersom... Ingen erbjuder oss reservationer för UEFI ESP-partitionen. Det finns recept på nätet som föreslår att du placerar ESP-partitionen på en flash-enhet i en USB-port, men det här är ett misslyckande. Det finns recept som använder mjukvaran mdadm RAID 1 med metadata version 0.9 som inte hindrar UEFI BIOS från att se den här partitionen, men detta lever till det lyckliga ögonblicket när BIOS eller ett annat hårdvaruoperativsystem skriver något till ESP:n och glömmer att synkronisera det till andra speglar.

Dessutom beror UEFI-start på NVRAM, som inte kommer att flytta tillsammans med diskarna till det nya systemet, eftersom är en del av moderkortet.

Så vi kommer inte att uppfinna ett nytt hjul igen. Vi har redan en färdig, beprövad farfarscykel, nu kallad Legacy/BIOS boot, som bär det stolta namnet CSM på UEFI-kompatibla system. Vi tar bara av den från hyllan, smörjer den, pumpar upp däcken och torkar av den med en fuktig trasa.

Desktopversionen av Ubuntu kan inte heller installeras ordentligt med Legacy bootloader, men här, som de säger, finns det åtminstone alternativ.

Och så samlar vi in ​​hårdvaran och laddar systemet från den startbara Ubuntu Live-flashenheten. Vi kommer att behöva ladda ner paket, så vi konfigurerar nätverket som fungerar för dig. Om det inte fungerar kan du ladda de nödvändiga paketen på en flashenhet i förväg.

Vi går in i skrivbordsmiljön, startar terminalemulatorn och kör iväg:

#sudo bash

Hur…?Raden ovan är den kanoniska triggern för holiwars om sudo. Cbоstörre möjligheter kommer ochоstörre ansvar. Frågan är om man kan ta på sig det själv. Många tror att det åtminstone inte är försiktigt att använda sudo på det här sättet. Dock:

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

Varför inte ZFS...?När vi installerar programvara på vår dator lånar vi i princip ut vår hårdvara till utvecklarna av denna programvara att driva.
När vi litar på denna programvara med säkerheten för våra data, tar vi ett lån som motsvarar kostnaden för att återställa dessa data, vilket vi kommer att behöva betala av någon gång.

Ur denna synvinkel är ZFS en Ferrari, och mdadm+lvm är mer som en cykel.

Subjektivt föredrar författaren att låna ut en cykel på kredit till okända personer istället för en Ferrari. Där är priset på emissionen inte högt. Inget behov av rättigheter. Enklare än trafikregler. Parkering är gratis. Längdåkningsförmågan är bättre. Du kan alltid fästa ben på en cykel, och du kan reparera en cykel med dina egna händer.

Varför då BTRFS...?För att kunna starta operativsystemet behöver vi ett filsystem som stöds i Legacy/BIOS GRUB ur lådan, och som samtidigt stöder livesnapshots. Vi kommer att använda den för /boot-partitionen. Dessutom föredrar författaren att använda denna FS för / (root), utan att glömma att notera att för all annan programvara kan du skapa separata partitioner på LVM och montera dem i de nödvändiga katalogerna.

Vi kommer inte att lagra några bilder av virtuella maskiner eller databaser på denna FS.
Denna FS kommer endast att användas för att skapa ögonblicksbilder av systemet utan att stänga av det och sedan överföra dessa ögonblicksbilder till en backup-skiva med hjälp av skicka/ta emot.

Dessutom föredrar författaren i allmänhet att ha ett minimum av mjukvara direkt på hårdvaran och köra all annan mjukvara i virtuella maskiner med hjälp av saker som att vidarebefordra GPU:er och PCI-USB-värdkontroller till KVM via IOMMU.

Det enda som finns kvar på hårdvaran är datalagring, virtualisering och backup.

Om du litar mer på ZFS är de i princip utbytbara för den angivna applikationen.

Författaren ignorerar dock medvetet de inbyggda speglings-/RAID- och redundansfunktionerna som ZFS, BRTFS och LVM har.

Som ett ytterligare argument har BTRFS förmågan att förvandla slumpmässiga skrivningar till sekventiella, vilket har en extremt positiv effekt på hastigheten för att synkronisera ögonblicksbilder/säkerhetskopior på hårddisken.

Låt oss skanna om alla enheter:

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

Låt oss ta en titt:

#lsscsi && nvme list
[0:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sda
[1:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sdb
[2:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sdc
[3:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sdd
[4:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sde
[5:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sdf
[6:0:0:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdg
[6:0:1:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdh
[6:0:2:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdi
[6:0:3:0] disk ATA HGST HTS721010A9 A3B0 /dev/sdj
[6:0:4:0] disk ATA HGST HTS721010A9 A3B0 /dev/sdk
[6:0:5:0] disk ATA HGST HTS721010A9 A3B0 /dev/sdl
[6:0:6:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdm
[6:0:7:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdn
Node SN Model Namespace Usage Format FW Rev
---------------- -------------------- ---------------------------------------- --------- -------------------------- ---------------- --------
/dev/nvme0n1 S466NXXXXXXX15L Samsung SSD 970 EVO 500GB 1 0,00 GB / 500,11 GB 512 B + 0 B 2B2QEXE7
/dev/nvme1n1 S5H7NXXXXXXX48N Samsung SSD 970 EVO 500GB 1 0,00 GB / 500,11 GB 512 B + 0 B 2B2QEXE7

Disklayout

NVMe SSD

Men vi kommer inte att markera dem på något sätt. Ändå ser inte vårt BIOS dessa enheter. Så de går helt och hållet till mjukvaru-RAID. Vi kommer inte ens att skapa sektioner där. Om du vill följa "kanon" eller "i princip", skapa en stor partition, som en hårddisk.

SATA HDD

Det finns ingen anledning att hitta på något speciellt här. Vi skapar en sektion för allt. Vi kommer att skapa en partition eftersom BIOS ser dessa diskar och kanske till och med försöker starta från dem. Vi kommer till och med att installera GRUB på dessa diskar senare så att systemet plötsligt kan göra detta.

#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 är här det blir intressant för oss.

För det första är våra enheter 2 TB stora. Detta är inom det acceptabla intervallet för MBR, vilket är vad vi kommer att använda. Kan vid behov ersättas med GPT. GPT-diskar har ett kompatibilitetslager som gör att MBR-kompatibla system kan se de första 4 partitionerna om de finns inom de första 2 terabyte. Huvudsaken är att startpartitionen och bios_grub-partitionen på dessa diskar ska vara i början. Detta låter dig till och med starta från GPT Legacy/BIOS-enheter.

Men detta är inte vårt fall.

Här kommer vi att skapa två sektioner. Den första kommer att vara 1 GB stor och användas för RAID 1 /boot.

Den andra kommer att användas för RAID 6 och tar upp allt kvarvarande ledigt utrymme förutom ett litet oallokerat område i slutet av enheten.

Vad är detta omärkta område?Enligt källor på nätverket har våra SATA SSD:er ombord en dynamiskt expanderbar SLC-cache som sträcker sig i storlek från 6 till 78 gigabyte. Vi får 6 gigabyte "gratis" på grund av skillnaden mellan "gigabyte" och "gibibyte" i databladet för enheten. De återstående 72 gigabyte tilldelas från oanvänt utrymme.

Här bör det noteras att vi har en SLC-cache, och utrymmet är upptaget i 4-bitars MLC-läge. Vilket för oss i praktiken betyder att för varje 4 gigabyte ledigt utrymme får vi bara 1 gigabyte SLC-cache.

Multiplicera 72 gigabyte med 4 och få 288 gigabyte. Detta är det lediga utrymmet som vi inte kommer att markera för att tillåta enheterna att utnyttja SLC-cachen fullt ut.

Således kommer vi effektivt att få upp till 312 gigabyte SLC-cache från totalt sex enheter. Av alla enheter kommer 2 att användas i RAID för redundans.

Denna mängd cache kommer att tillåta oss att extremt sällan i verkliga livet stöta på en situation där en skrivning inte går till cachen. Detta kompenserar extremt väl för den tråkigaste nackdelen med QLC-minne - den extremt låga skrivhastigheten när data skrivs förbi cachen. Om dina belastningar inte stämmer överens med detta, så rekommenderar jag att du tänker noga på hur länge din SSD kommer att hålla under en sådan belastning, med hänsyn till TBW från 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

Skapa arrayer

Först måste vi byta namn på maskinen. Detta är nödvändigt eftersom värdnamnet är en del av arraynamnet någonstans inuti mdadm och påverkar något någonstans. Naturligtvis kan arrayer bytas om senare, men detta är ett onödigt steg.

#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

Varför -anta-rent...?För att undvika att initiera arrayer. För både RAID nivå 1 och 6 är detta giltigt. Allt kan fungera utan initiering om det är en ny array. Dessutom är initialisering av SSD-arrayen vid skapandet ett slöseri med TBW-resurser. Vi använder TRIM/DISCARD där det är möjligt på sammansatta SSD-arrayer för att "initiera" dem.

För SSD-arrayer stöds RAID 1 DISCARD direkt.

För SSD RAID 6 DISCARD-arrayer måste du aktivera det i kärnmodulens parametrar.

Detta bör endast göras om alla SSD:er som används i nivå 4/5/6-arrayer i detta system har fungerande stöd för discard_zeroes_data. Ibland stöter du på konstiga enheter som talar om för kärnan att den här funktionen stöds, men i själva verket finns den inte där, eller så fungerar funktionen inte alltid. För tillfället finns support nästan överallt, dock finns det gamla enheter och firmware med fel. Av denna anledning är DISCARD-stödet inaktiverat som standard för RAID 6.

Observera, följande kommando kommer att förstöra all data på NVMe-enheter genom att "initiera" arrayen med "nollor".

#blkdiscard /dev/md0

Om något går fel, försök att ange ett steg.

#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

Varför så stor...?Att öka chunk-storleken har en positiv effekt på hastigheten för slumpmässig avläsning i block upp till chunk-size inklusive. Detta beror på att en operation av lämplig storlek eller mindre kan utföras helt på en enda enhet. Därför summeras IOPS från alla enheter. Enligt statistik överstiger 99% av IO inte 512K.

RAID 6 IOPS per skrivning alltid mindre än eller lika med IOPS för en enhet. När, som en slumpmässig läsning, IOPS kan vara flera gånger större än för en enhet, och här är blockstorleken av avgörande betydelse.
Författaren ser inte poängen med att försöka optimera en parameter som är dålig i RAID 6 by-design och istället optimera vad RAID 6 är bra på.
Vi kommer att kompensera för den dåliga slumpmässiga skrivningen av RAID 6 med en NVMe-cache och tunn-provisioning-trick.

Vi har ännu inte aktiverat DISCARD för RAID 6. Så vi kommer inte att "initiera" denna array för tillfället. Vi kommer att göra detta senare, efter att ha installerat 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

För hastighet vill vi placera rotfilsystemet på NVMe RAID 1 som är /dev/md0.
Men vi kommer fortfarande att behöva denna snabba array för andra behov, såsom swap, metadata och LVM-cache och LVM-tunn metadata, så vi kommer att skapa en LVM VG på denna array.

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

Låt oss skapa en partition för rotfilsystemet.

#lvcreate -L 128G --name root root

Låt oss skapa en partition för att byta efter storleken på RAM-minnet.

#lvcreate -L 32G --name swap root

OS installation

Totalt har vi allt som behövs för att installera systemet.

Starta systeminstallationsguiden från Ubuntu Live-miljön. Normal installation. Endast vid val av diskar för installation måste du ange följande:

  • /dev/md1, - monteringspunkt /boot, FS - BTRFS
  • /dev/root/root (alias /dev/mapper/root-root), - monteringspunkt / (root), FS - BTRFS
  • /dev/root/swap (aka /dev/mapper/root-swap), - använd som swappartition
  • Installera starthanteraren på /dev/sda

När du väljer BTRFS som rotfilsystem kommer installationsprogrammet automatiskt att skapa två BTRFS-volymer med namnet "@" för / (root), och "@home" för /home.

Låt oss börja installationen...

Installationen avslutas med en modal dialogruta som indikerar ett fel vid installationen av starthanteraren. Tyvärr kommer du inte att kunna avsluta den här dialogrutan med standardmetoder och fortsätta installationen. Vi loggar ut från systemet och loggar in igen, och hamnar på ett rent Ubuntu Live-skrivbord. Öppna terminalen och igen:

#sudo bash

Skapa en chroot-miljö för att fortsätta 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

Låt oss konfigurera nätverket och värdnamnet 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

Låt oss gå in i chroot-miljön:

#chroot /mnt/chroot

Först och främst kommer vi att leverera paketen:

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

Låt oss kontrollera och fixa alla paket som installerades snett på grund av ofullständig systeminstallation:

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

Om något inte fungerar kan du behöva redigera /etc/apt/sources.list först

Låt oss justera parametrarna för RAID 6-modulen för att aktivera TRIM/DISCARD:

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

Låt oss justera våra arrayer lite:

#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

Vad var det..?Vi har skapat en uppsättning udev-regler som gör följande:

  • Ställ in blockcachestorleken för RAID 2020 för att vara tillräcklig för 6. Standardvärdet verkar inte ha ändrats sedan Linux skapades och har inte varit tillräckligt på länge.
  • Reservera ett minimum av IO för varaktigheten av arraykontroller/synkroniseringar. Detta för att förhindra att dina arrayer fastnar i ett tillstånd av evig synkronisering under belastning.
  • Begränsa den maximala IO under kontroller/synkronisering av arrayer. Detta är nödvändigt för att synkronisering/kontroll av SSD-RAID:er inte ska göra dina hårddiskar skarpa. Detta gäller särskilt för NVMe. (Kommer du ihåg kylaren? Jag skojade inte.)
  • Förbjud diskar från att stoppa spindelrotation (HDD) via APM och ställ in vilotiden för diskkontroller till 7 timmar. Du kan inaktivera APM helt om dina enheter kan göra det (-B 255). Med standardvärdet stannar enheterna efter fem sekunder. Sedan vill operativsystemet återställa diskcachen, diskarna kommer att snurra upp igen och allt börjar om igen. Skivorna har ett begränsat maximalt antal spindelrotationer. En sådan enkel standardcykel kan enkelt döda dina diskar på ett par år. Alla diskar lider inte av detta, men våra är "bärbara" sådana, med lämpliga standardinställningar, som får RAID att se ut som en mini-MAID.
  • Installera readahead på diskar (roterande) 1 megabyte - två på varandra följande block/chunk RAID 6
  • Inaktivera readahead på själva arrayerna.

Låt oss redigera /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

Varför är det så..?Vi kommer att söka efter /boot-partitionen med UUID. Arraynamn kan teoretiskt sett ändras.

Vi kommer att söka efter de återstående sektionerna efter LVM-namn i /dev/mapper/vg-lv-notationen, eftersom de identifierar partitioner ganska unikt.

Vi använder inte UUID för LVM pga UUID för LVM-volymer och deras ögonblicksbilder kan vara desamma.Montera /dev/mapper/root-root.. två gånger?Ja. Exakt. Funktion hos BTRFS. Detta filsystem kan monteras flera gånger med olika subvolymer.

På grund av samma funktion rekommenderar jag att du aldrig skapar LVM-ögonblicksbilder av aktiva BTRFS-volymer. Du kan få en överraskning när du startar om.

Låt oss återskapa mdadm-konfigurationen:

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

Låt oss justera LVM-inställningarna:

#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

Vad var det..?Vi har aktiverat automatisk expansion av tunna LVM-pooler efter att ha nått 90 % av det upptagna utrymmet med 5 % av volymen.

Vi har ökat det maximala antalet cacheblock för LVM-cache.

Vi har förhindrat LVM från att söka efter LVM-volymer (PV) på:

  • enheter som innehåller LVM-cache (cdata)
  • enheter cachelagrade med LVM-cache, förbi cachen ( _corig). I det här fallet kommer själva den cachade enheten fortfarande att skannas genom cachen (bara ).
  • enheter som innehåller LVM-cache-metadata (cmeta)
  • alla enheter i VG med namnbilderna. Här kommer vi att ha diskbilder av virtuella maskiner, och vi vill inte att LVM på värden ska aktivera volymer som tillhör gäst-OS.
  • alla enheter i VG med namnet backup. Här kommer vi att ha säkerhetskopior av virtuella maskinbilder.
  • alla enheter vars namn slutar med "gpv" (fysisk gästvolym)

Vi har aktiverat DISCARD-stöd när du frigör ledigt utrymme på LVM VG. Var försiktig. Detta kommer att ta bort LV på SSD ganska tidskrävande. Detta gäller speciellt SSD RAID 6. Men enligt planen kommer vi att använda tunn provisionering, så detta kommer inte att hindra oss alls.

Låt oss uppdatera initramfs-bilden:

#update-initramfs -u -k all

Installera och konfigurera grub:

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

Vilka diskar ska man välja?Alla som är sd*. Systemet måste kunna starta från valfri fungerande SATA-enhet eller SSD.

Varför lade de till os-prober..?För överdriven självständighet och lekfulla händer.

Det fungerar inte korrekt om en av RAID:erna är i ett degraderat tillstånd. Den försöker söka efter operativsystemet på partitioner som används i virtuella maskiner som körs på denna hårdvara.

Om du behöver det kan du lämna det, men tänk på allt ovanstående. Jag rekommenderar att du letar efter recept för att bli av med stygga händer på nätet.

Med detta har vi slutfört den första installationen. Det är dags att starta om till det nyinstallerade operativsystemet. Glöm inte att ta bort den startbara Live CD/USB-skivan.

#exit
#reboot

Välj någon av SATA SSD:erna som startenhet.

LVM på SATA SSD

Vid det här laget har vi redan startat upp i det nya operativsystemet, konfigurerat nätverket, apt, öppnat terminalemulatorn och startat:

#sudo bash

Låt oss fortsätta.

"Initiera" arrayen från SATA SSD:

#blkdiscard /dev/md2

Om det inte fungerar, försök:

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

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

Varför ännu en VG..?Faktum är att vi redan har en VG som heter root. Varför inte lägga till allt i en VG?

Om det finns flera PV:er i en VG måste alla PV:er finnas (online) för att VG ska aktiveras korrekt. Undantaget är LVM RAID, som vi medvetet inte använder.

Vi vill verkligen att om det blir ett fel (läs dataförlust) på någon av RAID 6-arrayerna så kommer operativsystemet att starta normalt och ge oss möjlighet att lösa problemet.

För att göra detta kommer vi på den första abstraktionsnivån att isolera varje typ av fysisk "media" till en separat VG.

Vetenskapligt sett hör olika RAID-arrayer till olika "tillförlitlighetsdomäner". Du bör inte skapa en ytterligare gemensam punkt av misslyckande för dem genom att klämma in dem i en VG.

Närvaron av LVM på "hårdvaru"-nivån kommer att tillåta oss att godtyckligt skära bitar av olika RAID-arrayer genom att kombinera dem på olika sätt. Till exempel - spring samtidigt bcache + LVM tunn, bcache + BTRFS, LVM cache + LVM tunn, en komplex ZFS-konfiguration med cacher, eller någon annan helvetisk blandning för att försöka jämföra allt.

På "hårdvaru"-nivån kommer vi inte att använda något annat än gamla goda "tjocka" LVM-volymer. Undantaget från denna regel kan vara backup-partitionen.

Jag tror att vid det här ögonblicket hade många läsare redan börjat misstänka något om den häckande dockan.

LVM på SATA HDD

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

Ny VG igen..?Vi vill verkligen att om diskarrayen som vi kommer att använda för säkerhetskopiering av data misslyckas, kommer vårt operativsystem att fortsätta att fungera normalt, samtidigt som vi behåller tillgången till icke-backupdata som vanligt. För att undvika VG-aktiveringsproblem skapar vi därför en separat VG.

Konfigurera LVM-cache

Låt oss skapa en LV på NVMe RAID 1 för att använda den som en cachningsenhet.

#lvcreate -L 70871154688B --name cache root

Varför är det så lite...?Faktum är att våra NVMe SSD:er också har en SLC-cache. 4 gigabyte "gratis" och 18 gigabyte dynamisk på grund av det lediga utrymmet som upptas i 3-bitars MLC. När denna cache är slut kommer NVMe SSD:er inte vara mycket snabbare än vår SATA SSD med cache. Av denna anledning är det faktiskt ingen mening för oss att göra LVM-cachepartitionen mycket större än dubbelt så stor som SLC-cachen för NVMe-enheten. För de NVMe-enheter som används anser författaren att det är rimligt att göra 32-64 gigabyte cache.

Den givna partitionsstorleken krävs för att organisera 64 gigabyte cache, cache-metadata och metadatasäkerhetskopiering.

Dessutom noterar jag att efter en smutsig systemavstängning kommer LVM att markera hela cachen som smutsig och synkroniseras igen. Dessutom kommer detta att upprepas varje gång lvchange används på den här enheten tills systemet startas om igen. Därför rekommenderar jag att du omedelbart återskapar cachen med lämpligt skript.

Låt oss skapa en LV på SATA RAID 6 för att använda den som en cachad enhet.

#lvcreate -L 3298543271936B --name cache data

Varför bara tre terabyte..?Så att du vid behov kan använda SATA SSD RAID 6 för vissa andra behov. Storleken på det cachade utrymmet kan ökas dynamiskt, i farten, utan att stoppa systemet. För att göra detta måste du tillfälligt stoppa och återaktivera cachen, men den utmärkande fördelen med LVM-cache framför till exempel bcache är att detta kan göras i farten.

Låt oss skapa en ny VG för cachning.

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

Låt oss skapa en LV på den cachade enheten.

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

Här tog vi omedelbart upp allt ledigt utrymme på /dev/data/cache så att alla andra nödvändiga partitioner skapades direkt på /dev/root/cache. Om du skapade något på fel ställe kan du flytta det med pvmove.

Låt oss skapa och aktivera 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

Varför sådan chunksize..?Genom praktiska experiment kunde författaren ta reda på att det bästa resultatet uppnås om storleken på LVM-cacheblocket sammanfaller med storleken på det tunna LVM-blocket. Dessutom, ju mindre storlek, desto bättre presterar konfigurationen i en slumpmässig inspelning.

64k är den minsta tillåtna blockstorleken för LVM-tunn.

Var försiktig med att skriva tillbaka..!Ja. Denna typ av cache skjuter upp skrivsynkronisering till den cachelagrade enheten. Det betyder att om cachen försvinner kan du förlora data på den cachelagrade enheten. Senare kommer författaren att berätta vilka åtgärder, utöver NVMe RAID 1, som kan vidtas för att kompensera för denna risk.

Denna cachetyp valdes avsiktligt för att kompensera för den dåliga slumpmässiga skrivprestandan hos RAID 6.

Låt oss kolla vad 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)

Endast [cachedata_corig] ska finnas på /dev/data/cache. Om något är fel, använd pvmove.

Du kan inaktivera cachen om det behövs med ett kommando:

#lvconvert -y --uncache cache/cachedata

Detta görs online. LVM synkroniserar helt enkelt cachen till disken, tar bort den och byter namn på cachedata_corig tillbaka till cachedata.

Ställer in LVM tunn

Låt oss grovt uppskatta hur mycket utrymme vi behöver för LVM tunna 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"

Runda upp till 4 gigabyte: 4294967296B

Multiplicera med två och lägg till 4194304B för LVM PV-metadata: 8594128896B
Låt oss skapa en separat partition på NVMe RAID 1 för att placera LVM tunna metadata och deras säkerhetskopia på den:

#lvcreate -L 8594128896B --name images root

För vad..?Här kan frågan uppstå: varför placera LVM tunna metadata separat om det fortfarande kommer att cachelagras på NVMe och kommer att fungera snabbt.

Även om hastigheten är viktig här är det långt ifrån huvudorsaken. Saken är att cachen är en punkt av misslyckande. Något kan hända med den, och om den tunna LVM-metadatan cachelagras kommer det att leda till att allt går förlorat helt. Utan fullständig metadata blir det nästan omöjligt att sätta ihop tunna volymer.

Genom att flytta metadata till en separat icke-cachad, men snabb, volym garanterar vi säkerheten för metadata i händelse av cacheförlust eller korruption. I det här fallet kommer all skada som orsakas av cacheförlust att lokaliseras i tunna volymer, vilket kommer att förenkla återställningsproceduren i storleksordningar. Med stor sannolikhet kommer dessa skador att återställas med hjälp av FS-loggar.

Dessutom, om en ögonblicksbild av en tunn volym togs tidigare, och efter det att cachen synkroniserades fullständigt minst en gång, kommer, på grund av den interna designen av LVM thin, integriteten för ögonblicksbilden att garanteras i händelse av cacheförlust .

Låt oss skapa en ny VG som kommer att ansvara för thin-provisioning:

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

Låt oss skapa en pool:

#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T images/thin-pool
Varför -Z yUtöver vad det här läget egentligen är avsett för - för att förhindra att data från en virtuell maskin läcker till en annan virtuell maskin vid omfördelning av utrymme - används dessutom nollställning för att öka hastigheten för slumpmässig skrivning i block mindre än 64k. All skrivning mindre än 64k till ett tidigare oallokerat område av den tunna volymen kommer att bli 64K kantjusterad i cachen. Detta kommer att tillåta operationen att utföras helt och hållet genom cachen, förbi den cachade enheten.

Låt oss flytta LV till motsvarande 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

Låt oss kolla:

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

Låt oss skapa en tunn volym för tester:

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

Vi kommer att installera paket för tester och övervakning:

#apt-get install sysstat fio

Så här kan du observera beteendet hos vår lagringskonfiguration 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å här kan vi testa vår 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

Försiktigt! Resurs!Den här koden kommer att köra 36 olika tester, var och en körs i 4 sekunder. Hälften av testerna är för inspelning. Du kan spela in mycket på NVMe på 4 sekunder. Upp till 3 gigabyte per sekund. Så varje körning av skrivtester kan äta upp till 216 gigabyte SSD-resurs från dig.

Läsa och skriva blandat?Ja. Det är vettigt att köra läs- och skrivtesten separat. Dessutom är det vettigt att se till att alla cachar är synkroniserade så att en tidigare gjord skrivning inte påverkar läsningen.

Resultaten kommer att variera mycket under den första lanseringen och efterföljande när cachen och den tunna volymen fylls upp, och även beroende på om systemet lyckades synkronisera de fyllda cacherna under den senaste lanseringen.

Jag rekommenderar bland annat att mäta hastigheten på en redan full tunn volym som en ögonblicksbild precis togs från. Författaren hade möjlighet att observera hur slumpmässiga skrivningar accelererar kraftigt direkt efter att den första ögonblicksbilden skapats, särskilt när cachen ännu inte är helt full. Detta sker på grund av copy-on-write-skrivsemantik, justering av cache och tunna volymblock, och det faktum att en slumpmässig skrivning till RAID 6 förvandlas till en slumpmässig läsning från RAID 6 följt av en skrivning till cachen. I vår konfiguration är slumpmässig läsning från RAID 6 upp till 6 gånger (antalet SATA SSD:er i arrayen) snabbare än att skriva. Därför att block för CoW allokeras sekventiellt från en tunn pool, sedan övergår inspelningen för det mesta också till sekventiell.

Båda dessa funktioner kan användas till din fördel.

Cachelagra "koherenta" ögonblicksbilder

För att minska risken för dataförlust i händelse av skada/förlust av cache, föreslår författaren att man ska införa praxis att rotera ögonblicksbilder för att garantera deras integritet i detta fall.

För det första, eftersom metadata med tunn volym finns på en uncachad enhet, kommer metadata att vara konsekventa och eventuella förluster kommer att isoleras inom datablock.

Följande rotationscykel för ögonblicksbild garanterar integriteten för data inuti ögonblicksbilderna i händelse av cacheförlust:

  1. För varje tunn volym med namnet <namn>, skapa en ögonblicksbild med namnet <namn>.cached
  2. Låt oss ställa in migreringströskeln till ett rimligt högt värde: #lvchange --quiet --cachesettings "migration_threshold=16384" cache/cachedata
  3. I slingan kontrollerar vi antalet smutsiga block i cachen: #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' tills vi får noll. Om nollan saknas för länge kan den skapas genom att tillfälligt växla cachen till genomskrivningsläge. Men med hänsyn till hastighetsegenskaperna hos våra SATA- och NVMe SSD-arrayer, såväl som deras TBW-resurs, kommer du antingen snabbt att kunna fånga ögonblicket utan att ändra cacheläget, eller så kommer din hårdvara helt att äta upp hela sin resurs i några dagar. På grund av resursbegränsningar kan systemet i princip inte vara under 100% skrivbelastning hela tiden. Våra NVMe SSD:er under 100 % skrivbelastning kommer att tömma resursen helt 3-4 dagar. SATA SSD:er kommer bara att hålla dubbelt så länge. Därför kommer vi att anta att det mesta av belastningen går till läsning, och vi har relativt kortvariga skurar av extremt hög aktivitet kombinerat med en låg belastning i genomsnitt för att skriva.
  4. Så snart vi fångade (eller gjorde) en nolla, byter vi namn på <namn>.cached till <namn>.committed. Den gamla <namn>.committed raderas.
  5. Alternativt, om cachen är 100 % full, kan den återskapas med ett skript och på så sätt rensa den. Med en halvtom cache fungerar systemet mycket snabbare när man skriver.
  6. Ställ in migreringströskeln till noll: #lvchange --quiet --cachesettings "migration_threshold=0" cache/cachedata Detta kommer tillfälligt att förhindra att cachen synkroniseras med huvudmediet.
  7. Vi väntar tills ganska många förändringar samlas i cachen #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' eller så slocknar timern.
  8. Vi upprepar igen.

Varför svårigheter med migrationströskel...?Saken är den att i verkligheten är en "slumpmässig" inspelning faktiskt inte helt slumpmässig. Om vi ​​skrev något till en sektor på 4 kilobyte i storlek, är det stor sannolikhet att inom de närmaste minuterna kommer en post att göras till samma eller en av de närliggande (+- 32K) sektorerna.

Genom att sätta migreringströskeln till noll skjuter vi upp skrivsynkroniseringen på SATA SSD:n och aggregerar flera ändringar till ett 64K-block i cachen. Detta sparar avsevärt resurserna för SATA SSD.

Var är koden..?Tyvärr anser författaren sig vara otillräckligt kompetent i utvecklingen av bash-skript eftersom han är 100% självlärd och utövar "google"-driven utveckling, därför anser han att den fruktansvärda koden som kommer ur hans händer inte bör användas av någon annan.

Jag tror att proffs inom detta område självständigt kommer att kunna skildra all logik som beskrivs ovan, om nödvändigt, och kanske till och med vackert designa den som en systemtjänst, som författaren försökte göra.

Ett sådant enkelt rotationsschema för ögonblicksbilder kommer att tillåta oss att inte bara ständigt ha en ögonblicksbild helt synkroniserad på SATA SSD, utan kommer också att tillåta oss, med hjälp av verktyget thin_delta, att ta reda på vilka block som ändrades efter att det skapades, och därmed lokalisera skador på huvudvolymerna, vilket avsevärt förenklar återhämtningen .

TRIMMA/KASSERA i libvirt/KVM

Därför att datalagringen kommer att användas för KVM som kör libvirt, då skulle det vara en bra idé att lära våra virtuella datorer att inte bara ta upp ledigt utrymme, utan också att frigöra det som inte längre behövs.

Detta görs genom att emulera TRIM/DISCARD-stöd på virtuella diskar. För att göra detta måste du ändra kontrolltypen till virtio-scsi och redigera xml.

#virsh edit vmname
<disk type='block' device='disk'>
<driver name='qemu' type='raw' cache='writethrough' io='threads' discard='unmap'/>
<source dev='/dev/images/vmname'/>
<backingStore/>
<target dev='sda' bus='scsi'/>
<alias name='scsi0-0-0-0'/>
<address type='drive' controller='0' bus='0' target='0' unit='0'/>
</disk>

<controller type='scsi' index='0' model='virtio-scsi'>
<alias name='scsi0'/>
<address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
</controller>

Sådana DISCARDs från gästoperativsystem behandlas korrekt av LVM, och block frigörs korrekt både i cachen och i den tunna poolen. I vårt fall sker detta huvudsakligen på ett försenat sätt, när nästa ögonblicksbild raderas.

BTRFS backup

Använd färdiga skript med extrem försiktighet och på egen risk. Författaren skrev denna kod själv och uteslutande för sig själv. Jag är säker på att många erfarna Linux-användare har liknande verktyg, och det finns inget behov av att kopiera någon annans.

Låt oss skapa en volym på säkerhetskopieringsenheten:

#lvcreate -L 256G --name backup backup

Låt oss formatera det i BTRFS:

#mkfs.btrfs /dev/backup/backup

Låt oss skapa monteringspunkter och montera rotundersektionerna 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

Låt oss skapa kataloger för säkerhetskopior:

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

Låt oss skapa en katalog för säkerhetskopieringsskript:

#mkdir /root/btrfs-backup

Låt oss kopiera skriptet:

Massor av läskig bash-kod. Använd på egen risk. Skriv inga arga brev till författaren...#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

Vad gör det ens..?Innehåller en uppsättning enkla kommandon för att skapa BTRFS ögonblicksbilder och kopiera dem till en annan FS med BTRFS skicka/ta emot.

Den första lanseringen kan vara relativt lång, eftersom... I början kommer all data att kopieras. Ytterligare lanseringar kommer att vara mycket snabba, eftersom... Endast ändringar kommer att kopieras.

Ett annat skript som vi lägger in i cron:

Lite mer bash-kod#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

Vad gör den..?Skapar och synkroniserar inkrementella ögonblicksbilder av de listade BTRFS-volymerna på backup-FS. Efter detta raderar den alla bilder skapade för 60 dagar sedan. Efter lanseringen kommer daterade ögonblicksbilder av de listade volymerna att visas i /backup/btrfs/back/remote/ underkatalogerna.

Låt oss ge koden körningsrättigheter:

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

Låt oss kolla det och lägga 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 tunn backup

Låt oss skapa en tunn pool på säkerhetskopieringsenheten:

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

Låt oss installera ddrescue, för... skript kommer att använda detta verktyg:

#apt-get install gddrescue

Låt oss skapa en katalog för skript:

#mkdir /root/lvm-thin-backup

Låt oss kopiera skripten:

Mycket bash inuti...#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

Vad gör den...?Innehåller en uppsättning kommandon för att manipulera tunna ögonblicksbilder och synkronisera skillnaden mellan två tunna ögonblicksbilder som tas emot via thin_delta till en annan blockenhet med hjälp av ddrescue och blkdiscard.

Ett annat skript som vi kommer att lägga i cron:

Lite 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

Vad gör den...?Använder föregående skript för att skapa och synkronisera säkerhetskopior av de listade tunna volymerna. Skriptet kommer att lämna inaktiva ögonblicksbilder av de listade volymerna, som behövs för att spåra ändringar sedan den senaste synkroniseringen.

Detta skript måste redigeras och specificera listan över tunna volymer för vilka säkerhetskopior ska göras. De angivna namnen är endast i illustrativt syfte. Om du vill kan du skriva ett skript som synkroniserar alla volymer.

Låt oss ge rättigheterna:

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

Låt oss kolla det och lägga 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örsta lanseringen kommer att bli lång, eftersom... tunna volymer kommer att synkroniseras helt genom att kopiera allt använt utrymme. Tack vare LVM tunna metadata vet vi vilka block som faktiskt används, så endast faktiskt använda tunna volymblock kommer att kopieras.

Efterföljande körningar kommer att kopiera data stegvis tack vare ändringsspårning via tunna LVM-metadata.

Låt oss se vad som hände:

#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

Vad har detta att göra med dockor som häckar?

Mest troligt, med tanke på att LVM LV logiska volymer kan vara LVM PV fysiska volymer för andra VG. LVM kan vara rekursivt, som dockor. Detta ger LVM extrem flexibilitet.

PS

I nästa artikel kommer vi att försöka använda flera liknande mobila lagringssystem/KVM som grund för att skapa ett geodistribuerat lagrings/vm-kluster med redundans på flera kontinenter med hjälp av hemdatorer, hemmainternet och P2P-nätverk.

Källa: will.com

Lägg en kommentar