Lep pozdrav.
Želim deliti s zajednicom praktično iskustvo izgradnje sistema za skladištenje podataka za KVM koristeći md RAID + LVM.
Program će sadržavati:
- Sastavljanje md RAID 1 od NVMe SSD.
- Sastavljanje md RAID 6 od SATA SSD i običnih diskova.
- Karakteristike rada TRIM/DISCARD na SSD RAID 1/6.
- Kreiranje pokretnog md RAID 1/6 niza na skupinama diskova.
- Instalacija sistema na NVMe RAID 1 u slučaju da BIOS ne podržava NVMe.
- Korišćenje LVM cache i LVM thin.
- Korišćenje BTRFS snimaka i send/receive za rezervne kopije.
- Korišćenje LVM thin snimaka i thin_delta za rezervne kopije u stilu BTRFS.
Ako vas je ovo zainteresovalo, molim vas ispod.
Izjava
Autor ne snosi nikakvu odgovornost za posledice korišćenja ili ne-korišćenja materijala/primeraka/koda/saveta/podataka iz ovog članka. Čitajući ili na neki način koristeći ovaj materijal, preuzimate odgovornost za sve posledice tih radnji. Moguće posledice uključuju:
- Isprženi do hrskave kore NVMe SSD.
- Potpuno iskorišćen resurs pisanja i kvar SSD-a.
- Potpuni gubitak svih podataka na svim uređajima, uključujući rezervne kopije.
- Nepouzdana računarska oprema.
- Potrošeno vreme, živci i novac.
- Bilo koje druge posledice koje nisu navedene iznad.
Strojna oprema
Na raspolaganju je bilo:
Matična ploča iz 2013. godine sa Z87 čipsetom zajedno sa Intel Core i7 / Haswell.
- Procesor sa 4 jezgra, 8 niti
- 32 Gigabajta DDR3 radne memorije
- 1 x 16 ili 2 x 8 PCIe 3.0
- 1 x 4 + 1 x 1 PCIe 2.0
- 6 x 6 GBps SATA 3 priključaka
SAS adapter LSI SAS9211-8I koji je preflahovan u IT/HBA režim. Firmware sa podrškom za RAID namerno je zamenjen sa HBA firmware-om kako bi:
- U bilo kojem trenutku bilo moguće izbaciti ovaj adapter i zameniti ga bilo kojim drugim prvim dostupnim.
- TRIM/DISCARD je normalno radio na diskovima, jer ti komande nisu uopšte podržane u RAID firmveru, a HBA u suštini nije bitno koje komande se prenose putem magistrale.
Fizički diskovi, — 8 komada HGST Travelstar 7K1000 kapaciteta 1 TB u formatu 2.5, kao za laptopove. Ovi diskovi su prethodno bili u RAID 6 nizu. U novom sistemu takođe će naći primenu. Za čuvanje lokalnih rezervnih kopija.
Dodatno je dodato:
6 komada SATA SSD modela Samsung 860 QVO 2TB. Od ovih SSD-a je tražena velika količina, prisustvo SLC keša, poželjna pouzdanost, i, niska cena. Obavezna je podrška za discard/zero koja se proverava linijom u dmesg:
ядро: ata1.00: Включение discard_zeroes_data
2 шт. NVMe SSD модели Samsung SSD 970 EVO 500GB.
Для этих SSD важна скорость случайного чтения/записи и ресурс под ваши нужды. Радиатор к ним. Обязательно. Совсем обязательно. Иначе, — прожарите их до хрустящей корочки при первой же синхронизации RAIDa.
Адаптер StarTech PEX8M2E2 для 2 x NVMe SSD с установкой в PCIe 3.0 8x слот. Это, опять же, просто HBA, но для NVMe. Отличается от дешевых адаптеров отсутствием требования поддержки PCIe bifurcation от материнской платы благодаря наличию встроенного PCIe коммутатора. Будет работать даже в самой древней системе, где есть PCIe, даже если это будет x1 PCIe 1.0 слот. Естественно, с соответствующей скоростью. Никаких RAIDов там нет. Встроенного BIOS на борту нет. Так что, ваша система магически не научится загружаться с NVMe и тем более делать NVMe RAID благодаря этому устройству.
Компонент этот был обусловлен исключительно наличием только одного свободного 8x PCIe 3.0 в системе, и, при наличии 2х свободных слотов, легко заменяется на два копеечных PEX4M2E1 или аналоги, которых можно купить где угодно по цене от 600 рублей.
Отказ от всевозможных аппаратных или встроенных в чипсет/BIOS RAIDов был сделан осознанно, с целью иметь возможность полностью заменить всю систему, за исключением самих SSD/HDD, сохранив все данные. В идеале, чтобы можно было сохранить даже установленную операционную систему при переезде на совсем новое/другое железо. Главное, чтобы были SATA и PCIe порты. Это как live CD или загрузочная флэшка, только очень быстрая и немного габаритная.
ЮморА то, знаете как бывает, — иногда нужно срочно взять весь массив с собой на вынос. А данные терять не хочется. Для этого все упомянутые носители удобно располагаются по салазкам в отсеках 5.25 стандартного корпуса.
Ну и, конечно же, для экспериментов с разными способами SSD кэширования в Linux.
Аппаратные рэйды — это скучно. Включаешь. Оно либо работает, либо нет. А с mdadm всегда есть варианты.
Programska oprema
Ранее на железе была установлена Debian 8 Jessie, которая близка к EOL. Был собран RAID 6 из выше упомянутых HDD в паре с LVM. На нем крутились virtualne stroje в kvm/libvirt.
Ker avtor ima ustrezne izkušnje pri ustvarjanju prenosnih zagonskih SATA/NVMe naprav, in da ne bi pretrgali običajnega apt predloge, smo izbrali Ubuntu 18.04 kot ciljni sistem, ki se je že dovolj ustalil, vendar ima še vedno tri leta podpore v prihodnosti.
V omenjenem sistemu so prisotni vsi potrebni gonilniki strojne opreme že iz paketa. Nobene zunanje programske opreme ali gonilnikov ne bomo potrebovali.
Priprava na namestitev
Za namestitev sistema bomo potrebovali sliko namiznega okolja Ubuntu. Namizni sistem ima nekakšen radikalen namestitveni program, ki kaže pretirano neizklopljivo avtonomijo in nujno vklopi UEFI sistemski razdelek na enega od diskov, kar pokvari vse. Ustvari se lahko le v UEFI načinu. Ni možnosti izbire.
To nam ne ustreza.
Zakaj?Na žalost je UEFI nalaganje izjemno slabo združljivo s programsko RAID obremenitvijo, ker nam nihče ne ponudi rezervacije za UEFI ESP razdelek. Na spletu so recepti, ki predlagajo, da ESP del postavimo na USB ključek, vendar je to točka odpovedi. Obstajajo recepti za uporabo programske mdadm RAID 1 z metapodatki različice 0.9, ki ne motijo, da UEFI BIOS vidi ta razdelek, a obstaja nevarnost, da to obstaja do trenutka, ko BIOS ali druga operacijska sistem na strojni opremi zapiše kaj v ESP in pozabi sinhronizirati na druge zrcale.
Poleg tega nalaganje UEFI odvisno od NVRAM, ki se ne bo preselil skupaj z diski na nov sistem, saj je del matične plošče.
Tako ne bomo izumljali novega kolesa. Že imamo pripravljenega, s časom preverjenega, dedkastega kolesa, ki se danes imenuje Legacy/BIOS zaganjalnik, znan tudi kot CSM na sistemih združljivih z UEFI. Preprosto ga bomo potegnili s police, mu olajšali delovanje, natančili pnevmatike in obrisali z vlažno krpo.
Namizna različica Ubuntu prav tako ne zna pravilno namestiti s Legacy zaganjalnikom, vendar, kot se reče, imamo vsaj možnosti.
Tako, sestavljamo strojno opremo in nalagamo sistem z zagonskega USB ključka Ubuntu Live. Morali bomo prenesti pakete, zato nastavimo omrežje, kot vam deluje. Če ne deluje — potrebne pakete lahko vnaprej prenesete na ključek.
Vstopimo v namizno okolje, zaženemo emulator terminala in gremo:
#sudo bash
Kako...?Zgornja vrstica je kanonični sprožilec razprav o sudo. S boZ večjimi možnostmi prihaja tudi večja odgovornost. Vprašanje je, ali jo lahko prevzamete. Mnogi menijo, da je uporaba sudo na ta način vsaj neprevidna. Vendar:oZakaj ne ZFS…?

#apt-get install mdadm lvm2 thin-provisioning-tools btrfs-tools util-linux lsscsi nvme-cli mc
Ko na svoj računalnik namestimo programsko opremo, v bistvu posojamo svoje strojne vire razvijalcem te programske opreme.Ko zaupamo tej programski opremi varnost svojih podatkov, si izposojamo kredit enak strošku obnove teh podatkov, ki ga bomo nekoč morali vrniti.
Z tega vidika je ZFS Ferrari, medtem ko mdadm+lvm bolj spominja na kolo.
Subjektivno, avtor raje posodi neznanim osebam kreditno kolo namesto Ferrarija. Cena je tu nizka. Ni potrebna licenca. Pravilnikom o prometu je lažje biti zvest. Parkirišča so brezplačna. Prehodnost je boljša. Na kolo lahko kadarkoli dodate noge, pa tudi kolo lahko popravite sami.
Zakaj potem BTRFS…?
Za zagon operacijskega sistema potrebujemo datotečni sistem, ki ga podpira Legacy/BIOS GRUB iz škatle in obenem omogoča trenutne posnetke. Uporabili ga bomo za razdelek /boot. Poleg tega avtor raje uporablja ta FS za / (korenski), ne da bi pozabil omeniti, da lahko za katero koli drugo programsko opremo ustvarimo ločene razdelke na LVM in jih namestimo v ustrezne mape.Niti slik
, niti podatkovne baze ne bomo shranjevali na tem FS. virtualnih napravTa FS bo uporabljena le za ustvarjanje trenutnih posnetkov sistema brez njegovega izklopa z nadaljnjim prenosom teh posnetkov na rezervni disk z uporabo send/receive.
Poleg tega avtor na splošno raje ohranja minimalno programsko opremo neposredno na strojni opremi in poganja vso preostalo programsko opremo v virtualnih napravah z uporabo takšnih stvari, kot so preusmerjanje GPU in PCI-USB Host-kontrolerjev v KVM prek IOMMU.
Na strojni opremi ostajajo le - shranjevanje podatkov, virtualizacija in varnostno kopiranje.
Če bolj zaupate ZFS, so v osnovi za navedeno uporabo medsebojno zamenljive.
Kljub temu avtor zavestno ignorira vgrajene funkcije zrcaljenja/RAID-a in redundance, ki jih imajo ZFS, BTRFS in LVM.
Kljub temu avtor zavestno ignorira vgrajene funkcije zrcaljenja/RAID in redundančnih funkcij, ki so na voljo v ZFS, BRTFS in LVM.
Kot dodatni argument ima BTRFS sposobnost pretvorbe slučajnog pisanja u sekvencijalno, što pozitivno utiče na brzinu sinhronizacije snimaka / rezervnih kopija na HDD-u.
Ponovo ćemo skenirati sve uređaje:
#udevadm control --reload-rules && udevadm trigger
Pogledajmo:
#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
Podela "diskova"
NVMe SSD
Nećemo ih uopšte deliti. Naš BIOS ne prepoznaje te uređaje. Dakle, potpuno će ići u softverski RAID. Čak nećemo ni kreirati particije. Ako želite po "kanonu" ili "na principijelan način" — napravite jednu veliku particiju, kao na HDD-u.
SATA HDD
Ovde ne treba puno izmišljati. Napravit ćemo jednu particiju za sve. Particiju činimo jer BIOS prepoznaje te diskove i čak može pokušati da se pokrene s njih. Kasnije ćemo instalirati GRUB na te diskove kako bi sistem iznenada mogao uspešno da se pokrene.
#cat >hdd.part << EOF
oznaka: dos
oznaka-id: 0x00000000
naprava: /dev/sdg
jedinica: sektori
/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
Ovde je naše zanimljivo.
Prvo, naši uređaji imaju veličinu od 2 TB. To je u okviru dozvoljenog za MBR, što ćemo iskoristiti. Po potrebi se može zameniti sa GPT. GPT diskovi imaju sloj kompatibilnosti koji omogućava MBR-kompatibilnim sistemima da vide prva 4 dela ako su smešteni unutar prva 2 terabajta. Najvažnije je da boot particija i particija bios_grub budu na početku ovih diskova. To omogućava čak i pokretanje sa GPT diskova u Legacy/BIOS režimu.
Međutim, to nije naš slučaj.
Ovde ćemo kreirati dve particije. Prva će imati veličinu od 1 GB i biće korišćena za RAID 1 /boot.
Druga će biti korišćena za RAID 6 i zauzimaće sve preostalo slobodno mesto, osim malog neoznačenog dela na kraju uređaja.
Šta je to neoznačeno područje?Prema izvorima na mreži, naši SATA SSD-ovi imaju dinamički proširivi SLC keš u veličini od 6 do 78 gigabajta. 6 gigabajta dobijamo "besplatno" zbog razlike između "gigabajta" i "gibibajta" u tehničkoj dokumentaciji uređaja. Ostalih 72 gigabajta se izdvajaju zbog neiskorišćenog prostora.
Treba napomenuti da je keš SLC, a prostor se koristi u režimu 4 bit MLC. Što za nas efikasno znači da za svakih 4 gigabajta slobodnog prostora dobijamo samo 1 gigabajt SLC keša.
Pomnožimo 72 gigabajta sa 4 i dobijamo 288 gigabajta. To je slobodno mesto koje nećemo označiti kako bi omogućili uređajima da u potpunosti iskoriste SLC keš.
Tako bomo učinkovito dosegli skupno 312 gigabajtov SLC predpomnilnika s šestimi enotami. Od vseh enot bosta dve uporabljeni v RAID-u za redundanco.
Taka količina predpomnilnika nam bo omogočila, da se v praksi zelo redko srečamo s situacijo, ko zapis ne poteka v predpomnilnik. To izjemno dobro kompenzira najhujšo pomanjkljivost QLC pomnilnika, to je izjemno nizko hitrost zapisa, ko se podatki zapisujejo mimo predpomnilnika. Če vaša obremenitev ne ustreza temu, vam priporočam, da dobro razmislite, koliko dlje bodo vaši SSD-ji prenesli takšno obremenitev, upoštevajoč TBW iz tehnične specifikacije.
#cat >ssd.part << EOF
oznaka: dos
oznaka-id: 0x00000000
naprava: /dev/sda
jedinica: sektori
/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
Ustvarjanje nizov
Najprej moramo preimenovati strežnik. To je potrebno, ker je ime gostitelja del imena niza nekje znotraj mdadm in nekako vpliva na nekaj. Nize je seveda mogoče preimenovati tudi kasneje, vendar je to dodatno delo.
#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
Zakaj —assume-clean…?Da ne bi inicializirali nizov. To je dovoljeno za oba nivoja RAID 1 in 6. Vse lahko deluje brez inicializacije, če je to nov niz. Še več, inicializacija nizov SSD ob ustvarjanju je prazna izguba virov TBW. Uporabljamo TRIM/DISCARD, kjer je to mogoče, na zbranih nizih SSD, da jih 'inicializiramo'.
Pri nizih SSD RAID 1 DISCARD je podprt iz škatle.
Pri nizih SSD RAID 6 je treba DISCARD omogočiti vparametrih jedrskega modula.
To bi morali storiti le, če vsi SSD-ji, ki se uporabljajo v nizih nivojev 4/5/6 v tem sistemu, podpirajo delovanje discard_zeroes_data. Včasih se srečujemo z nenavadnimi enotami, ki jedru sporočajo, da podpirajo to funkcijo, a v resnici je nimajo, ali funkcija ne deluje vedno. Trenutno je podpora prisotna skoraj povsod, vendar se stare enote in pomanjkljive programske opreme še vedno pojavljajo. Zaradi tega je podpora DISCARD privzeto izklopljena za RAID 6.
Pozor, naslednji ukaz bo uničil vse podatke na NVMe enotah, saj bo 'inicializiral' niz 'z ničlami'.
#blkdiscard /dev/md0
Če gre kaj narobe, poskusite navesti korak.
#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
Zakaj tako veliko…?Povečanje chunk-size pozitivno vpliva na hitrost naključnega branja blokov do chunk-size vključujoč. To se zgodi, ker lahko ena operacija ustrezne velikosti ali manj popolnoma poteka na eni napravi. Zato se IOPS vseh enot sešteje. Po statistiki 99% IO ne presega 512K.
V RAID 6 so zapisne IOPS je vedno manjše ali enako IOPS enemu disku. Medtem ko je lahko pri naključnem branju IOPS večje kot pri enem disku večkrat, pri čemer je velikost bloka ključnega pomena.
Avtor ne vidi smisla v poskušanju optimizacije parametra, ki je slabe narave pri RAID 6 po zasnovi, in namesto tega optimizira tisto, v čemer se RAID 6 dobro izkaže.
Slabo naključno pisanje RAID 6 bomo kompenzirali s predpomnilnikom na NVMe in triki s thin-provisioning.
Še nismo omogočili DISCARD za RAID 6. Zato tega niza še ne bomo 'inicializirali'. To bomo storili pozneje, po namestitvi 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
Za hitrost želimo postaviti korenski FS na NVMe RAID 1, ki je /dev/md0.
Kljub temu bomo to hitro matrico potrebovali še za druge namene, kot so swap, metapodatki in predpomnilnik LVM-cache ter metapodatki LVM-thin, zato bomo na tej matriki ustvarili LVM VG.
#pvcreate /dev/md0
#vgcreate root /dev/md0
Ustvarili bomo particijo za korenski FS.
#lvcreate -L 128G --name root root
Ustvarili bomo particijo za swap velikosti RAM-a.
#lvcreate -L 32G --name swap root
Namestitev OS
Skupaj imamo vse potrebno za namestitev sistema.
Zagnali bomo čarovnika za namestitev sistema iz okolja Ubuntu Live. Običajna namestitev. Samo v fazi izbire diskov za namestitev je treba navesti naslednje:
- /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), — использовать как раздел подкачки
- Naložnik namestite na /dev/sda
Pri izbiri BTRFS kot korenskega FS, bo namestitveni program samodejno ustvaril dva BTRFS volumna z imeni '@' za / (koren) in '@home' za /home.
Začnemo z namestitvijo ...
Namestitev se bo zaključila s modalnim dialogom, ki obvešča o napaki pri namestitvi naložnika. Na žalost se iz tega dialoga ni mogoče normalno izstopiti in nadaljevati namestitvi. Opravimo odjavo iz sistema in se ponovno prijavimo, da pridemo na čisto delovno površino Ubuntu Live. Odprimo terminal in ponovno:
#sudo bash
Ustvarimo chroot okolje, da nadaljujemo z namestitvijo:
#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
Nastavimo omrežje in hostname v chroot:
#cat /etc/hostname >/mnt/chroot/etc/hostname
#cat /etc/hosts >/mnt/chroot/etc/hosts
#cat /etc/resolv.conf >/mnt/chroot/etc/resolv.conf
Vstopimo v chroot okolje:
#chroot /mnt/chroot
Najprej namestimo pakete:
apt-get install --reinstall mdadm lvm2 thin-provisioning-tools btrfs-tools util-linux lsscsi nvme-cli mc debsums hdparm
Preverimo in popravimo vse pakete, ki so se slabo namestili zaradi nedokončane namestitve sistema:
#CORRUPTED_PACKAGES=$(debsums -s 2>&1 | awk '{print $6}' | uniq)
#apt-get install --reinstall $CORRUPTED_PACKAGES
Če kaj ne gre, boste morda prej morali malo urediti /etc/apt/sources.list
Popravimo parametre za RAID 6 modul, da omogočimo TRIM/DISCARD:
#cat >/etc/modprobe.d/raid456.conf << EOF
opcije raid456 naprave_handle_discard_safely=1
EOF
Malo prilagodimo naše matrice:
#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
Kaj je to bilo..?Ustvarili smo niz udev pravil, ki bodo naredila naslednje:
- Določite ustrezno velikost predpomnilnika za RAID 6 za leto 2020. Očitno privzeta vrednost ni bila spremenjena od začetka sistema Linux, kar že dolgo ni primerno.
- Rezervirajte minimalno IO med preverjanjem/sinhronizacijo nizov. To je potrebno, da se vaši nizovi ne zataknejo v stanju večne sinhronizacije pod obremenitvijo.
- Omejite maksimalno IO med preverjanjem/sinhronizacijo nizov. To je potrebno, da sinhronizacija/preverjanje SSD RAID-ov ne pregreje vaših diskov do hrustljave skorje. To je še posebej pomembno za NVMe. (Se spomnite na hladilnik? Nisem se hecal.)
- Prepovejte diskom prek APM ustaviti vrtenje plošč (HDD) in nastavite časovno omejitev za spanje krmilnikov diskov na 7 ur. APM lahko popolnoma onemogočite, če to vaši diski podpirajo (-B 255). Pri privzeti vrednosti se diski ustavijo po petih sekundah. Nato želi operacijski sistem ponastaviti predpomnilnik diska, diski se spet zavrtijo in vse se začne znova. Diski imajo omejeno največje število zavrtitev. Tak preprost cikel po privzetih nastavitvah lahko zlahka uniči vaše diske v nekaj letih. Ne trpijo vsi diski, toda naši „prenosni“, s privzetimi nastavitvami, ki iz RAID-a delajo krivo različico mini-MAID-a.
- Nastavite readahead na vrtečih se diskih na 1 megabajt — dva zaporedna bloka/chunk RAID 6
- Prepovejte readahead na samih nizih.
Popravimo /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
Zakaj tako..?Del /boot bomo iskali po UUID, ker se lahko imena nizov teoretično spremenijo.
Preostale delitve bomo iskali po LVM imenih v notaciji /dev/mapper/vg-lv, ker unikatno identificirajo delitve.
Ne uporabljamo UUID za LVM, ker se UUID LVM-jev in njihovih posnetkov lahko ujema.Dvakrat montiramo /dev/mapper/root-root..?Da. Ravno tako. Značilnost BTRFS. To datotečni sistem lahko montiramo večkrat z različnimi subvol.
Zaradi te iste značilnosti priporočam, da nikoli ne ustvarjate LVM posnetkov aktivnih BTRFS volumenov. Lahko vam prinese presenečenje po ponovnem zagonu.
Ponovno generiramo config mdadm:
#/usr/share/mdadm/mkconf | sed 's/#DEVICE/DEVICE/g' >/etc/mdadm/mdadm.conf
Prilagodimo nastavitve 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
Kaj je to bilo..?Vključili smo samodejno širitev LVM thin bazenov, ko dosežejo 90 % zasedenosti prostora, za 5 % njihove kapacitete.
Povečali smo največje število predpomnilniških blokov za LVM cache.
Prepovedali smo LVM, da išče LVM volumne (PV) na:
- napravah, ki vsebujejo LVM cache (cdata)
- napravah, ki so bile predpomnjene z LVM cache ob obhodu predpomnilnika (<lv_name>_corig). Kljub temu bo predpomnjena naprava še vedno skenirana skozi predpomnilnik (samo <lv_name>).
- napravah, ki vsebujejo metapodatke LVM cache (cmeta)
- vseh napravah v VG z imenom images. Tu bomo imeli slike diskov virtualnih strojev in ne želimo, da LVM na gostitelju aktivira volumne, ki pripadajo gostujočemu operacijskemu sistemu.
- vseh napravah v VG z imenom backup. Tu bomo imeli varnostne kopije slik virtualnih strojev.
- vseh napravah, katerih ime se konča na «gpv» (guest physical volume)
Vključili smo podporo za DISCARD pri sproščanju prostega prostora na LVM VG. Bodite previdni. To bo podaljšalo brisanje LV na SSD-ju. Zlasti se to nanaša na SSD RAID 6. Vendar pa nameravamo uporabiti thin provisioning, zato nam to ne bo povzročilo težav.
Posodobili bomo sliko initramfs:
#update-initramfs -u -k all
Namestili in konfigurirali bomo grub:
#apt-get install grub-pc
#apt-get purge os-prober
#dpkg-reconfigure grub-pc
Katere diske izbrati?Vse, ki so sd*. Sistem mora biti sposoben zagnati se z vsakega delujočega SATA diska ali SSD-ja.
Zakaj smo izklopili os-prober..?Zaradi prekomerne samostojnosti in igrivih rok.
Ne deluje pravilno, če je eden od RAID-ov v degradiranem stanju. Poskuša iskati operacijski sistem na particijah, ki se uporabljajo v virtualnih strojih, ki delujejo na tej strojni opremi.
Če ga potrebujete, ga lahko pustite, vendar imejte v mislih vse zgoraj navedeno. Priporočam, da poiščete recepte, kako se znebiti igrivih rok na spletu.
Na tem mestu smo končali začetno namestitev. Čas je, da se ponovno zaženemo v pravkar nameščenem operacijskem sistemu. Ne pozabite odstraniti zagon Live CD/USB.
#exit
#reboot
Kot napravo za zagon izberemo kateri koli SATA SSD.
LVM na SATA SSD
Do tega trenutka smo se že zagnali v nov operacijski sistem, konfigurirali omrežje, apt, odprli emulator terminala in zagnali:
#sudo bash
Nadaljujmo.
Inicializiramo niz iz SATA SSD:
#blkdiscard /dev/md2
Če ne uspe, poskusimo:
#blkdiscard --step 65536 /dev/md2
Ustvarimo LVM VG na SATA SSD:
#pvcreate /dev/md2
#vgcreate data /dev/md2
Zakaj še en VG..?Dejansko že imamo VG z imenom root. Zakaj ne dodamo vsega v en VG?
Če VG vsebuje več PV, morajo vsi PV biti prisotni (online) za pravilno aktivacijo VG. Izjema je LVM RAID, ki ga namerno ne uporabljamo.
Želimo si, da bi se operacijski sistem vedno normalno naložil in nam omogočil reševanje težave, tudi če pride do izgube podatkov na katerem koli od RAID 6 nizov.
Zato bomo na prvi ravni abstrakcije vsak tip fizičnega ‘nosilca’ izolirali v ločeno VG.
Če se izrazimo strokovno, različni RAID nizi spadajo v različne ‘domena zanesljivosti’. Ne smemo ustvarjati dodatne skupne točke okvare tako, da vse stlačimo v eno VG.
Prisotnost LVM na ‘železni’ ravni nam bo omogočila poljubno reže različne RAID nize in jih kombinira po svoji volji. Na primer, - zagnali bomo hkrati bcache + LVM thin, bcache + BTRFS, LVM cache + LVM thin, kompleksno konfiguracijo ZFS s predpomnilniki ali kakšno drugo zapleteno mešanico, da vse to preizkusimo in primerjamo.
Na ‘železni’ ravni ne bomo uporabljali nič drugega kot stari dobri ‘debeli’ LVM volumni. Morda bo izjema to pravilo le razdelek za varnostno kopiranje.
Mislim, da so do tega trenutka mnogi bralci že začeli sumiti glede matrjoške.
LVM na SATA HDD
#pvcreate /dev/md3
#vgcreate backup /dev/md3
Spet nova VG..?Želimo si, da bi se naša operacijska sistem normalno izvajala tudi, če pride do okvare diska, ki ga bomo uporabljali za varnostno kopijo podatkov, medtem ko ohranja dostop do nevarnostno zaščitenih podatkov. Zato, da se izognemo težavam z aktivacijo VG, ustvarjamo ločeno VG.
Nastavitev LVM cache
Ustvarili bomo LV na NVMe RAID 1 za uporabo kot predpomnilniško napravo.
#lvcreate -L 70871154688B --name cache root
Zakaj je toliko malo...?Stvar je v tem, da imajo naši NVMe SSD tudi SLC predpomnilnik. 4 gigabajte ‘brezplačnega’ in 18 gigabajtov dinamičnega prostora, ki ga zaseda 3-bit MLC. Po izčrpanju tega predpomnilnika NVMe SSD ne bodo veliko hitrejši od našega SATA SSD s predpomnilnikom. Pravzaprav zato ni smiselno, da bi LVM cache razdelek naredili zelo velik, bolj kot dvakratnik prostornine SLC predpomnilnika NVMe naprave. Za uporabljene NVMe naprave avtor meni, da je smiselno narediti 32-64 gigabajtov predpomnilnika.
Naveden velikost razdelka je potrebna za organizacijo 64 gigabajtov predpomnilnika, shranjevanje predpomnilniških metadata in varnostno kopijo metadata.
Dodatno ću napomenuti da će nakon neurednog isključivanja sistema LVM označiti sav keš kao prljav i ponovno će ga sinhronizovati. Štaviše, to će se ponavljati pri svakoj upotrebi lvchange na ovom uređaju do ponovnog pokretanja sistema. Stoga, preporučujem odmah da ponovo kreirate keš odgovarajućim skriptom.
Kreirajmo LV na SATA RAID 6 da bismo ga koristili kao keširani uređaj.
#lvcreate -L 3298543271936B --name cache data
Zašto samo tri terabajta..?Da bismo, po potrebi, mogli koristiti SATA SSD RAID 6 za neke druge svrhe. Veličinu keširanog prostora moguće je dinamički povećati, u hodu, bez zaustavljanja rada sistema. Za to je potrebno privremeno zaustaviti i ponovo uključiti keš, ali, dalja prednost LVM-cache u odnosu na, na primer, bcache je to što se to može raditi u hodu.
Kreirajmo novu VG za keširanje.
#pvcreate /dev/root/cache
#pvcreate /dev/data/cache
#vgcreate cache /dev/root/cache /dev/data/cache
Kreirajmo LV na keširanom uređaju.
#lvcreate -L 3298539077632B --name cachedata cache /dev/data/cache
Ovde smo odmah zauzeli sav slobodan prostor na /dev/data/cache tako da su sve druge potrebne particije odmah kreirane na /dev/root/cache. Ako vam se nešto nije kreiralo tamo, možete premestiti pomoću pvmove.
Kreirajmo i uključimo keš:
#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
Zašto takav chunksize..?Metodom praktičnih eksperimenata, autor je saznao da se najbolji rezultati postižu ako se veličina bloka LVM keša poklapa sa veličinom bloka LVM thin. Pri tome, što je manja veličina, to bolje se pokazuje konfiguracija u slučaju nasumičnog pisanja.
64k je minimalna veličina bloka dozvoljena za LVM thin.
Pazite na writeback..!Da. Ova vrsta keša odlaže sinhronizaciju zapisa na keširani uređaj. To dovodi do toga da, u slučaju gubitka keša, možete izgubiti podatke na keširanom uređaju. Kasnije će autor reći koje mere, osim NVMe RAID 1, možete preduzeti da nadoknadite ovaj rizik.
Ova vrsta keša je izabrana namerno da bi nadoknadila nisku performansu RAID 6 u slučaju nasumičnog pisanja.
Proverimo šta smo postigli:
#lvs -a -o lv_name,lv_size,devices --units B cache
LV LSize naprave
[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)
Na /dev/data/cache treba biti samo [cachedata_corig]. Ako nije tako, koristite pvmove.
Isključiti keš po potrebi može se jednoj komandom:
#lvconvert -y --uncache cache/cachedata
To se radi on-line. LVM jednostavno sinhronizuje keš na disk, uklanja ga i preimenuje cachedata_corig nazad u cachedata.
Podešavanje LVM thin
Približno procenimo koliko prostora nam je potrebno za metapodatke LVM thin:
#thin_metadata_size --block-size=64k --pool-size=6terabytes --max-thins=100000 -u bytes
thin_metadata_size - 3385794560 bajtov ocenjeno velikost metapodatkov za "--block-size=64kibibytes --pool-size=6terabytes --max-thins=100000"
Zaokružimo na 4 gigabajta: 4294967296B
Pomnožimo na dva i dodajmo 4194304B za LVM PV metapodatke: 8594128896B
Kreirajmo poseban odjeljak na NVMe RAID 1 kako bismo smjestili LVM thin metapodatke i njihovu rezervnu kopiju:
#lvcreate -L 8594128896B --name images root
Zašto..?Ovdje se može postaviti pitanje zašto smještati LVM thin metapodatke odvojeno, kada će se oni ionako keširati na NVMe i raditi brzo.
Brzina ovdje i dalje jeste važna, ali nije osnovni razlog. Bit je u tome da je keš točka kvara. Može se nešto dogoditi s njim, a ako su LVM thin metapodatci pohranjeni u kešu, to će dovesti do potpune gubitka svega. Bez cjelovitih metapodataka, gotovo će biti nemoguće sastaviti tanke volumene.
Premještajući metapodatke na poseban brzi, ali ne-keširani volumen, jamčimo očuvanje metapodataka u slučaju gubitka ili oštećenja keša. U tom slučaju, sve štete izazvane gubitkom keša bit će lokalizirane unutar tankih volumena, što će drastično pojednostaviti postupak oporavka. S velikom vjerojatnošću, te štete bit će obnovljive pomoću FS zapisa.
Štoviše, ukoliko je ranije napravljen trenutačni snimak tankog volumena, i nakon toga je keš barem jednom potpuno sinkroniziran, zbog specifičnosti unutarnje strukture LVM thin, cjelovitost snimka bit će zajamčena u slučaju gubitka keša.
Kreirajmo novu VG koja će biti odgovorna za thin-provisioning:
#pvcreate /dev/root/images
#pvcreate /dev/cache/cachedata
#vgcreate images /dev/root/images /dev/cache/cachedata
Kreirajmo pool:
#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T images/thin-pool
Zašto -Z yOsim onoga za što je taj način zapravo namijenjen — sprečavanje propuštanja podataka iz jedne virtualne mašine u drugu prilikom ponovne dodjele prostora — zeroing se dodatno koristi za povećanje brzine slučajnog zapisivanja blokovima manjim od 64k. Svaki zapis manji od 64k u prethodno nealokirano područje tankog volumena će se pretvoriti u 64K usklađenih s granicom keša. To će omogućiti izvođenje operacije potpuno kroz keš, mimo keširanog uređaja.
Premjestimo LV na odgovarajuće 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
Provjerimo:
#lvs -a -o lv_name,lv_size,devices --units B images
LV LSize naprave
[lvol0_pmspare] 4294967296B /dev/root/images(0)
tanek-jamstvo 274877906944B tanek-jamstvo_tdata(0)
[thin-pool_tdata] 274877906944B /dev/cache/cachedata(0)
[thin-pool_tmeta] 4294967296B /dev/root/images(1024)
Kreirajmo tanki volumen za testove:
#lvcreate -V 64G --thin-pool thin-pool --name test images
Instalirajmo pakete za testiranje i praćenje:
#apt-get install sysstat fio
Ovako možemo pratiti ponašanje naše konfiguracije pohrane u stvarnom vremenu:
#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)'
Ovako možemo testirati našu konfiguraciju:
#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
Pazi! Resurs!Ta koda bo zagnala 36 različnih testov, od katerih se bo vsak izvajal 4 sekunde. Polovina testov je za pisanje. V 4 sekundah je na NVMe mogoče napisati zelo veliko. Do 3 gigabajte na sekundo. Torej, vsak zagon testov za pisanje lahko porabi do 216 gigabajtov SSD virov.
Branje in pisanje mešano?Da. Teste za branje in pisanje ima smisel izvajati ločeno. Poleg tega se je smiselno prepričati, da so vse predpomnilnike sinhronizirane, da prejšnje pisanje ne vpliva na branje.
Rezultati se bodo močno razlikovali pri prvem zagonu in pri naslednjih, ko se predpomnilnik in tanka enota zapolnita, pa tudi glede na to, ali je sistem uspel sinhronizirati predpomnilnike, ki so se napolnili pri prejšnjem zagonu.
Poleg tega priporočam merjenje hitrosti na že napolnjeni tanki enoti, s katere je bil pravkar ustvarjen posnetek. Avtor je imel priložnost opazovati, kako se naključno pisanje močno pospeši takoj po ustvarjanju prvega posnetka, še posebej, ko predpomnilnik še ni popolnoma napolnjen. To se zgodi zaradi semantike zapisovanja copy-on-write, poravnave blokov predpomnilnika in tanke enote ter zato, ker se naključno pisanje na RAID 6 spremeni v naključno branje z RAID 6, pri čemer se nato zapiše v predpomnilnik. V naši konfiguraciji je naključno branje na RAID 6 do 6-krat (število SATA SSD-jev v niz) hitrejše od pisanja. Ker se blokov za CoW dodeljuje zaporedno iz tanke enote, to pisanje večinoma postane tudi zaporedno.
Obe ti lastnosti lahko ugodno izkoristimo.
Predpomnilni «koherentni» posnetki
Za zmanjšanje tveganja izgube podatkov v primeru poškodbe/izgube predpomnilnika avtor predlaga uvedbo prakse rotacije posnetkov, ki zagotavlja njihovo celovitost v tem primeru.
Prvič, ker so metapodatki tankih enot shranjeni na napravi brez predpomnilnika, bodo metapodatki celoviti, morebitne izgube bodo izolirane znotraj podatkovnih blokov.
Naslednji cikel rotacije posnetkov zagotavlja celovitost podatkov znotraj posnetkov v primeru izgube predpomnilnika:
- Za vsako tanko enoto z imenom ustvarimo posnetek z imenom .cached
- Nastavimo prag migracije na razumno visoko vrednost:
#lvchange --quiet --cachesettings "migration_threshold=16384" cache/cachedata - V ciklu preverimo število umazanih blokov v predpomnilniku:
#lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}'dokler ne preidemo na ničlo. Če ni ničle predolgo, jo je mogoče začasno ustvariti tako, da preklopimo predpomnilnik v način writethrough. Vendar, ob upoštevanju hitrostnih lastnosti naših SATA in NVMe SSD, ter njihovega TBW, bodisi boste dovolj hitro ujele trenutek brez spremembe načina predpomnilnika, bodisi bo vaša oprema v celoti porabila vse svoje vire v nekaj dneh. Zaradi omejitev virov sistem v bistvu ne more biti pod 100% obremenitvijo pri pisanju nenehno. Naši NVMe SSD pod 100% obremenitvijo pri pisanju v celoti porabijo vire v 3-4 dneh. SATA SSD bodo preživeli le približno dvakrat dlje. Zato bomo predpostavili, da večina obremenitve poteka pri branju, pri pisanju pa imamo relativno kratkotrajne vrhunce izjemno visoke aktivnosti v kombinaciji z nizko obremenitvijo v povprečju. - Ko ujamemo (ali ustvarimo) ničlo — preimenujemo <ime>.cached v <ime>.committed. Stari <ime>.committed pri tem izbrišemo.
- Neobvezno, če je predpomnilnik poln do 100%, ga je mogoče ponovno ustvariti s skriptom, s čimer ga očistimo. S polovično praznim predpomnilnikom sistem deluje veliko hitreje pri pisanju.
- Nastavimo prag migracije na nič:
#lvchange --quiet --cachesettings "migration_threshold=0" cache/cachedataTo bo začasno prepovedaloSinkronizacijo predpomnilnika na glavni nosilec. - Čakamo, da se v predpomnilniku nabere dovolj veliko število sprememb
#lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}'ali da se sproži časovnik. - Ponovimo znova.
Zakaj zapletati s pragom migracije…?Vse je v tem, da v resnični praksi 'naključni' zapis v resnici ni čisto naključni. Če smo v sektor velikosti 4 kilobajti zapisali nekaj, je velika verjetnost, da bo v naslednjih nekaj minutah opravljen zapis v tem istem ali enem od sosednjih (+- 32K) sektorjev.
Z nastavitvijo praga migracije na nič odlašamo s sinkronizacijo pisanja na SATA SSD in agregiramo več sprememb enega bloka 64K v predpomnilniku. Tako znatno prihranimo vire SATA SSD.
Kje je koda..?Na žalost avtor meni, da ni dovolj usposobljen na področju razvoja bash skript, saj je 100% samouk in prakticira 'google'-voden razvoj, zato meni, da je tista strašna koda, ki prihaja iz njegovih rok, bolje, da je ne uporablja nihče drug.
Mislim, da bodo strokovnjaki na tem področju lahko sami upodobili vso zgoraj opisano logiko, če bo potreba, in, morda, celo lepo oblikovali v obliki systemd storitve, kot je to poskušal storiti avtor.
Takva jednostavna shema rotacije snimaka omogućit će nam ne samo da stalno imamo jedan potpuno sinkronizovan snimak na SATA SSD-u, već i da pomoću alata thin_delta saznamo koji su blokovi promenjeni nakon njegovog stvaranja, čime značajno olakšavamo lociranje oštećenja na glavnim volumenima i oporavak.
TRIM/DISCARD u libvirt/KVM
Budući da će skladište podataka biti korišćeno za KVM pod upravljanjem libvirt-a, bilo bi dobro naučiti naše VM da ne samo da zauzimaju slobodno mesto, već i da oslobađaju ono što više nije potrebno.
To se postiže emulacijom podrške za TRIM/DISCARD na virtuelnim diskovima. Za to je potrebno promeniti tip kontrolera na virtio-scsi i urediti xml.
#virsh edit vmname
<disk type='block' device='disk'>
<driver name='qemu' type='raw' cache='writethrough' io='threads' discard='unmap'/>
>
<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>
Takvi DISCARD-ovi iz gostujućih OS-a pravilno se obrađuju od strane LVM-a, a blokovi se pravilno oslobađaju kako u kešu, tako i u tankom skladištu. U našem slučaju, to se dešava uglavnom odloženo, prilikom brisanja sledećeg snimka.
Backup BTRFS
Koristiti gotove skripte sa izuzetnom opreznošću i na vlastitu odgovornost. Autor je ovaj kod pisao sam i isključivo za sebe. Uveren sam da mnogi iskusni korisnici Linux-a imaju slične improvizacije, i prepisivanje tuđih neće biti potrebno.
Napravimo volumen na rezervnom uređaju:
#lvcreate -L 256G --name backup backup
Formatiramo u BTRFS:
#mkfs.btrfs /dev/backup/backup
Kreiramo tačke montiranja i montiramo glavne poddirektorijume FS-a:
#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
Kreiramo direktorijume za rezervne kopije:
#mkdir /backup/btrfs/back/remote
#mkdir /backup/btrfs/back/remote/root
#mkdir /backup/btrfs/back/remote/boot
Kreiramo direktorijum za skripte rezervnog kopiranja:
#mkdir /root/btrfs-backup
Kopiramo skriptu:
Puno strašnog bash koda. Koristiti na svoju odgovornost. Autor ne želi uvredljive poruke…#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 "Čekam na zaključavanje..."
wait_lock || terminate "Nije uspelo da se dobije zaključavanje. Izlazim..."
echo "Dobio zaključavanje..."
}
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"
če [ -d "$SOURCE_BASE_PATH" ]
then
echo "$SOURCE_BASE_PATH najdeno"
else
echo "$SOURCE_BASE_PATH Datoteka ni najdena, ustvarjam posnetek iz $SOURCE_PATH v $SOURCE_BASE_PATH"
btrfs subvolume snapshot -r $SOURCE_PATH $SOURCE_BASE_PATH
sync
če [ -d "$TARGET_BASE_PATH" ]
then
echo "$TARGET_BASE_PATH najdeno, ni v sinhronizaciji z virom... odstranjujem..."
btrfs subvolume delete -c $TARGET_BASE_PATH
sync
fi
fi
če [ -d "$TARGET_BASE_PATH" ]
then
echo "$TARGET_BASE_PATH najdeno"
else
echo "$TARGET_BASE_PATH ni najdeno. Sinhronizacija v $TARGET_BASE_DIR"
btrfs send $SOURCE_BASE_PATH | btrfs receive $TARGET_BASE_DIR
sync
fi
če [ -d "$SOURCE_PEND_PATH" ]
then
echo "$SOURCE_PEND_PATH najdeno, odstranjujem..."
btrfs subvolume delete -c $SOURCE_PEND_PATH
sync
fi
btrfs subvolume snapshot -r $SOURCE_PATH $SOURCE_PEND_PATH
sync
če [ -d "$TARGET_PEND_PATH" ]
then
echo "$TARGET_PEND_PATH najdeno, odstranjujem..."
btrfs subvolume delete -c $TARGET_PEND_PATH
sync
fi
echo "Pošiljanje $SOURCE_PEND_PATH v $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
}
funkcija seznam()
{
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"
}
funkcija odstrani()
{
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
}
funkcija odstraniVse()
{
DATE_OFFSET="$2"
FILTER="$(filter "$DATE_OFFSET")"
medtem ko prebere -r SNAPSHOT ; stori
odstrani "$1" "$SNAPSHOT"
nadaljujte < <(seznam "$1" | grep "$FILTER")
}
(
COMMAND="$1"
premik
primer "$COMMAND" v
"--help")
echo "Pomoč"
;;
"suffix")
suffix
;;
"filter")
filter "$1"
;;
"backup")
wait_lock_or_terminate
backup "$1"
;;
"list")
seznam "$1"
;;
"remove")
wait_lock_or_terminate
odstrani "$1" "$2"
;;
"removeall")
wait_lock_or_terminate
odstraniVse "$1" "$2"
;;
*)
echo "Nič..."
;;
esac
) 98>$LOCK_FILE
EOF
Kaj pa to počne..?Vsebuje niz najpreprostejših ukazov za ustvarjanje posnetkov BTRFS in njihovo kopiranje na drugo FS z uporabo BTRFS send/receive.
Prvi zagon je lahko razmeroma dolg, saj bodo vsi podatki najprej kopirani. Kasnejša izvajanja bodo zelo hitra, saj bodo kopirane le spremembe.
Še en skript, ki ga bomo dodali v cron:
Še malo bash kode#cat >/root/btrfs-backup/cron-daily.sh << EOF
#!/bin/bash
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
SCRIPT_FILE="$(realpath $0)"
SCRIPT_DIR="$(dirname $SCRIPT_FILE)"
SCRIPT_NAME="$(basename -s .sh $SCRIPT_FILE)"
BACKUP_SCRIPT="$SCRIPT_DIR/btrfs-backup.sh"
RETENTION="-60 dni"
$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
Kaj počne..?Ustvari in sinhronizira na backup FS inkrementalne posnetke navedenih BTRFS volumov. Po tem odstranite vse posnetke, ustvarjene pred 60 dnevi. Po zagonu se bodo v podmapah /backup/btrfs/back/remote/ pojavili datirani posnetki navedenih volumov.
Dajmo kodi pravice za izvajanje:
#chmod +x /root/btrfs-backup/cron-daily.sh
#chmod +x /root/btrfs-backup/btrfs-backup.sh
Preverimo in dodamo v 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
Zavarovanje LVM thin
Ustvarimo tanek bazen na rezervni napravi:
#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T backup/thin-pool
Namestimo ddrescue, saj bodo skripte uporabljale to orodje:
#apt-get install gddrescue
Ustvarimo katalog za skripte:
#mkdir /root/lvm-thin-backup
Kopirajmo skripte:
Zelo veliko bash kode...#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 "Čekam na zaključavanje..."
wait_lock || terminate "Nije uspelo da se dobije zaključavanje. Izlazim..."
echo "Dobio zaključavanje..."
}
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 "Virni LV ni tanek.")
exit 1
fi
if [ "$DIFF_TARGET_POOL" == "" ]
then
(>&2 echo "Ciljni LV ni tanek.")
exit 1
fi
if [ "$DIFF_SOURCE_POOL" != "$DIFF_TARGET_POOL" ]
then
(>&2 echo "Virni in ciljni LV pripadata različnim tanekim bazam.")
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 "Snapshot metapodatkov tanekega basa že obstaja. Predvidevamo, da je zastarelo. Odklenili bomo snapshot metapodatkov čez 5 sekund.")
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 "Ustvarjanje snapshot metapodatkov tanekega basa ni uspelo.")
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 je najden"
else
echo "$DISCARD_LV ni najden v $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"
}
funkcija seznam()
{
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"
}
funkcija odstrani()
{
REMOVE_TARGET_VG="$BACKUPS"
REMOVE_TARGET_LV="$1"
lvremove -y "$REMOVE_TARGET_VG/$REMOVE_TARGET_LV"
sync
}
funkcija odstraniVse()
{
DATE_OFFSET="$3"
FILTER="$(filter "$DATE_OFFSET")"
medtem ko prebere -r SNAPSHOT ; stori
remove "$SNAPSHOT"
done < <(list "$1" "$2" | grep "$FILTER")
}
(
COMMAND="$1"
premik
primer "$COMMAND" v
"--help")
echo "Pomoč"
;;
"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 "Nič..."
;;
esac
) 98>$LOCK_FILE
EOF
Kaj to počne…?Vsebuje niz ukazov za upravljanje tankimi posnetki in sinhronizacijo razlik med dvema tankima posnetkoma, pridobljenima z thin_delta, na drugo blok napravo z uporabo ddrescue in blkdiscard.
Še en skript, ki ga bomo dodali v cron:
Še malo bash#cat >/root/lvm-thin-backup/cron-daily.sh << EOF
#!/bin/bash
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
SCRIPT_FILE="$(realpath $0)"
SCRIPT_DIR="$(dirname $SCRIPT_FILE)"
SCRIPT_NAME="$(basename -s .sh $SCRIPT_FILE)"
BACKUP_SCRIPT="$SCRIPT_DIR/lvm-thin-backup.sh"
RETENTION="-60 days"
$BACKUP_SCRIPT backup images linux-dev
$BACKUP_SCRIPT backup images win8
$BACKUP_SCRIPT backup images win8-data
#etc
$BACKUP_SCRIPT removeall images linux-dev "$RETENTION"
$BACKUP_SCRIPT removeall images win8 "$RETENTION"
$BACKUP_SCRIPT removeall images win8-data "$RETENTION"
#etc
EOF
Kaj to počne…?Uporablja prejšnji skript za ustvarjanje in sinhronizacijo varnostnih kopij navedenih tankih volumenov. Skript bo pustil neaktivne posnetke navedenih volumenov, ki so potrebni za sledenje spremembam od zadnje sinhronizacije.
Ta skript je treba urediti, da določi seznam tankih volumenov, za katere je potrebno narediti varnostne kopije. Navedena imena so dana le za primer. Po želji lahko napišete skript, ki bo sinhroniziral vse volumne.
Določimo pravice:
#chmod +x /root/lvm-thin-backup/cron-daily.sh
#chmod +x /root/lvm-thin-backup/lvm-thin-backup.sh
Preverimo in dodamo v 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
Prvi zagon bo dolg, ker bodo tanki volumni popolnoma sinhronizirani s kopiranjem vse uporabljenega prostora. Zaradi metapodatkov LVM thin vemo, kateri bloki so dejansko uporabljeni, tako da se bodo kopirali samo dejansko uporabljeni bloki tankih volumenov.
Nadaljnji zagoni bodo kopirali podatke inkrementalno zaradi sledenja spremembam prek metapodatkov LVM thin.
Pogledajmo šta je izašlo:
#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
Kaj imajo matroške s tem?
Verjetno zato, ker so logične enote LVM LV lahko fizične enote LVM PV za druge VG. LVM je lahko rekurziven, kot matroške. To daje LVM izjemno prilagodljivost.
P.S.
V naslednjem članku bomo poskusili uporabiti več podobnih mobilnih SHR/KVM kot osnovo za ustvarjanje geo-porazdeljenega storage/vm klasterja z varnostnim kopiranjem na več celinah preko domačih namizij, domačega interneta in P2P omrežij.
Izvor: habr.com
