Co mají společného LVM a Matrjoška?

Dobrý den.
Rád bych se s komunitou podělil o své praktické zkušenosti s budováním systému ukládání dat pro KVM pomocí md RAID + LVM.

Součástí programu bude:

  • Sestavení md RAID 1 z NVMe SSD.
  • Sestavení md RAID 6 ze SATA SSD a běžných disků.
  • Vlastnosti provozu TRIM/DISCARD na SSD RAID 1/6.
  • Vytvoření zaváděcího pole md RAID 1/6 na společné sadě disků.
  • Instalace systému na NVMe RAID 1, když v BIOSu není podpora NVMe.
  • Použití mezipaměti LVM a tenkého LVM.
  • Používání snímků BTRFS a odesílání a přijímání pro zálohování.
  • Použití tenkých snímků LVM a thin_delta pro zálohy ve stylu BTRFS.

Pokud máte zájem, podívejte se na kočku.

Prohlášení

Autor nenese žádnou odpovědnost za důsledky použití nebo nepoužití materiálů/příkladů/kódu/tipů/dat z tohoto článku. Přečtením nebo použitím tohoto materiálu jakýmkoliv způsobem přebíráte odpovědnost za všechny důsledky těchto akcí. Mezi možné důsledky patří:

  • Křupavé NVMe SSD.
  • Zcela vyčerpán záznamový zdroj a selhání SSD disků.
  • Úplná ztráta všech dat na všech discích, včetně záložních kopií.
  • Vadný počítačový hardware.
  • Ztracený čas, nervy a peníze.
  • Jakékoli další důsledky, které nejsou uvedeny výše.

Železo

K dispozici byly:

Základní deska z roku 2013 s čipovou sadou Z87, kompletní s Intel Core i7 / Haswell.

  • Procesor 4 jádra, 8 vláken
  • 32 GB DDR3 RAM
  • 1 x 16 nebo 2 x 8 PCIe 3.0
  • 1 x 4 + 1 x 1 PCIe 2.0
  • 6 x 6 GBps SATA 3 konektory

Adaptér SAS LSI SAS9211-8I přešel do režimu IT / HBA. Firmware s podporou RAID byl záměrně nahrazen firmwarem HBA, aby:

  1. Tento adaptér můžete kdykoli vyhodit a nahradit jej jakýmkoli jiným, na který jste narazili.
  2. TRIM/Discard fungovalo na discích normálně, protože... ve firmwaru RAID nejsou tyto příkazy podporovány vůbec a HBA je obecně jedno, jaké příkazy jsou přenášeny po sběrnici.

Pevné disky - 8 kusů HGST Travelstar 7K1000 s kapacitou 1 TB ve formátu 2.5 jako u notebooků. Tyto disky byly dříve v poli RAID 6. Využití budou mít i v novém systému. K ukládání místních záloh.

Navíc přidáno:

6 kusů SATA SSD model Samsung 860 QVO 2TB. Tyto SSD vyžadovaly velký objem, byla požadována přítomnost SLC cache, spolehlivost a nízká cena. Byla vyžadována podpora pro discard/nulu, což je kontrolováno řádkem v dmesg:

kernel: ata1.00: Enabling discard_zeroes_data

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

U těchto SSD je důležitá rychlost náhodného čtení/zápisu a kapacita zdrojů pro vaše potřeby. Radiátor pro ně. Nezbytně. Absolutně. V opačném případě je během první synchronizace RAID smažte do křupava.

StarTech PEX8M2E2 adaptér pro 2 x NVMe SSD nainstalovaný ve slotu PCIe 3.0 8x. Toto je opět jen HBA, ale pro NVMe. Od levných adaptérů se liší tím, že nevyžaduje podporu PCIe bifurkace ze základní desky kvůli přítomnosti vestavěného PCIe switche. Bude fungovat i v nejstarším systému s PCIe, i když se jedná o slot x1 PCIe 1.0. Samozřejmě v odpovídající rychlosti. Nejsou tam žádné RAIDy. Na desce není vestavěný BIOS. Váš systém se tedy magicky nenaučí bootovat pomocí NVMe, natož NVMe RAID díky tomuto zařízení.

Tato součást byla způsobena pouze přítomností pouze jednoho volného 8x PCIe 3.0 v systému, a pokud jsou k dispozici 2 volné sloty, lze jej snadno nahradit dvěma centy PEX4M2E1 nebo analogy, které lze zakoupit kdekoli za cenu 600 rublů.

Odmítnutí všech druhů hardwaru nebo vestavěných čipsetů/BIOS RAIDů bylo provedeno záměrně, aby bylo možné kompletně nahradit celý systém s výjimkou samotných SSD/HDD při zachování všech dat. Ideálně tak, abyste při přechodu na zcela nový/jiný hardware ušetřili i nainstalovaný operační systém. Hlavní věc je, že existují porty SATA a PCIe. Je to jako živé CD nebo bootovací flash disk, jen velmi rychlý a trochu objemný.

ЮморV opačném případě víte, co se stane - někdy potřebujete naléhavě vzít s sebou celé pole a odvézt. Ale nechci přijít o data. K tomu jsou všechna zmíněná média pohodlně umístěna na sklíčkách v 5.25 šachtách standardního pouzdra.

No a samozřejmě za experimentování s různými metodami ukládání do mezipaměti SSD v Linuxu.

Hardwarové nájezdy jsou nuda. Zapnout. Buď to funguje, nebo ne. A s mdadm jsou vždy možnosti.

Software

Dříve byl Debian 8 Jessie nainstalován na hardware, který se blíží EOL. RAID 6 byl sestaven z výše uvedených HDD spárovaných s LVM. Spouštěl virtuální stroje v kvm/libvirt.

Protože Autor má patřičné zkušenosti s tvorbou přenosných bootovatelných SATA/NVMe flash disků a také, aby se neporušila obvyklá apt šablona, ​​bylo jako cílový systém zvoleno Ubuntu 18.04, které je již dostatečně stabilizované, ale má ještě 3 roky podporu v budoucnu.

Zmíněný systém obsahuje všechny hardwarové ovladače, které po vybalení potřebujeme. Nepotřebujeme žádný software ani ovladače třetích stran.

Příprava na instalaci

K instalaci systému potřebujeme Ubuntu Desktop Image. Serverový systém má jakýsi energický instalátor, který ukazuje přílišnou nezávislost, kterou nelze deaktivovat strčením systémového oddílu UEFI na jeden z disků, což zkazí všechnu krásu. V souladu s tím se instaluje pouze v režimu UEFI. Nenabízí žádné možnosti.

Nejsme s tím spokojeni.

Proč?Bohužel spouštění UEFI je extrémně špatně kompatibilní se zaváděcím softwarovým RAID, protože... Rezervaci oddílu UEFI ESP nám nikdo nenabízí. Na internetu existují recepty, které doporučují umístit oddíl ESP na flash disk do portu USB, ale to je bod selhání. Existují recepty využívající software mdadm RAID 1 s metadaty verze 0.9, které nebrání systému UEFI BIOS vidět tento oddíl, ale tento přetrvává až do šťastného okamžiku, kdy BIOS nebo jiný hardwarový OS něco zapíše do ESP a zapomene to synchronizovat s jiným zrcadla.

Navíc spouštění UEFI závisí na NVRAM, která se nepřesune spolu s disky do nového systému, protože je součástí základní desky.

Takže nebudeme znovu vynalézat nové kolo. Máme již hotové, časem prověřené dědečkovo kolo, nyní nazývané Legacy/BIOS boot, nesoucí hrdé jméno CSM na systémech kompatibilních s UEFI. Prostě to sundáme z police, namažeme, napumpujeme pneumatiky a otřeme vlhkým hadříkem.

Desktopovou verzi Ubuntu také nelze správně nainstalovat pomocí zavaděče Legacy, ale jak se říká, alespoň existují možnosti.

A tak shromažďujeme hardware a načítáme systém ze spouštěcí flash disku Ubuntu Live. Budeme muset stáhnout balíčky, takže nastavíme síť, která vám bude fungovat. Pokud to nefunguje, můžete předem nahrát potřebné balíčky na flash disk.

Přejdeme do prostředí Desktop, spustíme emulátor terminálu a jedeme:

#sudo bash

Jak…?Řádek výše je kanonický spouštěč pro holiwars o sudo. C bоpřicházejí větší příležitosti aоvětší zodpovědnost. Otázkou je, jestli to dokážeš vzít na sebe. Mnoho lidí si myslí, že používání sudo tímto způsobem není přinejmenším opatrné. Nicméně:

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

Proč ne ZFS...?Když instalujeme software do našeho počítače, v podstatě půjčujeme náš hardware vývojářům tohoto softwaru k řízení.
Když tomuto softwaru důvěřujeme v bezpečnost našich dat, vezmeme si půjčku rovnající se nákladům na obnovu těchto dat, kterou budeme muset někdy splatit.

Z tohoto pohledu je ZFS Ferrari a mdadm+lvm je spíše kolo.

Subjektivně autor dává přednost půjčování kola na úvěr neznámým jedincům místo Ferrari. Tam cena emise není vysoká. Není potřeba práva. Jednodušší než pravidla silničního provozu. Parkování je zdarma. Schopnost běhu na lyžích je lepší. Na kolo můžete vždy připevnit nohy a kolo můžete opravit vlastníma rukama.

Proč tedy BTRFS...?Abychom mohli nabootovat operační systém, potřebujeme souborový systém, který je po vybalení podporován v Legacy/BIOS GRUB a zároveň podporuje živé snímky. Použijeme jej pro oddíl /boot. Kromě toho autor preferuje použití tohoto FS pro / (root), přičemž nezapomněl poznamenat, že pro jakýkoli jiný software můžete vytvořit samostatné oddíly na LVM a připojit je do potřebných adresářů.

Na tomto FS nebudeme ukládat žádné obrazy virtuálních strojů ani databází.
Tento FS bude použit pouze k vytvoření snapshotů systému bez jeho vypnutí a následnému přenosu těchto snapshotů na záložní disk pomocí send/recieve.

Navíc autor obecně preferuje ponechat minimum softwaru přímo na hardwaru a veškerý další software spouštět ve virtuálních strojích pomocí věcí, jako je přeposílání GPU a PCI-USB Host řadičů do KVM přes IOMMU.

Jediné, co na hardwaru zůstalo, je ukládání dat, virtualizace a zálohování.

Pokud více důvěřujete ZFS, pak jsou v zásadě pro uvedenou aplikaci zaměnitelné.

Autor však záměrně ignoruje vestavěné funkce zrcadlení/RAID a redundance, které mají ZFS, BRTFS a LVM.

Jako další argument má BTRFS možnost přeměnit náhodné zápisy na sekvenční, což má mimořádně pozitivní vliv na rychlost synchronizace snímků/záloh na HDD.

Pojďme znovu prohledat všechna zařízení:

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

Pojďme se podívat kolem sebe:

#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

Rozložení disku

NVMe SSD

Ale nebudeme je nijak označovat. Přesto náš BIOS tyto jednotky nevidí. Přejdou tedy výhradně na softwarový RAID. Nebudeme tam ani vytvářet sekce. Pokud se chcete řídit „kánonem“ nebo „zásadně“, vytvořte jeden velký oddíl, například HDD.

SATA HDD

Tady není potřeba nic speciálního vymýšlet. Vytvoříme jednu sekci pro všechno. Vytvoříme oddíl, protože BIOS tyto disky vidí a může se z nich dokonce pokusit nabootovat. Dokonce na tyto disky později nainstalujeme GRUB, aby to systém najednou mohl udělat.

#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

Tady to pro nás začíná být zajímavé.

Za prvé, naše disky mají velikost 2 TB. To je v přijatelném rozsahu pro MBR, což je to, co budeme používat. V případě potřeby lze nahradit GPT. Disky GPT mají vrstvu kompatibility, která umožňuje systémům kompatibilním s MBR vidět první 4 oddíly, pokud jsou umístěny v prvních 2 terabajtech. Hlavní věc je, že bootovací oddíl a oddíl bios_grub na těchto discích by měly být na začátku. To vám dokonce umožňuje zavést systém z jednotek GPT Legacy/BIOS.

Ale to není náš případ.

Zde vytvoříme dvě sekce. První bude mít velikost 1 GB a bude se používat pro RAID 1 /boot.

Druhý bude použit pro RAID 6 a zabere veškerý zbývající volný prostor kromě malé nepřidělené oblasti na konci disku.

Co je to za neoznačenou oblast?Podle zdrojů v síti mají naše SATA SSD na palubě dynamicky rozšiřitelnou mezipaměť SLC o velikosti od 6 do 78 gigabajtů. Získáme 6 gigabajtů „zdarma“ kvůli rozdílu mezi „gigabajty“ a „gibibajty“ v datovém listu jednotky. Zbývajících 72 gigabajtů je přiděleno z nevyužitého místa.

Zde je třeba poznamenat, že máme SLC cache a místo je obsazeno ve 4bitovém MLC režimu. Což pro nás efektivně znamená, že za každé 4 gigabajty volného místa získáme pouze 1 gigabajt SLC cache.

Vynásobte 72 gigabajtů 4 a získáte 288 gigabajtů. Toto je volné místo, které nebudeme vymezovat, aby disky mohly plně využívat mezipaměť SLC.

Efektivně tak získáme až 312 gigabajtů SLC cache z celkem šesti disků. Ze všech disků budou 2 použity v RAID pro redundanci.

Toto množství mezipaměti nám umožní velmi zřídka se v reálném životě setkat se situací, kdy zápis neprojde do mezipaměti. To extrémně dobře kompenzuje nejsmutnější nevýhodu paměti QLC – extrémně nízkou rychlost zápisu, když jsou data zapisována bez vyrovnávací paměti. Pokud tomu vaše zátěže neodpovídají, pak doporučuji si pořádně rozmyslet, jak dlouho vám SSD při takové zátěži vydrží s přihlédnutím k TBW z datového listu.

#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

Vytváření polí

Nejprve musíme stroj přejmenovat. To je nutné, protože název hostitele je součástí názvu pole někde uvnitř mdadm a někde něco ovlivňuje. Pole lze samozřejmě později přejmenovat, ale to je zbytečný krok.

#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

Proč -předpokládej-čisté...?Aby se zabránilo inicializaci polí. To platí pro obě úrovně RAID 1 a 6. Vše může fungovat bez inicializace, pokud se jedná o nové pole. Navíc inicializace pole SSD při vytvoření je plýtváním zdrojů TBW. Kde je to možné, používáme TRIM/DISCARD na sestavená pole SSD k jejich „inicializaci“.

Pro pole SSD je již po vybalení podporován RAID 1 DISCARD.

U polí SSD RAID 6 DISCARD jej musíte povolit v parametrech modulu jádra.

To by mělo být provedeno pouze v případě, že všechny SSD používané v polích úrovně 4/5/6 v tomto systému mají funkční podporu pro discard_zeroes_data. Občas narazíte na podivné jednotky, které jádru sdělují, že tato funkce je podporována, ale ve skutečnosti tam není, nebo funkce ne vždy funguje. V tuto chvíli je podpora dostupná téměř všude, nicméně existují staré disky a firmware s chybami. Z tohoto důvodu je podpora DISCARD pro RAID 6 ve výchozím nastavení zakázána.

Pozor, následující příkaz zničí všechna data na jednotkách NVMe „inicializací“ pole s „nulami“.

#blkdiscard /dev/md0

Pokud se něco pokazí, zkuste zadat krok.

#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

Proč tak velký...?Zvětšení velikosti bloku má pozitivní vliv na rychlost náhodného čtení v blocích až do velikosti bloku včetně. To se děje proto, že jednu operaci vhodné velikosti nebo menší lze provést zcela na jediném zařízení. Proto se IOPS ze všech zařízení sečtou. Podle statistik 99% IO nepřesahuje 512K.

RAID 6 IOPS na zápis vždy menší nebo rovno IOPS jednoho disku. Když jako náhodné čtení může být IOPS několikanásobně vyšší než u jednoho disku, a zde je velikost bloku klíčová.
Autor nevidí smysl ve snaze optimalizovat parametr, který je špatný v RAID 6 by-design a místo toho optimalizuje to, v čem je RAID 6 dobrý.
Špatný náhodný zápis RAID 6 vynahradíme mezipamětí NVMe a triky pro tenké poskytování.

Dosud jsme nepovolili DISCARD pro RAID 6. Toto pole tedy prozatím nebudeme „inicializovat“. Uděláme to později, po instalaci OS.

SATA HDD

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

LVM na NVMe RAID

Kvůli rychlosti chceme kořenový souborový systém umístit na NVMe RAID 1, což je /dev/md0.
Toto rychlé pole však budeme stále potřebovat pro další potřeby, jako je swap, metadata a LVM-cache a LVM-thin metadata, takže na tomto poli vytvoříme LVM VG.

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

Vytvořme oddíl pro kořenový souborový systém.

#lvcreate -L 128G --name root root

Vytvořme oddíl pro swapování podle velikosti RAM.

#lvcreate -L 32G --name swap root

Instalace OS

Celkem máme vše potřebné k instalaci systému.

Spusťte průvodce instalací systému z prostředí Ubuntu Live. Normální instalace. Pouze ve fázi výběru disků pro instalaci musíte zadat následující:

  • /dev/md1, - bod připojení /boot, FS - BTRFS
  • /dev/root/root (také znám jako /dev/mapper/root-root), - přípojný bod / (root), FS - BTRFS
  • /dev/root/swap (aka /dev/mapper/root-swap), - použít jako odkládací oddíl
  • Nainstalujte bootloader na /dev/sda

Když vyberete BTRFS jako kořenový souborový systém, instalační program automaticky vytvoří dva svazky BTRFS s názvem „@“ pro / (root) a „@home“ pro /home.

Spustíme instalaci...

Instalace bude ukončena modálním dialogovým oknem indikujícím chybu při instalaci bootloaderu. Tento dialog bohužel nebudete moci ukončit standardními prostředky a pokračovat v instalaci. Odhlásíme se ze systému a znovu se přihlásíme a skončíme na čisté ploše Ubuntu Live. Otevřete terminál a znovu:

#sudo bash

Chcete-li pokračovat v instalaci, vytvořte prostředí chroot:

#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

Pojďme nakonfigurovat síť a název hostitele v chrootu:

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

Pojďme do prostředí chroot:

#chroot /mnt/chroot

Nejprve doručíme balíčky:

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

Pojďme zkontrolovat a opravit všechny balíčky, které byly nainstalovány křivě kvůli nedokončené instalaci systému:

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

Pokud něco nefunguje, možná budete muset nejprve upravit /etc/apt/sources.list

Upravme parametry pro modul RAID 6, aby bylo možné TRIM/DISCARD:

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

Pojďme trochu upravit naše pole:

#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

Co to bylo..?Vytvořili jsme sadu pravidel udev, která budou dělat následující:

  • Nastavte velikost mezipaměti bloků pro RAID 2020 tak, aby byla adekvátní pro rok 6. Výchozí hodnota, jak se zdá, se od vytvoření Linuxu nezměnila a již dlouhou dobu není adekvátní.
  • Vyhraďte si minimálně IO po dobu kontrol/synchronizací pole. To má zabránit tomu, aby vaše pole uvízla ve stavu věčné synchronizace při zatížení.
  • Omezte maximální IO během kontrol/synchronizace polí. To je nezbytné, aby synchronizace/kontrola SSD RAID nezpestřila vaše disky. To platí zejména pro NVMe. (Pamatujete si na radiátor? Nedělal jsem si legraci.)
  • Zakažte diskům zastavování rotace vřetena (HDD) prostřednictvím APM a nastavte časový limit spánku pro řadiče disků na 7 hodin. APM můžete úplně zakázat, pokud to vaše disky umí (-B 255). Při výchozí hodnotě se pohony zastaví po pěti sekundách. Poté chce OS resetovat mezipaměť disku, disky se znovu roztočí a vše začne znovu. Kotouče mají omezený maximální počet otáček vřetena. Takový jednoduchý výchozí cyklus může snadno zabít vaše disky za pár let. Ne všechny disky tím trpí, ale naše jsou „notebookové“ s příslušným výchozím nastavením, díky kterému RAID vypadá jako mini-MAID.
  • Nainstalujte předčítání na disky (rotující) 1 megabajt – dva po sobě jdoucí bloky/blok RAID 6
  • Zakažte předčítání na samotných polích.

Upravme /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

Proč to..?Budeme hledat oddíl /boot podle UUID. Pojmenování pole by se teoreticky mohlo změnit.

Zbývající sekce budeme hledat podle jmen LVM v zápisu /dev/mapper/vg-lv, protože identifikují oddíly zcela jednoznačně.

UUID pro LVM nepoužíváme, protože UUID svazků LVM a jejich snímků mohou být stejné.Připojit /dev/mapper/root-root.. dvakrát?Ano. Přesně tak. Funkce BTRFS. Tento souborový systém lze připojit několikrát s různými subvols.

Kvůli stejné funkci doporučuji nikdy nevytvářet snímky LVM aktivních svazků BTRFS. Po restartu můžete být překvapení.

Pojďme znovu vygenerovat konfiguraci mdadm:

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

Upravme nastavení 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

Co to bylo..?Povolili jsme automatické rozšíření tenkých fondů LVM při dosažení 90 % obsazeného prostoru o 5 % objemu.

Zvýšili jsme maximální počet bloků mezipaměti pro mezipaměť LVM.

Zabránili jsme LVM vyhledávat svazky LVM (PV) na:

  • zařízení obsahující mezipaměť LVM (cdata)
  • zařízení uložená v mezipaměti pomocí mezipaměti LVM, obcházení mezipaměti ( _corig). V tomto případě bude samotné zařízení uložené v mezipaměti stále kontrolováno prostřednictvím mezipaměti (pouze ).
  • zařízení obsahující metadata mezipaměti LVM (cmeta)
  • všechna zařízení ve VG s názvy obrázků. Zde budeme mít obrazy disků virtuálních strojů a nechceme, aby LVM na hostiteli aktivoval svazky patřící hostujícímu OS.
  • všechna zařízení ve VG s názvem backup. Zde budeme mít záložní kopie obrazů virtuálních strojů.
  • všechna zařízení, jejichž název končí na „gpv“ (fyzický svazek pro hosty)

Povolili jsme podporu DISCARD při uvolňování volného místa na LVM VG. Buď opatrný. Díky tomu bude mazání LV na SSD poměrně časově náročné. Týká se to zejména SSD RAID 6. Podle plánu ale použijeme tenké zajišťování, takže nám to nebude vůbec překážet.

Pojďme aktualizovat obrázek initramfs:

#update-initramfs -u -k all

Nainstalujte a nakonfigurujte grub:

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

Jaké disky vybrat?Všichni, kteří jsou sd*. Systém musí být schopen zavést systém z jakéhokoli funkčního disku SATA nebo SSD.

Proč přidali os-prober..?Pro nadměrnou nezávislost a hravé ruce.

Nepracuje správně, pokud je jeden z RAID ve zhoršeném stavu. Snaží se hledat OS na oddílech, které se používají ve virtuálních strojích běžících na tomto hardwaru.

Pokud to potřebujete, můžete to nechat, ale mějte na paměti vše výše uvedené. Doporučuji si na internetu vyhledat recepty, jak se zbavit neposlušných rukou.

Tímto jsme dokončili úvodní instalaci. Je čas restartovat do nově nainstalovaného OS. Nezapomeňte vyjmout zaváděcí Live CD/USB.

#exit
#reboot

Jako spouštěcí zařízení vyberte libovolný SATA SSD.

LVM na SATA SSD

V tuto chvíli jsme již zavedli nový OS, nakonfigurovali síť, apt, otevřeli emulátor terminálu a spustili:

#sudo bash

Pojďme pokračovat.

„Inicializovat“ pole ze SATA SSD:

#blkdiscard /dev/md2

Pokud to nefunguje, zkuste:

#blkdiscard --step 65536 /dev/md2
Vytvořte LVM VG na SATA SSD:

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

Proč další VG..?Ve skutečnosti již máme VG s názvem root. Proč nepřidat vše do jednoho VG?

Pokud je ve VG několik PV, pak pro správnou aktivaci VG musí být přítomny všechny PV (online). Výjimkou je LVM RAID, který záměrně nepoužíváme.

Opravdu chceme, aby v případě poruchy (ztráta dat při čtení) na některém z polí RAID 6 se operační systém normálně nabootoval a dal nám možnost problém vyřešit.

Abychom toho dosáhli, na první úrovni abstrakce izolujeme každý typ fyzického „média“ do samostatného VG.

Vědecky řečeno, různá pole RAID patří do různých „domén spolehlivosti“. Neměli byste pro ně vytvářet další společný bod selhání tím, že je nacpete do jednoho VG.

Přítomnost LVM na „hardwarové“ úrovni nám umožní libovolně řezat kusy různých polí RAID jejich kombinací různými způsoby. Například - běh zároveň bcache + LVM tenký, bcache + BTRFS, LVM cache + LVM tenký, složitá konfigurace ZFS s mezipamětí nebo jakákoliv jiná pekelná směs pro vyzkoušení a porovnání.

Na „hardwarové“ úrovni nebudeme používat nic jiného než staré dobré „tlusté“ svazky LVM. Výjimkou z tohoto pravidla může být záložní oddíl.

Myslím, že v tuto chvíli již mnoho čtenářů začalo tušit něco o hnízdící panence.

LVM na SATA HDD

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

Zase nové VG..?Opravdu si přejeme, aby v případě výpadku diskového pole, které budeme používat pro zálohování dat, náš operační systém nadále normálně fungoval a přitom si zachoval přístup k nezálohovaným datům jako obvykle. Proto, abychom se vyhnuli problémům s aktivací VG, vytváříme samostatný VG.

Nastavení mezipaměti LVM

Vytvořme LV na NVMe RAID 1, abychom jej mohli použít jako zařízení pro ukládání do mezipaměti.

#lvcreate -L 70871154688B --name cache root

Proč je toho tak málo...?Faktem je, že naše NVMe SSD mají také SLC cache. 4 gigabajty „zdarma“ a 18 gigabajtů dynamických díky volnému prostoru obsazenému v 3bitovém MLC. Jakmile bude tato mezipaměť vyčerpána, NVMe SSD nebudou o mnoho rychlejší než naše SATA SSD s mezipamětí. Ve skutečnosti z tohoto důvodu nedává smysl, aby byl oddíl mezipaměti LVM mnohem větší, než je dvojnásobek velikosti mezipaměti SLC disku NVMe. Pro použité NVMe disky autor považuje za rozumné udělat 32-64 gigabajtů cache.

Daná velikost oddílu je nutná k uspořádání 64 gigabajtů mezipaměti, metadat mezipaměti a zálohy metadat.

Dále podotýkám, že po špinavém vypnutí systému LVM označí celou mezipaměť jako špinavou a znovu se synchronizuje. Navíc se to bude opakovat při každém použití lvchange na tomto zařízení, dokud nebude systém znovu restartován. Proto doporučuji okamžitě znovu vytvořit mezipaměť pomocí příslušného skriptu.

Vytvořme LV na SATA RAID 6, abychom jej mohli použít jako zařízení v mezipaměti.

#lvcreate -L 3298543271936B --name cache data

Proč jen tři terabajty?Takže v případě potřeby můžete použít SATA SSD RAID 6 pro některé další potřeby. Velikost mezipaměti lze dynamicky zvětšovat za běhu, bez zastavení systému. Chcete-li to provést, musíte mezipaměť dočasně zastavit a znovu povolit, ale výraznou výhodou LVM-cache oproti například bcache je, že to lze provést za běhu.

Pojďme vytvořit nový VG pro ukládání do mezipaměti.

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

Vytvořme LV na zařízení v mezipaměti.

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

Zde jsme okamžitě zabrali veškeré volné místo na /dev/data/cache, takže všechny ostatní potřebné oddíly byly okamžitě vytvořeny na /dev/root/cache. Pokud jste něco vytvořili na špatném místě, můžete to přesunout pomocí pvmove.

Pojďme vytvořit a povolit mezipaměť:

#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

Proč taková velikost..?Praktickými experimenty se autorovi podařilo zjistit, že nejlepšího výsledku je dosaženo, pokud se velikost bloku mezipaměti LVM shoduje s velikostí tenkého bloku LVM. Navíc, čím menší je velikost, tím lépe si konfigurace vede v náhodném záznamu.

64k je minimální velikost bloku povolená pro LVM thin.

Pozor na odepisování..!Ano. Tento typ mezipaměti odkládá synchronizaci zápisu do mezipaměti zařízení. To znamená, že pokud dojde ke ztrátě mezipaměti, můžete ztratit data na zařízení uloženém v mezipaměti. Později vám autor prozradí, jakými opatřeními lze kromě NVMe RAID 1 toto riziko kompenzovat.

Tento typ mezipaměti byl vybrán záměrně, aby kompenzoval špatný výkon náhodného zápisu RAID 6.

Podívejme se, co máme:

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

Pouze [cachedata_corig] by měl být umístěn na /dev/data/cache. Pokud je něco špatně, použijte pvmove.

V případě potřeby můžete mezipaměť zakázat jedním příkazem:

#lvconvert -y --uncache cache/cachedata

To se provádí on-line. LVM jednoduše synchronizuje mezipaměť na disk, odstraní ji a přejmenuje cachedata_corig zpět na cachedata.

Nastavení LVM tenký

Pojďme zhruba odhadnout, kolik místa potřebujeme pro tenká metadata LVM:

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

Zaokrouhleno na 4 gigabajty: 4294967296B

Vynásobte dvěma a přidejte 4194304B pro metadata LVM PV: 8594128896B
Vytvořme samostatný oddíl na NVMe RAID 1, abychom na něj umístili tenká metadata LVM a jejich záložní kopii:

#lvcreate -L 8594128896B --name images root

Proč..?Zde může vyvstat otázka: proč umisťovat tenká metadata LVM odděleně, pokud budou stále uložena v mezipaměti na NVMe a budou rychle fungovat.

I když je zde rychlost důležitá, zdaleka nejde o hlavní důvod. Jde o to, že mezipaměť je bod selhání. Mohlo by se mu něco stát, a pokud se tenká metadata LVM uloží do mezipaměti, způsobí to úplnou ztrátu všeho. Bez úplných metadat bude téměř nemožné sestavit tenké svazky.

Přesunutím metadat do samostatného svazku bez mezipaměti, ale rychlého, zaručujeme bezpečnost metadat v případě ztráty nebo poškození mezipaměti. V tomto případě budou všechna poškození způsobená ztrátou mezipaměti lokalizována uvnitř tenkých svazků, což řádově zjednoduší proceduru obnovy. S vysokou pravděpodobností budou tyto škody obnoveny pomocí protokolů FS.

Navíc, pokud byl dříve pořízen snímek tenkého svazku a poté byla mezipaměť alespoň jednou plně synchronizována, pak bude v případě ztráty mezipaměti zaručena integrita snímku díky vnitřnímu návrhu LVM thin. .

Pojďme vytvořit nový VG, který bude zodpovědný za tenké poskytování:

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

Vytvoříme bazén:

#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T images/thin-pool
Proč -Z yKromě toho, k čemu je tento režim ve skutečnosti určen – k zamezení úniku dat z jednoho virtuálního stroje na jiný virtuální stroj při přerozdělování prostoru – se nulování navíc používá ke zvýšení rychlosti náhodného zápisu v blocích menších než 64k. Jakýkoli zápis menší než 64 kB do dříve nepřidělené oblasti tenkého svazku bude v mezipaměti zarovnán na okraj 64 kB. To umožní, aby byla operace provedena výhradně prostřednictvím mezipaměti, čímž se obejde zařízení uložené v mezipaměti.

Přesuňme LV na odpovídající 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

Šek:

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

Vytvořme tenký svazek pro testy:

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

Nainstalujeme balíčky pro testy a monitorování:

#apt-get install sysstat fio

Takto můžete sledovat chování naší konfigurace úložiště v reálném čase:

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

Takto můžeme otestovat naši konfiguraci:

#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

Opatrně! Zdroj!Tento kód spustí 36 různých testů, každý po dobu 4 sekund. Polovina testů je pro záznam. Na NVMe toho za 4 sekundy nahrajete hodně. Až 3 gigabajty za sekundu. Každý běh testů zápisu od vás tedy může sníst až 216 gigabajtů zdroje SSD.

Smíšené čtení a psaní?Ano. Má smysl spouštět testy čtení a zápisu samostatně. Kromě toho má smysl zajistit, aby byly všechny mezipaměti synchronizovány, aby dříve provedený zápis neovlivnil čtení.

Výsledky se budou značně lišit během prvního spuštění a následujících, jak se zaplní mezipaměť a tenký svazek, a také v závislosti na tom, zda se systému podařilo synchronizovat mezipaměti zaplněné při posledním spuštění.

Mimo jiné doporučuji změřit rychlost na již plném tenkém svazku, ze kterého byla právě pořízena momentka. Autor měl možnost pozorovat, jak se náhodné zápisy prudce zrychlují ihned po vytvoření prvního snímku, zvláště když cache ještě není zcela plná. K tomu dochází v důsledku sémantiky zápisu kopírování při zápisu, zarovnání mezipaměti a bloků tenkého svazku a skutečnosti, že náhodný zápis do RAID 6 se změní na náhodné čtení z RAID 6 následované zápisem do mezipaměti. V naší konfiguraci je náhodné čtení z RAID 6 až 6krát (počet SATA SSD v poli) rychlejší než zápis. Protože bloky pro CoW jsou alokovány postupně z tenkého fondu, pak se záznam z větší části také změní na sekvenční.

Obě tyto funkce lze využít ve svůj prospěch.

Ukládejte do mezipaměti „koherentní“ snímky

Aby se snížilo riziko ztráty dat v případě poškození/ztráty mezipaměti, autor navrhuje zavést praxi rotace snímků, aby byla v tomto případě zaručena jejich integrita.

Za prvé, protože metadata tenkého svazku jsou umístěna na zařízení bez mezipaměti, metadata budou konzistentní a možné ztráty budou izolovány v rámci datových bloků.

Následující cyklus rotace snímků zaručuje integritu dat uvnitř snímků v případě ztráty mezipaměti:

  1. Pro každý tenký svazek s názvem <název> vytvořte snímek s názvem <název>.cached
  2. Nastavíme práh migrace na rozumně vysokou hodnotu: #lvchange --quiet --cachesettings "migration_threshold=16384" cache/cachedata
  3. Ve smyčce kontrolujeme počet špinavých bloků v mezipaměti: #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' dokud nedostaneme nulu. Pokud nula chybí příliš dlouho, lze ji vytvořit dočasným přepnutím mezipaměti do režimu zápisu. Vezmeme-li však v úvahu rychlostní charakteristiky našich polí SATA a NVMe SSD a také jejich TBW zdroj, buď budete schopni rychle zachytit okamžik bez změny režimu mezipaměti, nebo váš hardware zcela spotřebuje celý svůj zdroj několik dní. Kvůli omezeným zdrojům systém v zásadě nemůže být neustále pod 100% zatížením zápisu. Naše NVMe SSD pod 100% zátěží při zápisu zcela vyčerpá zdroj 3 4-dny. SATA SSD vydrží jen dvakrát déle. Proto budeme předpokládat, že většina zátěže jde na čtení a máme relativně krátkodobé výbuchy extrémně vysoké aktivity kombinované s nízkou průměrnou zátěží pro zápis.
  4. Jakmile zachytíme (nebo uděláme) nulu, přejmenujeme <name>.cached na <name>.committed. Staré <name>.committed je odstraněno.
  5. Volitelně, pokud je mezipaměť 100% plná, lze ji znovu vytvořit skriptem a tím ji vymazat. S poloprázdnou cache pracuje systém při zápisu mnohem rychleji.
  6. Nastavte práh migrace na nulu: #lvchange --quiet --cachesettings "migration_threshold=0" cache/cachedata To dočasně zabrání synchronizaci mezipaměti s hlavním médiem.
  7. Čekáme, až se v mezipaměti nahromadí poměrně hodně změn #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' nebo se časovač vypne.
  8. Znovu opakujeme.

Proč potíže s migračním prahem...?Jde o to, že v reálné praxi „náhodný“ záznam vlastně není úplně náhodný. Pokud jsme něco zapsali do sektoru o velikosti 4 kB, je vysoká pravděpodobnost, že v příštích několika minutách bude proveden záznam do stejného nebo jednoho ze sousedních (+- 32K) sektorů.

Nastavením prahu migrace na nulu odložíme synchronizaci zápisu na SATA SSD a agregujeme několik změn do jednoho 64K bloku v mezipaměti. To výrazně šetří zdroje SATA SSD.

Kde je kód..?Bohužel se autor považuje za nedostatečně kompetentního ve vývoji bash skriptů, protože je 100% samouk a praktikuje vývoj řízený „googlem“, proto se domnívá, že ten hrozný kód, který se mu vynoří z rukou, by neměl nikdo používat. jiný.

Myslím, že profesionálové v této oblasti dokážou v případě potřeby samostatně vykreslit veškerou výše popsanou logiku a možná ji i krásně navrhnout jako systémovou službu, jak se o to pokusil autor.

Takové jednoduché schéma rotace snímků nám umožní nejen mít neustále jeden snímek plně synchronizovaný na SATA SSD, ale také nám umožní pomocí utility thin_delta zjistit, které bloky byly po jeho vytvoření změněny, a lokalizovat tak poškození na hlavní svazky, což výrazně zjednodušuje obnovu.

TRIM/DISCARD v libvirt/KVM

Protože datové úložiště bude sloužit pro KVM, na kterém běží libvirt, pak by bylo dobré naučit naše VM nejen zabírat volné místo, ale také uvolňovat to, co již není potřeba.

To se provádí emulací podpory TRIM/DISCARD na virtuálních discích. Chcete-li to provést, musíte změnit typ ovladače na virtio-scsi a upravit 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>

Takové DISCARD z hostujících OS jsou správně zpracovány LVM a bloky jsou správně uvolněny jak v mezipaměti, tak v tenkém fondu. V našem případě k tomu dochází především se zpožděním, při mazání dalšího snímku.

Záloha BTRFS

Používejte hotové skripty s extrémní opatrnost a na vlastní nebezpečí. Autor napsal tento kód sám a výhradně pro sebe. Jsem si jistý, že mnoho zkušených uživatelů Linuxu má podobné nástroje a není třeba kopírovat nástroje někoho jiného.

Vytvořme svazek na zálohovacím zařízení:

#lvcreate -L 256G --name backup backup

Pojďme to naformátovat v BTRFS:

#mkfs.btrfs /dev/backup/backup

Pojďme vytvořit přípojné body a připojit kořenové podsekce souborového systému:

#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

Vytvoříme adresáře pro zálohy:

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

Vytvořme adresář pro zálohovací skripty:

#mkdir /root/btrfs-backup

Zkopírujeme skript:

Spousta děsivého bash kódu. Použití na vlastní nebezpečí. Nepište rozhněvané dopisy autorovi...#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

Co to vůbec dělá..?Obsahuje sadu jednoduchých příkazů pro vytváření BTRFS snapshotů a jejich kopírování do jiného FS pomocí BTRFS send/recieve.

První spuštění může být relativně dlouhé, protože... Na začátku budou všechna data zkopírována. Další starty budou velmi rychlé, protože... Budou zkopírovány pouze změny.

Další skript, který vložíme do cronu:

Ještě nějaký bash kód#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

Co to dělá..?Vytváří a synchronizuje přírůstkové snímky uvedených svazků BTRFS na záložním FS. Poté smaže všechny obrázky vytvořené před 60 dny. Po spuštění se v podadresářích /backup/btrfs/back/remote/ objeví datované snímky uvedených svazků.

Dejme práva ke spuštění kódu:

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

Pojďme to zkontrolovat a dát to do cronu:

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

Tenká záloha LVM

Vytvořme tenký fond na zálohovacím zařízení:

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

Pojďme nainstalovat ddrescue, protože... skripty budou používat tento nástroj:

#apt-get install gddrescue

Vytvoříme adresář pro skripty:

#mkdir /root/lvm-thin-backup

Zkopírujeme skripty:

Uvnitř spousta šmejdů...#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

Co to dělá...?Obsahuje sadu příkazů pro manipulaci s tenkými snímky a synchronizaci rozdílu mezi dvěma tenkými snímky přijatými přes thin_delta do jiného blokového zařízení pomocí ddrescue a blkdiscard.

Další skript, který vložíme do cronu:

Trochu víc šmejd#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

Co to dělá...?Používá předchozí skript k vytváření a synchronizaci záloh uvedených tenkých svazků. Skript ponechá neaktivní snímky uvedených svazků, které jsou potřeba ke sledování změn od poslední synchronizace.

Tento skript je nutné upravit a určit seznam tenkých svazků, pro které by se měly vytvářet záložní kopie. Uvedené názvy jsou pouze ilustrativní. Pokud chcete, můžete napsat skript, který bude synchronizovat všechny svazky.

Dejme práva:

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

Pojďme to zkontrolovat a dát to do cronu:

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

První spuštění bude dlouhé, protože... tenké svazky budou plně synchronizovány zkopírováním veškerého použitého prostoru. Díky tenkým metadatům LVM víme, které bloky se skutečně používají, takže budou zkopírovány pouze skutečně použité tenké objemové bloky.

Následné běhy budou kopírovat data postupně díky sledování změn prostřednictvím tenkých metadat LVM.

Uvidíme, co se stane:

#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

Co to má společného s hnízdícími panenkami?

S největší pravděpodobností, vzhledem k tomu, že logické svazky LVM LV mohou být fyzickými svazky LVM PV pro jiné VG. LVM může být rekurzivní, jako hnízdící panenky. To poskytuje LVM extrémní flexibilitu.

PS

V příštím článku se pokusíme použít několik podobných mobilních úložných systémů/KVM jako základ pro vytvoření geo-distribuovaného storage/vm clusteru s redundancí na několika kontinentech pomocí domácích desktopů, domácího internetu a P2P sítí.

Zdroj: www.habr.com

Přidat komentář