Што имаат заедничко LVM и Matryoshka?

Добар ден.
Би сакал да го споделам со заедницата моето практично искуство за изградба на систем за складирање податоци за KVM користејќи md RAID + LVM.

Програмата ќе вклучува:

  • Изградба на md RAID 1 од NVMe SSD.
  • Склопување на md RAID 6 од SATA SSD и обични дискови.
  • Карактеристики на операцијата TRIM/DISCARD на SSD RAID 1/6.
  • Создавање бутабилна низа md RAID 1/6 на заеднички сет на дискови.
  • Инсталирање на системот на NVMe RAID 1 кога нема поддршка за NVMe во BIOS-от.
  • Користење на LVM кеш и LVM тенок.
  • Користење на снимки од BTRFS и испраќање/примање за резервна копија.
  • Користење на тенки снимки на LVM и thin_delta за бекап во стилот на BTRFS.

Ако сте заинтересирани, ве молиме погледнете мачка.

Изјава

Авторот не сноси никаква одговорност за последиците од користење или некористење материјали/примери/код/совети/податоци од овој напис. Со читање или користење на овој материјал на кој било начин, вие преземате одговорност за сите последици од овие дејства. Можните последици вклучуваат:

  • Пржени NVMe SSD-дискови.
  • Целосно искористен ресурс за снимање и неуспех на SSD-дискови.
  • Целосно губење на сите податоци на сите дискови, вклучувајќи ги и резервните копии.
  • Неисправен компјутерски хардвер.
  • Залудно потрошено време, нерви и пари.
  • Сите други последици кои не се наведени погоре.

Железо

Достапни беа:

Матична плоча од околу 2013 година со чипсет Z87, комплет со Intel Core i7 / Haswell.

  • Процесор 4 јадра, 8 нишки
  • 32 GB DDR3 RAM меморија
  • 1 x 16 или 2 x 8 PCIe 3.0
  • 1 x 4 + 1 x 1 PCIe 2.0
  • SATA 6 конектори од 6 x 3 GBps

SAS адаптерот LSI SAS9211-8I блесна во режимот IT / HBA. Фирмверот со овозможен RAID е намерно заменет со фирмверот HBA за:

  1. Може да го исфрлите овој адаптер во секое време и да го замените со кој било друг што ќе наидете.
  2. TRIM/Discard работеше нормално на дисковите, бидејќи... во RAID фирмверот овие команди воопшто не се поддржани, а HBA, генерално, не се грижи какви команди се пренесуваат преку автобусот.

Хард дискови - 8 парчиња HGST Travelstar 7K1000 со капацитет од 1 TB во 2.5 форма фактор, како за лаптопи. Овие дискови претходно беа во низа RAID 6. Тие ќе имаат употреба и во новиот систем. За складирање локални резервни копии.

Дополнително додадено:

6 парчиња SATA SSD модел Samsung 860 QVO 2TB. Овие SSD дискови бараа голем волумен, се посакуваше присуство на кеш SLC, сигурност и ниска цена. Беше потребна поддршка за отфрли/нула, што се проверува со линијата во dmesg:

kernel: ata1.00: Enabling discard_zeroes_data

2 парчиња NVMe SSD модел Samsung SSD 970 EVO 500GB.

За овие SSD дискови, случајната брзина на читање/запишување и капацитетот на ресурсите се важни за вашите потреби. Радијатор за нив. Задолжително. Апсолутно. Во спротивно, пржете ги додека не станат крцкави при првата RAID синхронизација.

StarTech PEX8M2E2 адаптер за 2 x NVMe SSD инсталиран во PCIe 3.0 8x слот. Ова, повторно, е само HBA, но за NVMe. Се разликува од евтините адаптери по тоа што не бара поддршка за бифуркација на PCIe од матичната плоча поради присуството на вграден PCIe прекинувач. Ќе работи дури и во најстариот систем со PCIe, дури и ако е слот x1 PCIe 1.0. Нормално, со соодветна брзина. Таму нема RAID-и. На одборот нема вграден BIOS. Значи, вашиот систем нема магично да научи да се подига со NVMe, а уште помалку NVMe RAID благодарение на овој уред.

Оваа компонента единствено се должи на присуството на само еден бесплатен 8x PCIe 3.0 во системот и, доколку има 2 бесплатни слота, може лесно да се замени со два денара PEX4M2E1 или аналози, кои може да се купат насекаде по цена од 600 рубли.

Отфрлањето на секаков вид хардвер или вграден чипсет/BIOS RAID е направено намерно, за да може целосно да се замени целиот систем, со исклучок на самите SSD/HDD, притоа да се зачуваат сите податоци. Идеално, за да можете да го зачувате дури и инсталираниот оперативен систем кога се префрлате на сосема нов/различен хардвер. Главната работа е дека има SATA и PCIe порти. Тоа е како живо ЦД или бутабилен флеш драјв, само многу брз и малку гломазен.

ЮморВо спротивно, знаете што се случува - понекогаш итно треба да ја земете целата низа со вас за да ја однесете. Но, не сакам да губам податоци. За да го направите ова, сите споменати медиуми се погодно лоцирани на слајдовите во 5.25 заливи на стандардното куќиште.

Па, и, се разбира, за експериментирање со различни методи на кеширање на SSD во Linux.

Хардверските рации се здодевни. Вклучете го. Или работи или не. И со mdadm секогаш има опции.

Софтвер

Претходно, на хардверот беше инсталиран Debian 8 Jessie, кој е близок до EOL. RAID 6 беше склопен од горенаведените HDD-и спарени со LVM. Работеше виртуелни машини во kvm/libvirt.

Бидејќи Авторот има соодветно искуство во создавање на преносни бутабилни SATA/NVMe флеш-дискови, а исто така, за да не се скрши вообичаениот шаблон, Ubuntu 18.04 беше избран како целен систем, кој веќе е доволно стабилизиран, но има уште 3 години. поддршка во иднина.

Споменатиот систем ги содржи сите хардверски драјвери кои ни се потребни надвор од кутијата. Не ни треба софтвер или драјвери од трета страна.

Подготовки за инсталација

За да го инсталираме системот ни треба Ubuntu Desktop Image. Серверскиот систем има некаков силен инсталатер, кој покажува прекумерна независност што не може да се оневозможи со туркање на системската партиција UEFI на еден од дисковите, расипувајќи ја целата убавина. Според тоа, тој е инсталиран само во режим UEFI. Не нуди никакви опции.

Не сме задоволни со ова.

Зошто?За жал, подигањето на UEFI е исклучително слабо компатибилно со софтверот за подигање RAID, бидејќи ... Никој не ни нуди резервации за партицијата UEFI ESP. Постојат рецепти на Интернет кои предлагаат поставување на партицијата ESP на флеш-уред во USB-порта, но ова е точка на неуспех. Постојат рецепти кои користат софтвер mdadm RAID 1 со метаподатоци верзија 0.9 кои не го спречуваат UEFI BIOS-от да ја види оваа партиција, но ова живее до среќниот момент кога BIOS-от или друг хардверски оперативен систем ќе напише нешто на ESP и ќе заборави да го синхронизира со други огледала.

Покрај тоа, подигањето на UEFI зависи од NVRAM, кој нема да се движи заедно со дисковите во новиот систем, бидејќи е дел од матичната плоча.

Значи, нема повторно да измислиме ново тркало. Веќе имаме готов, проверен на време дедо велосипед, сега наречен Legacy/BIOS boot, кој го носи гордото име CSM на системи компатибилни со UEFI. Само ќе го тргнеме од полицата, ќе го подмачкаме, ќе ги испумпаме гумите и ќе го избришеме со влажна крпа.

Десктоп верзијата на Ubuntu, исто така, не може да се инсталира правилно со подигнувачот Legacy, но овде, како што велат, барем има опции.

И така, го собираме хардверот и го вчитуваме системот од бутабилен флеш драјв Ubuntu Live. Ќе треба да преземаме пакети, па ќе ја поставиме мрежата што работи за вас. Ако не работи, можете однапред да ги вчитате потребните пакети на флеш-уред.

Влегуваме во околината на Desktop, го стартуваме терминалниот емулатор и тргнуваме:

#sudo bash

Како…?Линијата погоре е канонскиот активирач за холиварите за судо. В бодоаѓаат поголеми можности иопоголема одговорност. Прашањето е дали можете да го преземете на себе. Многу луѓе мислат дека користењето судо на овој начин барем не е внимателно. Сепак:

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

Зошто не ZFS...?Кога инсталираме софтвер на нашиот компјутер, ние во суштина го позајмуваме нашиот хардвер на развивачите на овој софтвер за да возат.
Кога му веруваме на овој софтвер за безбедноста на нашите податоци, земаме заем еднаков на трошоците за обновување на овие податоци, што ќе треба да го исплатиме еден ден.

Од оваа гледна точка, ZFS е Ферари, а mdadm+lvm е повеќе како велосипед.

Субјективно, авторот претпочита да позајми велосипед на кредит на непознати лица наместо Ферари. Таму цената на изданието не е висока. Нема потреба од права. Поедноставно од сообраќајните прописи. Паркингот е бесплатен. Способноста за крос-кантри е подобра. Секогаш можете да закачите нозе на велосипед, а велосипедот можете да го поправите со свои раце.

Зошто тогаш BTRFS...?За да го подигнеме оперативниот систем, потребен ни е датотечен систем кој е поддржан во Legacy/BIOS GRUB надвор од кутијата, а во исто време поддржува живи снимки. Ќе го користиме за партицијата /boot. Покрај тоа, авторот претпочита да го користи овој FS за / (root), не заборавајќи да забележи дека за кој било друг софтвер можете да креирате посебни партиции на LVM и да ги монтирате во потребните директориуми.

Ние нема да складираме слики од виртуелни машини или бази на податоци на овој FS.
Овој FS ќе се користи само за креирање снимки од системот без исклучување, а потоа пренесување на овие снимки на резервен диск со помош на испраќање/примање.

Покрај тоа, авторот генерално претпочита да задржи минимум софтвер директно на хардверот и да го стартува целиот друг софтвер во виртуелни машини користејќи работи како што се препраќање графички процесори и контролери PCI-USB домаќин на KVM преку IOMMU.

Единственото нешто што останува на хардверот е складирање на податоци, виртуелизација и резервна копија.

Ако повеќе му верувате на ZFS, тогаш, во принцип, за наведената апликација тие се заменливи.

Сепак, авторот намерно ги игнорира вградените функции за пресликување/RAID и вишок што ги имаат ZFS, BRTFS и LVM.

Како дополнителен аргумент, BTRFS има можност да ги претвори случајните записи во секвенцијални, што има исклучително позитивно влијание врз брзината на синхронизирање снимки/резервни копии на HDD.

Ајде повторно да ги скенираме сите уреди:

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

Ајде да погледнеме наоколу:

#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

Распоред на дискот

NVMe SSD

Но, ние нема да ги обележиме на кој било начин. Сепак, нашиот BIOS-от не ги гледа овие дискови. Значи, тие целосно ќе одат на софтверски RAID. Нема ни да создаваме делови таму. Ако сакате да го следите „канонот“ или „главно“, креирајте една голема партиција, како HDD.

SATA HDD

Нема потреба да измислувате ништо посебно овде. Ќе создадеме еден дел за сè. Ќе создадеме партиција бидејќи BIOS-от ги гледа овие дискови и може дури и да се обиде да се подигне од нив. Ние дури и ќе го инсталираме GRUB на овие дискови подоцна, така што системот може одеднаш да го направи тоа.

#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

Тука работите стануваат интересни за нас.

Прво, нашите дискови се со големина од 2 ТБ. Ова е во рамките на прифатливиот опсег за MBR, што е она што ќе го користиме. Доколку е потребно, може да се замени со GPT. GPT-дисковите имаат слој за компатибилност што им овозможува на MBR-компатибилните системи да ги видат првите 4 партиции доколку се наоѓаат во првите 2 терабајти. Главната работа е дека партицијата за подигање и партицијата bios_grub на овие дискови треба да бидат на почетокот. Ова дури ви овозможува да се подигнете од GPT Legacy/BIOS-дискови.

Но, ова не е нашиот случај.

Овде ќе создадеме два дела. Првиот ќе биде со големина од 1 GB и ќе се користи за RAID 1 /boot.

Вториот ќе се користи за RAID 6 и ќе го заземе целиот преостанат слободен простор освен мала нераспределена површина на крајот од погонот.

Која е оваа необележана област?Според извори на мрежата, нашите SATA SSD-дискови имаат вградено динамично проширување на кешот SLC со големина од 6 до 78 гигабајти. Добиваме 6 гигабајти „бесплатно“ поради разликата помеѓу „гигабајти“ и „гибибајти“ во листот со податоци на уредот. Останатите 72 гигабајти се распределени од неискористениот простор.

Овде треба да се забележи дека имаме SLC кеш, а просторот е окупиран во 4 битен MLC режим. Што за нас ефективно значи дека за секои 4 гигабајти слободен простор ќе добиеме само 1 гигабајт SLC кеш.

Помножете 72 гигабајти со 4 и добијте 288 гигабајти. Ова е слободниот простор што нема да го одбележиме за да им дозволиме на дисковите целосно да го користат кешот SLC.

Така, ефективно ќе добиеме до 312 гигабајти SLC кеш од вкупно шест дискови. Од сите дискови, 2 ќе се користат во RAID за вишок.

Оваа количина на кеш ќе ни овозможи исклучително ретко во реалниот живот да се сретнеме со ситуација кога запишувањето не оди во кешот. Ова исклучително добро го компензира најтажниот недостаток на QLC меморијата - екстремно малата брзина на пишување кога податоците се пишуваат заобиколувајќи го кешот. Ако вашите оптоварувања не одговараат на ова, тогаш ви препорачувам добро да размислите колку долго ќе трае вашиот SSD под такво оптоварување, земајќи го предвид TBW од листот со податоци.

#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

Креирање низи

Прво, треба да ја преименуваме машината. Ова е неопходно бидејќи името на домаќинот е дел од името на низата некаде во mdadm и влијае на нешто некаде. Се разбира, низите може да се преименуваат подоцна, но ова е непотребен чекор.

#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

Зошто -да претпоставиме-чисти...?За да се избегне иницијализирање на низи. За двете нивоа RAID 1 и 6 ова е валидно. Сè може да работи без иницијализација ако е нова низа. Покрај тоа, иницијализирањето на SSD низата при создавањето е губење на TBW ресурси. Ние користиме TRIM/DISCARD каде што е можно на склопени SSD-низи за да ги „иницијализираме“.

За SSD-низите, RAID 1 DISCARD е поддржан надвор од кутијата.

За SSD RAID 6 DISCARD низи, мора да го овозможите во параметрите на модулот на јадрото.

Ова треба да се направи само ако сите SSD-дискови што се користат во низите на ниво 4/5/6 во овој систем имаат работна поддршка за discard_zeroes_data. Понекогаш наидувате на чудни дискови кои му кажуваат на кернелот дека оваа функција е поддржана, но всушност ја нема, или функцијата не работи секогаш. Во моментов, поддршката е достапна речиси насекаде, но има стари дискови и фирмвер со грешки. Поради оваа причина, поддршката DISCARD е стандардно оневозможена за RAID 6.

Внимание, следнава команда ќе ги уништи сите податоци на NVMe дисковите со „иницијализирање“ на низата со „нули“.

#blkdiscard /dev/md0

Ако нешто тргне наопаку, обидете се да наведете чекор.

#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

Зошто толку голема...?Зголемувањето на големината на парчето има позитивен ефект врз брзината на случајно читање во блокови до големина на парче. Ова се случува затоа што една операција со соодветна големина или помала може целосно да се заврши на еден уред. Затоа, IOPS од сите уреди е сумиран. Според статистичките податоци, 99% од IO не надминува 512K.

RAID 6 IOPS по запишување секогаш помала или еднаква на IOPS на еден диск. Кога, како случајно читање, IOPS може да биде неколку пати поголем од оној на еден диск, и тука големината на блокот е од клучна важност.
Авторот не ја гледа поентата во обидот да се оптимизира параметар кој е лош во дизајнот на RAID 6 и наместо тоа го оптимизира она во што е добар RAID 6.
Ќе го компензираме лошото случајно пишување на RAID 6 со кеш NVMe и трикови за слабо обезбедување.

Сè уште не сме овозможиле DISCARD за RAID 6. Затоа, засега нема да ја „иницијализираме“ оваа низа. Ова ќе го направиме подоцна, откако ќе го инсталираме ОС.

SATA HDD

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

LVM на NVMe RAID

За брзина, сакаме да го поставиме root-датотечниот систем на NVMe RAID 1 кој е /dev/md0.
Сепак, сè уште ќе ни треба оваа брза низа за други потреби, како што се замена, метаподатоци и LVM-кеш и LVM-тенки метаподатоци, па ќе создадеме LVM VG на оваа низа.

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

Ајде да создадеме партиција за root датотечен систем.

#lvcreate -L 128G --name root root

Ајде да создадеме партиција за замена според големината на RAM-от.

#lvcreate -L 32G --name swap root

Инсталација на ОС

Севкупно, имаме се што е потребно за да го инсталираме системот.

Стартувајте го волшебникот за инсталација на системот од околината на Ubuntu Live. Нормална инсталација. Само во фазата на избор на дискови за инсталација, треба да го наведете следново:

  • /dev/md1, - точка на монтирање /подигање, FS - BTRFS
  • /dev/root/root (ака /dev/mapper/root-root), - точка на монтирање / (root), FS - BTRFS
  • /dev/root/swap (ака /dev/mapper/root-swap), - се користи како swap партиција
  • Инсталирајте го подигнувачот на /dev/sda

Кога ќе изберете BTRFS како root датотечен систем, инсталерот автоматски ќе создаде два тома BTRFS со име „@“ за / (root) и „@home“ за /home.

Да ја започнеме инсталацијата...

Инсталирањето ќе заврши со модален дијалог прозорец што укажува на грешка при инсталирањето на подигнувачот. За жал, нема да можете да излезете од овој дијалог користејќи стандардни средства и да продолжите со инсталацијата. Се одјавуваме од системот и повторно се најавуваме, завршувајќи во чиста работна површина на Ubuntu Live. Отворете го терминалот и повторно:

#sudo bash

Направете 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

Ајде да ги конфигурираме мрежата и името на домаќинот во chroot:

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

Ајде да одиме во chroot околината:

#chroot /mnt/chroot

Најпрво ќе ги доставиме пакетите:

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

Ајде да ги провериме и поправиме сите пакети што беа инсталирани криво поради нецелосна инсталација на системот:

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

Ако нешто не успее, можеби ќе треба прво да го уредите /etc/apt/sources.list

Ајде да ги прилагодиме параметрите за модулот RAID 6 за да овозможиме TRIM/DISCARD:

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

Ајде малку да ги дотеруваме нашите низи:

#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

Што беше тоа..?Создадовме збир на правила на udev кои ќе го направат следново:

  • Поставете ја големината на блок кешот за RAID 2020 да биде соодветна за 6 година. Стандардната вредност, се чини, не е променета од создавањето на Linux и не е соодветна долго време.
  • Резервирајте минимум IO за времетраењето на проверките/синхронизациите на низата. Ова е за да спречите вашите низи да заглават во состојба на вечна синхронизација под оптоварување.
  • Ограничете го максималниот IO при проверки/синхронизација на низи. Ова е неопходно за синхронизирањето/проверувањето на SSD RAID-ите да не ги пржи вашите дискови до свежина. Ова е особено точно за NVMe. (Се сеќавате на радијаторот? Не се шегував.)
  • Забранете ги дисковите да запираат ротација на вретеното (HDD) преку APM и поставете го времето на мирување за контролерите на дискот на 7 часа. Можете целосно да го оневозможите APM ако вашите дискови можат да го направат тоа (-B 255). Со стандардната вредност, погоните ќе запрат по пет секунди. Тогаш оперативниот систем сака да го ресетира кешот на дискот, дисковите повторно ќе се вртат и сè ќе започне одново. Дисковите имаат ограничен максимален број на ротации на вретеното. Таков едноставен стандарден циклус лесно може да ги убие вашите дискови за неколку години. Не страдаат сите дискови од ова, но нашите се „лаптоп“ со соодветни стандардни поставки, што прават RAID да изгледа како мини-MAID.
  • Инсталирајте читано на дискови (ротирачки) 1 мегабајт - два последователни блока/дел RAID 6
  • Оневозможете читање напред на самите низи.

Ајде да уредиме /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

Зошто е тоа..?Ќе ја бараме партицијата /boot по UUID. Името на низата теоретски може да се промени.

Ќе ги бараме преостанатите делови по имиња на LVM во нотацијата /dev/mapper/vg-lv, бидејќи тие ги идентификуваат партициите сосема уникатно.

Не користиме UUID за LVM затоа што UUID на томовите на LVM и нивните снимки може да бидат исти.Монтирајте го /dev/mapper/root-root.. двапати?Да. Точно. Карактеристика на BTRFS. Овој датотечен систем може да се монтира неколку пати со различни подволи.

Поради истата карактеристика, препорачувам никогаш да не создавате снимки од LVM од активните томови на BTRFS. Може да добиете изненадување кога ќе се рестартирате.

Ајде да ја регенерираме конфигурацијата mdadm:

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

Ајде да ги прилагодиме поставките за 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

Што беше тоа..?Овозможивме автоматско проширување на тенки базени LVM со достигнување на 90% од зафатениот простор за 5% од волуменот.

Го зголемивме максималниот број на кеш блокови за кешот LVM.

Спречивме LVM да бара LVM волумени (PV) на:

  • уреди што содржат LVM кеш (cdata)
  • уреди кеширани со користење на LVM кеш, заобиколувајќи го кешот ( _corig). Во овој случај, самиот кеширан уред сè уште ќе се скенира низ кешот (само ).
  • уреди кои содржат метаподатоци за кешот на LVM (cmeta)
  • сите уреди во VG со слики со имиња. Овде ќе имаме слики од диск од виртуелни машини и не сакаме LVM на домаќинот да активира томови кои припаѓаат на гостинскиот оперативен систем.
  • сите уреди во VG со резервна копија на името. Овде ќе имаме резервни копии од сликите на виртуелната машина.
  • сите уреди чие име завршува со „gpv“ (физички волумен за гости)

Овозможивме поддршка за DISCARD кога ослободуваме слободен простор на LVM VG. Внимавај. Ова ќе го направи бришењето на LV на SSD-то прилично време. Ова особено се однесува на SSD RAID 6. Сепак, според планот, ќе користиме тенко обезбедување, така што тоа воопшто нема да не пречи.

Ајде да ја ажурираме сликата на initramfs:

#update-initramfs -u -k all

Инсталирајте и конфигурирајте грб:

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

Кои дискови треба да ги изберете?Сите што се сд*. Системот мора да може да се подигне од кој било работен SATA-диск или SSD.

Зошто додадоа os-prober..?За прекумерна независност и разиграни раце.

Не работи правилно ако еден од RAID е во деградирана состојба. Се обидува да го бара ОС на партиции што се користат во виртуелните машини што работат на овој хардвер.

Ако ви треба, можете да го оставите, но имајте го на ум сето горенаведено. Препорачувам да барате рецепти за да се ослободите од непослушните раце на Интернет.

Со ова ја завршивме првичната инсталација. Време е да се рестартира во новоинсталираниот ОС. Не заборавајте да го отстраните бутабилното Live CD/USB.

#exit
#reboot

Изберете кој било од SATA SSD-овите како уред за подигање.

LVM на SATA SSD

Во овој момент, ние веќе се подигнавме во новиот оперативен систем, ја конфигуриравме мрежата, apt, го отворивме емулаторот на терминалот и трчавме:

#sudo bash

Да продолжиме

„Иницијализирајте ја“ низата од SATA SSD:

#blkdiscard /dev/md2

Ако не работи, тогаш обидете се:

#blkdiscard --step 65536 /dev/md2
Креирајте LVM VG на SATA SSD:

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

Зошто друго VG..?Всушност, ние веќе имаме VG наречен root. Зошто да не додадете сè во еден VG?

Ако има неколку PV во VG, тогаш за VG да се активира правилно, сите PV мора да бидат присутни (онлајн). Исклучок е LVM RAID, кој намерно не го користиме.

Навистина сакаме ако има дефект (загуба за читање податоци) на некоја од низите RAID 6, оперативниот систем да се подигне нормално и да ни даде можност да го решиме проблемот.

За да го направите ова, на првото ниво на апстракција ќе го изолираме секој тип на физички „медиуми“ во посебен VG.

Научно гледано, различни RAID низи припаѓаат на различни „домени на доверливост“. Не треба да им создавате дополнителна заедничка точка на неуспех со тоа што ќе ги соберете во еден VG.

Присуството на LVM на ниво на „хардвер“ ќе ни овозможи произволно да сечеме парчиња од различни RAID низи со нивно комбинирање на различни начини. На пример - трчај истовремено bcache + LVM тенок, bcache + BTRFS, LVM кеш + LVM тенок, сложена ZFS конфигурација со кеш, или која било друга пеколна мешавина за да се обидете и да го споредите сето тоа.

На ниво на „хардвер“, нема да користиме ништо друго освен старите добри „дебели“ волумени на LVM. Исклучок од ова правило може да биде резервната партиција.

Мислам дека до овој момент, многу читатели веќе почнаа да се сомневаат во нешто во врска со куклата за гнездење.

LVM на SATA HDD

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

Повторно ново VG..?Навистина сакаме ако низата на дискови што ќе ја користиме за резервна копија на податоци не успее, нашиот оперативен систем да продолжи да работи нормално, а притоа да го одржува пристапот до податоците што не се резервни, како и обично. Затоа, за да избегнеме проблеми со активирањето на VG, создаваме посебен VG.

Поставување кеш на LVM

Ајде да создадеме LV на NVMe RAID 1 за да го користиме како уред за кеширање.

#lvcreate -L 70871154688B --name cache root

Зошто има толку малку...?Факт е дека нашите NVMe SSD-дискови имаат и SLC кеш. 4 гигабајти „бесплатно“ и 18 гигабајти динамична поради слободниот простор зафатен во 3-битниот MLC. Штом оваа кеш е исцрпена, NVMe SSD-дисковите нема да бидат многу побрзи од нашиот SATA SSD со кеш. Всушност, поради оваа причина, нема смисла да ја направиме партицијата на кешот LVM многу поголема од двојно поголема од големината на кешот SLC на уредот NVMe. За користените NVMe дискови, авторот смета дека е разумно да се направат 32-64 гигабајти кеш.

Зададената големина на партицијата е потребна за да се организираат 64 гигабајти кеш, кеш метаподатоци и резервна копија на метаподатоци.

Дополнително, забележувам дека по исклучување на валканиот систем, LVM ќе ја означи целата кеш како валкана и повторно ќе се синхронизира. Покрај тоа, ова ќе се повторува секогаш кога lvchange се користи на овој уред додека системот не се рестартира повторно. Затоа, препорачувам веднаш да го рекреирате кешот користејќи соодветна скрипта.

Ајде да создадеме LV на SATA RAID 6 за да го користиме како кеширан уред.

#lvcreate -L 3298543271936B --name cache data

Зошто само три терабајти..?Така што, доколку е потребно, можете да користите SATA SSD RAID 6 за некои други потреби. Големината на кешираниот простор може да се зголемува динамично, во лет, без да се запре системот. За да го направите ова, треба привремено да го запрете и повторно да го вклучите кешот, но карактеристичната предност на LVM-cache во однос на, на пример, bcache е тоа што тоа може да се направи во лет.

Ајде да создадеме нов VG за кеширање.

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

Ајде да создадеме LV на кешираниот уред.

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

Овде веднаш го зазедовме целиот слободен простор на /dev/data/cache, така што сите други потребни партиции беа креирани веднаш на /dev/root/cache. Ако сте создале нешто на погрешно место, можете да го преместите користејќи pvmove.

Ајде да го создадеме и овозможиме кешот:

#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

Зошто толкаво крупно..?Преку практични експерименти, авторот можеше да открие дека најдобриот резултат се постигнува ако големината на кешот блок на LVM се совпаѓа со големината на тенкиот блок LVM. Покрај тоа, колку е помала големината, толку подобро се изведува конфигурацијата во случајно снимање.

64k е минималната големина на блокот дозволена за LVM Thin.

Внимавајте пишете..!Да. Овој тип на кеш ја одложува синхронизацијата на запишување на кешираниот уред. Ова значи дека ако кешот се изгуби, може да изгубите податоци на кешираниот уред. Подоцна, авторот ќе ви каже кои мерки, покрај NVMe RAID 1, може да се преземат за да се компензира овој ризик.

Овој тип на кеш е избран намерно за да се компензира слабата изведба на случаен запишување на RAID 6.

Ајде да провериме што добивме:

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

Само [cachedata_corig] треба да се наоѓа на /dev/data/cache. Ако нешто не е во ред, тогаш користете pvmove.

Можете да го оневозможите кешот ако е потребно со една команда:

#lvconvert -y --uncache cache/cachedata

Ова е направено on-line. LVM едноставно ќе го синхронизира кешот со дискот, ќе го отстрани и ќе го преименува cachedata_corig назад во кеш податоци.

Поставување LVM тенок

Ајде грубо да процениме колку простор ни треба за тенки метаподатоци на 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"

Заокружете до 4 гигабајти: 4294967296B

Помножете се со два и додадете 4194304B за метаподатоци LVM PV: 8594128896B
Ајде да создадеме посебна партиција на NVMe RAID 1 за да поставиме тенки метаподатоци на LVM и нивната резервна копија на неа:

#lvcreate -L 8594128896B --name images root

За што..?Овде може да се појави прашањето: зошто тенките метаподатоци на LVM да ги ставате посебно ако тие сè уште ќе бидат кеширани на NVMe и ќе работат брзо.

Иако брзината е важна овде, таа е далеку од главната причина. Работата е што кешот е точка на неуспех. Нешто може да се случи со него, и ако тенките метаподатоци на LVM се кешираат, тоа ќе предизвика сè да биде целосно изгубено. Без целосни метаподатоци, ќе биде речиси невозможно да се соберат тенки волумени.

Со преместување на метаподатоците во посебен не-кеширан, но брз волумен, ја гарантираме безбедноста на метаподатоците во случај на губење или оштетување на кешот. Во овој случај, целата штета предизвикана од губење на кешот ќе биде локализирана во тенки волумени, што ќе ја поедностави процедурата за обновување по редови на големина. Со голема веројатност, овие штети ќе бидат обновени со користење на логови на FS.

Освен тоа, ако претходно е направена слика од тенок волумен, а после тоа кешот бил целосно синхронизиран барем еднаш, тогаш, поради внатрешниот дизајн на LVM thin, интегритетот на снимката ќе се гарантира во случај на губење на кешот. .

Ајде да создадеме нов VG кој ќе биде одговорен за тенко обезбедување:

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

Ајде да создадеме базен:

#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T images/thin-pool
Зошто -Z yПокрај она за што всушност е наменет овој режим - да се спречи протекување на податоци од една виртуелна машина на друга виртуелна машина при прераспределба на просторот - дополнително се користи нулањето за да се зголеми брзината на случајно пишување во блокови помали од 64 k. Секое запишување помало од 64 k на претходно нераспределената област на тенок волумен ќе стане 64K порамнето со рабовите во кешот. Ова ќе овозможи операцијата да се изврши целосно преку кешот, заобиколувајќи го кешираниот уред.

Ајде да ги преместиме НН на соодветните 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

Ајде да провериме:

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

Ајде да создадеме тенок волумен за тестови:

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

Ќе инсталираме пакети за тестови и следење:

#apt-get install sysstat fio

Еве како можете да го набљудувате однесувањето на нашата конфигурација за складирање во реално време:

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

Еве како можеме да ја тестираме нашата конфигурација:

#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

Внимателно! Ресурс!Овој код ќе изврши 36 различни тестови, секој од 4 секунди. Половина од тестовите се за снимање. Можете да снимате многу на NVMe за 4 секунди. До 3 гигабајти во секунда. Така, секоја серија на тестови за пишување може да јаде до 216 гигабајти SSD ресурс од вас.

Читање и пишување измешано?Да. Има смисла да се извршуваат тестовите за читање и пишување одделно. Покрај тоа, има смисла да се осигура дека сите кешови се синхронизирани, така што претходно направеното запишување не влијае на читањето.

Резултатите ќе се разликуваат во голема мера за време на првото лансирање и следните, бидејќи кешот и тенкиот волумен се пополнуваат, а исто така во зависност од тоа дали системот успеал да ги синхронизира кешовите пополнети при последното лансирање.

Меѓу другото, препорачувам да ја измерите брзината на веќе целосно тенок волумен од кој штотуку е направена снимка. Авторот имал можност да набљудува како случајните записи нагло се забрзуваат веднаш по создавањето на првата слика, особено кога кешот сè уште не е целосно полн. Ова се случува поради семантиката за пишување копирање-на-пишување, порамнување на кешот и блоковите со тенок волумен, како и фактот дека случајното запишување во RAID 6 се претвора во случајно читање од RAID 6 проследено со запишување во кешот. Во нашата конфигурација, случајното читање од RAID 6 е до 6 пати (бројот на SATA SSD во низата) побрзо од пишувањето. Бидејќи блоковите за CoW се доделуваат последователно од тенок базен, а потоа снимањето, во најголем дел, исто така се претвора во секвенцијално.

И двете од овие карактеристики може да се користат во ваша корист.

Кеширате „кохерентни“ снимки

За да се намали ризикот од губење на податоци во случај на оштетување/загуба на кешот, авторот предлага да се воведе практика на ротирачки снимки за да се гарантира нивниот интегритет во овој случај.

Прво, бидејќи метаподатоците со тенок волумен се наоѓаат на некеширан уред, метаподатоците ќе бидат конзистентни и можните загуби ќе бидат изолирани во податочните блокови.

Следниот циклус на ротација на снимката го гарантира интегритетот на податоците во снимките во случај на губење на кешот:

  1. За секој тенок волумен со името <име>, креирајте слика со името <име>. кеширана
  2. Ајде да го поставиме прагот на миграција на разумна висока вредност: #lvchange --quiet --cachesettings "migration_threshold=16384" cache/cachedata
  3. Во јамката го проверуваме бројот на валкани блокови во кешот: #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' додека не добиеме нула. Ако нулата недостасува предолго, може да се создаде со привремено префрлање на кешот во режим на запишување. Меѓутоа, земајќи ги предвид карактеристиките на брзината на нашите SATA и NVMe SSD низи, како и нивниот ресурс TBW, или ќе можете брзо да го уловите моментот без да го промените режимот на кешот, или вашиот хардвер целосно ќе го изеде целиот свој ресурс во неколку дена. Поради ограничувањата на ресурсите, системот, во принцип, не може да биде под 100% оптоварување за пишување цело време. Нашите NVMe SSD-дискови под 100% оптоварување за пишување целосно ќе го исцрпат ресурсот -3 4 денови. SATA SSD-дисковите ќе траат само двојно подолго. Затоа, ќе претпоставиме дека најголемиот дел од оптоварувањето оди на читање, а имаме релативно краткорочни изливи на екстремно висока активност во комбинација со мало оптоварување во просек за пишување.
  4. Штом фативме (или направивме) нула, го преименуваме <name>.cached во <name>.committed. Стариот <name>.committed е избришан.
  5. Изборно, ако кешот е 100% полн, може да се рекреира со скрипта, со што ќе се исчисти. Со полупразна кеш, системот работи многу побрзо при пишување.
  6. Поставете го прагот на миграција на нула: #lvchange --quiet --cachesettings "migration_threshold=0" cache/cachedata Ова привремено ќе спречи синхронизирање на кешот со главниот медиум.
  7. Чекаме додека не се акумулираат доста промени во кешот #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' или тајмерот ќе се исклучи.
  8. Повторуваме повторно.

Зошто тешкотии со миграцискиот праг...?Работата е во тоа што во вистинска пракса, „случајното“ снимање всушност не е сосема случајно. Ако напишеме нешто на сектор со големина од 4 килобајти, постои голема веројатност дека во следните неколку минути ќе се направи запис до истиот или еден од соседните (+- 32K) сектори.

Со поставување на прагот на миграција на нула, ја одложуваме синхронизацијата на запишување на SATA SSD и собираме неколку промени во еден блок од 64K во кешот. Ова значително го заштедува ресурсот на SATA SSD.

Каде е кодот..?За жал, авторот се смета себеси за недоволно компетентен за развој на баш скрипти бидејќи е 100% самоук и практикува развој управуван од „google“, па затоа смета дека страшниот код што излегува од неговите раце не треба никој да го користи. друго.

Мислам дека професионалците во оваа област ќе можат самостојно да ја прикажат целата логика опишана погоре, доколку е потребно, и, можеби, дури и убаво да ја дизајнираат како системска услуга, како што се обиде да направи авторот.

Таквата едноставна шема за ротација на снимката ќе ни овозможи не само постојано да имаме една слика целосно синхронизирана на SATA SSD, туку исто така ќе ни овозможи, користејќи ја алатката thin_delta, да откриеме кои блокови се сменети по неговото создавање и со тоа да ја локализираме штетата на главните тома, во голема мера го поедноставуваат закрепнувањето.

ИСКРИМ/ОТПАЛИ во libvirt/KVM

Бидејќи складирањето податоци ќе се користи за KVM да работи libvirt, тогаш би било добра идеја да ги научиме нашите VM не само да зафаќаат слободен простор, туку и да го ослободат она што повеќе не е потребно.

Ова се прави со емулирање на поддршката TRIM/DISCARD на виртуелни дискови. За да го направите ова, треба да го промените типот на контролорот во virtio-scsi и да го уредите 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>

Ваквите отфрли од гостински оперативни системи се правилно обработени од LVM, а блоковите се ослободуваат правилно и во кешот и во тенкиот базен. Во нашиот случај, ова се случува главно на одложен начин, при бришење на следната слика.

Бекап на BTRFS

Користете готови скрипти со крајност претпазливост и на сопствен ризик. Овој код авторот го напишал самиот и исклучиво за себе. Сигурен сум дека многу искусни корисници на Линукс имаат слични алатки и нема потреба да ги копирате туѓите.

Ајде да создадеме јачина на звук на резервниот уред:

#lvcreate -L 256G --name backup backup

Ајде да го форматираме во BTRFS:

#mkfs.btrfs /dev/backup/backup

Ајде да создадеме точки за монтирање и да ги монтираме основните потсекции на датотечниот систем:

#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

Ајде да создадеме директориуми за резервни копии:

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

Ајде да создадеме директориум за резервни скрипти:

#mkdir /root/btrfs-backup

Ајде да го копираме сценариото:

Многу застрашувачки баш код. Користете на ваш сопствен ризик. Не пишувајте лути писма до авторот...#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

Што прави воопшто..?Содржи збир на едноставни команди за креирање снимки од BTRFS и нивно копирање на друг FS користејќи испраќање/примање на BTRFS.

Првото лансирање може да биде релативно долго, бидејќи ... На почетокот, сите податоци ќе бидат копирани. Понатамошните лансирања ќе бидат многу брзи, бидејќи ... Ќе се копираат само промените.

Друга скрипта што ќе ја ставиме во cron:

Уште малку баш код#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

Што прави..?Создава и синхронизира поединечни снимки од наведените тома BTRFS на резервната FS. После ова, ги брише сите слики создадени пред 60 дена. По лансирањето, датираните снимки од наведените томови ќе се појават во поддиректориумите /backup/btrfs/back/mote/.

Да ги дадеме правата за извршување на кодот:

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

Ајде да го провериме и да го ставиме во кронот:

#/usr/bin/nice -n 19 /usr/bin/ionice -c 3 /root/btrfs-backup/cron-daily.sh 2>&1 | /usr/bin/logger -t btrfs-backup
#cat /var/log/syslog | grep btrfs-backup
#crontab -e
0 2 * * * /usr/bin/nice -n 19 /usr/bin/ionice -c 3 /root/btrfs-backup/cron-daily.sh 2>&1 | /usr/bin/logger -t btrfs-backup

LVM тенка резервна копија

Ајде да создадеме тенок базен на резервниот уред:

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

Ајде да инсталираме ddrescue, бидејќи ... скриптите ќе ја користат оваа алатка:

#apt-get install gddrescue

Ајде да создадеме директориум за скрипти:

#mkdir /root/lvm-thin-backup

Ајде да ги копираме скриптите:

Многу баш внатре...#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

Што прави...?Содржи збир на команди за манипулирање со тенки снимки и синхронизирање на разликата помеѓу две тенки снимки примени преку thin_delta на друг блок уред со помош на ddrescue и blkdiscard.

Друга скрипта што ќе ја ставиме во cron:

Уште малку баш#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

Што прави...?Ја користи претходната скрипта за креирање и синхронизирање резервни копии на наведените тенки томови. Скриптата ќе остави неактивни снимки од наведените томови, кои се потребни за следење на промените од последната синхронизација.

Оваа скрипта мора да се уредува, наведувајќи ја листата на тенки томови за кои треба да се направат резервни копии. Наведените имиња се само за илустративни цели. Ако сакате, можете да напишете скрипта што ќе ги синхронизира сите томови.

Да ги дадеме правата:

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

Ајде да го провериме и да го ставиме во кронот:

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

Првото лансирање ќе биде долго, бидејќи ... тенки томови ќе бидат целосно синхронизирани со копирање на целиот искористен простор. Благодарение на тенките метаподатоци на LVM, знаеме кои блокови всушност се користат, така што ќе се копираат само вистински искористените блокови со тенок волумен.

Последователните работи постепено ќе ги копираат податоците благодарение на следењето на промените преку тенки метаподатоци на LVM.

Ајде да видиме што се случи:

#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

Каква врска има ова со куклите за гнездење?

Најверојатно, имајќи предвид дека LVM LV логичките волумени можат да бидат физички волумени на LVM PV за други VG. LVM може да биде рекурзивен, како кукли за гнездење. Ова му дава на LVM екстремна флексибилност.

PS

Во следната статија, ќе се обидеме да користиме неколку слични мобилни системи за складирање/KVM како основа за создавање на гео-дистрибуиран кластер за складирање/vm со вишок на неколку континенти користејќи домашни десктоп компјутери, домашниот интернет и P2P мрежи.

Извор: www.habr.com

Додадете коментар