God dag.
Jag vill dela med mig av praktisk erfarenhet av att bygga ett lagringssystem för KVM med hjÀlp av md RAID + LVM.
Programmet kommer att innehÄlla:
- Montering av md RAID 1 med NVMe SSD.
- Montering av md RAID 6 med SATA SSD och vanliga diskar.
- Egenskaper för TRIM/DISCARD i SSD RAID 1/6.
- Skapande av en startbar md RAID 1/6-array med en uppsÀttning gemensamma diskar.
- Installation av systemet pÄ NVMe RAID 1 i avsaknad av stöd för NVMe i BIOS.
- AnvÀndning av LVM-cache och LVM-thin.
- AnvÀndning av BTRFS-snapshots och send/receive för sÀkerhetskopiering.
- AnvÀndning av LVM-thin snapshots och thin_delta för BTRFS-liknande sÀkerhetskopiering.
Om det lÄter intressant, var vÀnlig scrolla ner.
Ansvarsfriskrivning
Författaren tar inget ansvar för konsekvenserna av att anvÀnda eller inte anvÀnda material/ exempel/ kod/ rÄd/ data frÄn denna artikel. Genom att lÀsa eller pÄ nÄgot sÀtt anvÀnda detta material tar du ansvar för alla konsekvenser av dessa ÄtgÀrder. Möjliga konsekvenser inkluderar:
- BrÀnda till krispighet NVMe SSD.
- FullstÀndigt förbrukad skrivresurs och fel pÄ SSD-enheter.
- Total förlust av all data pÄ alla lagringsenheter, inklusive sÀkerhetskopior.
- Defekt datorutrustning.
- Tappad tid, nerver och pengar.
- Alla andra konsekvenser som inte Àr listade ovan.
HÄrdvara
TillgÀngligt var:
Ett moderkort frÄn omkring 2013 baserat pÄ Z87-chipset med Intel Core i7 / Haswell.
- Processor med 4 kÀrnor, 8 trÄdar
- 32 Gigabyte 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-anslutningar
SAS-adapter LSI SAS9211-8I omprogrammerad till IT / HBA-lÀge. Firmware med RAID-stöd har avsiktligt bytts ut mot HBA-firmware för att:
- Man enkelt ska kunna slÀnga denna adapter och byta till vilken annan som helst som finns tillgÀnglig.
- TRIM/Discard fungerade normalt pÄ diskarna, dÄ dessa kommandon inte alls stöds i RAID-firmware, medan HBA i stort sett inne har nÄgot emot vilka kommandon som skickas via bussen.
HĂ„rddiskar, â 8 stycken HGST Travelstar 7K1000 med 1 TB kapacitet i 2.5" formfaktor, avsedd för bĂ€rbara datorer. Dessa diskar var tidigare i en RAID 6-array. I det nya systemet kommer de ocksĂ„ att anvĂ€ndas. För lagring av lokala sĂ€kerhetskopior.
Dessutom lades till:
6 stycken SATA SSD-modeller Samsung 860 QVO 2TB. FrÄn dessa SSD kunde stor kapacitet, SLC-cache och önskvÀrdhet av tillförlitlighet, samt ett lÄgt pris förvÀntas. Det var obligatoriskt med stöd för discard/zero som verifieras med en rad i dmesg:
kernel: ata1.00: Aktiverar discard_zeroes_data
2 stycken NVMe SSD av modell Samsung SSD 970 EVO 500GB.
För dessa SSD:er Àr hastigheten för slumpmÀssig lÀsning/ skrivning och hÄllbarhet viktig för dina behov. En kylare till dem. Absolut nödvÀndigt. Annars brÀnner du dem till en krispig konsistens vid första RAID-synkroniseringen.
StarTech PEX8M2E2-adapter för 2 x NVMe SSD installerad i PCIe 3.0 8x slot. Detta Àr, Äterigen, bara en HBA, men för NVMe. Den skiljer sig frÄn billigare adaptrar genom att den inte krÀver stöd för PCIe bifurkation frÄn moderkortet tack vare den inbyggda PCIe-switch. Kommer att fungera Àven i de Àldsta systemen som har PCIe, Àven om det Àr en x1 PCIe 1.0 slot. Naturligtvis, med motsvarande hastighet. Det finns inga RAID-funktioner dÀr. Ingen inbyggd BIOS ombord. SÄ, ditt system kommer inte magiskt att lÀra sig att starta frÄn NVMe, Àn mindre att göra NVMe RAID tack vare denna enhet.
Denna komponent valdes uteslutande pÄ grund av att det bara fanns en ledig 8x PCIe 3.0 i systemet, och om det fanns 2 lediga slots, kan den lÀtt bytas ut mot tvÄ billiga PEX4M2E1 eller motsvarande, som kan köpas var som helst för priser frÄn 600 rubel.
AvstÄendet frÄn alla möjliga hÄrdvaru- eller inbyggda chipset/BIO RAID:er gjordes medvetet, för att möjliggöra en fullstÀndig ersÀttning av hela systemet, förutom sjÀlva SSD/HDD, samtidigt som alla data bevaras. Idéellt för att Àven kunna spara det installerade operativsystemet vid flytt till helt ny/annat hÄrdvara. Huvudsakligen att det finns SATA- och PCIe-portar. Det Àr som en live CD eller bootbar USB, bara mycket snabbare och lite mer skrymmande.
HumorFör som ni vet, ibland mÄste man snabbt ta hela arrayen med sig. Och man vill inte förlora data. För detta ÀndamÄl Àr alla nÀmnda lagringsmedia smart placerade i hÄllare i standard 5.25 chassi.
SjÀlvklart för experiment med olika metoder för SSD-cache i Linux.
HÄrdvaru-RAID Àr trÄkigt. SÀtter pÄ det. Det fungerar eller inte. Men med mdadm finns det alltid alternativ.
Programvara
Tidigare var Debian 8 Jessie installerat pÄ hÄrdvaran som Àr nÀra EOL. En RAID 6 byggdes med de ovan nÀmnda HDD:erna i par med LVM. PÄ den kördes virtuella maskiner i kvm/libvirt.
Eftersom författaren har rÀtt erfarenhet av att skapa bÀrbara startbara SATA/NVMe flashenheter och för att inte förstöra den vanliga apt-mallen, valdes Ubuntu 18.04 som mÄlsystem, som redan har stabiliserat sig tillrÀckligt men fortfarande har tre Ärs support kvar.
I det nÀmnda systemet finns alla nödvÀndiga drivrutiner för hÄrdvaran direkt frÄn början. Ingen tredje parts programvara eller drivrutiner kommer att behövas.
Förberedelse för installation
För att installera systemet behöver vi Ubuntu Desktop Image. Den serverversionen har en ganska komplicerad installerare som visar en överdriven och icke-avstÀngd sjÀlvstÀndighet genom att alltid installera UEFI-systempartitionen pÄ en av diskarna, vilket förstör hela estetiken. DÀrför installeras det bara i UEFI-lÀge. Det finns inga andra alternativ.
Det hÀr Àr oacceptabelt för oss.
Varför?TyvÀrr Àr UEFI-booting extremt dÄligt kompatibel med mjukvarubaserad RAID, eftersom ingen erbjuder reservutrymme för UEFI ESP-partitionen. Det finns recept pÄ nÀtet som föreslÄr att placera ESP-partitionen pÄ en flashenhet i USB-porten, men detta Àr en felpunkt. Det finns ocksÄ recept som anvÀnder mjukvarubaserad mdadm RAID 1 med metadata version 0.9 som inte hindrar UEFI BIOS frÄn att se den hÀr partitionen, men detta hÄller bara tills det lyckosamma ögonblicket dÄ BIOS eller ett annat operativsystem pÄ hÄrdvaran skriver nÄgot till ESP utan att synkronisera med de andra speglarna.
Dessutom beror UEFI-booting pÄ NVRAM, som inte flyttas tillsammans med diskarna till det nya systemet, eftersom den Àr en del av moderkortet.
SÄ vi kommer inte att uppfinna den nya cykeln. Vi har redan en fÀrdig, beprövad gammaldags cykel som nu kallas Legacy/BIOS boot, som har det stolta namnet CSM pÄ UEFI-kompatibla system. Vi tar bara ner den frÄn hyllan, smörjer den, pumpar dÀcken och torkar av den med en fuktig trasa.
Desktop-versionen av Ubuntu klarar inte att installeras korrekt med Legacy-bootloadern, men hÀr finns Ätminstone alternativ.
SÄ vi samlar ihop hÄrdvaran och laddar systemet frÄn en startbar flashenhet med Ubuntu Live. Vi kommer att behöva ladda ner paket, sÄ vi stÀller in nÀtverket, vilket som helst fungerar. Om det inte fungerar kan de nödvÀndiga paketen laddas ner i förvÀg till flashenheten.
Vi gÄr in i Desktop-miljön, öppnar terminalemulatorn, och sÄ kör vi:
#sudo bash
Hur�Raden ovan Àr en kanonisk utlösare för debatter om sudo. Med bbÀsta resultatet (subjektiv Äsikt).Större möjligheter kommer med ettbÀsta resultatet (subjektiv Äsikt).större ansvar. FrÄgan Àr om du kan ta det pÄ dig. MÄnga anser att anvÀndningen av sudo pÄ det sÀttet Àr, Ätminstone oansvarigt. Men:

#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, i grund och botten lÄnar vi ut vÄr hÄrdvara till utvecklarna av denna programvara.
NÀr vi litar pÄ denna programvara med att skydda vÄra data, tar vi i skuld en kostnad motsvarande ÄterstÀllningen av dessa data, som vi en dag mÄste betala.
Ur den synvinkeln Àr ZFS en Ferrari, medan mdadm+lvm mer liknar en cykel.
Subjektivt föredrar författaren att lÄna en okÀnd person en kreditcykel istÀllet för en Ferrari. Priset Àr lÄgt. Inga rÀttigheter krÀvs. Enklare vÀgtrafikregler. Parkering Àr gratis. Framkomligheten Àr bÀttre. Man kan alltid fÀsta ben pÄ cykeln, och man kan Àven reparera cykeln sjÀlv.
Varför dĂ„ BTRFSâŠ?För att ladda operativsystemet behöver vi ett filsystem som stöds av Legacy/BIOS GRUB frĂ„n början, och som dessutom stödjer snapshots i realtid. Vi kommer att anvĂ€nda det för âboot-partitionen. Dessutom föredrar författaren att anvĂ€nda detta filsystem för â (roten) utan att glömma att det för annan programvara kan skapas separata partitioner pĂ„ LVM och monteras i de nödvĂ€ndiga katalogerna.
Varken bilder virtuella maskinereller databaser kommer vi att lagra pÄ detta filsystem.
Detta filsystem kommer endast att anvÀndas för att skapa ögonblicksbilder av systemet utan att stÀnga av det, följt av att dessa ögonblicksbilder överförs till en sÀkerhetsdisk med hjÀlp av send/receive.
Dessutom föredrar författaren oftast att hÄlla sÄ lite programvara som möjligt direkt pÄ hÄrdvaran och köra all annan mjukvara i virtuella maskiner med hjÀlp av hÄllare för GPU och PCI-USB-vÀrdkontrollrar i KVM via IOMMU.
PÄ hÄrdvaran finns endast lagring av data, virtualisering och sÀkerhetskopiering kvar.
Om du litar mer pÄ ZFS, Àr de i princip utbytbara för det angivna anvÀndningsomrÄdet.
Trots detta ignorerar författaren medvetet de inbyggda funktionerna för spegling â RAID och redundans som finns i ZFS, BTRFS och LVM.
Som ett ytterligare argument har BTRFS egenskapen att omvandla slumpmÀssiga skrivningar till sekventiella, vilket har en mycket positiv inverkan pÄ hastigheten för synkronisering av snapshots / sÀkerhetskopior pÄ HDD.
Vi skannar om alla enheter:
#udevadm control --reload-rules && udevadm trigger
Vi ser oss omkring:
#lsscsi && nvme list
[0:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sda
[1:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sdb
[2:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sdc
[3:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sdd
[4:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sde
[5:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sdf
[6:0:0:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdg
[6:0:1:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdh
[6:0:2:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdi
[6:0:3:0] disk ATA HGST HTS721010A9 A3B0 /dev/sdj
[6:0:4:0] disk ATA HGST HTS721010A9 A3B0 /dev/sdk
[6:0:5:0] disk ATA HGST HTS721010A9 A3B0 /dev/sdl
[6:0:6:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdm
[6:0:7:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdn
Node SN Modell Namespace AnvÀndning Format FW Rev
---------------- -------------------- ---------------------------------------- --------- -------------------------- ---------------- --------
/dev/nvme0n1 S466NXXXXXXX15L Samsung SSD 970 EVO 500GB 1 0,00 GB / 500,11 GB 512 B + 0 B 2B2QEXE7
/dev/nvme1n1 S5H7NXXXXXXX48N Samsung SSD 970 EVO 500GB 1 0,00 GB / 500,11 GB 512 B + 0 B 2B2QEXE7
Partitionering av "diskar"
NVMe SSD
Men vi kommer inte att partitionera dem. VĂ„r BIOS ser Ă€ndĂ„ inte dessa enheter. SĂ„ de gĂ„r helt in i mjukvaru-RAID. Vi kommer inte ens att skapa nĂ„gra partitioner dĂ€r. Om man vill göra det "enligt kanon" eller "principiellt" â skapa en stor partition, precis som för HDD.
SATA HDD
HÀr behöver vi inte uppfinna nÄgot nytt. Vi kommer att skapa en partition för allt. Vi skapar en partition eftersom dessa diskar syns i BIOS och det kan till och med försöka starta frÄn dem. Vi kommer Àven senare att installera GRUB pÄ dessa diskar sÄ att systemet ovÀntat kan starta.
#cat >hdd.part << EOF
etikett: dos
etikett-id: 0x00000000
enhet: /dev/sdg
enhet: sektorer
/dev/sdg1 : start= 2048, size= 1953523120, type=fd, bootable
EOF
#sfdisk /dev/sdg < hdd.part
#sfdisk /dev/sdh < hdd.part
#sfdisk /dev/sdi < hdd.part
#sfdisk /dev/sdj < hdd.part
#sfdisk /dev/sdk < hdd.part
#sfdisk /dev/sdl < hdd.part
#sfdisk /dev/sdm < hdd.part
#sfdisk /dev/sdn < hdd.part
SATA SSD
HÀr Àr det mest intressant.
För det första har vÄra enheter en storlek pÄ 2 TB. Detta ligger inom grÀnserna för MBR, vilket vi kommer att dra nytta av. Om nödvÀndigt kan det bytas ut mot GPT. GPT-diskar har ett kompatibilitetslager som gör att MBR-kompatibla system kan se de första 4 partitionerna om de ligger inom de första 2 terabyterna. Det viktigaste Àr att den startande partitionen och partitionen bios_grub pÄ dessa diskar ligger i början. Detta gör det till och med möjligt att göra Legacy/BIOS-start frÄn GPT-diskar.
Men det Àr inte vÄrt fall.
HÀr kommer vi att skapa tvÄ partitioner. 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 ta upp allt kvarvarande ledigt utrymme förutom ett litet icke-partionerat omrÄde i slutet av lagringsenheten.
Vad Àr det för icke-partionerat omrÄde?Enligt kÀllor pÄ nÀtet har vÄra SATA SSD:er en dynamiskt utökad SLC-cache pÄ mellan 6 och 78 gigabyte. 6 gigabyte fÄr vi "kostnadsfritt" pÄ grund av skillnaden mellan "gigabyte" och "gibibyte" i lagringsenhetens datablad. De andra 72 gigabyten tilldelas pÄ grund av outnyttjat utrymme.
HÀr bör det noteras att cachen Àr SLC och utrymmet anvÀnds i 4 bit MLC-lÀge. Det som effektivt innebÀr för oss Àr att för varje 4 gigabyte ledigt utrymme fÄr vi bara 1 gigabyte SLC-cache.
Vi multiplicerar 72 gigabyte med 4 och fÄr 288 gigabyte. Detta Àr det lediga utrymme som vi inte kommer att partitionera för att lÄta enheterna fullt utnyttja SLC-cachen.
PÄ sÄ sÀtt fÄr vi effektivt upp till 312 gigabyte SLC-cache totalt frÄn sex lagringsenheter. Av alla lagringsenheter kommer 2 att anvÀndas i RAID för redundans.
Denna mĂ€ngd cache kommer att göra att vi sĂ€llan stöter pĂ„ situationer dĂ€r skrivningar inte gĂ„r via cachen. Detta kompenserar extremt bra för den mest nedslĂ„ende nackdelen med QLC-minne â den extremt lĂ„ga skrivhastigheten nĂ€r data skrivs utan att passera cachen. Om dina belastningar inte överensstĂ€mmer med detta rekommenderar jag att du noga övervĂ€ger hur lĂ€nge dina SSD:er kommer att klara en sĂ„dan belastning med hĂ€nsyn till TBW i specifikationen.
#cat >ssd.part << EOF
etikett: dos
etikett-id: 0x00000000
enhet: /dev/sda
enhet: sektorer
/dev/sda1 : start= 2048, size= 2097152, type=fd, bootable
/dev/sda2 : start= 2099200, size= 3300950016, type=fd
EOF
#sfdisk /dev/sda < ssd.part
#sfdisk /dev/sdb < ssd.part
#sfdisk /dev/sdc < ssd.part
#sfdisk /dev/sdd < ssd.part
#sfdisk /dev/sde < ssd.part
#sfdisk /dev/sdf < ssd.part
Skapande av enheter
Först behöver vi döpa om maskinen. Detta Àr nödvÀndigt eftersom vÀrdnamnet Àr en del av namnet pÄ enheten nÄgonstans i mdadm och pÄverkar nÄgot. Enheter kan sjÀlvklart döpas om senare, men det Àr onödiga ÄtgÀrder.
#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 âassume-cleanâŠ?För att inte initiera enheterna. För bĂ„da RAID-nivĂ„erna 1 och 6 Ă€r detta acceptabelt. Allt kan fungera utan initiering om det Ă€r en ny enhet. Dessutom Ă€r initiering av SSD-enheter vid skapandet en slöseri med TBW-resurser. Vi anvĂ€nder TRIM/DISCARD dĂ€r det Ă€r möjligt pĂ„ de sammansatta SSD-enheterna för att "initiera" dem.
För SSD RAID 1 stöds DISCARD ur lÄdan.
För SSD RAID 6 mÄste DISCARD aktiveras i kÀrnmodulens instÀllningar.
Detta bör göras endast om alla SSD-enheter som anvÀnds i nivÄer 4/5/6 i detta system har fungerande stöd för discard_zeroes_data. Ibland dyker det upp konstiga enheter som rapporterar till kÀrnan att de stödjer denna funktion, men i verkligheten gör de det inte, eller funktionen fungerar inte alltid. För nÀrvarande finns det praktiskt taget stöd överallt, men Àldre enheter och felaktiga firmwareversioner förekommer. Av denna anledning Àr DISCARD-stödet som standard inaktiverat för RAID 6.
Observera, nÀsta kommando kommer att förstöra all data pÄ NVMe-enheterna genom att "initiera" enheten med "nollor".
#blkdiscard /dev/md0
Om nÄgot gÄr fel, försök ange 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âŠ?Ăkningen av chunk-storleken pĂ„verkar hastigheten för slumpmĂ€ssig lĂ€sning av block upp till och med chunk-storleken. Detta sker eftersom en operation av motsvarande storlek eller mindre kan utföras helt pĂ„ en enhet. DĂ€rför summeras IOPS frĂ„n alla enheter. Enligt statistik överskrider 99% av IO inte 512K.
I RAID 6 Àr IOPS för skrivning alltid mindre eller lika med IOPS för en enda enhet. Vid slumpmÀssig lÀsning kan IOPS vara högre Àn för en enda enhet flera gÄnger, och hÀr Àr blockstorleken avgörande.
Författaren ser ingen mening i att försöka optimera en parameter som Àr dÄlig för RAID 6 enligt design, och istÀllet optimerar vad RAID 6 gör bra.
Den dÄliga slumpmÀssiga skrivningen av RAID 6 kommer vi att kompensera med cache pÄ NVMe och tricks med thin-provisioning.
Vi har Ànnu inte aktiverat DISCARD för RAID 6. SÄ vi kommer inte att "initiera" denna array Ànnu. Vi gör det senare, efter operativsystemets installation.
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 /dev/md0.
Denna snabba array kommer vi dock fortfarande behöva för andra behov, sÄsom swap, metdata och LVM-cache samt metdata för LVM-thin, dÀrför kommer vi att skapa en LVM VG pÄ denna array.
#pvcreate /dev/md0
#vgcreate root /dev/md0
Vi skapar en partition för rotfilsystemet.
#lvcreate -L 128G --name root root
Vi skapar en partition för swap som Àr i storlek med RAM-minnet.
#lvcreate -L 32G --name swap root
Installation av OS
Sammantaget har vi allt som behövs för att installera systemet.
Vi startar installationsguiden frÄn Ubuntu Live-miljön. Vanlig installation. Endast under valet av diskar för installation mÄste vi ange följande:
- /dev/md1, â ŃĐŸŃĐșа ĐŒĐŸĐœŃĐžŃĐŸĐČĐ°ĐœĐžŃ /boot, ЀХ â BTRFS
- /dev/root/root (a.k.a /dev/mapper/root-root), â ŃĐŸŃĐșа ĐŒĐŸĐœŃĐžŃĐŸĐČĐ°ĐœĐžŃ / (ĐșĐŸŃĐ”ĐœŃ), ЀХ â BTRFS
- /dev/root/swap (a.k.a /dev/mapper/root-swap), â ĐžŃĐżĐŸĐ»ŃĐ·ĐŸĐČаŃŃ ĐșаĐș ŃазЎДл ĐżĐŸĐŽĐșаŃĐșĐž
- Installera bootloader pÄ /dev/sda
NÀr BTRFS vÀljs som rotfilsystem kommer installationsprogrammet automatiskt att skapa tvÄ BTRFS-volymer med namnen "@" för / (rot) och "@home" för /home.
Vi startar installationenâŠ
Installationen kommer att avslutas med en modal dialog som meddelar om ett bootloader installationsfel. TyvĂ€rr, blir det inte möjligt att stĂ€nga dialogen pĂ„ vanligt sĂ€tt och fortsĂ€tta installationen. Logga ut frĂ„n systemet och logga in igen, för att hamna pĂ„ skrivbordet för Ubuntu Live. Ăppna terminalen, och Ă„terigen:
#sudo bash
Vi skapar 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
Vi gÄr in i chroot-miljön:
#chroot /mnt/chroot
Först installerar vi paketen:
apt-get install --reinstall mdadm lvm2 thin-provisioning-tools btrfs-tools util-linux lsscsi nvme-cli mc debsums hdparm
Vi kontrollerar och ÄtgÀrdar alla paket som har installerats felaktigt pÄ grund av en ofullstÀndig systeminstallation:
#CORRUPTED_PACKAGES=$(debsums -s 2>&1 | awk '{print $6}' | uniq)
#apt-get install --reinstall $CORRUPTED_PACKAGES
Om nÄgot gick snett kan det hÀnda att du först behöver redigera /etc/apt/sources.list
Vi justerar instÀllningarna 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
Vi justerar lite av vÄra arrayer:
#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 kommer att göra följande:
- StÀll in en adekvat cachestorlek för RAID 6 för Är 2020. StandardvÀrdet verkar inte ha Àndrats sedan Linux skapades och Àr sedan lÀnge otillrÀckligt.
- Reservera minst IO under kontroller/synkronisering av arrayerna. Detta Àr nödvÀndigt för att förhindra att dina arrayer fastnar i ett tillstÄnd av evig synkronisering under belastning.
- BegrÀnsa maximalt IO under kontroller/synkronisering av arrayerna. Detta Àr viktigt för att synkronisering/kontroll av SSD RAID inte ska överhetta dina lagringsenheter. SÀrskilt viktigt för NVMe. (Kommer du ihÄg kylflÀnsen? Jag skÀmtade inte.)
- Förhindra att diskarna stoppar spinnandet av spindeln (HDD) via APM och stÀll in en sömntimeout för diskens kontroller pÄ 7 timmar. Du kan helt stÀnga av APM om dina diskar stödjer det (-B 255). Med standardvÀrdet kommer diskarna att stÀngas av efter fem sekunder. DÀrefter vill operativsystemet ÄterstÀlla diskens cache, diskarna kommer att snurra igen, och processen börjar om. Diskar har ett begrÀnsat maximalt antal snurrningar av spindeln. Denna enkla cykel som Àr standard kan lÀtt döda dina diskar pÄ nÄgra Är. Detta drabbar inte alla diskar, men vÄra
- StÀll in readahead pÄ snurrande diskar till 1 megabyte - tvÄ sekventiella block/chunk RAID 6
- Förhindra 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 sÄ..?Vi kommer att söka efter partitionen /boot via UUID eftersom namnen pÄ arrayerna teoretiskt kan Àndras.
Vi kommer att söka efter övriga partitioner via LVM-namn i formatet /dev/mapper/vg-lv, eftersom de identifierar partitionerna tillrÀckligt unikt.
Vi anvÀnder inte UUID för LVM eftersom UUID för LVM-volymer och deras snapshotar kan överlappa.Mountar vi /dev/mapper/root-root tvÄ gÄnger..?Ja. Precis sÄ. Detta Àr en egenskap hos BTRFS. Detta filsystem kan monteras flera gÄnger med olika subvol.
Med anledning av denna egenskap rekommenderar jag att aldrig skapa LVM-snapshotar av aktiva BTRFS-volymer. Du kan fÄ en överraskning vid omstart.
Vi regenererar konfigurationen för mdadm:
#/usr/share/mdadm/mkconf | sed 's/#DEVICE/DEVICE/g' >/etc/mdadm/mdadm.conf
Vi justerar instÀllningarna för LVM:
#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 utvidgning av LVM thin-pooler nÀr 90 % av utrymmet Àr upptaget 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 som Àr cachelagrade med LVM-cache förbi cachen (<lv_name>_corig). Den cachelagrade enheten kommer ÀndÄ att skannas genom cache (bara <lv_name>).
- enheter som innehÄller metadata för LVM-cache (cmeta)
- alla enheter i VG med namnet images. HÀr kommer vi att ha avbilder av virtuella maskiners diskar, och vi vill inte att LVM pÄ vÀrden aktiverar volymer som tillhör gÀstoperativsystemet.
- alla enheter i VG med namnet backup. HÀr kommer vi att ha sÀkerhetskopior av avbilder av virtuella maskiner.
- alla enheter vars namn slutar med "gpv" (guest physical volume)
Vi har aktiverat DISCARD-stöd nÀr vi frigör ledigt utrymme pÄ LVM VG. Var försiktig. Detta kommer att göra borttagning av LV pÄ SSD ganska lÄngsam. SÀrskilt gÀller detta SSD RAID 6. Men enligt planen kommer vi att anvÀnda thin provisioning, sÄ det hÀr kommer inte att hindra oss.
Vi uppdaterar initramfs-avbilden:
#update-initramfs -u -k all
Vi installerar och konfigurerar grub:
#apt-get install grub-pc
#apt-get purge os-prober
#dpkg-reconfigure grub-pc
Vilka diskar ska vi vÀlja?Alla som sd*. Systemet ska kunna starta frÄn vilken fungerande SATA-disk eller SSD som helst.
Varför har vi dödat os-prober..?För överdriven sjÀlvstÀndighet och nyfikenhet.
Det fungerar inte korrekt om nÄgon av RAID:erna Àr i ett degraderat tillstÄnd. Det försöker söka efter OS pÄ partitioner som anvÀnds i virtuella maskiner som körs pÄ denna hÄrdvara.
Om du behöver det kan du lÄta det vara, men tÀnk pÄ allt ovanstÄende. Jag rekommenderar att du söker efter lösningar för att bli av med nyfikenheten pÄ nÀtet.
HÀrmed avslutar vi den ursprungliga installationen. Det Àr dags att starta om till det nyinstallerade operativsystemet. Glöm inte att ta bort den bootbara Live CD/USB:n.
#exit
#reboot
Som boot-enhet vÀljer vi vilken som helst av SATA SSD.
LVM pÄ SATA SSD
Vid det hÀr laget har vi redan startat upp till det nya operativsystemet, konfigurerat nÀtverket, apt, öppnat terminalemulatorn och kört:
#sudo bash
Vi fortsÀtter.
Vi "initialiserar" matrisen av SATA SSD:
#blkdiscard /dev/md2
Om det inte fungerar, försök:
#blkdiscard --step 65536 /dev/md2
Vi skapar LVM VG pÄ SATA SSD:
#pvcreate /dev/md2
#vgcreate data /dev/md2
Varför ytterligare en VG..?Vi har redan en VG med namnet root. Varför inte lÀgga till allt i en VG?
Om det finns flera PV i VG mÄste alla PV vara nÀrvarande (online) för att VG ska aktiveras korrekt. Undantaget Àr LVM RAID, som vi avsiktligt inte anvÀnder.
Vi vill verkligen att nÀr det sker ett fel (lÀs dataloss) i nÄgon av RAID 6-arrayerna, sÄ att operativsystemet laddas som det ska och ger oss möjlighet att lösa problemet.
För detta syfte kommer vi pÄ den första abstraheringsnivÄn att isolera varje typ av fysisk "bÀrare" i en separat VG.
Om vi pratar vetenskapligt, sÄ tillhör olika RAID-arrayer olika "pÄlitlighetsomrÄden". Det Àr inte bra att skapa en ytterligare gemensam felpunkt för dem genom att stoppa dem i en VG.
Att ha LVM pÄ "hÄrdvarunivÄ" kommer att göra det möjligt för oss att dela upp olika RAID-arrayer pÄ olika sÀtt genom att kombinera dem. Till exempel, att köra samtidigt bcache + LVM thin, bcache + BTRFS, LVM cache + LVM thin, en komplex ZFS-konfiguration med cachar eller vilken annan hemsk kombination som helst, för att prova och jÀmföra allt detta.
PÄ "hÄrdvarunivÄ" kommer vi inte att anvÀnda nÄgot annat Àn de gamla goda "tjocka" LVM-volymerna. Undantaget frÄn denna regel kan möjligen vara partitionen för sÀkerhetskopiering.
Jag tror att mÄnga lÀsare har börjat misstÀnka nÄgot angÄende matryoshkan vid det hÀr laget.
LVM pÄ SATA HDD
#pvcreate /dev/md3
#vgcreate backup /dev/md3
Ănnu en ny VG..?Vi vill verkligen att nĂ€r diskarrayen som vi kommer att anvĂ€nda för att sĂ€kerhetskopiera data gĂ„r ner, sĂ„ ska vĂ„rt operativsystem fortsĂ€tta att fungera som det ska, med fortsatt tillgĂ„ng till icke-sĂ€kerhetskopierade data. DĂ€rför, för att undvika problem med aktiveringen av VG, skapar vi en separat VG.
InstÀllning av LVM-cache
Vi skapar en LV pÄ NVMe RAID 1 för att anvÀnda den som en cachande enhet.
#lvcreate -L 70871154688B --name cache root
Varför sÄ lite�Det handlar om att vÄra NVMe SSD:er ocksÄ har SLC-cache. 4 gigabyte "gratis" och 18 gigabyte dynamiskt pÄ grund av det lediga utrymmet som upptas i 3-bit MLC. NÀr denna cache har uttömts kommer NVMe SSD:er att vara inte mycket snabbare Àn vÄra SATA SSD:er med cache. Just dÀrför finns det ingen mening med att göra LVM-cachepartitionen mycket större Àn dubbla storleken pÄ NVMe-lagringens SLC-cache. För de anvÀnda NVMe-lagringarna anser författaren att det Àr rimligt att göra en cache pÄ 32-64 gigabyte.
Den angivna storleken pÄ partitionen Àr nödvÀndig för att organisera 64 gigabyte cache, lagra cache-metadata och en backup av metadata.
Jag vill ocksÄ nÀmna att efter en plötslig avstÀngning av systemet kommer LVM att mÀrka hela cachen som smutsig och synkronisera den pÄ nytt. Dessutom kommer detta att upprepas varje gÄng lvchange anvÀnds pÄ den hÀr enheten fram till nÀsta omstart av systemet. DÀrför rekommenderar jag att genast Äterskapa cachen med hjÀlp av ett lÀmpligt skript.
Vi skapar ett LV pÄ SATA RAID 6 för att anvÀnda det som en cachad enhet.
#lvcreate -L 3298543271936B --name cache data
Varför bara tre terabyte..?SÄ att SATA SSD RAID 6 kan anvÀndas för andra ÀndamÄl vid behov. Den cachade utrymmesstorleken kan ökas dynamiskt, pÄ sprÄng, utan att stÀnga av systemet. För detta behöver cachen tillfÀlligt stoppas och aktiveras igen, men en av de utmÀrkande fördelarna med LVM-cache framför bcache Àr att detta kan göras pÄ sprÄng.
Vi skapar en ny VG för caching.
#pvcreate /dev/root/cache
#pvcreate /dev/data/cache
#vgcreate cache /dev/root/cache /dev/data/cache
Vi skapar ett LV pÄ den cachade enheten.
#lvcreate -L 3298539077632B --name cachedata cache /dev/data/cache
HÀr har vi genast tagit upp allt ledigt utrymme pÄ /dev/data/cache sÄ att alla andra nödvÀndiga partitioner skapas direkt pÄ /dev/root/cache. Om nÄgot skapades nÄgon annanstans kan det flyttas med hjÀlp av pvmove.
Vi skapar och aktiverar 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 en sÄdan chunksize..?Genom praktiska experiment har författaren kommit fram till att bÀsta resultat uppnÄs om storleken pÄ LVM-cachen överensstÀmmer med storleken pÄ LVM thin-blocket. Samtidigt visar sig konfigurationen prestera bÀttre vid mindre blockstorlekar i samband med slumpmÀssiga skrivningar.
64k Àr den minimi blockstorlek som tillÄts för LVM thin.
Var försiktig med writeback..!Ja. Denna typ av cache skjuter upp synkroniseringen av skrivningar till den cachade enheten. Detta innebÀr att vid förlust av cachen kan data gÄ förlorade pÄ den cachade enheten. Senare kommer författaren att berÀtta vilka ÄtgÀrder, förutom NVMe RAID 1, som kan vidtas för att kompensera för denna risk.
Denna typ av cache har valts med avsikt för att kompensera för den lÄga prestandan hos RAID 6 vid slumpmÀssiga skrivningar.
LÄt oss kontrollera vad vi har Ästadkommit:
#lvs -a -o lv_name,lv_size,devices --units B cache
LV LSize Enheter
[cache] 68719476736B cache_cdata(0)
[cache_cdata] 68719476736B /dev/root/cache(0)
[cache_cmeta] 1073741824B /dev/root/cache(16384)
cachedata 3298539077632B cachedata_corig(0)
[cachedata_corig] 3298539077632B /dev/data/cache(0)
[lvol0_pmspare] 1073741824B /dev/root/cache(16640)
PÄ /dev/data/cache ska endast [cachedata_corig] placeras. Om nÄgot Àr fel, anvÀnd pvmove.
Cachen kan stÀngas av vid behov med ett enda kommando:
#lvconvert -y --uncache cache/cachedata
Detta görs online. LVM synkroniserar bara cachen till disken, tar bort den och döper om cachedata_corig tillbaka till cachedata.
InstÀllning av LVM thin
Vi gör en ungefÀrlig uppskattning av hur mycket utrymme vi behöver för metadata för LVM thin:
#thin_metadata_size --block-size=64k --pool-size=6terabytes --max-thins=100000 -u bytes
thin_metadata_size - 3385794560 byte uppskattad metadata omrÄdesstorlek för "--block-size=64kibibytes --pool-size=6terabytes --max-thins=100000"
Vi rundar av till 4 gigabyte: 4294967296B
Vi multiplicerar med tvÄ och lÀgger till 4194304B för LVM PV metadata: 8594128896B
Vi skapar en separat partition pÄ NVMe RAID 1 för att lagra LVM thin-metadata och dess sÀkerhetskopiering:
#lvcreate -L 8594128896B --name images root
Varför..?HÀr kan en frÄga uppstÄ, varför lagra LVM thin-metadata separat, om de ÀndÄ kommer att cachas pÄ NVMe och arbeta snabbt.
Ăven om hastighet Ă€r viktig, Ă€r det lĂ„ngt ifrĂ„n den huvudsakliga orsaken. Det handlar om att cachen Ă€r en enskild felpunkt. NĂ„got kan hĂ€nda med den, och om LVM thin-metadata Ă€r cachade kan det leda till en total förlust av allt. Utan hela metadata blir det i princip omöjligt att Ă„terstĂ€lla de tunna volymerna.
Genom att flytta metadata till en separat, icke-cachad men snabb volym, garanterar vi att metadata bevaras i hÀndelse av förlust eller skada pÄ cachen. I det hÀr fallet kommer alla skador orsakade av cachförlust att lokaliseras inom de tunna volymerna, vilket avsevÀrt förenklar ÄterstÀllningsproceduren. Med stor sannolikhet kommer dessa skador att kunna ÄterstÀllas med hjÀlp av FS-loggar.
Dessutom, om en ögonblicksbild av den tunna volymen tidigare har tagits, och efter det har cachen en gÄng fullstÀndigt synkroniserats, kommer integriteten av ögonblicksbilden att garanteras vid förlust av cachen, pÄ grund av de interna egenskaperna hos LVM thin.
Vi skapar 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
Vi skapar en pool:
#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T images/thin-pool
Varför -Z yDessutom, för vad detta lĂ€ge faktiskt Ă€r avsett â att förhindra att data frĂ„n en virtuell maskin lĂ€cker till en annan vid omfördelning av utrymme â anvĂ€nds zeroing ocksĂ„ för att öka hastigheten för slumpmĂ€ssiga skrivningar med block mindre Ă€n 64k. Varje skrivning mindre Ă€n 64k i ett tidigare icke-allokerat omrĂ„de av den tunna volymen kommer att omvandlas till 64K som Ă€r anpassade till cachegrĂ€nsen. Detta gör att operationen kan utföras helt via cachen utan att passera den cachade enheten.
Vi flyttar 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 kontrollera:
#lvs -a -o lv_name,lv_size,devices --units B images
LV LSize Enheter
[lvol0_pmspare] 4294967296B /dev/root/images(0)
tunneltank 274877906944B tunneltank_tdata(0)
[tunneltank_tdata] 274877906944B /dev/cache/cachedata(0)
[tunneltank_tmeta] 4294967296B /dev/root/images(1024)
Vi skapar en tunn volym för tester:
#lvcreate -V 64G --thin-pool thin-pool --name test images
Vi installerar paket för tester och övervakning:
#apt-get install sysstat fio
SÄ hÀr kan vi övervaka 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
Varning! Resurs!Den hÀr koden kommer att köra 36 olika tester, var och en av dem kommer att pÄgÄ i 4 sekunder. HÀlften av testerna handlar om skrivning. Under 4 sekunder kan man skriva en hel del pÄ NVMe. Upp till 3 gigabyte per sekund. SÄ varje körning av skrivtesterna kan anvÀnda upp till 216 gigabyte SSD-resurser.
LÀsa och skriva pÄ en gÄng?Ja. Det Àr meningsfullt att köra lÀsa- och skrivtester separat. Dessutom Àr det viktigt att se till att alla cache-minnen Àr synkroniserade, sÄ att tidigare skrivningar inte pÄverkar lÀsningen.
Resultaten kommer att variera kraftigt vid första körningen jÀmfört med efterföljande körningar, beroende pÄ cache-fyllning och tunna volymer, samt om systemet har lyckats synkronisera cachen som fylldes vid den föregÄende körningen.
Förutom detta rekommenderar jag att mÀta hastigheten pÄ en redan fylld tunn volym, som just fÄtt en snapshot. Författaren har haft möjlighet att observera hur slumpmÀssig skrivning plötsligt ökar strax efter att den första snapshoten skapats, sÀrskilt nÀr cachen inte Àr helt fylld. Detta beror pÄ copy-on-write-semantiken, blockutjÀmning av cache och den tunna volymen, och det faktum att slumpmÀssig skrivning pÄ RAID 6 omvandlas till slumpmÀssig lÀsning med RAID 6 följt av skrivning till cache. I vÄr konfiguration Àr slumpmÀssig lÀsning pÄ RAID 6 upp till 6 gÄnger (antalet SATA SSD i arrayen) snabbare Àn skrivning. Eftersom blocken för CoW tilldelas sekventiellt frÄn den tunna poolen, omvandlas skrivningen för det mesta ocksÄ till sekventiell.
BÄda dessa funktioner kan nyttjas fördelaktigt.
Cache-'koherent' snapshots
För att minska risken för dataförlust vid skador/förlust av cache föreslÄr författaren att införa en praktik med rotation av snapshots som garanterar deras integritet i sÄdana fall.
För det första, tack vare att metadata för tunna volymer Àr placerade pÄ ett icke-cacheat enhet, kommer metadata att vara intakt, och eventuella förluster kommer att isoleras inom datablokkarna.
Den följande rotationcykeln av snapshots garanterar dataintegritet inom snapshots i hÀndelse av cacheförlust:
- För varje tunn volym med namnet <namn> skapar vi en snapshot med namnet <namn>.cached
- LÄt oss sÀtta migrationsgrÀnsen pÄ ett rimligt högt vÀrde:
#lvchange --quiet --cachesettings "migration_threshold=16384" cache/cachedata - I cykeln 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 dröjer för lĂ€nge kan den temporĂ€rt skapas genom att stĂ€lla om cachen till writethrough-lĂ€ge. Men med tanke pĂ„ hastighetskarakteristikerna hos vĂ„ra SATA och NVMe SSD:er, samt deras TBW-resurs, kommer du antingen att kunna fĂ„nga ögonblicket ganska snabbt utan att Ă€ndra cachelĂ€ge, eller sĂ„ kommer din hĂ„rdvara att förbruka hela sin resurs pĂ„ bara nĂ„gra dagar. PĂ„ grund av resursbegrĂ€nsningar Ă€r systemet i princip oförmöget att vara under 100 % skrivbelastning konstant. VĂ„ra NVMe SSD:er kommer helt att förbruka sin resurs vid 100 % skrivbelastning 3-4 dagar. SATA SSD:er kommer att leva ungefĂ€r tvĂ„ gĂ„nger lĂ€ngre. DĂ€rför kommer vi att anta att den största delen av belastningen gĂ„r pĂ„ lĂ€sning, och att skrivningen ger relativt kortvariga toppar med extremt hög aktivitet i kombination med lĂ„g genomsnittlig belastning. - SĂ„ snart vi fĂ„ngar (eller skapar) nollan â byt namn pĂ„ <namn>.cached till <namn>.committed. Den gamla <namn>.committed tas bort.
- Valfritt, om cachen Àr fylld till 100 %, kan den Äterskapas med ett skript, vilket pÄ sÄ sÀtt rensar den. Med en halvtom cache fungerar systemet mycket snabbare vid skrivning.
- Vi stÀller in migration threshold pÄ noll:
#lvchange --quiet --cachesettings "migration_threshold=0" cache/cachedataDetta kommer tillfÀlligt att förbjuda synkronisering av cachen till den primÀra enheten. - Vi vÀntar tills det finns tillrÀckligt mÄnga förÀndringar i cachen
#lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}'eller timern gÄr. - Vi upprepar processen.
Varför komplicera med migration threshold�Saken Àr den att i verklig praktik Àr "slumptals"-skrivning faktiskt inte helt slumpmÀssig. Om vi har skrivit nÄgot till en sektor pÄ 4 kilobyte, finns det en stor sannolikhet att det kommer att göras en skrivning till denna eller en av de nÀrliggande (+- 32K) sektorerna inom nÄgra minuter.
Genom att stÀlla in migration threshold pÄ noll skjuter vi upp synkroniseringen av skrivningen till SATA SSD:n och aggregerar flera förÀndringar av en 64K-block i cachen. PÄ sÄ sÀtt sparar vi betydligt pÄ resursen för SATA SSD.
Men var Àr koden..?TyvÀrr anser författaren att han inte Àr tillrÀckligt kompetent inom utvecklingen av bash-skript eftersom han Àr en 100 % sjÀlvlÀrd och praktiserar "google"-driven utveckling, dÀrför anser han att den skrÀmmande koden som kommer under hans hÀnder inte bör anvÀndas av nÄgon annan.
Jag tror att proffsen inom detta omrÄde sjÀlva kommer att kunna avbilda hela den ovan beskrivna logiken vid behov, och kanske till och med snyggt utforma det som en systemd-tjÀnst, som författaren försökte göra.
En enkel rotationsschema för snapshots gör att vi stÀndigt kan ha en fullt synkroniserad snapshot pÄ SATA SSD, och med hjÀlp av verktyget thin_delta kan vi ta reda pÄ vilka block som har Àndrats efter att den skapades, och dÀrmed lokalisera skador pÄ de primÀra volymerna, vilket kraftigt förenklar ÄterstÀllningen.
TRIM/DISCARD i libvirt/KVM
Eftersom datalagret kommer att anvÀndas för KVM under libvirt, vore det bra att lÀra vÄra VM att inte bara ta upp ledigt utrymme utan ocksÄ frigöra onödigt utrymme.
Detta görs genom att emulera stöd för TRIM/DISCARD pÄ virtuella diskar. För att göra detta mÄste vi Àndra kontrollerstypen 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'/>
</disk>
<controller type='scsi' index='0' model='virtio-scsi'>
<alias name='scsi0'/>
</controller>
SÄdana DISCARDs frÄn gÀst-OS hanteras korrekt av LVM, och block frigörs bÄde i cachen och i det tunna lagret. I vÄrt fall sker detta huvudsakligen fördröjt, vid borttagning av nÀsta snapshot.
Backup av BTRFS
AnvÀnd fÀrdiga skript med största 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 hacklösningar, och det behövs ingen kopiering av andras.
Vi skapar en volym pÄ sÀkerhetsenheten:
#lvcreate -L 256G --name backup backup
Formatera som BTRFS:
#mkfs.btrfs /dev/backup/backup
Skapa monteringspunkter och montera rotunderkategorier för 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
Skapa kataloger för sÀkerhetskopior:
#mkdir /backup/btrfs/back/remote
#mkdir /backup/btrfs/back/remote/root
#mkdir /backup/btrfs/back/remote/boot
Skapa en katalog för sÀkerhetskopieringsskript:
#mkdir /root/btrfs-backup
Kopiera skriptet:
Mycket skrĂ€mmande bash-kod. AnvĂ€nd pĂ„ egen risk. 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 "VÀntar pÄ lÄs..."
wait_lock || terminate "Misslyckades med att fÄ lÄs. Avslutar..."
echo "Fick lÄs..."
}
function suffix()
{
FORMATTED_DATE=$(date +"$DATE_FORMAT")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}
function filter()
{
FORMATTED_DATE=$(date --date="$1" +"$DATE_PREFIX")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}
function backup()
{
SOURCE_PATH="$MOUNTS$1"
TARGET_PATH="$BACKUPS$1"
SOURCE_BASE_PATH="$MOUNTS$1$BASE_SUFFIX"
TARGET_BASE_PATH="$BACKUPS$1$BASE_SUFFIX"
TARGET_BASE_DIR="$(dirname $TARGET_BASE_PATH)"
SOURCE_PEND_PATH="$MOUNTS$1$PEND_SUFFIX"
TARGET_PEND_PATH="$BACKUPS$1$PEND_SUFFIX"
if [ -d "$SOURCE_BASE_PATH" ]
then
echo "$SOURCE_BASE_PATH hittades"
else
echo "$SOURCE_BASE_PATH Filen hittades inte, skapar snapshot av $SOURCE_PATH till $SOURCE_BASE_PATH"
btrfs subvolymsnapshot -r $SOURCE_PATH $SOURCE_BASE_PATH
sync
if [ -d "$TARGET_BASE_PATH" ]
then
echo "$TARGET_BASE_PATH hittades, Àr ur synk med kÀllan... tar bort..."
btrfs subvolym ta bort -c $TARGET_BASE_PATH
sync
fi
fi
if [ -d "$TARGET_BASE_PATH" ]
then
echo "$TARGET_BASE_PATH hittades"
else
echo "$TARGET_BASE_PATH hittades inte. Synkroniserar till $TARGET_BASE_DIR"
btrfs send $SOURCE_BASE_PATH | btrfs ta emot $TARGET_BASE_DIR
sync
fi
if [ -d "$SOURCE_PEND_PATH" ]
then
echo "$SOURCE_PEND_PATH hittades, tar bort..."
btrfs subvolym ta bort -c $SOURCE_PEND_PATH
sync
fi
btrfs subvolymsnapshot -r $SOURCE_PATH $SOURCE_PEND_PATH
sync
if [ -d "$TARGET_PEND_PATH" ]
then
echo "$TARGET_PEND_PATH hittades, tar bort..."
btrfs subvolym ta bort -c $TARGET_PEND_PATH
sync
fi
echo "Skickar $SOURCE_PEND_PATH till $TARGET_PEND_PATH"
btrfs send -p $SOURCE_BASE_PATH $SOURCE_PEND_PATH | btrfs ta emot $TARGET_BASE_DIR
sync
TARGET_DATE_SUFFIX=$(suffix)
btrfs subvolymsnapshot -r $TARGET_PEND_PATH "$TARGET_PATH$TARGET_DATE_SUFFIX"
sync
btrfs subvolym ta bort -c $SOURCE_BASE_PATH
sync
btrfs subvolym ta bort -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 subvolym ta bort -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 "HjÀlp"
;;
"suffix")
suffix
;;
"filter")
filter "$1"
;;
"backup")
wait_lock_or_terminate
backup "$1"
;;
"list")
list "$1"
;;
"remove")
wait_lock_or_terminate
remove "$1" "$2"
;;
"removeall")
wait_lock_or_terminate
removeall "$1" "$2"
;;
*)
echo "Ingen.."
;;
esac
) 98>$LOCK_FILE
EOF
Vad gör det..?InnehÄller en uppsÀttning grundlÀggande kommandon för att skapa snapshots av BTRFS och kopiera dem till ett annat filsystem via BTRFS send/receive.
Första körningen kan ta relativt lÄng tid, eftersom alla data kommer att kopieras frÄn början. Efterföljande körningar kommer att vara mycket snabba, eftersom endast Àndringar kommer att kopieras.
Ănnu ett skript som vi lĂ€gger 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 dagar"
$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 det..?Skapar och synkroniserar inkrementella snapshots av de angivna BTRFS-volymerna pÄ backup-filsystemet. Efter det tar det bort alla snapshots skapade för 60 dagar sedan. Efter körning kommer daterade snapshots av de angivna volymerna att dyka upp i underkatalogerna /backup/btrfs/back/remote/.
LÄt oss ge koden körbehörighet:
#chmod +x /root/btrfs-backup/cron-daily.sh
#chmod +x /root/btrfs-backup/btrfs-backup.sh
LÄt oss kontrollera 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 thin backup
Skapa en tunn pool pÄ reservdisken:
#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T backup/thin-pool
Installera ddrescue, eftersom skripten kommer att anvÀnda detta verktyg:
#apt-get install gddrescue
Skapa en katalog för skripten:
#mkdir /root/lvm-thin-backup
Vi kopierar skript:
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 "VÀntar pÄ lÄs..."
wait_lock || terminate "Misslyckades med att fÄ lÄs. Avslutar..."
echo "Fick lÄs..."
}
function suffix()
{
FORMATTED_DATE=$(date +"$DATE_FORMAT")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}
function filter()
{
FORMATTED_DATE=$(date --date="$1" +"$DATE_PREFIX")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}
function read_thin_id {
lvs --rows --reportformat basic --quiet -othin_id "$1/$2" | awk '{print $2}'
}
function read_pool_lv {
lvs --rows --reportformat basic --quiet -opool_lv "$1/$2" | awk '{print $2}'
}
function read_lv_dm_path {
lvs --rows --reportformat basic --quiet -olv_dm_path "$1/$2" | awk '{print $2}'
}
function read_lv_active {
lvs --rows --reportformat basic --quiet -olv_active "$1/$2" | awk '{print $2}'
}
function read_lv_chunk_size {
lvs --rows --reportformat basic --quiet --units b --nosuffix -ochunk_size "$1/$2" | awk '{print $2}'
}
function read_lv_size {
lvs --rows --reportformat basic --quiet --units b --nosuffix -olv_size "$1/$2" | awk '{print $2}'
}
function activate_volume {
lvchange -ay -Ky "$1/$2"
}
function deactivate_volume {
lvchange -an "$1/$2"
}
function read_thin_metadata_snap {
dmsetup status "$1" | awk '{print $7}'
}
function thindiff()
{
DIFF_VG="$1"
DIFF_SOURCE="$2"
DIFF_TARGET="$3"
DIFF_SOURCE_POOL=$(read_pool_lv $DIFF_VG $DIFF_SOURCE)
DIFF_TARGET_POOL=$(read_pool_lv $DIFF_VG $DIFF_TARGET)
if [ "$DIFF_SOURCE_POOL" == "" ]
then
(>&2 echo "Ursprung LV Àr inte tunt.")
exit 1
fi
if [ "$DIFF_TARGET_POOL" == "" ]
then
(>&2 echo "MÄl LV Àr inte tunt.")
exit 1
fi
if [ "$DIFF_SOURCE_POOL" != "$DIFF_TARGET_POOL" ]
then
(>&2 echo "KÀlla och mÄl LV tillhör olika tunt pooler.")
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 "Metadata snapshot för tunn pool finns redan. Antar att det Àr gammalt. Kommer att frigöra metadata snapshot om 5 sekunder.")
sleep 5
dmsetup message $DIFF_POOL_PATH_TPOOL 0 release_metadata_snap
fi
dmsetup message $DIFF_POOL_PATH_TPOOL 0 reserve_metadata_snap
DIFF_POOL_METADATA_SNAP=$(read_thin_metadata_snap $DIFF_POOL_PATH_TPOOL)
if [ "$DIFF_POOL_METADATA_SNAP" == "-" ]
then
(>&2 echo "Misslyckades att skapa metadata snapshot för tunn pool.")
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 hittad"
else
echo "$DISCARD_LV hittades inte i $DISCARD_VG"
exit 1
fi
DISCARD_LV_POOL=$(read_pool_lv $DISCARD_VG $DISCARD_LV)
DISCARD_LV_SIZE=$(read_lv_size "$DISCARD_VG" "$DISCARD_LV")
lvremove -y --quiet "$DISCARD_LV_PATH" || exit 1
lvcreate --thin-pool "$DISCARD_LV_POOL" -V "$DISCARD_LV_SIZE"B --name "$DISCARD_LV" "$DISCARD_VG" || exit 1
}
function backup()
{
SOURCE_VG="$1"
SOURCE_LV="$2"
TARGET_VG="$BACKUPS"
TARGET_LV="$SOURCE_VG-$SOURCE_LV"
SOURCE_BASE_LV="$SOURCE_LV$BASE_SUFFIX"
TARGET_BASE_LV="$TARGET_LV$BASE_SUFFIX"
SOURCE_PEND_LV="$SOURCE_LV$PEND_SUFFIX"
TARGET_PEND_LV="$TARGET_LV$PEND_SUFFIX"
SOURCE_BASE_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_BASE_LV")
SOURCE_PEND_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_PEND_LV")
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
TARGET_PEND_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_PEND_LV")
if [ "$SOURCE_BASE_LV_PATH" != "" ]
then
echo "$SOURCE_BASE_LV_PATH found"
else
echo "KĂ€llbas hittades inte, skapar snapshot av $SOURCE_VG/$SOURCE_LV till $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 "Gör av med $SOURCE_BASE_LV_PATH dÄ vi behöver starta om."
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 hittades, inte synkroniserad med kÀllan... tar bort..."
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 hittades"
else
echo "$TARGET_VG/$TARGET_LV hittades inte. Skapar tom volym."
lvcreate --thin-pool "$BACKUPS_POOL" -V "$SOURCE_BASE_SIZE"B --name "$TARGET_BASE_LV" "$TARGET_VG" || exit 1
echo "MÄste starta om. Gör av med kÀllan pÄ $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 "Gör av med mÄlet pÄ $TARGET_BASE_LV_PATH"
discard_volume "$TARGET_VG" "$TARGET_BASE_LV"
sync
fi
if [ "$SOURCE_PEND_LV_PATH" != "" ]
then
echo "$SOURCE_PEND_LV_PATH hittades och tas bort..."
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 hittades och tas bort..."
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 "Synkroniserar $SOURCE_PEND_LV_PATH till $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 hittades inte"
exit 1
fi
if [ "$TARGET_BASE_LV_PATH" != "" ]
then
echo "$TARGET_BASE_LV_PATH hittades"
else
echo "$TARGET_BASE_LV_PATH hittades inte"
exit 1
fi
activate_volume "$TARGET_VG" "$TARGET_BASE_LV"
activate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
echo JÀmför "$SOURCE_BASE_LV_PATH" med "$TARGET_BASE_LV_PATH"
cmp "$SOURCE_BASE_LV_PATH" "$TARGET_BASE_LV_PATH"
echo FĂ€rdig...
deactivate_volume "$TARGET_VG" "$TARGET_BASE_LV"
deactivate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
}
function resync()
{
SOURCE_VG="$1"
SOURCE_LV="$2"
TARGET_VG="$BACKUPS"
TARGET_LV="$SOURCE_VG-$SOURCE_LV"
SOURCE_BASE_LV="$SOURCE_LV$BASE_SUFFIX"
TARGET_BASE_LV="$TARGET_LV$BASE_SUFFIX"
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
SOURCE_BASE_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_BASE_LV")
if [ "$SOURCE_BASE_LV_PATH" != "" ]
then
echo "$SOURCE_BASE_LV_PATH found"
else
echo "$SOURCE_BASE_LV_PATH hittades inte"
exit 1
fi
if [ "$TARGET_BASE_LV_PATH" != "" ]
then
echo "$TARGET_BASE_LV_PATH hittades"
else
echo "$TARGET_BASE_LV_PATH hittades inte"
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 Synkroniserar "$SOURCE_BASE_LV_PATH" till "$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 "Synkroniserar $SYNC_LENGTH_BYTES byte vid $SYNC_OFFSET_BYTES frÄn $SOURCE_BASE_LV_PATH till $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 FĂ€rdig...
deactivate_volume "$TARGET_VG" "$TARGET_BASE_LV"
deactivate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
}
function list()
{
LIST_SOURCE_VG="$1"
LIST_SOURCE_LV="$2"
LIST_TARGET_VG="$BACKUPS"
LIST_TARGET_LV="$LIST_SOURCE_VG-$LIST_SOURCE_LV"
LIST_TARGET_BASE_LV="$LIST_TARGET_LV$SNAP_SUFFIX"
lvs -olv_name | grep "$LIST_TARGET_BASE_LV.$DATE_REGEX"
}
function remove()
{
REMOVE_TARGET_VG="$BACKUPS"
REMOVE_TARGET_LV="$1"
lvremove -y "$REMOVE_TARGET_VG/$REMOVE_TARGET_LV"
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 "HjÀlp"
;;
"suffix")
suffix
;;
"filter")
filter "$1"
;;
"backup")
wait_lock_or_terminate
backup "$1" "$2"
;;
"list")
list "$1" "$2"
;;
"thindiff")
thindiff "$1" "$2" "$3"
;;
"thinsync")
thinsync "$1" "$2" "$3" "$4"
;;
"verify")
wait_lock_or_terminate
verify "$1" "$2"
;;
"resync")
wait_lock_or_terminate
resync "$1" "$2"
;;
"remove")
wait_lock_or_terminate
remove "$1"
;;
"removeall")
wait_lock_or_terminate
removeall "$1" "$2" "$3"
;;
*)
echo "Ingen.."
;;
esac
) 98>$LOCK_FILE
EOF
Vad gör det...?InnehÄller ett set med kommandon för att manipulera tunna snapshots och synkronisera skillnader mellan tvÄ tunna snapshots med hjÀlp av thin_delta till en annan blockenhet med ddrescue och blkdiscard.
Ytterligare ett skript som vi kommer att lÀgga till 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 dagar"
$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 det...?AnvÀnder det föregÄende skriptet för att skapa och synkronisera backuper av de angivna tunna volymerna. Skriptet kommer att lÀmna kvar inaktiva snapshots av de angivna volymerna som behövs för att spÄra förÀndringar sedan senaste synkronisering.
Detta skript behöver redigeras för att ange listan över tunna volymer som behöver backas upp. De angivna namnen Àr endast exempel. Om sÄ önskas kan ett skript skrivas som synkroniserar alla volymer.
Ge rÀttigheter:
#chmod +x /root/lvm-thin-backup/cron-daily.sh
#chmod +x /root/lvm-thin-backup/lvm-thin-backup.sh
LÄt oss kontrollera 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
Första körningen kommer att ta tid, eftersom de tunna volymerna kommer att synkroniseras helt genom att kopiera hela det anvÀnda utrymmet. Tack vare LVM thin metadata vet vi vilka block som faktiskt anvÀnds, sÄ endast de verkligt anvÀnda blocken kommer att kopieras.
Efterföljande körningar kommer att kopiera data inkrementellt tack vare spÄrning av förÀndringar via LVM thin metadata.
vid "laddning" av containern visar att containermiljön starkt beror pÄ den bÀddade montering (Endast början av en lÄng utdata visas).
#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 mar 26 09:11 .
drwxr-xr-x 1 root root 16 mar 6 09:30 ..
drwxr-xr-x 1 root root 322 mar 26 02:00 .@base
drwxr-xr-x 1 root root 516 mar 6 09:39 .@snap.2020-03-06-09-39-37
drwxr-xr-x 1 root root 516 mar 6 09:39 .@snap.2020-03-06-09-39-57
...
/backup/btrfs/back/remote/root:
total 0
drwxr-xr-x 1 root root 2820 mar 26 09:11 .
drwxr-xr-x 1 root root 16 mar 6 09:30 ..
drwxr-xr-x 1 root root 240 mar 26 09:11 @.@base
drwxr-xr-x 1 root root 22 mar 26 09:11 @home.@base
drwxr-xr-x 1 root root 22 mar 6 09:39 @home.@snap.2020-03-06-09-39-35
drwxr-xr-x 1 root root 22 mar 6 09:39 @home.@snap.2020-03-06-09-39-57
...
drwxr-xr-x 1 root root 240 mar 6 09:39 @.@snap.2020-03-06-09-39-26
drwxr-xr-x 1 root root 240 mar 6 09:39 @.@snap.2020-03-06-09-39-56
...
#lvs -olv_name,lv_size images && lvs -olv_name,lv_size backup
LV LSize
linux-dev 128,00g
linux-dev.base 128,00g
thin-pool 1,38t
win8 128,00g
win8-data 2,00t
win8-data.base 2,00t
win8.base 128,00g
LV LSize
backup 256,00g
images-linux-dev.base 128,00g
images-linux-dev.snap.2020-03-08-10-09-11 128,00g
images-linux-dev.snap.2020-03-08-10-09-25 128,00g
...
images-win8-data.base 2,00t
images-win8-data.snap.2020-03-16-14-11-55 2,00t
images-win8-data.snap.2020-03-16-14-19-50 2,00t
...
images-win8.base 128,00g
images-win8.snap.2020-03-17-04-51-46 128,00g
images-win8.snap.2020-03-18-03-02-49 128,00g
...
thin-pool <2,09t
Vad har matryoshkor med detta att göra?
Det handlar antagligen om att logiska volymer LVM LV kan vara fysiska volymer LVM PV för andra VG. LVM kan vara rekursivt, som matryoshkor. Detta ger LVM en extraordinÀr flexibilitet.
P.S.
I nÀsta artikel kommer vi att försöka anvÀnda flera sÄdana mobila lagringssystem/KVM som grund för att skapa ett geo-fördelat lagrings-/vm-kluster med redundans pÄ flera kontinenter genom hem-datorer, hem-internet och P2P-nÀtverk.
KĂ€lla: habr.com
