Was haben LVM und eine Matroschka gemeinsam?

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

Das Programm umfasst:

  • 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 einem allgemeinen Plattenmix.
  • Installation des Systems auf NVMe RAID 1 bei fehlender 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 BTRFS-Ă€hnliche Backups.

Wenn Sie interessiert sind, klicken Sie bitte hier.

Haftungsausschluss

Der Autor ĂŒbernimmt keine Verantwortung fĂŒr die Folgen der Verwendung oder Nicht-Verwendung von Materialien/Beispielen/Codes/RatschlĂ€gen/Daten aus diesem Artikel. Durch das Lesen oder die Verwendung dieses Materials ĂŒbernehmen Sie die Verantwortung fĂŒr alle Folgen dieser Handlungen. Zu den möglichen Konsequenzen gehören:

  • Bis zur Knusprigkeit gebratene NVMe SSD.
  • Völlig erschöpfter Schreibressourcen und Ausfall von SSDs.
  • VollstĂ€ndiger Datenverlust auf allen Speichermedien, einschließlich der Backups.
  • Defekte Computerhardware.
  • Verlorene Zeit, Nerven und Geld.
  • Alle anderen Folgen, die oben nicht aufgefĂŒhrt sind.

Hardware

VerfĂŒgbar war:

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

  • Prozessor mit 4 Kernen und 8 Threads
  • 32 Gigabyte DDR3-Arbeitsspeicher
  • 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, geflasht in den IT / HBA-Modus. Die RAID-fÀhige Firmware wurde absichtlich gegen HBA-Firmware ausgetauscht, um:

  1. Den Adapter jederzeit ersetzen zu können, ohne auf ein bestimmtes Modell angewiesen zu sein.
  2. TRIM/Discard funktionierte normal auf den Laufwerken, da diese Befehle in der RAID-Firmware ĂŒberhaupt nicht unterstĂŒtzt werden, wĂ€hrend HBA im Allgemeinen egal ist, welche Befehle ĂŒber den Bus gesendet werden.

Festplatten — 8 StĂŒck HGST Travelstar 7K1000 mit einem Volumen von 1 TB im 2,5-Zoll-Format, wie sie fĂŒr Laptops verwendet werden. Diese Platten waren zuvor in einem RAID 6-Verbund. In dem neuen System finden sie ebenfalls Verwendung fĂŒr die Speicherung lokaler Backups.

ZusĂ€tzlich wurde hinzugefĂŒgt:

6 StĂŒck SATA SSDs vom Modell Samsung 860 QVO 2TB. Diese SSDs benötigten großen Speicherplatz, SLC-Cache, eine gewisse ZuverlĂ€ssigkeit und einen gĂŒnstigen Preis. Die UnterstĂŒtzung von Discard/Zero war unbedingt erforderlich, was durch eine Zeile im dmesg ĂŒberprĂŒft wird:

kernel: ata1.00: Discard_zeroes_data aktivieren

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

FĂŒr diese SSDs ist die Geschwindigkeit beim zufĂ€lligen Lesen/Schreiben und die Lebensdauer fĂŒr Ihre BedĂŒrfnisse wichtig. Ein KĂŒhler ist ein Muss. Absolut nötig. Andernfalls werden sie bei der ersten RAID-Synchronisation knusprig gebraten.

StarTech PEX8M2E2 Adapter fĂŒr 2 x NVMe SSDs zur Installation in einen PCIe 3.0 8x Slot. Dies ist wiederum einfach ein HBA, aber fĂŒr NVMe. Er unterscheidet sich von preiswerten Adaptern durch das Fehlen der Anforderung einer PCIe-Bifurkation vom Motherboard, dank eines integrierten PCIe-Switches. Er funktioniert sogar in den Ă€ltesten Systemen mit PCIe, selbst wenn es sich um einen x1 PCIe 1.0 Slot handelt. NatĂŒrlich mit der entsprechenden Geschwindigkeit. Es gibt keine RAIDs dort. Einen integrierten BIOS gibt es nicht. Ihre Systeme werden also nicht magisch lernen, von NVMe zu booten, geschweige denn NVMe-RAIDs zu erstellen, dank dieses GerĂ€ts.

Dieses Komponente wurde ausschließlich durch die VerfĂŒgbarkeit eines einzigen freien 8x PCIe 3.0 in der Systemstruktur beeinflusst. Bei Vorhandensein von zwei freien SteckplĂ€tzen lĂ€sst es sich problemlos durch zwei gĂŒnstige PEX4M2E1 oder Ă€hnliche Komponenten ersetzen, die ĂŒberall ab einem Preis von 600 Rubel erhĂ€ltlich sind.

Die bewusste Entscheidung, auf jegliche Hardware- oder im Chipsatz/BIOS integrierte RAID-Lösungen zu verzichten, wurde getroffen, um die vollstÀndige Ersetzbarkeit des gesamten Systems zu gewÀhrleisten, mit Ausnahme der SSDs/HDDs, wobei alle Daten erhalten bleiben. Idealerweise sollte sogar das installierte Betriebssystem beim Umstieg auf völlig neue/andere Hardware beibehalten werden können. Wichtig sind nur SATA- und PCIe-Ports. Es ist wie ein Live-CD oder ein bootfÀhiger USB-Stick, jedoch sehr schnell und etwas volumetrisch.

HumorSie wissen ja, wie das manchmal ist – manchmal muss man den gesamten Speicher dringend mitnehmen. Und Daten möchte man nicht verlieren. Deshalb sind alle genannten Speicher bequem in Schienen in den 5.25-Slots eines StandardgehĂ€uses untergebracht.

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

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

Software

FrĂŒher wurde auf der Hardware Debian 8 Jessie installiert, die kurz vor dem EOL steht. Ein RAID 6 wurde aus den oben genannten HDDs in Kombination mit LVM erstellt. Darauf liefen virtuelle Maschinen in kvm/libvirt.

Da der Autor ĂŒber entsprechende Erfahrungen im Erstellen 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 stabilisiert ist, aber immer noch drei Jahre UnterstĂŒtzung in Aussicht hat.

In dem genannten System sind alle erforderlichen Hardwaretreiber bereits standardmĂ€ĂŸig enthalten. Es sind keine zusĂ€tzlichen Software- oder Treiberlösungen erforderlich.

Vorbereitung zur Installation

FĂŒr die Installation des Systems benötigen wir das Ubuntu Desktop-Image. Das Installationsprogramm des Server-Systems ist ĂŒbermĂ€ĂŸig unabhĂ€ngig und fĂŒgt immer einen UEFI-Systembereich auf einer der Festplatten hinzu, was das gesamte Setup stört. Daher kann es nur im UEFI-Modus installiert werden. Es werden keine Alternativen angeboten.

Das ist fĂŒr uns inakzeptabel.

Warum?Leider ist UEFI-Boot extrem schlecht mit softwarebasiertem RAID kompatibel, da niemand Backup-Optionen fĂŒr die UEFI ESP-Partition anbietet. Im Internet finden sich Lösungen, die vorschlagen, die ESP-Partition auf einem USB-Stick abzulegen, aber das ist ein Fehlerpunkt. Es gibt Lösungen, die ein softwarebasiertes mdadm RAID 1 mit Metadaten der Version 0.9 nutzen, was das UEFI-BIOS nicht daran hindert, diese Partition zu erkennen, jedoch funktioniert dies nur bis zu dem Moment, an dem das BIOS oder ein anderes Betriebssystem etwas in die ESP-Partition schreibt, ohne sie mit den anderen Spiegeln zu synchronisieren.

DarĂŒber hinaus hĂ€ngt der UEFI-Boot von NVRAM ab, das nicht mit den Festplatten in ein neues System umgezogen werden kann, da es Teil des Motherboards ist.

Wir werden also kein neues Rad erfinden. Wir haben bereits ein bewÀhrtes, altbewÀhrtes Rad, das mittlerweile als Legacy/BIOS-Boot bekannt ist und den stolzen Namen CSM auf UEFI-kompatiblen Systemen trÀgt. Wir nehmen es einfach vom Regal, schmieren es, pumpen die Reifen auf und wischen es mit einem feuchten Tuch ab.

Die Desktop-Version von Ubuntu kann ebenfalls nicht ordentlich mit dem Legacy-Boot installiert werden, aber hier gibt es, wie man so schön sagt, immerhin Optionen.

Also, wir stellen die Hardware zusammen und laden das System von einem bootfĂ€higen Ubuntu Live-USB. Wir mĂŒssen Pakete herunterladen, daher richten wir das Netzwerk ein, das bei Ihnen funktioniert. Falls es nicht funktioniert, können die benötigten Pakete im Voraus auf den USB-Stick geladen werden.

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

#sudo bash

Wie
?Die oben stehende Zeile ist ein kanonischer Trigger fĂŒr die Debatten ĂŒber sudo. MitgrĂ¶ĂŸtenden grĂ¶ĂŸeren Möglichkeiten kommt auch einegrĂ¶ĂŸtengrĂ¶ĂŸere Verantwortung. Die Frage ist, ob Sie bereit sind, diese zu ĂŒbernehmen. Viele glauben, dass die Verwendung von sudo auf diese Weise zumindest nicht vorsichtig 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 Grunde die Hardware, um diese Software auszufĂŒhren.
Wenn wir dieser Software die Sicherheit unserer Daten anvertrauen, nehmen wir einen Kredit in Höhe der Kosten fĂŒr die Wiederherstellung dieser Daten auf, den wir irgendwann zurĂŒckzahlen mĂŒssen.

Aus dieser Perspektive ist ZFS wie ein Ferrari, wÀhrend mdadm+lvm mehr wie ein Fahrrad ist.

Subjektiv zieht der Autor es vor, ein geleastes Fahrrad unbekannten Personen zu leihen, anstatt einen Ferrari zu fahren. Der Preis ist dabei nicht hoch. Es sind keine FĂŒhrerscheine erforderlich. Die Verkehrsregeln sind einfacher. ParkplĂ€tze sind kostenlos. Die ZugĂ€nglichkeit ist besser. Man kann dem Fahrrad immer FĂŒĂŸe anbringen, und es lĂ€sst sich problemlos selbst reparieren.

Warum also BTRFS
?Um ein Betriebssystem zu booten, benötigen wir ein Dateisystem, das von Legacy/BIOS GRUB standardmĂ€ĂŸig unterstĂŒtzt wird und gleichzeitig die Möglichkeit bietet, Snapshots in Echtzeit zu erstellen. Wir werden es fĂŒr die /boot-Partition verwenden. DarĂŒber hinaus zieht der Autor vor, dieses FS fĂŒr / (Root) zu nutzen und weist darauf hin, dass fĂŒr jede andere Software separate Partitionen auf LVM erstellt und in die benötigten Verzeichnisse eingebunden werden können.

Weder Images virtuellen Maschinen, noch Datenbanken werden wir auf diesem FS speichern.
Dieses FS wird ausschließlich zur Erstellung von sofortigen System-Snapshots verwendet, ohne dass das System heruntergefahren wird, gefolgt von der Übertragung dieser Snapshots auf ein Backup-Laufwerk mittels send/receive.

DarĂŒber hinaus bevorzugt der Autor, die Software auf der Hardware auf ein Minimum zu beschrĂ€nken und die gesamte andere Software in virtuellen Maschinen auszufĂŒhren, wobei er Techniken wie die GPU- und PCI-USB-Host-Controller-Passthrough in KVM ĂŒber IOMMU verwendet.

Auf der Hardware verbleiben lediglich - die Datenspeicherung, Virtualisierung und Datensicherung.

Wenn Sie ZFS mehr vertrauen, sind sie fĂŒr den angegebenen Anwendungsfall grundsĂ€tzlich austauschbar.

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

Als zusÀtzliches Argument hat BTRFS die FÀhigkeit, zufÀllige SchreibvorgÀnge in sequenzielle umzuwandeln, was sich positiv auf die Geschwindigkeit der Synchronisierung von Snapshots / Backups auf HDDs auswirkt.

Scannen wir alle GerÀte erneut:

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

Lassen Sie uns umsehen:

#lsscsi && nvme list
[0:0:0:0] Laufwerk ATA Samsung SSD 860 2B6Q /dev/sda
[1:0:0:0] Laufwerk ATA Samsung SSD 860 2B6Q /dev/sdb
[2:0:0:0] Laufwerk ATA Samsung SSD 860 2B6Q /dev/sdc
[3:0:0:0] Laufwerk ATA Samsung SSD 860 2B6Q /dev/sdd
[4:0:0:0] Laufwerk ATA Samsung SSD 860 2B6Q /dev/sde
[5:0:0:0] Laufwerk ATA Samsung SSD 860 2B6Q /dev/sdf
[6:0:0:0] Laufwerk ATA HGST HTS721010A9 A3J0 /dev/sdg
[6:0:1:0] Laufwerk ATA HGST HTS721010A9 A3J0 /dev/sdh
[6:0:2:0] Laufwerk ATA HGST HTS721010A9 A3J0 /dev/sdi
[6:0:3:0] Laufwerk ATA HGST HTS721010A9 A3B0 /dev/sdj
[6:0:4:0] Laufwerk ATA HGST HTS721010A9 A3B0 /dev/sdk
[6:0:5:0] Laufwerk ATA HGST HTS721010A9 A3B0 /dev/sdl
[6:0:6:0] Laufwerk ATA HGST HTS721010A9 A3J0 /dev/sdm
[6:0:7:0] Laufwerk 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 „Festplatten“

NVMe SSD

Wir werden sie jedoch nicht partitionieren. Unser BIOS erkennt diese SpeichergerĂ€te ohnehin nicht. Daher werden sie komplett in ein Software-RAID integriert. Wir werden dort nicht einmal Partitionen erstellen. Wenn man es nach dem „Kanon“ oder „grundsĂ€tzlich“ möchte – erstellen Sie eine große Partition, wie bei HDDs.

SATA HDD

Hier muss nichts Besonders erfunden werden. Wir werden einen Abschnitt fĂŒr alles erstellen. Wir erstellen diesen Abschnitt, weil das BIOS diese Laufwerke erkennt und sogar versuchen kann, davon zu booten. SpĂ€ter werden wir sogar GRUB auf diesen Laufwerken installieren, damit das System unerwartet 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 ist es am interessantesten.

ZunĂ€chst haben unsere Laufwerke eine GrĂ¶ĂŸe von 2 TB. Dies liegt im zulĂ€ssigen Bereich fĂŒr MBR, was wir nutzen werden. Bei Bedarf kann auf GPT umgestiegen werden. GPT-Laufwerke haben eine KompatibilitĂ€tsschicht, die es MBR-kompatiblen Systemen ermöglicht, die ersten 4 Partitionen zu sehen, wenn sie innerhalb der ersten 2 Terabyte angeordnet sind. Wichtig ist, dass die Bootpartition und die Partition bios_grub an den Anfang dieser Laufwerke gesetzt werden. Dies ermöglicht sogar das Booten von GPT-Laufwerken im Legacy/Bios-Modus.

Das ist jedoch 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 ganzen restlichen Speicherplatz einnehmen, mit Ausnahme eines kleinen nicht zugewiesenen Bereichs am Ende des Laufwerks.

Was ist dieser nicht zugewiesene Bereich?Laut online Quellen verfĂŒgen unsere SATA SSDs ĂŒber einen dynamisch erweiterbaren SLC-Cache von 6 bis 78 Gigabyte. Die ersten 6 Gigabyte erhalten wir "kostenlos" durch die Differenz zwischen "Gigabyte" und "Gibibyte" im technischen Datenblatt des Speichers. Die restlichen 72 Gigabyte werden durch ungenutzten Speicherplatz bereitgestellt.

Hier sollte angemerkt werden, dass unser Cache SLC ist, wĂ€hrend der Speicher im 4-Bit-MLC-Modus belegt ist. FĂŒr uns bedeutet das effektiv, dass wir fĂŒr jede 4 Gigabyte freien Speicher nur 1 Gigabyte SLC-Cache erhalten.

Wir multiplizieren 72 Gigabyte mit 4 und erhalten 288 Gigabyte. Das ist der freie Speicherplatz, den wir nicht einteilen werden, um den Speichern die volle Nutzung des SLC-Caches zu ermöglichen.

Somit erhalten wir insgesamt bis zu 312 Gigabyte SLC-Cache aus sechs Speicherlaufwerken. Von allen Laufwerken werden 2 im RAID fĂŒr Redundanz eingesetzt.

Eine derartige Cache-GrĂ¶ĂŸe sorgt dafĂŒr, dass wir in der Praxis selten mit der Situation konfrontiert werden, in der die Daten nicht im Cache geschrieben werden. Das kompensiert hervorragend den grĂ¶ĂŸten Nachteil von QLC-Speicher, nĂ€mlich die extrem niedrige Schreibgeschwindigkeit, wenn Daten um den Cache herum geschrieben werden. Wenn Ihre Lasten dies nicht unterstĂŒtzen, empfehle ich Ihnen, ernsthaft darĂŒber nachzudenken, wie lange Ihre SSDs unter solch einer Belastung angesichts des TBW aus dem technischen 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

Arrays erstellen

ZunĂ€chst mĂŒssen wir den Rechner umbenennen. Das ist notwendig, weil der Hostname Teil des Namens des Arrays ist, irgendwo in mdadm, und auf etwas Einfluss hat. Arrays können zwar spĂ€ter umbenannt werden, aber das ist zusĂ€tzlicher Aufwand.

#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 das zulĂ€ssig. Alles kann auch ohne Initialisierung funktionieren, wenn es sich um ein neues Array handelt. DarĂŒber hinaus ist die Initialisierung eines SSD-Arrays bei der Erstellung eine reine Verschwendung von TBW-Ressourcen. Wir verwenden TRIM/DISCARD, wo immer möglich, auf den zusammengebauten SSD-Arrays, um sie „zu initialisieren“.

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

Bei SSD RAID 6 muss DISCARD in den Modulen der Kernel-Parameter aktiviert werden.

Dies sollte nur geschehen, wenn alle SSDs, die in den Level-4/5/6-Arrays dieses Systems verwendet werden, eine funktionierende UnterstĂŒtzung fĂŒr discard_zeroes_data haben. Manchmal gibt es seltsame Speicher, die dem Kernel die UnterstĂŒtzung dieser Funktion melden, aber tatsĂ€chlich ist sie nicht vorhanden oder funktioniert nicht immer. Derzeit ist die UnterstĂŒtzung nahezu ĂŒberall verfĂŒgbar, jedoch treten bei Ă€lteren Speichern und fehlerhaften Firmware-Versionen Probleme auf. Aus diesem Grund ist die DISCARD-UnterstĂŒtzung standardmĂ€ĂŸig fĂŒr RAID 6 deaktiviert.

Achtung, der folgende Befehl wird alle Daten auf NVMe-Laufwerken „initialisieren“ und sie mit „Nullen“ ĂŒberschreiben.

#blkdiscard /dev/md0

Wenn etwas schiefgeht, versuchen Sie, den 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-size wirkt sich positiv auf die Geschwindigkeit des zufĂ€lligen Lesens von Blöcken bis zur chunk-size aus. Dies geschieht, weil eine Operation der entsprechenden GrĂ¶ĂŸe oder kleiner vollstĂ€ndig auf einem GerĂ€t abgeschlossen werden kann. Daher werden die IOPS aller GerĂ€te summiert. Statistisch gesehen ĂŒberschreiten 99 % der IOs nicht 512K.

Bei RAID 6 sind die IOPS beim Schreiben immer weniger oder gleich IOPS eines einzelnen Speichermediums. Umgekehrt kann die IOPS bei zufĂ€lligem Lesen mehrere Male höher sein als bei einem einzelnen Medium, wobei die BlockgrĂ¶ĂŸe entscheidend ist.
Der Autor sieht keinen Sinn darin, einen Parameter zu optimieren, der bei RAID 6 von Grund auf schlecht ist, und optimiert stattdessen das, was RAID 6 gut kann.
Die schlechten zufÀlligen SchreibvorgÀnge von RAID 6 werden wir mit NVMe-Cache und Tricks mit Thin-Provisioning ausgleichen.

Wir haben DISCARD fĂŒr RAID 6 vorerst nicht aktiviert. Daher werden wir dieses Array vorerst nicht "initialisieren". Das holen wir spĂ€ter nach, nach der Betriebssysteminstallation.

SATA HDD

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

LVM auf NVMe RAID

FĂŒr die Geschwindigkeit möchten wir das Root-Dateisystem auf NVMe RAID 1 platzieren, das /dev/md0 ist.
Dennoch brauchen wir dieses schnelle Array auch fĂŒr andere Zwecke, wie Swap, Metadaten und LVM-Cache sowie LVM-thin-Metadaten. Daher werden wir auf diesem Array ein LVM VG erstellen.

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

Erstellen wir eine Partition fĂŒr das Root-Dateisystem.

#lvcreate -L 128G --name root root

Erstellen wir eine Partition fĂŒr den Swap in der GrĂ¶ĂŸe des Arbeitsspeichers.

#lvcreate -L 32G --name swap root

Betriebssysteminstallation

Zusammenfassend haben wir alles Notwendige, um das System zu installieren.

Wir starten den Installationsassistenten aus der Ubuntu Live-Umgebung. Standardinstallation. Nur im Schritt der Auswahl der Installationslaufwerke muss Folgendes angegeben werden:

  • /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), — ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČать ĐșаĐș разЎДл ĐżĐŸĐŽĐșачĐșĐž
  • Bootloader auf /dev/sda installieren

Wenn Sie BTRFS als Root-Dateisystem wĂ€hlen, erstellt der Installer automatisch zwei BTRFS-Volumes mit den Namen "@" fĂŒr / (Root) und "@home" fĂŒr /home.

Wir starten die Installation


Die Installation wird mit einem modalen Dialogfenster abgeschlossen, das ĂŒber einen Fehler im Bootloader informiert. Leider können Sie diesen Dialog nicht mit den Standardmitteln verlassen und die Installation fortsetzen. Melden Sie sich aus dem System ab und wieder an, um auf den frischen Desktop von Ubuntu Live zu gelangen. Öffnen Sie das Terminal und erneut:

#sudo bash

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

#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

Lassen Sie uns das Netzwerk und den Hostnamen im chroot konfigurieren:

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

Zugriff auf die chroot-Umgebung:

#chroot /mnt/chroot

Als Erstes installieren wir die Pakete:

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

Wir ĂŒberprĂŒfen und beheben alle Pakete, die aufgrund einer unvollstĂ€ndigen Systeminstallation fehlerhaft installiert wurden:

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

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

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

#cat >/etc/modprobe.d/raid456.conf << EOF
Optionen raid456 GerÀte_sicher_entfernen=1
EOF

Lassen Sie uns unsere Arrays etwas optimieren:

#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

Was war das..?Wir haben eine Reihe von udev-Regeln erstellt, die Folgendes bewirken werden:

  • Einen angemessenen Cache-GrĂ¶ĂŸenwert fĂŒr RAID 6 im Jahr 2020 festlegen. Der Standardwert scheint seit der EinfĂŒhrung von Linux unverĂ€ndert zu sein und ist mittlerweile nicht mehr zeitgemĂ€ĂŸ.
  • Mindestens I/O wĂ€hrend der ÜberprĂŒfungen/Synchronisierungen der Arrays reservieren. Dies ist notwendig, damit Ihre Arrays nicht in einem Zustand ewiger Synchronisierung unter Last stecken bleiben.
  • Maximal I/O wĂ€hrend der ÜberprĂŒfungen/Synchronisierungen der Arrays begrenzen. Dies ist wichtig, damit die Synchronisation/ÜberprĂŒfung von SSD-RAIDs Ihre SpeichergerĂ€te nicht ĂŒberhitzt. Besonders relevant fĂŒr NVMe. (Erinnern Sie sich an den KĂŒhler? Ich habe nicht gewitzelt.)
  • Verhindern Sie durch APM, dass Festplatten den Spindelbetrieb (HDD) einstellen, und setzen Sie einen Timeout von 7 Stunden fĂŒr die Festplattenspeichercontroller. APM kann vollstĂ€ndig deaktiviert werden, wenn Ihre Festplatten dies unterstĂŒtzen (-B 255). Mit dem Standardwert stoppen die Festplatten nach fĂŒnf Sekunden. Dann möchte das Betriebssystem den Festplattencache zurĂŒcksetzen, die Festplatten drehen sich wieder und das Ganze beginnt von vorne. Festplatten haben eine begrenzte Anzahl von Spindeldrehungen. Dieser einfache Zyklus kann im Standardmodus Ihre Festplatten innerhalb von ein paar Jahren leicht schĂ€digen. Nicht alle Festplatten sind davon betroffen, aber unsere „Laptop-Modelle“ mit den entsprechenden Standardeinstellungen, die RAID in eine krumme Mini-MAID-Ă€hnliche Konfiguration verwandeln, schon.
  • Setzen Sie das Readahead auf festplattenbasierten (rotierenden) Festplatten auf 1 Megabyte – zwei aufeinander folgende Blöcke/Chunks RAID 6.
  • Verhindern Sie Readahead auf den Arrays selbst.

Lassen Sie uns /etc/fstab bearbeiten:

#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 ĂŒber UUID suchen, da die Benennung der Arrays theoretisch variieren kann.

Die anderen Partitionen suchen wir ĂŒber die LVM-Namen in der Notation /dev/mapper/vg-lv, da sie die Partitionen ausreichend einzigartig identifizieren.

Verwenden Sie keine UUID fĂŒr LVM, da UUIDs von LVM-Volumes und deren Snapshots ĂŒbereinstimmen können.Mounten wir /dev/mapper/root-root doppelt..?Ja. Genau so. Das Besondere an BTRFS ist, dass dieses Dateisystem mehrfach mit unterschiedlichen Subvolumes gemountet werden kann.

Aufgrund dieses Merkmals empfehle ich dringend, niemals LVM-Snapshots von aktiven BTRFS-Volumes zu erstellen. Dies könnte beim Neustart Überraschungen bringen.

Lass uns die mdadm-Konfiguration neu generieren:

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

Wir passen die LVM-Einstellungen 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

Was war das..?Wir haben die automatische Erweiterung der LVM thin-Pools aktiviert, wenn 90 % des Speicherplatzes belegt sind, um 5 % des Volumens zu erhöhen.

Wir haben die maximale Anzahl an Cache-Blöcken fĂŒr den LVM-Cache erhöht.

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

  • GerĂ€ten, die LVM-Cache (cdata) enthalten.
  • GerĂ€ten, die durch LVM-Cache umgangen werden (<lv_name>_corig). Das gecachte GerĂ€t selbst wird dennoch durch den Cache gescannt (einfach <lv_name>).
  • GerĂ€ten, die Metadaten des LVM-Caches (cmeta) enthalten.
  • Allen GerĂ€ten im VG mit dem Namen images. Hier werden wir Disk-Images von virtuellen Maschinen haben und wir wollen nicht, dass LVM auf dem Host Volumes aktiviert, die der Gast-OS gehören.
  • auf allen GerĂ€ten in VG mit dem Namen Backup. Hier haben wir die Sicherungskopien der Abbildungen virtueller Maschinen.
  • auf allen GerĂ€ten, deren Namen auf „gpv“ (guest physical volume) enden.

Wir haben die UnterstĂŒtzung von DISCARD beim Freigeben von Speicherplatz auf LVM VG aktiviert. Seien Sie vorsichtig. Dies kann die Löschung von LV auf SSD erheblich verlĂ€ngern. Besonders gilt dies fĂŒr SSD RAID 6. Nach dem Plan werden wir jedoch Thin Provisioning verwenden, sodass es uns nicht weiter stören wird.

Aktualisieren wir das initramfs-Image:

#update-initramfs -u -k all

Installieren und konfigurieren wir grub:

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

Welche Festplatten sollen gewÀ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 entfernt?Wegen ĂŒbermĂ€ĂŸiger SelbststĂ€ndigkeit und unruhigen HĂ€nden.

Es funktioniert nicht korrekt, wenn einer der RAID-Arrays im degradierenden Zustand ist. Es versucht, OS auf Partitionen zu suchen, die von virtuellen Maschinen verwendet werden, die auf dieser Hardware laufen.

Wenn Sie es benötigen, können Sie es behalten, aber beachten Sie alles, was oben erwÀhnt wurde. Ich empfehle, im Internet nach Rezepten zur Beseitigung unruhiger HÀnde zu suchen.

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

#exit
#reboot

WÀhlen Sie als Boot-GerÀt eine der SATA-SSDs aus.

LVM auf SATA SSD

Zu diesem Zeitpunkt sind wir bereits im neuen Betriebssystem gestartet, haben das Netzwerk, apt konfiguriert, das Terminal-Emulator geöffnet und gestartet:

#sudo bash

Lassen Sie uns fortfahren.

„Initialisieren“ Sie das Array aus SATA SSDs:

#blkdiscard /dev/md2

Wenn es nicht funktioniert hat, versuchen wir es mit:

#blkdiscard --step 65536 /dev/md2
Erstellen Sie eine LVM VG auf der SATA SSD:

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

Warum noch eine VG..?TatsÀchlich haben wir bereits eine VG mit dem Namen root. Warum alles nicht in eine VG packen?

Wenn es in der VG mehrere PV gibt, mĂŒssen alle PV fĂŒr eine korrekte Aktivierung der VG vorhanden sein (online). Eine Ausnahme bildet das LVM RAID, das wir absichtlich nicht verwenden.

Wir möchten sehr, dass das Betriebssystem beim Ausfall (d.h. Datenverlust) eines der RAID 6-Arrays normal startet und uns die Möglichkeit gibt, das Problem zu lösen.

DafĂŒr werden wir auf der ersten Abstraktionsebene jeden Typ des physischen „Speichers“ in eine separate VG isolieren.

Wissenschaftlich betrachtet gehören verschiedene RAID-Arrays zu unterschiedlichen „ZuverlĂ€ssigkeitsdomĂ€nen“. Es ist nicht ratsam, einen zusĂ€tzlichen gemeinsamen Ausfallpunkt zu schaffen, indem man alles in eine VG packt.

Die VerfĂŒgbarkeit von LVM auf der „Hardware“-Ebene ermöglicht es uns, verschiedene Raid-Gruppen in beliebig kleine StĂŒcke zu schneiden und sie unterschiedlich zu kombinieren. Zum Beispiel können wir gleichzeitig bcache + LVM thin, bcache + BTRFS, LVM cache + LVM thin, eine komplexe ZFS-Konfiguration mit Caches oder jede andere teuflische Mischung starten, um alles anzufassen und zu vergleichen.

Auf der „Hardware“-Ebene werden wir nichts anderes als die guten alten „dicken“ LVM-Volumes verwenden. Eine Ausnahme von dieser Regel könnte der Bereich fĂŒr Backups sein.

Ich denke, zu diesem Zeitpunkt haben viele Leser bereits einige VerdachtsschlĂŒsse bezĂŒglich der Matroschka gezogen.

LVM auf SATA HDD

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

Schon wieder eine neue VG..?Wir wollen auf jeden Fall, dass, wenn das Disk-Array, das wir fĂŒr die Datensicherung verwenden, ausfĂ€llt, unser Betriebssystem weiterhin normal funktioniert und zugleich den Zugriff auf nicht gesicherte Daten aufrechterhĂ€lt. Daher, um Probleme bei der Aktivierung der VG zu vermeiden, erstellen wir eine separate VG.

Einrichtung des 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
?Unsere NVMe SSDs verfĂŒgen ebenfalls ĂŒber einen SLC-Cache, der 4 Gigabyte "kostenlos" und 18 Gigabyte dynamisch durch den freien Platz im 3-Bit MLC bereitstellt. Nach Erschöpfung dieses Caches werden unsere NVMe SSDs nicht viel schneller sein als unsere SATA SSDs mit Cache. Aus diesem Grund macht es keinen Sinn, das LVM-Cache-Volume wesentlich grĂ¶ĂŸer als das doppelte Volumen des SLC-Caches des NVMe-Laufwerks zu gestalten. FĂŒr die verwendeten NVMe-Laufwerke hĂ€lt der Autor es fĂŒr vernĂŒnftig, einen Cache von 32-64 Gigabyte zu erstellen.

Die angegebene PartitionsgrĂ¶ĂŸe ist erforderlich, um einen Cache von 64 Gigabyte zu organisieren, Metadaten des Caches zu speichern und eine Sicherung der Metadaten bereitzustellen.

ZusĂ€tzlich möchte ich darauf hinweisen, dass nach einem unsauberen Herunterfahren des Systems LVM den gesamten Cache als unsauber kennzeichnet und ihn erneut synchronisieren wird. DarĂŒber hinaus wird dies bei jedem Einsatz von lvchange auf diesem GerĂ€t bis zum nĂ€chsten Neustart des Systems wiederholt. Daher empfehle ich, den Cache sofort mit dem entsprechenden Skript neu zu erstellen.

Wir erstellen ein LV auf SATA RAID 6, um es als cachendes GerÀt zu nutzen.

#lvcreate -L 3298543271936B --name cache data

Warum nur drei Terabyte..?Damit SATA SSD RAID 6 fĂŒr andere Anforderungen genutzt werden kann, lĂ€sst sich der zu cachebare Speicher dynamisch und ohne Unterbrechung des Systems erhöhen. DafĂŒr muss der Cache vorĂŒbergehend gestoppt und dann wieder aktiviert werden. Ein herausragender Vorteil von LVM-cache gegenĂŒber 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 cachebaren GerÀt.

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

Hier haben wir den gesamten verfĂŒgbaren Speicher auf /dev/data/cache belegt, damit alle anderen benötigten Partitionen sofort auf /dev/root/cache erstellt werden. Falls 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 eine solche chunksize..?Durch praktische Experimente hat der Autor herausgefunden, dass die besten Ergebnisse erzielt werden, wenn die BlockgrĂ¶ĂŸe des LVM-Caches mit der BlockgrĂ¶ĂŸe von LVM thin ĂŒbereinstimmt. Dabei gilt: Je kleiner die BlockgrĂ¶ĂŸe, 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 bei writeback..!Ja. Dieser Cachetyp verzögert die Synchronisation von EintrĂ€gen auf das cachebare GerĂ€t. Dies kann dazu fĂŒhren, dass bei Verlust des Caches Daten auf dem cachebaren GerĂ€t verloren gehen. SpĂ€ter wird der Autor erlĂ€utern, welche Maßnahmen neben NVMe RAID 1 ergriffen werden können, um dieses Risiko zu kompensieren.

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

Lassen Sie uns prĂŒfen, 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 kann man bei Bedarf mit einem Befehl deaktivieren:

#lvconvert -y --uncache cache/cachedata

Das geschieht online. LVM synchronisiert einfach den Cache auf die Festplatte, entfernt ihn und benennt cachedata_corig wieder in cachedata um.

LVM Thin-Konfiguration

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 3.385.794.560 Bytes fĂŒr "--block-size=64kibibytes --pool-size=6terabytes --max-thins=100000"

Runden wir auf 4 Gigabyte: 4294967296B

Multiplizieren wir mit zwei und addieren 4194304B fĂŒr die Metadaten von LVM PV: 8594128896B
Lassen Sie uns eine separate Partition auf NVMe RAID 1 erstellen, um die Metadaten von LVM Thin und deren Backup darauf zu speichern:

#lvcreate -L 8594128896B --name images root

Warum..?Hier könnte die Frage aufkommen, warum die Metadaten von LVM Thin separat gespeichert werden sollten, wenn sie ohnehin auf NVMe zwischengespeichert werden und schnell arbeiten.

Die Geschwindigkeit ist zwar wichtig, doch nicht der Hauptgrund. Es geht darum, dass der Cache einen Schwachpunkt darstellt. Wenn etwas mit ihm passiert und die LVM thin-Metadaten im Cache gespeichert sind, kann dies zu einem vollstĂ€ndigen Datenverlust fĂŒhren. Ohne intakte Metadaten wird es nahezu unmöglich sein, die Thin-Volumes wiederherzustellen.

Indem wir die Metadaten auf ein separates, nicht gecached, aber schnelles Volume verschieben, gewĂ€hrleisten wir den Schutz der Metadaten im Falle eines Verlusts oder einer BeschĂ€digung des Caches. In diesem Fall werden alle SchĂ€den, die durch den Verlust des Caches verursacht werden, innerhalb der Thin-Volumes lokalisiert, was den Wiederherstellungsprozess erheblich vereinfacht. Mit großer Wahrscheinlichkeit können diese SchĂ€den mit Hilfe der FS-Logs wiederhergestellt werden.

DarĂŒber hinaus wird die IntegritĂ€t des Snapshots gewĂ€hrleistet, wenn zuvor ein Snapshot des Thin-Volumes erstellt wurde und der Cache nach diesem einmal vollstĂ€ndig synchronisiert wurde, aufgrund der spezifischen Eigenschaften von LVM thin.

Legen wir eine neue VG an, die fĂŒr das Thin-Provisioning verantwortlich ist:

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

Erstellen wir einen Pool:

#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T images/thin-pool
Warum -Z yNeben dem ursprĂŒnglichen Zweck dieses Modus, der darin besteht, das Auslaufen von Daten zwischen virtuellen Maschinen beim Umverteilen von Speicher zu verhindern, wird Zeroing auch verwendet, um die Geschwindigkeit von zufĂ€lligen SchreibvorgĂ€ngen mit Blöcken unter 64k zu erhöhen. Jeder Schreibvorgang unter 64k in einem zuvor nicht zugeordneten Bereich des Thin-Volumes wird in 64K blockausgerichtete Cache-grenzwerte umgewandelt. Dadurch kann der Vorgang vollstĂ€ndig ĂŒber den Cache durchgefĂŒhrt werden, ohne dass das zwischengespeicherte GerĂ€t betroffen ist.

Lassen Sie uns das LV auf die entsprechenden PV verschieben:

#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

ÜberprĂŒfen Sie den Status:

#lvs -a -o lv_name,lv_size,devices --units B images
LV LSize GerÀte
[lvol0_pmspare] 4 GiB /dev/root/images(0)
thin-pool 256 GiB thin-pool_tdata(0)
[thin-pool_tdata] 256 GiB /dev/cache/cachedata(0)
[thin-pool_tmeta] 4 GiB /dev/root/images(1024)

Erstellen wir ein Thin-Volume fĂŒr Tests:

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

Installieren wir die Pakete fĂŒr Tests und Überwachung:

#apt-get install sysstat fio

So können Sie das Verhalten unserer Speicher-Konfiguration 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 Sie 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

Achtung! Ressource!Dieser Code fĂŒhrt 36 verschiedene Tests aus, von denen jeder 4 Sekunden dauert. Die HĂ€lfte der Tests sind SchreibvorgĂ€nge. In 4 Sekunden kann die NVMe eine enorme Menge an Daten schreiben – bis zu 3 Gigabyte pro Sekunde. Daher kann jeder Start eines Schreibtests bis zu 216 Gigabyte an SSD-Ressourcen verbrauchen.

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

Die Ergebnisse werden beim ersten Start und bei den folgenden stark variieren, wĂ€hrend der Cache und das Thin Volume gefĂŒllt werden. Dies hĂ€ngt auch davon ab, ob das System die beim vorherigen Start gefĂŒllten Caches synchronisieren konnte.

Ich empfehle außerdem, die Geschwindigkeit auf einem bereits gefĂŒllten Thin Volume zu messen, von dem gerade ein Snapshot erstellt wurde. Der Autor hat beobachtet, dass sich zufĂ€llige SchreibvorgĂ€nge nach der Erstellung des ersten Snapshots deutlich beschleunigen, insbesondere wenn der Cache noch nicht vollstĂ€ndig gefĂŒllt ist. Dies geschieht aufgrund der Copy-on-Write-Semantik, die das Blockausrichten zwischen Cache und Thin Volume und die Tatsache, dass zufĂ€llige SchreibvorgĂ€nge auf RAID 6 in zufĂ€llige LesevorgĂ€nge mit nachfolgendem Schreiben in den Cache umgewandelt werden. In unserer Konfiguration ist zufĂ€lliges Lesen auf RAID 6 bis zu 6-mal (Anzahl der SATA SSDs im Array) schneller als das Schreiben. Da die Blöcke fĂŒr CoW sequenziell aus dem Thin Pool zugewiesen werden, verwandelt sich das Schreiben grĂ¶ĂŸtenteils ebenfalls in sequenzielle SchreibvorgĂ€nge.

Beide diese Funktionen können gewinnbringend genutzt werden.

Cache-„kohĂ€rente“ Snapshots

Um das Risiko eines Datenverlusts bei BeschĂ€digung/Verlust des Caches zu reduzieren, empfiehlt der Autor die EinfĂŒhrung einer Praxis zur Rotation von Snapshots, die die IntegritĂ€t in diesem Fall gewĂ€hrleistet.

Erstens, da die Metadaten der dĂŒnnen Volumes auf einem nicht-cachierten GerĂ€t gespeichert sind, bleiben die Metadaten intakt, und mögliche Verluste werden innerhalb der Datenblöcke isoliert.

Der nÀchste Zyklus zur Rotation der Snapshots garantiert die IntegritÀt der Daten innerhalb der Snapshots im Falle eines Cacheverlustes:

  1. FĂŒr jedes dĂŒnne Volume mit dem Namen <ĐžĐŒŃ> erstellen wir einen Snapshot mit dem Namen <ĐžĐŒŃ>.cached
  2. Setzen wir den Migration Threshold auf einen angemessen 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}' Wenn wir keine Null erhalten, kann diese vorĂŒbergehend erstellt werden, indem der Cache in den Writethrough-Modus versetzt wird. Allerdings werden, angesichts der Geschwindigkeitseigenschaften unserer SATA- und NVMe-SSDs sowie deren TBW-Ressource, Sie entweder den Moment schnell genug erfassen können, ohne den Cache-Modus zu Ă€ndern, oder Ihre Hardware wird ihre gesamte Ressource innerhalb weniger Tage verbrauchen. Aufgrund der RessourcenschrĂ€nkungen kann das System grundsĂ€tzlich nicht stĂ€ndig unter 100 % Schreiblast stehen. Unsere NVMe-SSDs werden bei 100 % Schreiblast ihre Ressource vollstĂ€ndig aufbrauchen in 3-4 Tagen. SATA-SSDs halten etwa doppelt so lange. Daher gehen wir davon aus, dass der Großteil der Last auf LesevorgĂ€nge entfĂ€llt, wĂ€hrend das Schreiben - relativ kurzfristige Spitzen extrem hoher AktivitĂ€t in Kombination mit niedrigerer durchschnittlicher Last - umfasst.
  4. Sobald Sie die Null erfasst (oder erstellt) haben, benennen Sie .cached in .committed um. Die alte .committed wird dabei gelöscht.
  5. Optional, wenn der Cache zu 100 % gefĂŒllt ist, kann er mit einem Skript neu erstellt werden, um ihn zu bereinigen. Mit einem halb leeren Cache arbeitet das System beim Schreiben wesentlich schneller.
  6. Setzen wir den Migration Threshold auf Null: #lvchange --quiet --cachesettings "migration_threshold=0" cache/cachedata Dies wird vorĂŒbergehend die Synchronisation des Caches auf das Hauptlaufwerk verhindern.
  7. Wir warten, bis im Cache ausreichend viele Änderungen gesammelt wurden. #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' oder der Timer ablĂ€uft.
  8. Wir wiederholen von vorn.

Warum die Schwierigkeiten mit dem Migrationstreshold
?Es liegt daran, dass in der Praxis eine "zufĂ€llige" Aufzeichnung in Wirklichkeit nicht ganz zufĂ€llig ist. Wenn wir etwas in einen 4-Kilobyte-Sektor schreiben, ist die Wahrscheinlichkeit groß, dass in den nĂ€chsten paar Minuten in diesen oder einen der benachbarten (+- 32K) Sektoren geschrieben wird.

Wenn wir den Migrationstreshold auf null setzen, verzögern wir die Synchronisation der Aufzeichnung auf dem SATA SSD und aggregieren mehrere Änderungen eines 64K-Blocks im Cache. Dadurch wird die Ressource des SATA SSD erheblich geschont.

Und wo ist der Code
?Leider hĂ€lt der Autor sich fĂŒr nicht kompetent genug in der Entwicklung von Bash-Skripten, da er zu 100 % Autodidakt ist und „google”-gesteuerte Entwicklung praktiziert. Daher glaubt er, dass der schreckliche Code, der aus seinen HĂ€nden kommt, von niemandem anders verwendet werden sollte.

Ich bin sicher, dass die Fachleute in der Lage sind, die zuvor beschriebene Logik bei Bedarf selbststÀndig umzusetzen und vielleicht sogar in Form eines systemd-Dienstes ansprechend zu gestalten, wie der Autor es versucht hat.

Ein solches einfaches Schema zur Rotation von Snapshots ermöglicht es uns nicht nur, stÀndig einen vollstÀndig syncronisierten Snapshot auf einem SATA-SSD zu haben, sondern auch mithilfe des Tools thin_delta herauszufinden, welche Blöcke nach seiner Erstellung geÀndert wurden, wodurch wir BeschÀdigungen auf den Hauptvolumen lokalisieren und die Wiederherstellung erheblich vereinfachen können.

TRIM/DISCARD in libvirt/KVM

Da der Speicher fĂŒr KVM unter libvirt verwendet wird, wĂ€re es sinnvoll, unsere VMs nicht nur Platz zu beanspruchen, sondern auch nicht mehr benötigten Platz freizugeben.

Dies erfolgt durch die Emulation der UnterstĂŒtzung von TRIM/DISCARD auf virtuellen Laufwerken. 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>

Ähnliche DISCARDs aus Guest-OS werden korrekt vom 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 Entfernen des nĂ€chsten Snapshots.

BTRFS-Backup

Verwenden Sie fertige Skripte mit Ă€ußerster Vorsicht und auf eigenes Risiko.. Der Autor hat diesen Code selbst geschrieben und ausschließlich fĂŒr sich selbst. Ich bin mir sicher, dass viele erfahrene Linux-Nutzer Ă€hnliche Hacks haben, und es nicht nötig sein wird, Ń‡ŃƒĐ¶ĐžĐ” zu kopieren.

Legen wir ein Volume auf dem Backup-GerÀt an:

#lvcreate -L 256G --name backup backup

Formatieren wir in BTRFS:

#mkfs.btrfs /dev/backup/backup

Erstellen wir Mount-Punkte und mounten wir die root-Subsysteme 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 Backups:

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

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

#mkdir /root/btrfs-backup

Kopieren wir das Skript:

Viel erschreckender Bash-Code. Nutzen Sie auf eigenes Risiko. Wenden Sie sich nicht mit wĂŒtenden E-Mails 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 vorhanden.."
;;
esac
) 98>$LOCK_FILE

EOF

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

Der erste Lauf kann relativ lange dauern, da zu Beginn alle Daten kopiert werden. Weitere LĂ€ufe werden sehr schnell sein, da nur die Änderungen kopiert werden.

Ein weiterer Skript, den wir in cron einfĂŒgen werden:

Ein wenig mehr 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..?Erstellt und synchronisiert inkrementelle Snapshots der angegebenen BTRFS-Volumes auf dem Backup-Dateisystem. Anschließend werden alle Snapshots, die Ă€lter als 60 Tage sind, gelöscht. Nach dem Start werden in den Unterverzeichnissen /backup/btrfs/back/remote/ datierte Snapshots der angegebenen Volumes erstellt.

Geben wir dem Skript AusfĂŒhrungsrechte:

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

ÜberprĂŒfen wir es und fĂŒge es zu Cron hinzu:

#/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 einen dĂŒnnen 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 Tool verwenden 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 im Einsatz...#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 "Der Quell-LV ist nicht thin.")
exit 1
fi

if [ "$DIFF_TARGET_POOL" == "" ]
then
(>&2 echo "Der Ziel-LV ist nicht thin.")
exit 1
fi

if [ "$DIFF_SOURCE_POOL" != "$DIFF_TARGET_POOL" ]
then
(>&2 echo "Quell- und Ziel-LVs gehören zu unterschiedlichen 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-Metadatensnapshot existiert bereits. Angenommen wird, dass dieser veraltet ist. Snapshot wird in 5 Sekunden freigegeben.")
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 "Fehler beim Erstellen des Thin-Pool-Metadatensnapshots.")
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' | 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 "Quellbasis nicht gefunden, erstelle Snapshot von $SOURCE_VG/$SOURCE_LV nach $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 "Entferne $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 mit der Quelle nicht synchron. 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. Leeres Volume erstellen."
lvcreate --thin-pool "$BACKUPS_POOL" -V "$SOURCE_BASE_SIZE"B --name "$TARGET_BASE_LV" "$TARGET_VG" || exit 1
echo "Muss neu gebootstrapped werden. Quelle bei $SOURCE_BASE_LV_PATH verwerfen"
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 "Ziel bei $TARGET_BASE_LV_PATH verwerfen"
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 mit $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 Synchronisiere "$SOURCE_BASE_LV_PATH" nach "$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 nach $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"
done < <(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 vorhanden.."
;;
esac
) 98>$LOCK_FILE

EOF

Was macht es
?EnthĂ€lt eine Reihe von Befehlen zur Manipulation von Thin-Snapshots und zur Synchronisierung der Unterschiede zwischen zwei Thin-Snapshots, die ĂŒber thin_delta auf ein anderes BlockgerĂ€t unter Verwendung von ddrescue und blkdiscard ĂŒbertragen werden.

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

Noch etwas 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 Thin-Volumes zu erstellen und zu synchronisieren. Das Skript lĂ€sst inaktive Snapshots der genannten Volumes zurĂŒck, die zum Verfolgen von Änderungen seit der letzten Synchronisierung erforderlich sind.

Dieses Skript muss bearbeitet werden, indem die Liste der Thin-Volumes angegeben wird, fĂŒr die Backups erstellt werden sollen. Die genannten Bezeichner dienen nur als Beispiel. Es ist möglich, ein Skript zu schreiben, das alle Volumes synchronisiert.

Rechte vergeben:

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

ÜberprĂŒfen wir es und fĂŒge es zu Cron hinzu:

#/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 lĂ€ngere Zeit in Anspruch nehmen, da die dĂŒnnen Volumes durch das vollstĂ€ndige Synchronisieren des gesamten genutzten Speicherplatzes erstellt werden. Dank der LVM thin Metadaten wissen wir, welche Blöcke tatsĂ€chlich verwendet werden, sodass nur die tatsĂ€chlich genutzten Blöcke der dĂŒnnen Volumes kopiert werden.

Bei den nachfolgenden Starts werden die Daten inkrementell kopiert, da die Änderungen ĂŒber die LVM thin Metadaten verfolgt werden.

Schauen wir uns an, was passiert ist:

#time /root/btrfs-backup/cron-daily.sh
reale 0m2,967s
Benutzer 0m0,225s
System 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 (LV) physische LVM-Volumes (PV) fĂŒr andere Volume-Gruppen (VG) sein können. LVM kann rekursiv sein, Ă€hnlich wie Matroschkas. Dies verleiht LVM eine außergewöhnliche FlexibilitĂ€t.

P.S.

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

Quelle: habr.com

Erwerben Sie zuverlĂ€ssiges Hosting fĂŒr Websites mit DDoS-Schutz, VPS VDS-Server đŸ”„ Kaufen Sie zuverlĂ€ssiges Hosting fĂŒr Websites mit DDoS-Schutz, VPS VDS-Server | ProHoster