Was haben LVM und eine Matroschka gemeinsam?

Guten Tag.
Ich möchte meine praktischen Erfahrungen beim Aufbau eines Datenspeichersystems fĂŒr KVM mit md RAID + LVM mit der Community teilen.

Das Programm wird beinhalten:

  • Aufbau von md RAID 1 aus NVMe SSD.
  • Aufbau von md RAID 6 aus SATA SSD und herkömmlichen Festplatten.
  • Besonderheiten der TRIM/DISCARD-Funktion bei SSD RAID 1/6.
  • Erstellung eines bootfĂ€higen md RAID 1/6 Arrays aus einer gemeinsamen Festplattenausstattung.
  • Installation des Systems auf NVMe RAID 1 ohne NVMe-UnterstĂŒtzung im BIOS.
  • Verwendung von LVM-Cache und LVM-thin.
  • Verwendung von BTRFS-Snapshots und send/receive fĂŒr Backups.
  • Verwendung von LVM-thin-Snapshots und thin_delta fĂŒr Backups im BTRFS-Stil.

Wenn es Sie interessiert, klicken Sie bitte auf die Fortsetzung.

Haftungsausschluss

Der Autor ĂŒbernimmt keine Verantwortung fĂŒr die Folgen der Nutzung oder Nichtnutzung von Materialien/Beispielen/Code/Tipps/Daten aus diesem Artikel. Durch das Lesen oder die Nutzung dieses Materials ĂŒbernehmen Sie die Verantwortung fĂŒr alle Folgen dieser Handlungen. Zu den möglichen Folgen gehören:

  • Durch die Hitze gebrannte NVMe SSD.
  • VollstĂ€ndig ausgeschöpfter Schreibvorrat und Ausfall von SSDs.
  • VollstĂ€ndiger Datenverlust auf allen Speichermedien, einschließlich Backups.
  • Defekte Hardware.
  • Verlorene Zeit, Nerven und Geld.
  • Alle anderen Folgen, die hier nicht aufgelistet sind.

Hardware

VerfĂŒgbar waren:

Mainboard aus dem Jahr 2013 mit Z87-Chipsatz, ausgestattet mit Intel Core i7 / Haswell.

  • 4-Kern-Prozessor, 8 Threads
  • 32 Gigabyte DDR3-RAM
  • 1 x 16 oder 2 x 8 PCIe 3.0
  • 1 x 4 + 1 x 1 PCIe 2.0
  • 6 x 6 Gbps SATA 3-AnschlĂŒsse

SAS-Adapter LSI SAS9211-8I, umprogrammiert in den IT/HBA-Modus. Die RAID-fÀhige Firmware wurde absichtlich gegen HBA-Firmware ausgetauscht, damit:

  1. Dieser Adapter jederzeit einfach ausgetauscht werden kann.
  2. TRIM/DISCARD auf den Festplatten war problemlos, da diese Befehle in der RAID-Firmware gar nicht unterstĂŒtzt werden, HBA dagegen ist es egal, welche Befehle ĂŒber den Bus gesendet werden.

8 HDDs — 8 StĂŒck HGST Travelstar 7K1000 mit 1 TB im 2,5-Zoll-Format, wie sie in Notebooks verwendet werden. Diese Disks waren zuvor in einem RAID 6-Array. In dem neuen System finden sie ebenfalls Verwendung, um lokale Backups zu speichern.

ZusĂ€tzlich wurde hinzugefĂŒgt:

6 SATA SSDs, Modell Samsung 860 QVO 2TB. Von diesen SSDs wurde ein großer Speicherbedarf, SLC-Cache, ZuverlĂ€ssigkeit und ein niedriger Preis gefordert. Discard/Zero-UnterstĂŒtzung war Pflicht, die durch eine Zeile in dmesg ĂŒberprĂŒft wurde:

Kernel: ata1.00: Aktivierung von discard_zeroes_data

2 StĂŒck NVMe SSD Modell Samsung SSD 970 EVO 500GB.

FĂŒr diese SSDs sind die Geschwindigkeit beim zufĂ€lligen Lesen/Schreiben und ihre Haltbarkeit wichtig. Ein KĂŒhler ist ein Muss. Unbedingt. Sonst – braten Sie sie bei der ersten RAID-Synchronisierung zu einer knusprigen Kruste.

Der StarTech PEX8M2E2 Adapter fĂŒr 2 x NVMe SSDs, installiert in einem PCIe 3.0 8x Slot. Das ist, wieder, einfach ein HBA, aber fĂŒr NVMe. Es unterscheidet sich von gĂŒnstigen Adaptern durch das Fehlen der Anforderung an die UnterstĂŒtzung von PCIe-Bifurkation durch das Motherboard, da ein integrierter PCIe-Switch vorhanden ist. Es wird sogar in den Ă€ltesten Systemen funktionieren, die PCIe haben, selbst wenn es ein x1 PCIe 1.0 Slot ist. NatĂŒrlich mit der entsprechenden Geschwindigkeit. Es gibt keine RAID-Funktionen. Es gibt kein integriertes BIOS an Bord. Ihre Systeme werden also nicht magisch lernen, von NVMe zu booten, geschweige denn NVMe RAID mit diesem GerĂ€t zu erstellen.

Dieses Komponente war ausschließlich durch das Vorhandensein nur eines freien 8x PCIe 3.0 Slots im System bedingt, und bei Vorhandensein von 2 freien Slots lĂ€sst es sich leicht gegen zwei gĂŒnstige PEX4M2E1 oder Ă€hnliche Modelle austauschen, die man ĂŒberall zu Preisen ab 600 Rubel kaufen kann.

Die Absage an jegliche Hardware- oder im Chipset/Bios integrierte RAID-Lösungen wurde bewusst getroffen, um die Möglichkeit zu haben, das gesamte System komplett zu ersetzen, mit Ausnahme der SSDs/HDDs, und alle Daten zu behalten. Idealerweise soll auch das installierte Betriebssystem beim Umstieg auf ganz neue/andere Hardware erhalten bleiben. Hauptsache, es gibt SATA- und PCIe-Ports. Es ist wie eine Live-CD oder ein bootfÀhiger USB-Stick, nur sehr schnell und etwas voluminös.

HumorWeil, wissen Sie, wie es manchmal ist – manchmal muss man das gesamte Array schnell mitnehmen. Und Daten möchte man nicht verlieren. Zu diesem Zweck sind alle genannten Speichermedien bequem in Schienen im 5.25 StandardgehĂ€use untergebracht.

Nun, und natĂŒrlich fĂŒr Experimente mit verschiedenen SSD-Caching-Methoden in Linux.

Hardware-RAIDs sind langweilig. Man schaltet sie ein. Es funktioniert oder nicht. Mit mdadm gibt es immer Optionen.

Software

FrĂŒher war auf der Hardware Debian 8 Jessie installiert, das nahe am EOL ist. Es wurde ein RAID 6 aus den oben genannten HDDs zusammen mit LVM aufgebaut. Darauf liefen virtuelle Maschinen in kvm/libvirt.

Da der Autor ĂŒber die geeignete Erfahrung in der Erstellung tragbarer bootfĂ€higer SATA/NVMe-Flashlaufwerke verfĂŒgt und um das gewohnte apt-Muster nicht zu stören, wurde als Zielsystem Ubuntu 18.04 gewĂ€hlt, das bereits ausreichend stabil ist, aber immer noch eine UnterstĂŒtzung von 3 Jahren in Aussicht hat.

Im genannten System sind alle notwendigen Treiber fĂŒr unsere Hardware bereits vorinstalliert. Es wird keine zusĂ€tzliche Software oder Treiber benötigt.

Vorbereitung zur Installation

FĂŒr die Installation des Systems benötigen wir das Ubuntu Desktop-Image. Das Server-System hat einen etwas umstĂ€ndlichen Installer, der ĂŒbermĂ€ĂŸige und nicht abschaltbare SelbststĂ€ndigkeit aufweist und unbedingt die UEFI-Systempartition auf eine der Festplatten verschiebt, wodurch die gesamte Struktur beeintrĂ€chtigt wird. Es wird also nur im UEFI-Modus installiert. Es werden keine anderen Optionen angeboten.

Das ist fĂŒr uns nicht akzeptabel.

Warum?Leider ist das Booten ĂŒber UEFI Ă€ußerst schlecht mit softwarebasiertem RAID kompatibel, da niemand Reserven fĂŒr die UEFI-ESP-Partition anbietet. Es gibt im Netz Rezepte, die vorschlagen, die ESP-Partition auf einem USB-Stick zu platzieren, aber das ist ein Ausfallpunkt. Es gibt Rezepte, die mit softwarebasiertem mdadm RAID 1 mit Metadaten der Version 0.9 arbeiten, die es dem UEFI-BIOS ermöglichen, diese Partition zu erkennen, aber das funktioniert nur bis zu dem glĂŒcklichen Moment, in dem das BIOS oder ein anderes Betriebssystem auf der Hardware etwas in die ESP Partition schreibt, ohne zuvor mit den anderen Spiegeln zu synchronisieren.

DarĂŒber hinaus hĂ€ngt das Booten ĂŒber UEFI von NVRAM ab, das nicht zusammen mit den Festplatten in das neue System migriert wird, da es Teil des Motherboards ist.

Daher werden wir kein neues Rad erfinden. Wir haben bereits ein fertiges, ĂŒber Jahre bewĂ€hrtes „Opa-Rad“, das heute Legacy/BIOS-Boot genannt wird und den stolzen Namen CSM auf UEFI-kompatiblen Systemen trĂ€gt. Wir werden es einfach vom Regal nehmen, einölen, die Reifen aufpumpen und mit einem feuchten Tuch abwischen.

Die Desktop-Version von Ubuntu kann auch nicht normal mit einem Legacy-Bootloader installiert werden, aber hier gibt es, wie man so schön sagt, immerhin einige Optionen.

Also, stellen wir die Hardware zusammen und laden das System von dem bootfĂ€higen Ubuntu Live-USB-Stick. Wir mĂŒssen Pakete herunterladen, also richten wir das Netzwerk ein, das funktioniert. Wenn es nicht funktioniert, können die benötigten Pakete vorher auf den USB-Stick geladen werden.

Wir gehen in die Desktop-Umgebung, starten den Terminal-Emulator, und los geht's:

#sudo bash

Wie
?Die Zeile oben ist der kanonische Auslöser fĂŒr die hitzigen Debatten ĂŒber sudo. Mit boMit grĂ¶ĂŸeren Möglichkeiten kommt auch eineogrĂ¶ĂŸere Verantwortung. Die Frage ist, ob Sie bereit sind, diese zu ĂŒbernehmen. Viele glauben, dass die Verwendung von sudo auf diese Weise zumindest unvorsichtig ist. Doch:

Video abspielen

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

Warum nicht ZFS
?Wenn wir Software auf unseren Computer installieren, leihen wir uns im Wesentlichen die Leistung unserer Hardware von den Entwicklern dieser Software.
Wenn wir diesem Software vertrauen, dass sie unsere Daten sichert, nehmen wir ein Darlehen in Höhe der Kosten fĂŒr die Wiederherstellung dieser Daten auf, das wir irgendwann begleichen mĂŒssen.

Aus dieser Sicht ist ZFS ein Ferrari, wÀhrend mdadm+lvm mehr einem Fahrrad Àhnelt.

Subjektiv zieht der Autor es vor, sich ein unbekanntes Fahrrad auf Kredit zu leihen, anstatt einen Ferrari. Der Preis ist vergleichsweise niedrig. Es gibt keine FĂŒhrerscheine zu beachten. Die Verkehrsregeln sind einfacher. Parken ist kostenlos. Die MobilitĂ€t ist besser. Man kann dem Fahrrad jederzeit RĂ€der anbringen und es lĂ€sst sich einfacher selbst reparieren.

Warum dann BTRFS
?Um das Betriebssystem zu laden, benötigen wir ein Dateisystem, das standardmĂ€ĂŸig von Legacy/BIOS GRUB unterstĂŒtzt wird und dabei Live-Snapshots bietet. Wir werden es fĂŒr die /boot-Partition verwenden. DarĂŒber hinaus zieht der Autor vor, dieses FS fĂŒr / (Root) zu verwenden, wobei zu beachten ist, dass fĂŒr jede andere Software separate Partitionen auf LVM erstellt und in die entsprechenden Verzeichnisse eingebunden werden können.

Weder Images virtuelle Maschinen, noch Datenbanken werden wir auf diesem FS speichern.
Dieses FS wird nur dazu verwendet, um sofortige System-Snapshots ohne Abschaltung zu erstellen und diese dann mittels send/receive auf die Backup-Disk zu ĂŒbertragen.

DarĂŒber hinaus zieht der Autor es vor, die Software auf der Hardware auf ein Minimum zu beschrĂ€nken und die gesamte andere Software in virtuellen Maschinen auszufĂŒhren, unter Verwendung von Features wie der Durchleitung von GPU und PCI-USB-Hauptcontrollern in KVM ĂŒber IOMMU.

Auf der Hardware verbleiben nur: Datenspeicherung, Virtualisierung und Backup.

Wenn Sie ZFS mehr vertrauen, sind sie prinzipiell fĂŒr den angegebenen Zweck austauschbar.

Dennoch ignoriert der Autor bewusst die integrierten Funktionen zur Spiegelung / RAID und Redundanz, die in ZFS, BTRFS und LVM vorhanden sind.

Als zusĂ€tzliches Argument hat BTRFS die FĂ€higkeit, zufĂ€llige SchreibvorgĂ€nge in sequenzielle umzuwandeln, was sich Ă€ußerst positiv auf die Geschwindigkeit der Synchronisierung von Snapshots/Sicherungen auf HDDs auswirkt.

Scan aller GerÀte neu:

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

Lassen Sie uns umsehen:

#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
Knoten SN Modell Namespace Nutzung 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

Partitionierung der „Laufwerke“

NVMe SSD

Wir werden sie jedoch nicht partitionieren. Unser BIOS erkennt diese Laufwerke sowieso nicht. Daher werden sie vollstĂ€ndig in ein softwarebasiertes RAID integriert. Wir werden dort nicht einmal Partitionen erstellen. Wenn es dem ‚Kanon‘ oder aus ‚prinzipiellen‘ GrĂŒnden entspricht – erstellen Sie eine große Partition wie bei einer HDD.

SATA HDD

Hier braucht man nicht viel zu erfinden. Wir werden eine Partition fĂŒr alles erstellen. Wir erstellen eine Partition, weil diese Laufwerke vom BIOS erkannt werden und es sogar versuchen kann, davon zu booten. Wir werden spĂ€ter GRUB auf diesen Laufwerken installieren, damit das System plötzlich funktioniert.

#cat >hdd.part << EOF
Bezeichnung: dos
Bezeichnungs-ID: 0x00000000
GerÀt: /dev/sdg
Einheit: Sektoren

/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

Hier wird es interessanter.

Erstens sind unsere Laufwerke 2 TB groß. Das liegt im zulĂ€ssigen Bereich fĂŒr MBR, was wir nutzen werden. Bei Bedarf kann man auf GPT wechseln. GPT-Laufwerke haben eine KompatibilitĂ€tsebene, die es MBR-kompatiblen Systemen ermöglicht, die ersten 4 Partitionen zu sehen, wenn sie innerhalb der ersten 2 Terabyte liegen. Wichtig ist, dass die bootfĂ€hige Partition und die Partition bios_grub am Anfang dieser Laufwerke platziert sind. Das ermöglicht sogar ein Booten von GPT-Laufwerken im Legacy/BIOS-Modus.

Aber das ist nicht unser Fall.

Hier werden wir zwei Partitionen erstellen. Die erste wird 1 GB groß sein und fĂŒr RAID 1 /boot verwendet werden.

Die zweite wird fĂŒr RAID 6 verwendet und den gesamten verbleibenden Speicherplatz einnehmen, mit Ausnahme eines kleinen nicht partitionierten Bereichs am Ende des Laufwerks.

Was ist dieser nicht partitionierte Bereich?Laut Quellen im Internet haben unsere SATA SSDs einen dynamisch erweiterbaren SLC-Cache von 6 bis 78 Gigabyte. Die 6 Gigabyte erhalten wir ‚kostenlos‘ durch die Differenz zwischen ‚Gigabyte‘ und ‚Gibibyte‘ im technischen Datenblatt des Laufwerks. Die restlichen 72 Gigabyte stehen aufgrund des ungenutzten Speicherplatzes zur VerfĂŒgung.

Hier muss angemerkt werden, dass unser Cache SLC ist und der Platz im 4-Bit-MLC-Modus belegt wird. Das bedeutet fĂŒr uns effektiv, dass wir fĂŒr jeweils 4 Gigabyte freien Speicherplatz nur 1 Gigabyte SLC-Cache erhalten.

Wir multiplizieren 72 Gigabyte mit 4 und erhalten 288 Gigabyte. Dies ist der freie Platz, den wir nicht partitionieren werden, um den Laufwerken die volle Nutzung des SLC-Caches zu ermöglichen.

Damit erhalten wir insgesamt bis zu 312 Gigabyte SLC-Cache von sechs Speichermedien effizient. Von allen Speichermedien werden 2 im RAID fĂŒr Redundanz verwendet.

Eine solche Cachemenge ermöglicht es uns, in der Praxis Ă€ußerst selten auf eine Situation zu stoßen, in der die SchreibvorgĂ€nge nicht im Cache erfolgen. Das kompensiert Ă€ußerst gut den schlimmsten Nachteil von QLC-Speicher, nĂ€mlich die extrem niedrige Schreibgeschwindigkeit, wenn Daten am Cache vorbei geschrieben werden. Wenn Ihre Lasten dies nicht unterstĂŒtzen, empfehle ich Ihnen, ernsthaft darĂŒber nachzudenken, wie lange Ihre SSDs bei dieser Belastung unter BerĂŒcksichtigung der TBW im Datenblatt halten werden.

#cat >ssd.part << EOF
Bezeichnung: dos
Bezeichnungs-ID: 0x00000000
GerÀt: /dev/sda
Einheit: Sektoren

/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

Erstellen von Arrays

ZunĂ€chst mĂŒssen wir den Computer umbenennen. Das ist notwendig, da der Hostname Teil des Array-Namens irgendwo innerhalb von mdadm ist und dort Einfluss hat. Arrays können natĂŒrlich spĂ€ter umbenannt werden, aber das sind ĂŒberflĂŒssige Schritte.

#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

Warum -assume-clean
?Um die Arrays nicht zu initialisieren. FĂŒr beide RAID-Stufen 1 und 6 ist dies akzeptabel. Alles kann ohne Initialisierung funktionieren, wenn es sich um ein neues Array handelt. DarĂŒber hinaus ist die Initialisierung eines SSD-Arrays bei der Erstellung eine sinnlose Verschwendung von TBW-Ressourcen. Wir verwenden TRIM/DISCARD, wo immer möglich, auf den geformten SSD-Arrays, um sie "zu initialisieren".

Bei SSD-RAID 1 wird DISCARDED standardmĂ€ĂŸig unterstĂŒtzt.

Bei SSD-RAID 6 muss DISCARDED in den Moduleinstellungen des Kernels aktiviert werden.

Das sollte nur dann gemacht werden, wenn alle SSDs, die in den Arrays der Ebenen 4/5/6 in diesem System verwendet werden, eine funktionierende UnterstĂŒtzung fĂŒr discard_zeroes_data haben. Manchmal trifft man auf seltsame SpeichergerĂ€te, die dem Kernel die UnterstĂŒtzung dieser Funktion melden, aber tatsĂ€chlich hat es nicht oder funktioniert nicht immer. Im Moment gibt es praktisch ĂŒberall UnterstĂŒtzung, jedoch kommen alte Speicher und fehlerhafte Firmware vor. Aus diesem Grund ist die DISCARD-UnterstĂŒtzung standardmĂ€ĂŸig fĂŒr RAID 6 deaktiviert.

Achtung, der folgende Befehl wird alle Daten auf NVMe-Speichern "initialisieren" und mit "Nullen" ĂŒberschreiben.

#blkdiscard /dev/md0

Wenn etwas schief geht, versuchen Sie einen Schritt anzugeben.

#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

Warum so groß
?Die Erhöhung der Chunk-GrĂ¶ĂŸe hat einen positiven Einfluss auf die Geschwindigkeit des zufĂ€lligen Lesens von Blöcken bis zu einschließlich der Chunk-GrĂ¶ĂŸe. Das liegt daran, dass eine Operation in dieser GrĂ¶ĂŸe oder kleiner vollstĂ€ndig auf einem GerĂ€t ausgefĂŒhrt werden kann. Daher summieren sich die IOPS aller GerĂ€te. Statistisch gesehen ĂŒberschreiten 99 % der IOs nicht 512K.

Bei RAID 6 sind die Schreib-IOPS geringer oder gleich den IOPS eines einzelnen Laufwerks. WĂ€hrend die IOPS bei zufĂ€lligem Lesen um ein Vielfaches höher sein können als bei einem einzelnen Laufwerk, ist die BlockgrĂ¶ĂŸe hier entscheidend. immer Der Autor sieht keinen Sinn darin, einen Parameter zu optimieren, der von Design her schlecht ist bei RAID 6, und optimiert stattdessen das, wo sich RAID 6 gut bewĂ€hrt.
Die schlechten zufÀlligen Schreiboperationen bei RAID 6 werden wir durch NVMe-Cache und Tricks mit Thin-Provisioning ausgleichen.
Wir haben DISCARD fĂŒr RAID 6 vorerst nicht aktiviert. Daher werden wir dieses Array momentan nicht "initialisieren". Das machen wir spĂ€ter, nach der Installation des Betriebssystems.

LVM auf NVMe RAID

SATA HDD

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

FĂŒr die Geschwindigkeit möchten wir das Root-Dateisystem auf dem NVMe RAID 1, das /dev/md0 ist, platzieren.

Dennoch benötigen wir dieses schnelle Array auch fĂŒr andere Zwecke, wie Swap, Metadaten und LVM-Cache sowie LVM-thin-Metadaten. Daher erstellen wir auf diesem Array ein LVM VG.
Wir erstellen eine Partition fĂŒr das Root-Dateisystem.

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

Wir erstellen eine Partition fĂŒr den Swap, die der GrĂ¶ĂŸe des Arbeitsspeichers entspricht.

#lvcreate -L 128G --name root root

Installation des Betriebssystems

#lvcreate -L 32G --name swap root

Wir haben alles Notwendige, um das System zu installieren.

Wir starten den Installationsassistenten aus der Ubuntu Live-Umgebung. Normale Installation. Lediglich beim Auswahl der Laufwerke fĂŒr die Installation muss Folgendes angegeben werden:

Bootloader auf /dev/sda installieren

  • /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), — ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČать ĐșаĐș разЎДл ĐżĐŸĐŽĐșачĐșĐž
  • Wenn BTRFS als Root-Dateisystem gewĂ€hlt wird, erstellt der Installer automatisch zwei BTRFS-Volumes mit den Namen "@" fĂŒr / (Root) und "@home" fĂŒr /home.

Wir starten die Installation


Die Installation endet mit einem modalem Dialogfenster, das ĂŒber einen Fehler beim Installieren des Bootloaders informiert. Leider kann dieser Dialog nicht auf gewöhnliche Weise verlassen werden, um die Installation fortzusetzen. Wir loggen uns aus der Sitzung aus und loggen uns erneut ein, um auf den sauberen Desktop von Ubuntu Live zu gelangen. Wir öffnen das Terminal und wieder:

Wir erstellen eine chroot-Umgebung, um die Installation fortzusetzen:

#sudo bash

Wir richten das Netzwerk und den Hostnamen in chroot ein:

#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

Wir betreten die chroot-Umgebung:

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

Als Erstes installieren wir die Pakete:

#chroot /mnt/chroot

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

Wir ĂŒberprĂŒfen und reparieren alle Pakete, die aufgrund einer unvollendeten Systeminstallation fehlerhaft installiert wurden:

Wenn etwas nicht funktioniert hat, mĂŒssen Sie möglicherweise zuvor /etc/apt/sources.list bearbeiten.

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

Wir passen die Parameter fĂŒr das RAID 6-Modul an, um TRIM/DISCARD zu aktivieren:

Wir nehmen einige Anpassungen an unseren Arrays vor:

#cat >/etc/modprobe.d/raid456.conf << EOF
options raid456 gerÀte_handhabung_sicher_entfernen=1
EOF

Was war das..?

#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

Wir haben eine Sammlung von udev-Regeln erstellt, die Folgendes tun werden:Wir haben eine Sammlung von udev-Regeln erstellt, die Folgendes tun werden:

  • Die angemessene Cache-GrĂ¶ĂŸe fĂŒr RAID 6 im Jahr 2020 einstellen. Der Standardwert scheint seit der EinfĂŒhrung von Linux unverĂ€ndert zu sein und ist seit langem nicht mehr angemessen.
  • Reservieren Sie wĂ€hrend der ÜberprĂŒfungen/Synchronisierungen der Arrays mindestens IO. Das ist notwendig, damit Ihre Arrays nicht in einem Zustand dauerhafter Synchronisation unter Last stecken bleiben.
  • Begrenzen Sie wĂ€hrend der ÜberprĂŒfungen/Synchronisierungen der Arrays das Maximum an IO. Dies ist notwendig, damit die Synchronisation/ÜberprĂŒfung der SSD-RAIDs Ihre Speicher nicht zu viel belastet. Besonders relevant fĂŒr NVMe. (Erinnern Sie sich an den KĂŒhlkörper? Ich habe es nicht im Spaß gemeint.)
  • Verbieten Sie ĂŒber APM den Festplatten das Stoppen des Spins (HDD) und setzen Sie einen Timeout fĂŒr den Schlafmodus der Festplattencenter auf 7 Stunden. APM kann ganz deaktiviert werden, wenn Ihre Festplatten das unterstĂŒtzen (-B 255). Bei der Standardeinstellung werden die Festplatten nach fĂŒnf Sekunden gestoppt. Dann möchte das OS den Festplattencache zurĂŒcksetzen, die Festplatten drehen sich wieder und alles geht von vorne los. Die Festplatten haben eine begrenzte maximale Anzahl an Spins. Ein solcher einfacher Zyklus kann bei den standardmĂ€ĂŸig eingestellten 'Notebook'-Festplatten Ihre Laufwerke innerhalb von zwei Jahren leicht ruinieren. Nicht alle Festplatten sind davon betroffen, aber unsere 'Notebook'-Modelle mit den entsprechenden Standardeinstellungen verwandeln RAID in eine schiefe Nachahmung von mini-MAID.
  • Stellen Sie den Readahead fĂŒr rotierende Festplatten auf 1 Megabyte ein – zwei aufeinanderfolgende Blöcke/Chunks fĂŒr RAID 6.
  • Verbieten Sie Readahead auf den Arrays selbst.

Bearbeiten wir /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

Warum so..?Wir werden die Partition /boot anhand der UUID suchen, da die Benennung der Arrays theoretisch variieren kann.

Die anderen Partitionen finden wir anhand der LVM-Namen in der Notation /dev/mapper/vg-lv, da sie die Partitionen ausreichend eindeutig identifizieren.

Verwenden Sie UUID nicht fĂŒr LVM, da die UUIDs von LVM-Volumes und ihren Snapshots ĂŒbereinstimmen können.Mounten wir /dev/mapper/root-root doppelt..?Ja. Genau so. Eine Besonderheit von BTRFS. Dieses Dateisystem kann mehrmals mit unterschiedlichen Subvolumes gemountet werden.

Aufgrund dieser Besonderheit empfehle ich, niemals LVM-Snapshots aktiver BTRFS-Volumes zu erstellen. Sie könnten bei einem Neustart eine Überraschung erleben.

Regenerieren wir die Konfiguration fĂŒr mdadm:

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

Passen wir die Einstellungen fĂŒr LVM an:

#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

Wir haben eine Sammlung von udev-Regeln erstellt, die Folgendes tun werden:Wir haben die automatische Erweiterung von LVM thin-Pools bei Erreichen von 90 % belegtem Speicherplatz um 5 % des Volumens aktiviert.

Wir haben die maximale Anzahl der Cache-Blöcke fĂŒr LVM-Cache erhöht.

Wir haben LVM verboten, LVM-Volumes (PV) zu suchen bei:

  • GerĂ€ten, die LVM-Cache (cdata) enthalten
  • GerĂ€ten, die ĂŒber LVM-Cache umgangen werden (<lv_name>_corig). Das gecachte GerĂ€t wird jedoch weiterhin ĂŒber den Cache (einfach <lv_name>) gescannt.
  • GerĂ€ten, die LVM-Cache-Metadaten (cmeta) enthalten
  • allen GerĂ€ten in VG mit dem Namen images. Hier werden wir Images von virtuellen Maschinen haben, und wir wollen nicht, dass LVM auf dem Host die Volumes aktiviert, die der Gast-OS gehören.
  • allen GerĂ€ten in VG mit dem Namen backup. Hier werden wir Sicherungskopien von Images virtueller Maschinen haben.
  • allen GerĂ€ten, deren Name mit „gpv“ endet (Guest Physical Volume)

Wir haben die DISCARD-UnterstĂŒtzung beim Freigeben von nicht verwendetem Speicherplatz auf LVM VG aktiviert. Seien Sie vorsichtig. Dies wird die Löschung von LV auf SSDs erheblich verlĂ€ngern. Insbesondere gilt dies fĂŒr SSD RAID 6. PlanmĂ€ĂŸig werden wir jedoch Thin Provisioning verwenden, sodass das uns ĂŒberhaupt nicht stören wird.

Aktualisieren wir das initramfs-Image:

#update-initramfs -u -k all

Wir installieren und konfigurieren grub:

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

Welche Festplatten sollten ausgewÀhlt werden?Alle, die sd* sind. Das System sollte in der Lage sein, von jeder funktionierenden SATA-Festplatte oder SSD zu booten.

Warum haben wir os-prober deaktiviert..?Aufgrund von ĂŒbermĂ€ĂŸiger EigenstĂ€ndigkeit und ungezogenen HĂ€nden.

Es funktioniert nicht korrekt, wenn einer der RAID-Arrays in einem degradierten Zustand ist. Es versucht, OS auf Partitionen zu finden, die in virtuellen Maschinen verwendet werden, die auf dieser Hardware laufen.

Wenn Sie es benötigen, können Sie es behalten, aber denken Sie an alles, was oben gesagt wurde. Ich empfehle, nach Lösungen zur Deaktivierung ungezogener HÀnde im Internet zu suchen.

Damit haben wir die Erstinstallation abgeschlossen. Es ist Zeit, in das gerade installierte OS neu zu starten. Vergessen Sie nicht, die bootfÀhige Live-CD/USB herauszunehmen.

#exit
#reboot

Als BootgerÀt wÀhlen wir irgendeine der SATA SSDs.

LVM auf SATA SSD

Zu diesem Zeitpunkt sind wir bereits ins neue OS gebootet, haben das Netzwerk, apt konfiguriert, ein Terminals Emulator geöffnet und ausgefĂŒhrt:

#sudo bash

Fahren wir fort.

Wir „initialisieren“ das Array aus SATA SSD:

#blkdiscard /dev/md2

Wenn es nicht funktioniert hat, probieren wir:

#blkdiscard --step 65536 /dev/md2
Wir erstellen LVM VG auf SATA SSD:

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

Warum noch ein VG..?TatsĂ€chlich haben wir bereits ein VG mit dem Namen root. Warum nicht alles in ein VG hinzufĂŒgen?

Wenn es in VG mehrere PV gibt, mĂŒssen zur korrekten Aktivierung von VG alle PV vorhanden sein (online). Eine Ausnahme bildet LVM RAID, das wir absichtlich nicht verwenden.

Wir möchten sehr, dass beim Ausfall (d.h. Datenverlust) eines der RAID 6-Array das Betriebssystem ordnungsgemĂ€ĂŸ bootet und uns die Möglichkeit gibt, das Problem zu lösen.

Zu diesem Zweck werden wir auf der ersten Abstraktionsebene jeden Typ physischer „DatentrĂ€ger“ in ein separates VG isolieren.

Wenn man es wissenschaftlich betrachtet, gehören verschiedene RAID-Arrays zu unterschiedlichen „ZuverlĂ€ssigkeitsbereichen“. Es ist nicht ratsam, dafĂŒr einen zusĂ€tzlichen gemeinsamen Fehlerpunkt zu schaffen, indem man sie in ein VG packt.

Das Vorhandensein von LVM auf der „Hardware“-Ebene ermöglicht es uns, willkĂŒrlich StĂŒcke verschiedener RAID-Arrays unterschiedlich zu kombinieren. Zum Beispiel, - zu starten gleichzeitig bcache + LVM thin, bcache + BTRFS, LVM cache + LVM thin, eine komplexe ZFS-Konfiguration mit Caches oder jede andere teuflische Mischung, um all das zu testen und zu vergleichen.

Auf der „Hardware“-Ebene werden wir nichts außer den altbewĂ€hrten „dicken“ LVM-Volumes verwenden. Eine Ausnahme von dieser Regel könnte möglicherweise die Partition fĂŒr Backups sein.

Ich denke, bis zu diesem Zeitpunkt haben viele Leser bereits anfĂ€ngliche Verdachtsmomente bezĂŒglich der Matroschka.

LVM auf SATA HDD

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

Wieder eine neue VG..?Wir möchten sehr, dass beim Ausfall des Arrays von Festplatten, das wir fĂŒr die Datensicherung verwenden, unser Betriebssystem ordnungsgemĂ€ĂŸ weiterarbeitet und dabei den Zugriff auf nicht gesicherte Daten aufrechterhĂ€lt. Daher, um Probleme bei der Aktivierung von VG zu vermeiden, erstellen wir ein separates VG.

Einrichtung von LVM Cache

Wir erstellen LV auf NVMe RAID 1, um es als Cache-GerÀt zu verwenden.

#lvcreate -L 70871154688B --name cache root

Warum so wenig
?Das liegt daran, dass unsere NVMe SSDs ebenfalls ĂŒber einen SLC-Cache verfĂŒgen. 4 Gigabyte „kostenlos“ und 18 Gigabyte dynamisch durch den belegten Platz im 3-Bit MLC. Nach dem Erschöpfen dieses Caches werden die NVMe SSDs nicht viel schneller sein als unsere SATA SSD mit Cache. Aus diesem Grund macht es fĂŒr uns keinen Sinn, die LVM Cache-Partition wesentlich grĂ¶ĂŸer als das Zweifache des SLC-Cache-Volumens des NVMe-Speichers zu gestalten. FĂŒr die verwendeten NVMe-Speicher hĂ€lt der Autor es fĂŒr sinnvoll, einen Cache von 32-64 Gigabyte zu erstellen.

Die angegebene PartitiongrĂ¶ĂŸe ist erforderlich, um 64 Gigabyte Cache zu organisieren, die Cache-Metadaten zu platzieren und ein Backup der Metadaten zu erstellen.

ZusĂ€tzlich möchte ich anmerken, dass nach einem unsachgemĂ€ĂŸen Ausschalten des Systems LVM den gesamten Cache als schmutzig markieren und neu synchronisieren wird. DarĂŒber hinaus wird dies bei jeder Verwendung von lvchange auf diesem GerĂ€t bis zu einem Neustart des Systems wiederholt. Daher empfehle ich, den Cache sofort mit dem entsprechenden Skript neu zu erstellen.

Wir erstellen ein LV im SATA RAID 6, um es als Cache-GerÀt zu verwenden.

#lvcreate -L 3298543271936B --name cache data

Warum nur drei Terabyte..?Damit man bei Bedarf das SATA SSD RAID 6 fĂŒr andere Zwecke nutzen kann. Der Speicherplatz fĂŒr den Cache kann dynamisch und ohne Unterbrechung des Betriebs des Systems erhöht werden. DafĂŒr muss der Cache vorĂŒbergehend gestoppt und dann wieder aktiviert werden, aber ein herausragender Vorteil von LVM-cache gegenĂŒber beispielsweise bcache ist, dass dies im laufenden Betrieb möglich ist.

Wir erstellen eine neue VG fĂŒr das Caching.

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

Wir erstellen ein LV auf dem zwischengespeicherten GerÀt.

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

Hier haben wir sofort den gesamten freien Speicher auf /dev/data/cache belegt, damit alle anderen benötigten Partitionen sofort auf /dev/root/cache erstellt werden. Wenn etwas nicht dort erstellt wurde, kann es mit pvmove verschoben werden.

Wir erstellen und aktivieren den Cache:

#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

Warum so ein chunksize..?Durch praktische Experimente hat der Autor herausgefunden, dass das beste Ergebnis erzielt wird, wenn die BlockgrĂ¶ĂŸe des LVM-Caches mit der BlockgrĂ¶ĂŸe des LVM-thin ĂŒbereinstimmt. Dabei zeigt sich, je kleiner die GrĂ¶ĂŸe ist, desto besser funktioniert die Konfiguration bei zufĂ€lligen SchreibvorgĂ€ngen.

64k ist die minimale BlockgrĂ¶ĂŸe, die fĂŒr LVM thin zulĂ€ssig ist.

Vorsicht writeback..!Ja. Dieser Cachetyp verzögert die Synchronisation von SchreibvorgĂ€ngen auf das zwischengespeicherte GerĂ€t. Dies kann dazu fĂŒhren, dass beim Verlust des Caches Daten auf dem zwischengespeicherten GerĂ€t verloren gehen. SpĂ€ter wird der Autor erlĂ€utern, welche Maßnahmen zusĂ€tzlich zu NVMe RAID 1 ergriffen werden können, um dieses Risiko zu minimieren.

Dieser Cachetyp wurde absichtlich gewÀhlt, um die niedrige Leistung von RAID 6 bei zufÀlligen SchreibvorgÀngen auszugleichen.

ÜberprĂŒfen wir, was wir erreicht haben:

#lvs -a -o lv_name,lv_size,devices --units B cache
LV LSize GerÀte
[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)

Auf /dev/data/cache sollte sich nur [cachedata_corig] befinden. Wenn etwas nicht stimmt, verwenden Sie pvmove.

Den Cache bei Bedarf kann man mit einem Befehl deaktivieren:

#lvconvert -y --uncache cache/cachedata

Dies geschieht online. LVM synchronisiert einfach den Cache auf die Festplatte, entfernt ihn und benennt cachedata_corig zurĂŒck in cachedata.

Konfiguration von LVM thin

SchĂ€tzen wir grob, wie viel Platz wir fĂŒr die Metadaten von LVM thin benötigen:

#thin_metadata_size --block-size=64k --pool-size=6terabytes --max-thins=100000 -u bytes
thin_metadata_size - geschĂ€tzte MetadatenbereichsgrĂ¶ĂŸe von 3385794560 Bytes fĂŒr "--block-size=64kibibytes --pool-size=6terabytes --max-thins=100000"

Runden wir auf 4 Gigabyte auf: 4294967296B

Wir multiplizieren mit zwei und addieren 4194304B fĂŒr die Metadaten von LVM PV: 8594128896B
Wir erstellen eine separate Partition auf NVMe RAID 1, um die Metadaten von LVM thin und deren Sicherung darauf zu speichern:

#lvcreate -L 8594128896B --name images root

Warum..?Hier könnte sich die Frage stellen, warum man die Metadaten von LVM thin separat speichern sollte, wenn sie ohnehin auf NVMe zwischengespeichert werden und schnell arbeiten.

Die Geschwindigkeit ist zwar wichtig, aber nicht der Hauptgrund. Es geht darum, dass der Cache einen Ausfallpunkt darstellt. Es kann etwas mit ihm passieren, und wenn die Metadaten von LVM thin im Cache gespeichert sind, könnte dies zum vollstĂ€ndigen Verlust aller Daten fĂŒhren. Ohne vollstĂ€ndige Metadaten wird es nahezu unmöglich sein, die thin Volumes wiederherzustellen.

Indem wir die Metadaten auf ein separates, nicht zwischengespeichertes, aber schnelles Volume verschieben, gewÀhrleisten wir die Sicherheit der Metadaten im Falle eines Verlusts oder einer BeschÀdigung des Caches. In diesem Fall werden alle durch den Verlust des Caches verursachten SchÀden innerhalb der thin Volumes lokalisiert, was die Wiederherstellung erheblich erleichtert. Mit hoher Wahrscheinlichkeit können diese SchÀden mithilfe der FS-Protokolle wiederhergestellt werden.

DarĂŒber hinaus, wenn zuvor ein Snapshot des thin Volumes erstellt wurde und der Cache danach mindestens einmal vollstĂ€ndig synchronisiert wurde, wird aufgrund der Besonderheiten der inneren Struktur von LVM thin die IntegritĂ€t des Snapshots im Falle eines Cache-Verlusts garantiert.

Wir erstellen eine neue VG, die fĂŒr das Thin-Provisioning verantwortlich ist:

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

Wir erstellen einen Pool:

#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T images/thin-pool
Warum -Z yDarĂŒber hinaus, abgesehen von dem Zweck, fĂŒr den dieser Modus eigentlich gedacht ist — um zu verhindern, dass Daten von einer virtuellen Maschine in eine andere verschĂŒttet werden, wenn der Speicherplatz neu verteilt wird — wird das Zeroing zusĂ€tzlich verwendet, um die Geschwindigkeit von Random Writes in Blöcken unter 64k zu erhöhen. Jede Schreiboperation unter 64k in einem zuvor nicht zugewiesenen Bereich des thin Volumes wird in 64K-Cache-ausgerichtete Blöcke umgewandelt. Dies ermöglicht es, die Operation vollstĂ€ndig ĂŒber den Cache durchzufĂŒhren, ohne das zwischengespeicherte GerĂ€t zu verwenden.

Wir verschieben das LV auf die entsprechenden 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

Wir ĂŒberprĂŒfen:

#lvs -a -o lv_name,lv_size,devices --units B images
LV LSize GerÀte
[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)

Wir erstellen ein thin Volume fĂŒr Tests:

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

Wir installieren Pakete fĂŒr Tests und Überwachung:

#apt-get install sysstat fio

So können wir das Verhalten unserer Speicherkonfiguration in Echtzeit beobachten:

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

So können wir unsere Konfiguration testen:

#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

Vorsicht! Ressource!Dieser Code fĂŒhrt 36 verschiedene Tests durch, von denen jeder 4 Sekunden dauert. Die HĂ€lfte der Tests ist auf das Schreiben ausgerichtet. In 4 Sekunden kann man auf NVMe sehr viel schreiben. Bis zu 3 Gigabyte pro Sekunde. Daher kann jeder Start eines Schreibtests bis zu 216 Gigabyte SSD-Ressourcen beanspruchen.

Lesen und Schreiben gemischt?Ja. Es macht Sinn, Lese- und Schreibtests separat durchzufĂŒhren. DarĂŒber hinaus ist es sinnvoll, sicherzustellen, dass alle Caches synchronisiert sind, damit frĂŒhere SchreibvorgĂ€nge das Lesen nicht beeinflussen.

Die Ergebnisse werden beim ersten Durchlauf stark variieren und mit den folgenden DurchlĂ€ufen, da sich die Caches und das Thin-Provisioning fĂŒllen, sowie abhĂ€ngig davon, ob das System die bei der vorherigen AusfĂŒhrung gefĂŒllten Caches synchronisieren konnte.

Nebenbei empfehle ich, die Geschwindigkeit auf einem bereits gefĂŒllten Thin-Provisioned Volume zu messen, von dem gerade ein Snapshot erstellt wurde. Der Autor hatte die Möglichkeit zu beobachten, wie sich zufĂ€llige SchreibvorgĂ€nge direkt nach der Erstellung des ersten Snapshots deutlich beschleunigen, insbesondere wenn der Cache noch nicht vollstĂ€ndig gefĂŒllt ist. Dies geschieht dank der Copy-on-Write-Semantik, der Blockausrichtung von Cache und Thin Volume und der Tatsache, dass zufĂ€lliges Schreiben auf RAID 6 in zufĂ€lliges Lesen auf RAID 6 umschlĂ€gt, gefolgt von einer Schreiboperation in den Cache. In unserer Konfiguration ist zufĂ€lliges Lesen mit RAID 6 bis zu 6-mal (Anzahl der SATA SSDs im Array) schneller als Schreiben. Da die Blöcke fĂŒr CoW sequenziell aus dem Thin-Pool zugewiesen werden, wird das Schreiben grĂ¶ĂŸtenteils ebenfalls in sequentielles Schreiben umgewandelt.

Beide diese Eigenschaften können vorteilhaft genutzt werden.

Cache-„kohĂ€rente“ Snapshots

Um das Risiko von Datenverlusten im Falle einer BeschĂ€digung oder eines Verlustes des Caches zu verringern, schlĂ€gt der Autor vor, eine Praxis der Snapshot-Rotation einzufĂŒhren, die deren IntegritĂ€t in diesem Fall gewĂ€hrleistet.

Erstens, da die Metadaten der Thin Volumes auf einem nicht-cached GerÀt gespeichert sind, bleiben die Metadaten integer, und potenzielle Verluste werden innerhalb der Dateneinheiten isoliert.

Der nÀchste Rotationszyklus der Snapshots garantiert die DatenintegritÀt innerhalb der Snapshots im Falle eines Cacheverlustes:

  1. FĂŒr jedes Thin Volume mit dem Namen erstellen wir einen Snapshot mit dem Namen .cached
  2. Wir setzen den Migration Threshold auf einen vernĂŒnftig hohen Wert: #lvchange --quiet --cachesettings "migration_threshold=16384" cache/cachedata
  3. Im Zyklus ĂŒberprĂŒfen wir die Anzahl der schmutzigen Blöcke im Cache: #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' Wir warten, bis wir Null erhalten. Wenn es zu lange dauert, um Null zu erreichen, kann temporĂ€r der Cache in den Writethrough-Modus versetzt werden. Allerdings, angesichts der Geschwindigkeitseigenschaften unserer SATA- und NVMe-SSD-Arrays sowie deren TBW-Ressourcen, werden Sie entweder schnell genug den Moment erfassen können, ohne den Cache-Modus zu Ă€ndern, oder Ihre Hardware wird ihren gesamten Ressourcen innerhalb weniger Tage verbrauchen. Aufgrund der RessourcenbeschrĂ€nkungen ist das System grundsĂ€tzlich nicht in der Lage, stĂ€ndig unter 100% Schreiblast zu laufen. Unsere NVMe-SSDs werden bei 100% Schreiblast ihre Ressourcen vollstĂ€ndig aufbrauchen in 3-4 Tagen. SATA-SSDs leben nur etwa doppelt so lange. Daher gehen wir davon aus, dass der Großteil der Last auf dem Lesen liegt, wĂ€hrend wir beim Schreiben relativ kurze, extrem hohe AktivitĂ€tsanstiege in Kombination mit einer geringen durchschnittlichen Last haben.
  4. Sobald wir Null erreicht haben (oder erstellt haben), benennen wir <name>.cached in <name>.committed um. Die alte <name>.committed wird gelöscht.
  5. Optional, wenn der Cache zu 100% gefĂŒllt ist, kann er mit einem Skript recreated werden, um ihn somit zu leeren. Mit einem halb leeren Cache funktioniert das System deutlich schneller beim Schreiben.
  6. Wir setzen die Migration Threshold auf Null: #lvchange --quiet --cachesettings "migration_threshold=0" cache/cachedata Dies wird vorĂŒbergehend das Synchronisieren des Caches auf das primĂ€re Medium verhindern.
  7. Wir warten, bis sich genug Änderungen im Cache angesammelt haben #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' oder der Timer ausgelöst wird.
  8. Wiederholen wir das Ganze.

Warum die KomplexitĂ€t mit dem Migration Threshold
?Die Sache ist, dass in der Praxis ‚zufĂ€lliges‘ Schreiben tatsĂ€chlich nicht ganz zufĂ€llig ist. Wenn wir etwas in einen Sektor von 4 Kilobyte schreiben, besteht eine hohe Wahrscheinlichkeit, dass in den nĂ€chsten paar Minuten in diesen oder einen der benachbarten (+- 32K) Sektoren ebenfalls geschrieben wird.

Indem wir die Migration Threshold auf Null setzen, verzögern wir das Synchronisieren des Schreibens auf die SATA-SSDs und aggregieren mehrere Änderungen eines 64K-Blocks im Cache. Dadurch wird die Ressource der SATA-SSDs deutlich gespart.

Und wo ist der Code..?Leider hĂ€lt der Autor sich fĂŒr nicht kompetent genug in Bezug auf die Entwicklung von Bash-Skripten, da er 100% autodidaktisch ist und ‚google‘-getriebenes Development praktiziert, weshalb er denkt, dass der schreckliche Code, der aus seinen HĂ€nden kommt, von niemandem sonst verwendet werden sollte.

Ich denke, die Fachleute in diesem Bereich können die oben beschriebene Logik bei Bedarf selbst umsetzen und vielleicht sogar ansprechend als systemd-Service gestalten, wie es der Autor versucht hat.

Ein solches einfaches Schema zur Rotation von Snapshots ermöglicht es uns, dauerhaft einen vollstĂ€ndig synchronisierten Snapshot auf einem SATA-SSD zu haben. DarĂŒber hinaus können wir mit dem Tool thin_delta herausfinden, welche Blöcke nach seiner Erstellung geĂ€ndert wurden, und somit BeschĂ€digungen auf den Hauptvolumen lokalisieren, was die Wiederherstellung erheblich vereinfacht.

TRIM/DISCARD in libvirt/KVM

Da der Datenspeicher fĂŒr KVM unter libvirt verwendet wird, wĂ€re es vorteilhaft, unsere VMs so zu konfigurieren, dass sie nicht nur den kostenlosen Speicherplatz belegen, sondern auch den bereits nicht mehr benötigten Speicher wieder freigeben.

Dies erfolgt durch Emulation der TRIM/DISCARD-UnterstĂŒtzung auf virtuellen Festplatten. Dazu muss der Controller-Typ auf virtio-scsi geĂ€ndert und die XML bearbeitet werden.

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


</disk>

<controller type='scsi' index='0' model='virtio-scsi'>
<alias name='scsi0'/>


</controller>

Solche DISCARDs von Gast-Betriebssystemen werden korrekt von LVM verarbeitet, und die Blöcke werden sowohl im Cache als auch im Thin-Pool korrekt freigegeben. In unserem Fall geschieht dies hauptsÀchlich verzögert beim Löschen des nÀchsten Snapshots.

BTRFS-Backup

Verwenden Sie fertige Skripte mit Ă€ußerster Vorsicht und auf eigene Gefahr. Der Autor hat diesen Code selbst und ausschließlich fĂŒr sich selbst geschrieben. Ich bin mir sicher, dass viele erfahrene Linux-Nutzer Ă€hnliche Workarounds haben und es nicht nötig ist, die von anderen zu kopieren.

Lassen Sie uns ein Volume auf dem SicherungsgerÀt erstellen:

#lvcreate -L 256G --name backup backup

Formatieren wir in BTRFS:

#mkfs.btrfs /dev/backup/backup

Erstellen wir die Mount-Punkte und mounten die Root-Subvolumes des FS:

#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

Erstellen wir Verzeichnisse fĂŒr die Backups:

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

Erstellen wir ein Verzeichnis fĂŒr die Backup-Skripte:

#mkdir /root/btrfs-backup

Kopieren wir das Skript:

Viele furchterregende bash-Codes. Auf eigenes Risiko verwenden. Wenden Sie sich nicht mit Wut an den Autor...#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 "Warte auf Sperre..."
wait_lock || terminate "Sperre konnte nicht erhalten werden. Beende..."
echo "Sperre erhalten..."
}

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 gefunden"
else
echo "$SOURCE_BASE_PATH Datei nicht gefunden, erstelle Snapshot von $SOURCE_PATH zu $SOURCE_BASE_PATH"
btrfs subvolume snapshot -r $SOURCE_PATH $SOURCE_BASE_PATH
sync
if [ -d "$TARGET_BASE_PATH" ]
then
echo "$TARGET_BASE_PATH gefunden, nicht synchron mit Quelle... wird entfernt..."
btrfs subvolume delete -c $TARGET_BASE_PATH
sync
fi
fi
if [ -d "$TARGET_BASE_PATH" ]
then
echo "$TARGET_BASE_PATH gefunden"
else
echo "$TARGET_BASE_PATH nicht gefunden. Synchronisiere zu $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 gefunden, wird entfernt..."
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 gefunden, wird entfernt..."
btrfs subvolume delete -c $TARGET_PEND_PATH
sync
fi
echo "Sende $SOURCE_PEND_PATH zu $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 "Hilfe"
;;
"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 "Keine.."
;;
esac
) 98>$LOCK_FILE

EOF

Was macht es ĂŒberhaupt..?EnthĂ€lt eine Reihe einfacher Befehle zur Erstellung von BTRFS-Snapshots und deren Übertragung auf ein anderes Dateisystem ĂŒber BTRFS send/receive.

Der erste Start kann relativ lange dauern, da zunĂ€chst alle Daten kopiert werden. Weitere Starts werden sehr schnell sein, da nur Änderungen kopiert werden.

Noch ein Skript, das wir in cron einfĂŒgen werden:

Noch ein wenig Bash-Code#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 Tage"
$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

Was macht es..?Es erstellt und synchronisiert inkrementelle Snapshots der aufgefĂŒhrten BTRFS-Volumes auf das Backup-Dateisystem. Danach werden alle Snapshots gelöscht, die vor 60 Tagen erstellt wurden. Nach der AusfĂŒhrung erscheinen in den Unterverzeichnissen /backup/btrfs/back/remote/ datierte Snapshots der aufgelisteten Volumes.

Geben wir dem Code AusfĂŒhrungsrechte:

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

ÜberprĂŒfen wir und fĂŒgen es in cron ein:

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

LVM thin Backup

Erstellen wir ein dĂŒnnes Pool auf dem Backup-GerĂ€t:

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

Installieren wir ddrescue, da die Skripte dieses Werkzeug nutzen werden:

#apt-get install gddrescue

Erstellen wir ein Verzeichnis fĂŒr die Skripte:

#mkdir /root/lvm-thin-backup

Kopieren wir die Skripte:

Sehr viel Bash innen...#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 "Warte auf Sperre..."
wait_lock || terminate "Sperre konnte nicht erhalten werden. Beende..."
echo "Sperre erhalten..."
}

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 "Quell-LV ist nicht dĂŒnn.")
exit 1
fi

if [ "$DIFF_TARGET_POOL" == "" ]
then
(>&2 echo "Ziel-LV ist nicht dĂŒnn.")
exit 1
fi

if [ "$DIFF_SOURCE_POOL" != "$DIFF_TARGET_POOL" ]
then
(>&2 echo "Quell- und Ziel-LVs gehören zu unterschiedlichen dĂŒnnen 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 "DĂŒnnpool-Metadatensnapshot existiert bereits. Gehe von veraltetem aus. Werde Metadatensnapshot in 5 Sekunden freigeben.")
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 "Lediglich erstellen des dĂŒnnen Pool-Metadatensnapshots fehlgeschlagen.")
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 gefunden"
else
echo "$DISCARD_LV nicht in $DISCARD_VG gefunden"
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 gefunden"
else
echo "Quelle Basis nicht gefunden, erstelle Snapshot von $SOURCE_VG/$SOURCE_LV zu $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 "Verwerfe $SOURCE_BASE_LV_PATH, da wir bootstrappen mĂŒssen."
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 gefunden, nicht synchronisiert mit der Quelle... entferne..."
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 gefunden"
else
echo "$TARGET_VG/$TARGET_LV nicht gefunden. Erstelle leervolumen."
lvcreate --thin-pool "$BACKUPS_POOL" -V "$SOURCE_BASE_SIZE"B --name "$TARGET_BASE_LV" "$TARGET_VG" || exit 1
echo "Muss neu bootstrappen. Verwerfe Quelle bei $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 "Verwerfe Ziel bei $TARGET_BASE_LV_PATH"
discard_volume "$TARGET_VG" "$TARGET_BASE_LV"
sync
fi
if [ "$SOURCE_PEND_LV_PATH" != "" ]
then
echo "$SOURCE_PEND_LV_PATH gefunden, entferne..."
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 gefunden, entferne..."
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 "Synchronisiere $SOURCE_PEND_LV_PATH zu $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 gefunden"
else
echo "$SOURCE_BASE_LV_PATH nicht gefunden"
exit 1
fi
if [ "$TARGET_BASE_LV_PATH" != "" ]
then
echo "$TARGET_BASE_LV_PATH gefunden"
else
echo "$TARGET_BASE_LV_PATH nicht gefunden"
exit 1
fi
activate_volume "$TARGET_VG" "$TARGET_BASE_LV"
activate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
echo Vergleiche "$SOURCE_BASE_LV_PATH" mit "$TARGET_BASE_LV_PATH"
cmp "$SOURCE_BASE_LV_PATH" "$TARGET_BASE_LV_PATH"
echo Fertig...
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 gefunden"
else
echo "$SOURCE_BASE_LV_PATH nicht gefunden"
exit 1
fi
if [ "$TARGET_BASE_LV_PATH" != "" ]
then
echo "$TARGET_BASE_LV_PATH gefunden"
else
echo "$TARGET_BASE_LV_PATH nicht gefunden"
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 Syncronisiere "$SOURCE_BASE_LV_PATH" zu "$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 "Synchronisiere $SYNC_LENGTH_BYTES Bytes bei $SYNC_OFFSET_BYTES von $SOURCE_BASE_LV_PATH zu $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
fertig
echo Fertig...
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"
fertig < <(list "$1" "$2" | grep "$FILTER")

}

(
COMMAND="$1"
Shift

case "$COMMAND" in
"--help")
echo "Hilfe"
;;
"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 "Keine.."
;;
esac
) 98>$LOCK_FILE

EOF

Was macht es
?EnthĂ€lt eine Sammlung von Befehlen zur Manipulation von schweren Snapshots und zur Synchronisation der Unterschiede zwischen zwei schweren Snapshots, die ĂŒber thin_delta auf ein anderes BlockgerĂ€t mithilfe von ddrescue und blkdiscard ĂŒbertragen wurden.

Ein weiteres Skript, das wir im Cron einfĂŒgen werden:

Ein weiteres bisschen 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 Tage"

$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

Was macht es
?Verwendet das vorherige Skript, um Backups der angegebenen dĂŒnnen Volumes zu erstellen und zu synchronisieren. Das Skript wird inaktive Snapshots der angegebenen Volumes hinterlassen, die benötigt werden, um die Änderungen seit der letzten Synchronisation zu verfolgen.

Dieses Skript muss bearbeitet werden, um die Liste der dĂŒnnen Volumes, fĂŒr die Backups erstellt werden sollen, anzugeben. Die genannten Namen dienen nur als Beispiel. Es kann ein Skript geschrieben werden, das alle Volumes synchronisiert.

Geben wir die Berechtigungen:

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

ÜberprĂŒfen wir und fĂŒgen es in cron ein:

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

Der erste Start wird lange dauern, da die dĂŒnnen Volumes durch die Kopie des gesamten verwendeten Speicherplatzes vollstĂ€ndig synchronisiert werden. Dank der Metadaten von LVM thin wissen wir, welche Blöcke tatsĂ€chlich verwendet werden, sodass nur die tatsĂ€chlich verwendeten Blöcke der dĂŒnnen Volumes kopiert werden.

Die nachfolgenden Starts werden die Daten inkrementell kopieren, dank der Änderungsverfolgung ĂŒber die Metadaten von LVM thin.

Schauen wir uns an, was herausgekommen ist:

#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 MĂ€r 26 09:11 .
drwxr-xr-x 1 root root 16 MĂ€r 6 09:30 ..
drwxr-xr-x 1 root root 322 MĂ€r 26 02:00 .@base
drwxr-xr-x 1 root root 516 MĂ€r 6 09:39 .@snap.2020-03-06-09-39-37
drwxr-xr-x 1 root root 516 MĂ€r 6 09:39 .@snap.2020-03-06-09-39-57
...
/backup/btrfs/back/remote/root:
total 0
drwxr-xr-x 1 root root 2820 MĂ€r 26 09:11 .
drwxr-xr-x 1 root root 16 MĂ€r 6 09:30 ..
drwxr-xr-x 1 root root 240 MĂ€r 26 09:11 @.@base
drwxr-xr-x 1 root root 22 MĂ€r 26 09:11 @home.@base
drwxr-xr-x 1 root root 22 MĂ€r 6 09:39 @home.@snap.2020-03-06-09-39-35
drwxr-xr-x 1 root root 22 MĂ€r 6 09:39 @home.@snap.2020-03-06-09-39-57
...
drwxr-xr-x 1 root root 240 MĂ€r 6 09:39 @.@snap.2020-03-06-09-39-26
drwxr-xr-x 1 root root 240 MĂ€r 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

Was haben Matroschkas damit zu tun?

Wahrscheinlich damit, dass logische LVM-Volumes physische LVM-PVs fĂŒr andere VGs sein können. LVM kann rekursiv sein, Ă€hnlich wie Matroschkas. Das verleiht LVM außergewöhnliche FlexibilitĂ€t.

P.S.

Im nĂ€chsten Artikel werden wir versuchen, mehrere Ă€hnliche mobile SAN/KVM als Basis fĂŒr die Schaffung eines geo-verteilten Storage/VM-Clusters mit Redundanz auf mehreren Kontinenten unter Verwendung von Heimdesktop-PCs, Heim-Internet und P2P-Netzwerken zu verwenden.

Quelle: habr.com

60GB SSD 8Gb DDR4