Dies ist eine alte Version des Dokuments!
Inhaltsverzeichnis
dnbd3-fuse-cow
Diese Erweiterung des fuse dnbd3-Clients ermöglicht, Images (virt. Abbilder) beschreibbar zu mounten. Änderungen werden in einer separaten Datei auf dem Client-Computer gespeichert (auch Copy on Write, kurz cow genannt) und im Hintergrund auf den cow-Server hochgeladen. Verbleibende Änderungen werden hochgeladen, sobald das Image ausgehängt wird. Nach Upload aller Änderungen werden sie in einer Kopie des Original-Images auf dem Cow-Server zusammengeführt, wenn dies in den Startparametern eingestellt wurde.
Ein typischer Anwendungsfall ist das Aktualisieren oder Hinzufügen von Software zu einem bestehenden Image. Der Vorteil ist, dass nicht erst das komplette Abbild heruntergeladen, lokal bearbeitet und anschließend wieder hochgeladen werden muss. Stattdessen wird das Image beschreibbar per dnbd3 gemountet; die Uploadzeit ist deutlich kürzer, da Änderungen bereits während der Session hochgeladen werden.
Installation
Diese Anleitung umfasst die Installation der nötigen Komponenten sowie die Anwendung der Tests.
Installation dnbd3-Server, dnbd3-fuse-Client, Testprogramm
Installation der Abhängigkeiten
Beispiel Ubuntu 22.04 oder Debian 11, andere Distributionen mit evtl. abweichenden Paketnamen:
apt-get update -y apt-get install -y make cmake clang-format linux-headers-generic libfuse-dev libjansson-dev libcurl4-openssl-dev
Download Quellcode
Download des Quellcodes aus dem git-Repository:
git clone -b fuse_cow_2 git://git.openslx.org/dnbd3.git
Bau und Installation
Vor dem Bau in das nach dem Download erzeugte Verzeichnis dnbd3 wechseln. Dann zur Konfiguration des Baus ausführen:
cmake -D DNBD3_KERNEL_MODULE=OFF \ -D DNBD3_BENCHMARK=OFF \ -D DNBD3_CLIENT_FUSE=ON \ -D DNBD3_CLIENT_FUSE_COW_TEST=ON \ -D DNBD3_SERVER=ON \ -D DNBD3_SERVER_FUSE=OFF \ -D DNBD3_SERVER_AFL=OFF \ -D DNBD3_SERVER_DEBUG_LOCKS=OFF \ -D DNBD3_SERVER_DEBUG_THREADS=OFF \ -D DNBD3_RELEASE_HARDEN=OFF \ -D DNBD3_PACKAGE_DOCKER=OFF
… und anstoßen:
make && make install
Ein ausgeführtes 'make install' installiert die ausführbaren Dateien dnbd3-fuse, dnbd3-fuse-cow-test sowie dnbd3-server nach /usr/local/bin. '/usr/local/bin' sollte im Pfad enthalten sein.
Autostart
Der dnbd3-Server wird am besten direkt beim Systemstart über systemd gestartet. Dazu folgende Datei unter /etc/systemd/system/dnbd3-server.service ablegen …
[Unit] Description=DNBD3 server Wants=network-online.target After=network-online.target [Service] Type=simple ExecStart=/usr/local/bin/dnbd3-server -n -c /etc/dnbd3-server Restart=on-failure User=dnbd3 [Install] WantedBy=multi-user.target
… und mittels 'systemctl enable dnbd3-server' zum automatischen Start registrieren. Ein Direktstart kann mittels 'systemctl daemon-reload', 'systemctl start dnbd3-server' erfolgen.
Installation cow_merger_service
Installation von .NET 6 SDK
Ubuntu 22.04
Beispiel Ubuntu 22.04
apt install dotnet6
Debian
Folgende Befehle gelten für Debian 10 und 11; für Debian 10 einfach vorkommende '11' durch '10' ersetzen.
wget https://packages.microsoft.com/config/debian/11/packages-microsoft-prod.deb -O packages-microsoft-prod.deb && \ dpkg -i packages-microsoft-prod.deb && \ rm packages-microsoft-prod.deb
Diese Befehle erzeugen einen Repositoriumseintrag unter /etc/apt/sources.list.d/microsoft-prod.list. Dieser kann nach erfolgter Installation gelöscht werden. Die eigentlich Installation erfolgt mit
apt-get update && apt-get install -y dotnet-sdk-6.0
Andere Distributionen
Für andere Distributionen kann Dotnet ggf. nach Eintrag einen Microsoft-Repositoriums per jeweiligem Paketmanager oder nach direktem Download der Paketdate erfolgen. Informationen dazu finden Sie bei Microsoft.
Download Quellcode
git clone "https://github.com/z0Kng/cow_merger_service.git"
Bau des cow_merger_service
cd cow_merger_service dotnet build cow_merger_service.csproj --runtime linux-x64
Empfehlenswert sind die Optionen -p:PublishSingleFile=true und -p:PublishTrimmed=True. Diese bewirken, dass der Server als einzelne Datei gebaut und die Dateigröße reduziert wird, –self-contained true bewirkt, dass .NET nicht auf dem Zielsystem installiert sein muss.
dotnet publish -c Release -o publish -p:PublishReadyToRun=true -p:PublishSingleFile=true -p:PublishTrimmed=True -p:PublishReadyToRunShowWarnings=true --self-contained true --runtime linux-x64
Erzeugte Dateien (Serverprogramm und Konfigurationsdatei) werden in cow_merger_service/publish/ abgelegt. Kopieren Sie die ausführbare Datei cow_merger_service und die Konfiguration appsettings.json an einen geeigneten Ort, beispielsweise in ein eigenes Verzeichnis hinter /opt.
Autostart
Der Start des cow_merger_services kann automatisiert werden. Erzeugen Sie dazu eine Datei in /etc/systemd/system/cow_merger_service.service folgenden Inhalts:
[Unit] Description=CoW merger service Wants=network-online.target After=network-online.target [Service] Type=simple WorkingDirectory=/opt/cow_merger_service Environment="DOTNET_CLI_TELEMETRY_OPTOUT=TRUE" ExecStart=/opt/cow_merger_service/cow_merger_service Restart=on_failure User=root [Install] WantedBy=multi-user.target
… und führen anschließend den Befehl 'systemctl enable cow_merger_service' aus. 'systemctl daemon-reload && systemctl start cow_merger_service' startet den Dienst direkt.
Konfiguration
Serverseitig sollten 3 Verzeichnisse erstellt werden:
mkdir [WorkingDirectory] mkdir [OriginalImageDirectory] mkdir [DestinationDirectory]
dnbd3-Server
Die Konfiguration des dnbd3-Servers erfolgt per server.conf. Eine Beispielkonfiguration ist in pkg/config/server.conf zu finden. Diese kann an einen passenden Ort kopiert werden; der Pfad kann dem dnbd3-Server mit dem Startparamerter –config übergeben werden. Die wichtigste Konfigurationsoption ist basePath, die das Verzeichnis bezeichnet, in dem die von Server bereitgestellten Images liegen. Die Abbilddateien müssen auf .r[1-9][0-9]* enden. Nach Ablage der Images in diesem Ordner kann der Server gestartet werden.
dnbd3-server --config [Pfad zur Config]
cow_merger_service
Die Konfiguration des cow_merger_service erfolgt über die Datei appsettings.json, welche sich im gleichen Order wie die Executable befindet.
Schlüssel | Wert | Beschreibung |
---|---|---|
WorkingDirectory | Pfad | Order in dem der Server die aktiven Images zur Bearbeitung zwischen speichert. |
OriginalImageDirectory | Pfad | Ordner der die Originalen Image Dateien enthält. |
DestinationDirectory | Pfad | In diesen Ordner werden die fertigen Images kopiert. |
Urls | url:port,..,url:port | Url Liste auf die der Server hören soll. |
Sämtliche genannten Werte aus der Tabelle müssen konfiguriert werden. Sollten sich die Serverprozesse cow_merger_service und der dnbd3-server auf dem selben System befinden, macht es Sinn, dass OriginalImageDirectory auf den gleichen Ordner wie der basePath des dnbd3 Servers zeigt.
Serverstart
cow_merger_service
dnbd3_fuse_cow
Es werden zwei leere Verzeichnise benötigt. Der mountPfad muss ein leerer Ordner sein, in diesen wird das Image gemounted. Im tmpCowPfad speichert die COW Erweiterung die Änderungen und Metadaten des Images zwischen und bei Benutzung des –cowStatFile Parametrs wird dort auch die status.txt angelegt welche debug informationen zur COW Erweiterung enthält.
mkdir [mountPfad] mkdir [tmpCowPfad]
opt | longopt | Paramter | Beschreibung |
---|---|---|---|
-f | Kein fork, Log wird in stdout geschrieben. | ||
-h | –host | <address1 address2..> | host Adressen der dnbd3 Server. |
-i | –image | <imagename> | Name des images das geladen werden soll. |
-c | –cow | <tmpCowPfad> | Erstellt eine neue Cow session, |
-L | –loadCow | <tmpCowPfad> | Läd eine cow Session von dem Pfad welche noch kein merge hatte. |
-C | –cowServer | <url:port> | Url/Ip und Port des cow_merger_service |
-m | –merge | Merged das Image sobald die Session beendet wird. | |
-x | –cowStatStdout | Schreibt den Cow status in stdout. | |
-y | –cowStatFile | Schreibt den Cow status in status.txt in <tmpCowPfad> . |
Beispiel Startparameter: Laden eines Images im cow-Modus mittels fuse-Client; Image wird nach erfolgtem unmount gemerged.
dnbd3-fuse [mountPfad] -f -h [dnbd3 server ip] -i test -c [tmpCowPfad] " -C [url:port] -m
Tests
Für die Tests wird ein komplettes Setup, bestehend aus dnbd3 server, cow_merger_service und dnbd3_fuse client benötigt. Das Testprogramm enthält zwei verschiedene Tests; bei beiden Test wird ein per dnbd3_fuse gemountetes Testabbild benötigt. Der Standardtest enthält verschiedene Tests, die allgemeine Funktionen und Edge Cases testen. Hierbei werden Schreib-, Lese- und Größenanderungen durchgeführt. Diese werden anschließend mit den erwarteten Werten verglichen.
Der Random Test führt zufällige Schreibvorgänge und Größenänderungen durch. Diese werden sowohl auf dem gemounteten Image als auch auf einer im Dateisystem befindlichen Kopie desselben durchgeführt. Beide Abbilder werden zum Abschluss auf Gleichheit getestet.
Allgemeine Testvorbereitungen
dnbd3-server, dnbd3-fuse-cow-test, dnbd3-fuse, und cow_merger_service sollten wie oben beschrieben erstellt und konfiguriert sein. Anschließend kann mit folgendem Befehl ein Testimage erstellt werden:
dnbd3-fuse-cow-test -c [Imagename].r1
Dieses Abbild kann anschließend in den basePath-Ordner des dnbd3-Servers verschoben werden und, falls der cow_merger_service-[OriginalImageDirectory]-Order nicht auf denselben zeigt, in diesen kopiert werden. Die Prozesse dnbd3-server und cow_merger_service müssen natürlich ebenfalls gestartet werden, falls noch nicht geschehen .
dnbd3-server --config [Pfad zur Config] cow_merger_service
Sollte der dnbd3-server schon laufen muss dieser neu gestartet werden.
Parameter des Testprogrammes
opt | longopt | Arguments | Beschreibung |
---|---|---|---|
-c | –testFile | <path> | Generiert eine Test Datei dem angegebenen Pfad, dieses Datei wird für die Tests benötigt. |
-t | –test | <file> | Führt den Standard Test an der angegebenen Datei durch. |
-d | –delay | <seconds> | Setzt den Warte Wert im Standart Test für den MultipleWrites test. |
-v | –verify | <file> | Dient zum verifizieren des Datei nachdem der Standard Test ausgeführt wurde. |
-r | –randomTest | <mountedFile> <normalFile> | Dieser Test schreibt und ändert die Größe beider Dateien zufällig, solange bis der Test beendet wird (ctrl+c). Dann werden beide Dateien auf gleichheit getestet. |
-x | –compare | <mergedFile> <normalFile> | Prüft beide Dateien auf Gleichheit |
-y | –minSizePercent | [1-200] | Minimaler Prozentualer wert auf die der Random Test das Image bei einer Größenänderung reduziert. |
-z | –maxSizePercent | [1-200] | Maximaler Prozentualer wert auf die der Random Test das Image bei einer Größenänderung erhöht. |
Standard-Test
1. Mit dem Fuse Client im cow modus eine neue Session starten:
dnbd3-fuse [mountPfad] -f -h [dnbd3 server ip] -i [imageName] -c [tmpCowPfad] -C [url:port] -y -x
2. Standard-Test starten:
cowtest/dnbd3-fuse-cow-test -t [mountPfad]
3. Nach Abschluss des Tests unmounten:
umount [mountPfad]
4. A alte cow-Sitzung wieder laden und merge (-m) aktivieren:
dnbd3-fuse [mountPfad] -f -h [dnbd3 server ip] -i [imageName] -c [tmpCowPfad] -C [url:port] -y -x -m
5. Image auf Fehler prüfen:
dnbd3-fuse-cow-test -v [mountPfad]
6. Unmount nach Abschluss der der Prüfung:
umount [mountPfad]
7. Nach Beendigung des merge-Vorgangs prüfen, ob im [DestinationDirectory] das neue Image vorhanden ist, dann das fertige Image auf Fehler prüfen:
dnbd3-fuse-cow-test -v "[DestinationDirectory]/[imageName].r2"
Random-Test
1. Kopie des Testimages auf einen beliebigen Ort innerhalb des lokalen Dateisystems als Vergleichsbasis des Random-Tests erstellen:
cp [OriginalImageDirectoryPfad]/[imageName].r1 [Pfad im lokalen Dateisystem]
2. Start einer neues Session mittels Fuse-Client im cow-Modus:
dnbd3-fuse [mountPfad] -f -h [dnbd3 server ip] -i [imageName] -c [tmpCowPfad] -C [url:port] -y -x
3. Teststart per dnbd3-fuse-cow-test (neues Terminal empfehlenswert):
dnbd3-fuse-cow-test --randomTest "[mountPfad]/img" "[Pfad im lokalen Dateisystem]/[imageName].r1"
4. Nach beliebiger Zeit den Random-Test beenden (Strg+C). Darauf werden beide Abbilder auf Gleichheit getestet.
5. Image des dnbd3-fuse-Clients unmounten:
umount [mountPfad]
6. Laden der alten cow-Session (erstes Terminal) und Merge (-m) aktivieren:
dnbd3-fuse [mountPfad] -f -h [dnbd3 server ip] -i [imageName] -L [tmpCowPfad] -C [url:port] -m -y -x
7. Beide Abbilder auf Gleichheit testen, um sicherzustellen, dass das Laden funktioniert:
dnbd3-fuse-cow-test --compare [mountPfad] "[Pfad im lokalen Dateisystem]/[imageName].r1"
8. Optional: Random-Test erneut ausführen (siehe Schritt 3 und 4).
9. Image des dnbd3-fuse clients unmounten; dies löst den Merge-Vorgang des cow_merger_service automatisch aus:
umount [mountPfad]
10. Nach Beendigung des Merge-Vorgangs sollte im [n DestinationDirectory] das neue Image vorhanden sein. Anschliessend können beide Abbilder verglichen werden:
dnbd3-fuse-cow-test --compare "[DestinationDirectory]/[imageName].r2" "[Pfad im lokalen Dateisystem]/[imageName].r1"
Funktionsweise
Struktur der Daten
Die Datenstruktur ist in zwei Hauptteile unterteilt. Die eigentlichen Daten die die Änderungen enthalten und die dazugehörenden Metadaten. Die Änderungen werden in der data Datei gespeichert während die Meta daten in der meta Datei gespeichert werden. Die Daten werden in Blöcken gespeichert, der dnbd3-Block ist 4096 Byte groß und 320 dnbd3-Blöcke werden in einen cow-Block zusammengefasst. Ein cow block hat eine cow_block_metadata_t Struktur, die die entsprechenden Metadaten enthält. Die Metadaten werden verwendet, um festzustellen, ob ein dnbd3-Block beschrieben wurde, wo dieser Block in der Datendatei gespeichert ist, wann er zuletzt geändert und wann er hochgeladen wurde. Aber dazu später mehr.
Blockmetadata
Die Datenstruktur zur Speicherung von „cow_block_metadata_t“ enthält eine Schicht 1 (L1) und eine Schicht 2 (L2). L1 enthält Pointer auf die L2's. Das gesamte L1-Array wird zu Beginn initialisiert und kann nicht in der Größe verändert werden, daher begrenzt die Größe des L1-Arrays die Gesamtgröße des Images. Die L2 werden dynamisch erstellt, wenn sie benötigt werden. Zu Beginn sind also alle L1-Pointer Null. Die L2's sind Arrays, die 1024 cow_block_metadata_t structs enthalten.
typedef struct cow_block_metadata { atomic_int_least64_t offset; atomic_uint_least64_t timeChanged; atomic_uint_least64_t uploads; atomic_char bitfield[40]; } cow_block_metadata_t;
COW_L2_STORAGE_CAPACITY = 1024 * 320 * 4096 COW_METADATA_STORAGE_CAPACITY = 320 * 4096
Jeder cow_block_metadata_t enthält ein 40 Byte langes, also 320 Bit großes Bitfeld. Das Bitfeld zeigt an, ob der entsprechende dnbd3-Block Daten enthält oder nicht. Beginnt das Bitfeld z.B. mit 01…, so enthalten die ersten 4096 Byte keine Daten und die nächsten 4096 Byte enthalten Daten. Jeder cow_block_metadata_t speichert also die Metadaten von bis zu 320*4096 Bytes, wenn alle Bits auf 1 gesetzt sind. Das Offset-Feld enthält das Offset, an welcher Stelle in der Datendatei die entsprechenden Daten gespeichert sind. Die timeChanged Feld enthält die Unix-Zeit, zu der der Block zuletzt geändert wurde. Sie ist 0, wenn er nie geändert wurde oder wenn die letzten Änderungen bereits hochgeladen wurden.
Die L2-Arrays und cow_block_metadata_t sind nach den ursprünglichen Offsets des Images sortiert. So adressiert der erste L1-Zeiger, d.h. das erste L2-Array, die ersten 1024 * 320 * 4096 Bytes (L2Size * bitfieldsize * DNBD3Blocksize) der Daten und so weiter.
Um zum Beispiel die „cow_block_metadata_t“ für den Offset 4033085440 zu erhalten, würde man L1[3] nehmen, da
4033085440 / ( COW_L2_STORAGE_CAPACITY ) ≈ 3.005
Dann würde man den fünften cow_block_metadata_t im L2-Array nehmen wegen
(4033085440 mod COW_L2_STORAGE_CAPACITY) / COW_METADATA_STORAGE_CAPACITY = 5
REST-API
Die folgende REST API wird genutzt zur Komunikationund Datenübertragung mit dem cow_merger_service.
/api/File/Create
POST Responses
Code | Description |
---|---|
200 | Success |
Diese Anfrage wird verwendet, sobald eine neue COW Session erstellt wird. Die zurückgegebene GUID wird in allen nachfolgenden Anfragen zur Identifizierung der Session verwendet.
/api/File/Update
POST Parameters
Name | Located in | Description | Required | Schema |
---|---|---|---|---|
guid | query | Yes | string (uuid) | |
blockNumber | query | Yes | integer |
Responses
Code | Description |
---|---|
200 | Success |
Dient zum Hochladen eines COW Block. Die Blocknummer ist die absolute Blocknummer. Der Body enthält einen „application/octet-stream“, wobei die ersten Bytes das Bitfeld sind, direkt gefolgt von den eigentlichen Blockdaten.
/api/File/StartMerge
POST Parameters
Name | Located in | Description | Required | Schema |
---|---|---|---|---|
guid | Form | Yes | string (uuid) | |
originalFileSize | Form | Yes | integer | |
newFileSize | Form | Yes | integer |
Responses
Code | Description |
---|---|
200 | Success |
Wird verwendet, um einen Merge auf dem Server zu starten.
/api/File/GetTopModifiedBlocks
GET Parameters
Name | Located in | Description | Required | Schema |
---|---|---|---|---|
guid | query | Yes | string (uuid) | |
amount | query | Yes | integer |
Responses
Code | Description |
---|---|
200 | Success |
Diese Anfrage liefert eine Liste mit den Block-IDs und der Anzahl der Uploads dieses Blocks, sortiert nach der Anzahl der Uploads. Dies ist nützlich, um die `COW_MIN_UPLOAD_DELAY` anzupassen.
/api/File/Status
GET Parameters
Name | Located in | Description | Required | Schema |
---|---|---|---|---|
guid | query | Yes | string (uuid) |
Responses
Code | Description |
---|---|
200 | Success |
Gibt das SessionStatus-Modell zurück, das Informationen über die Session liefert.
Models
BlockStatistics
Name | Type | Description | Required |
---|---|---|---|
blockNumber | integer | Yes | |
modifications | integer | Yes |
SessionState
Name | Type | Description | Required |
---|---|---|---|
SessionState | string |
SessionStatus
Name | Type | Description | Required |
---|---|---|---|
state | string | _Enum:_ `„Copying“`, `„Active“`, `„Merging“`, `„Done“`, `„Failed“` | Yes |
imageName | string | Yes | |
originalImageVersion | integer | Yes | |
newImageVersion | integer | Yes | |
mergedBlocks | integer | Yes | |
totalBlocks | integer | Yes |
Alter Schrott
[Auswanzen, was verwendbar, Rest weg]
- fuse-client mit cow-Erweiterung Readme: https://git.openslx.org/dnbd3.git/tree/src/fuse/cowDoc/readme.md?h=fuse_cow_2
- cow-Server image-merge: https://github.com/z0Kng/cow_merger_service/blob/master/README.md
dnbd3 Branch fuse_cow_2 gebaut mit:
cmake -D DNBD3_KERNEL_MODULE=OFF -D DNBD3_BENCHMARK=OFF -D DNBD3_CLIENT_FUSE=ON -D DNBD3_CLIENT_FUSE_COW_TEST=ON -D DNBD3_SERVER=ON -D DNBD3_SERVER_FUSE=OFF -D DNBD3_SERVER_AFL=OFF -D DNBD3_SERVER_DEBUG_LOCKS=OFF -D DNBD3_SERVER_DEBUG_THREADS=OFF -D DNBD3_RELEASE_HARDEN=OFF -D DNBD3_PACKAGE_DOCKER=OFF
cow_merger_service-Bau:
dotnet build cow_merger_service.csproj --runtime linux-x64 dotnet publish -c Release -o publish -p:PublishReadyToRun=true -p:PublishSingleFile=true -p:PublishTrimmed=True -p:PublishReadyToRunShowWarnings=true --self-contained true --runtime linux-x64
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "Settings": { "WorkingDirectory": "/srv/cow_merger_service/WorkingDirectory", "OriginalImageDirectory": "/srv/dnbd3", "DestinationDirectory": "/srv/cow_merger_service/Output" }, "AllowedHosts": "*", "Urls": "http://localhost:5000"
(/usr/local/bin/)dnbd3-fuse-cow-test
Image generieren:
./dnbd3-fuse „/home/user/VMs/mount“ -f -h localhost -i [imageName] -c „/home/user/temp“ -C „192.168.178.20:5000“
Also für „cow mode“ -c <pfad > setzt halt den ordner wo er die temporären daten lagern kann (änderungen etc) -C <adress> sagt ihm wo er den cow server findet.
Sobald die beiden parameter gesetzt sind ist der fuse client quasi im „cow mode“.
Statt -c <pfad> geht auch -L <pfad> wenn man die alte session laden will. Und mit -m merged er nach dem unmounten.
dnbd3-fuse „/srv/cow_merger_service/mount“ -f -h localhost -i /srv/dnbd3/test.dnbd3 -c „/srv/cow_merger_service/WorkingDirectory“ -C „192.168.2.130:5000“
Nachfolgend wird davon ausgegangen, dass '/usr/local/bin' im Pfad enthalten ist.