Versorgt ein Server zum Beispiel Thin Clients mit einem Read-only-Dateisystem, muss er das nicht unbedingt mit NFS oder Samba tun. Für viele Aufgaben reichen Remote-Blockdevices, die kräftig an der Performance- und Effizienz-Spirale drehen.
Einen Fileserver hat praktisch jede Firma oder Organisation. Dennoch verschwenden viele Plattenplatz durch redundante Datenhaltung: Meist steckt in allen Arbeitsplatzrechnern noch eine eigene Festplatte, obwohl das installierte Betriebssystem und die eingesetzten Applikationen unternehmens- oder abteilungsweit identisch sind. Zentrale Datenverwaltung (teils auch unter dem Modebegriff Speichervirtualisierung bekannt) führt hier zu festplattenlosen Rechnern, die je nach Anwendung Thin, Fat oder Diskless Clients heißen.
Server und Netz belastet
Das Konzept setzt schnelle und zuverlässige Netzwerke sowie durchsatzstarke Server voraus. Klassische Implementierungen mit einem Netzwerk-Dateisystem belasten Server und Netz aber unnötig: Wann immer der Server Dateisysteme verbreitet, an denen die Clients nichts ändern, eröffnen sich wirksame Optimierungsmöglichkeiten mit Hilfe von Netzwerk-Blockgeräten (siehe Kasten “Motivation”). Diese verzichten darauf, Schreib- und Leseoperationen der Clients zu koordinieren. Die Geschwindigkeitsvorteile gelten für jeden Read-only-Fileserver, egal ob CD-Server, Dokumentenarchiv oder Server für Diskless Clients.
Traditionelles NFS
Für den Linux-Kernel ist es unerheblich, wo er sein Root-Filesystem findet: auf der lokalen Festplatte, einem SCSI-Raid, Flash-Device oder auf einem fernen Server im Netz. Für Letzteren nutzt Linux üblicherweise das Netzwerk-Dateisystem NFS. Es ist weit verbreitet, einfach zu konfigurieren und seit Jahren Bestandteil des Kernels. Sein Alter merkt man NFS jedoch an: Bis einschließlich Version 3 existiert kein ernst zu nehmendes Sicherheitskonzept. Der Server verlässt sich blind auf die User-Authentifizierung am Client und unterscheidet Clients nur anhand ihrer IP-Adresse. Bei NFS heißt eine Freigabe Export, zu definieren auf dem Server in der Datei »/etc/exports«:
/home 10.8.4.0/255.255.255.0(rw,async) /home *.mydomain.site(rw,async)
Das Beispiel exportiert die Homeverzeichnisse aller Benutzer für alle Maschinen im Subnetz 10.8.4.0 mit der Netzmaske 255.255.255.0 sowie für alle Rechner, deren IP-Adresse – rückwärts aufgelöst – Namen aus der Domain »mydomain.site« liefert.
Die in Klammern stehenden Optionen legen fest, dass Clients auf die Freigabe schreiben dürfen, der Benutzer Root bleibt jedoch davon ausgenommen. Das Schlüsselwort »async« fordert für alle Schreibvorgänge eine sofortige Bestätigung an, ohne dass der Client wartet, bis der Server diese Aktion abgeschlossen hat. Das beschleunigt das Schreiben auf Kosten der Datensicherheit.
Dass NFS per Default dem Client-Systemadministrator keinen privilegierten Zugriff gewährt, ist für Angreifer nur eine minimale Hürde. Nichts hindert sie daran, die passenden Benutzer-IDs anzunehmen und sich damit in interessanten Verzeichnissen zu bewegen.
Overhead
NFS arbeitet RPC-basiert (Remote Procedure Call). Es erledigt jeden Zugriff über ferne Funktionsaufrufe. Aus Programmierersicht ist das elegant, leidet aber unter einem deutlichen Overhead besonders bei kleinen Dateien. Die Benutzer eines NFS-Homeverzeichnisses mögen das verschmerzen, beim gemeinsamen Root-Dateisystem von vielen Diskless Clients wirkt sich der Aufwand aber deutlich nachteilig aus. Das Sys-V-Bootverfahren mit seinen vielen kurzen Startskripten ist besonders anfällig für den Overhead.
Widersprüche auflösen
Bei der Suche nach flinkeren Verfahren erweisen sich die Anforderungen Skalierbarkeit, Performance, Datenintegrität und Verfügbarkeit als teils gegensätzlich. Zum Beispiel erhöht Caching die Skalierbarkeit und Performance deutlich, vermindert aber die Datenintegrität. Ohne Zusatzmaßnahmen ist nicht mehr garantiert, dass die Daten im Cache der Clients denen des Servers entsprechen. Synchronisation verbessert zwar die Integrität wieder, das geht aber auf Kosten der Skalierbarkeit.
Bleibt die Flucht nach unten: Statt das Filesystem zu zentralisieren, ist es auch möglich, die Festplatten auf Blockdevice-Ebene aus der Ferne anzusteuern (siehe Kasten “Von Blöcken und Dateien”). Das Prinzip ist bei Storage Area Networks (SAN) etabliert, die meist auf einer eigenen, LAN-unabhängigen Fibre-Channel-Fabric basieren. Die Technik ist allerdings recht teuer. Für preiswertere Endgeräte hat die Industrie noch den I-SCSI-Standard im Angebot. Der Linux-Kernel bringt die notwendige Treiberunterstützung mit. Allerdings ist der I-SCSI-Overhead für einfache Standardarbeitsplätze noch zu hoch.
Der durchaus sinnvolle Ansatz lässt sich mit einigen Einschränkungen auch weitaus günstiger realisieren. Seit vier Jahren liegt der Code für das Network Block Device (NBD) dem Mainstream-Kernel bei. Das Modul ist recht einfach gestrickt: Es überlässt die Integritätsprüfung der übertragenen Daten dem darunter liegenden TCP. Ausgehend von NBD gibt es weitere Entwicklungen für besondere Anforderungen (siehe unten).
NBD einsetzen
Ob der Kernel NBD als Modul konfiguriert hat, zeigt sich nach dem Aufruf »modprobe nbd«. Es darf keine Fehlermeldung erscheinen. Die Userspace-Tools, etwa »/usr/bin/nbd-server«, liefern die meisten Distributionen als eigenes Paket mit. Aktuell ist die Version 2.8.4, die 2.9 steht kurz bevor [3]. Suse Linux 10.0 enthält noch Version 2.7.4, sie erfüllt aber genauso ihren Zweck.
Lokal testen
Für einen ersten Test empfiehlt es sich, Netzwerkprobleme auszuschließen und Client und Server auf derselben Maschine zu betreiben. Am einfachsten klappt das mit einer freien Partition. Alternativ eignet sich eine Containerdatei, ausgestattet mit einem beliebigen Dateisystem (Listing 1). Die Autoren haben Reiser-FS, Ext 2 und XFS erfolgreich getestet. Jedes Formatwerkzeug warnt zwar, dass es sich bei der Datei nicht um ein Blockgerät handelt (Zeile 8), das kann man aber getrost ignorieren.
Im Beispiel legt der Admin eine 100 MByte große Datei an (Zeilen 1 und 2), stattet sie mit einem Ext-2-Filesystem aus (Zeile 6), startet den NBD-Server auf Port 5000 (Zeile 14) und mountet das Device in Zeile 15 auf derselben Maschine. Die Portnummer ist frei wählbar. So lassen sich auch mehrere Server für verschiedene Container starten. Der NBD-Client setzt das geladene Kernelmodul voraus und benötigt als Parameter beim Aufruf die IP-Adresse und Portnummer des Servers, gefolgt vom Namen des lokalen Blockdevice.
| Listing 1: NBD-Test |
|---|
01 hermes:~ # mkdir /exports 02 hermes:~ # dd if=/dev/zero of=/exports/nbd-export bs=1024 count=100000 03 100000+0 Datensätze ein 04 100000+0 Datensätze aus 05 102400000 bytes (102 MB) copied, 0,987222 seconds, 84 MB/s 06 hermes:~ # mke2fs nbd-export 07 mke2fs 1.38 (30-Jun-2005) 08 /exports/nbd-export ist kein spezielles Block-Gerät. 09 Trotzdem fortsetzen? (y,n) y 10 Dateisystem-Label= 11 OS-Typ: Linux 12 Blockgröße=1024 (log=0) 13 [...] 14 hermes:~ # nbd-server 5000 /exports/nbd-export 15 hermes:~ # nbd-client 127.0.0.1 5000 /dev/nbd0 16 Negotiation: ..size = 100000KB 17 bs=1024, sz=100000 18 hermes:~ # mount /dev/nbd0 /mnt 19 hermes:~ # ls /mnt 20 . .. lost+found 21 hermes:~ # df 22 Dateisystem 1K-Blöcke Benutzt Verfügbar Ben% Eingehängt auf 23 [...] 24 /dev/nbd0 96828 13 91815 1% /mnt 25 hermes:/ # time dd if=/dev/zero of=/mnt/text count=8192 bs=1024 26 8192+0 Datensätze ein 27 8192+0 Datensätze aus 28 8388608 bytes (8,4 MB) copied, 0,038112 seconds, 220 MB/s 29 30 real 0m0.046s 31 user 0m0.008s 32 sys 0m0.036s |
Für einen Performancevergleich bietet es sich an, die Datei per Loopback-Device zu mounten: »mount -o loop /exports/nbd-export /mnt«. Die recht grobe Messmethode per Time-Aufruf (vergleiche Listing 1, Zeile 25) ergibt nun:
8192+0 Datensätze ein 8192+0 Datensätze aus 8388608 bytes (8,4 MB) copied, 0,034517 seconds, 243 MB/s real 0m0.040s user 0m0.008s sys 0m0.024s
Der Performance-Unterschied hält sich in Grenzen: Das Netzwerk-Blockdevice schreibt die Daten zehn Prozent langsamer und braucht 50 Prozent mehr Systemzeit als beim direkten Mount. Da Client und Server auf einer Maschine laufen, bremst kein Netzwerk.
Aus der Ferne
Um Client und Server auf jeweils eigenen Rechnern zu betreiben, genügt es, im »nbd-client«-Aufruf die 127.0.0.1-Adresse gegen die IP des Servers zu tauschen. Der »nbd-server« kann statt einfacher Containerfiles auch LVM-Volumes oder ganze Partitionen exportieren. Dazu tauscht man den Dateinamen gegen den Namen des speziellen Device aus: »nbd-server 5001 /dev/sda4«.
Die Verbindung zum Server trennt der Client durch den Aufruf »nbd-client -d Device«. Vorher sollte jedoch das eingehängte Blockdevice ausgemountet sein:
umount /mnt nbd-client -d /dev/nbd0
Wie der Kasten “Von Blöcken und Dateien” erklärt, führt das Schreiben auf ein von mehreren Maschinen benutztes Netzwerk-Blockdevice ins Chaos. In vielen Szenarien ist das auch gar nicht nötig. Für das Root-Filesystem von Diskless Clients, ein gemeinsames Applikationsverzeichnisses oder ein Dokumentenarchiv genügt lesender Zugriff.
Nur lesen
Mit der Option »-r« startet der Blockdevice-Server im Nur-lesen-Modus. Damit kann kein Client Änderungen am Blockdevice vornehmen. Das ist für Journaling-Filesysteme fatal. Weder Ext 3 noch Reiser-FS oder XFS ließen sich in einem Test auf einem nur lesbaren Blockdevice mounten. Die Fehlermeldungen fielen je nach Dateisystem unterschiedlich harsch aus; Abbildung 3 zeigt den Kommentar von Reiser-FS.

Abbildung 3: Der Versuch, Reiser-FS auf einem nur lesbaren Blockdevice zu mounten, schlägt fehl. Das Dateisystem will unbedingt einen entsprechenden Eintrag in sein Journal einfügen. Gemeinsam genutzte Netzwerk-Blockdevices dürfen aber nicht beschreibbar sein.
Auch der Einsatz der Read-only-Mountoption »-o ro« bringt keine Entspannung, Journaling-Filesysteme wollen immer in ihr Journal schreiben, auch wenn sie keine Files verändern. Die Dateisysteme Squash-FS [8] und Ext 2 hatten jedoch keine Schwierigkeiten, sie verzichten auf Schreibversuche.
Copy on Write
Als Alternative kennt der NBD-Server die Option »-c«, Copy on Write (Listing 2, Zeile 1). Er legt für jeden Client eine Datei an, in der er alle Änderungen speichert. Zeile 9 zeigt ein solches File. Nach einem Disconnect des Clients gehen die Änderungen allerdings verloren, obwohl die Datei selbst erhalten bleibt. Die Entwickler beschreiben die Option »-c« als nicht besonders performant. Folglich empfehlen sich Client-seitige Lösungen, etwa Union-FS oder das COW-Modul (Copy on Write, [1]).
| Listing 2: Copy on Write |
|---|
01 hermes:~ # nbd-server 5000 /exports/nbd-export -c 02 hermes:~ # nbd-client 127.0.0.1 5000 /dev/nbd0 03 hermes:~ # mount -t xfs /dev/nbd0 /mnt 04 hermes:~ # ls -al /exports 05 insgesamt 100008 06 drwxr-xr-x 2 root root 60 2006-04-03 12:39 . 07 drwxr-xr-x 23 root root 4096 2006-03-25 21:16 .. 08 -rw-r--r-- 1 root root 102400000 2006-04-03 12:33 nbd-export 09 -rw------- 1 root root 270336 2006-04-03 12:39 nbd-export-127.0.0.1-7812.diff |
Leider ist das Sicherheitskonzept von NBD bestenfalls rudimentär. Ähnlich zu NFS, jedoch deutlich unflexibler, kann der Admin eine Liste von Rechnern angeben, denen der Server Zugriff auf das Device gestattet. Weiter gehende Authentifizierung, Integritätssicherung oder gar Verschlüsselung fehlen.
Dummerweise setzen Diskless Clients voraus, dass sie an vielen Stellen im Filesystem Dateien im Betrieb anlegen oder verändern dürfen, zum Beispiel »/etc/resolv.conf« oder die diversen Temp-Verzeichnisse. Ein früherer Artikel [1] stellte dafür transluzente Dateisysteme wie Union-FS vor, um Änderungen per überlagerter RAM-Disk zu erlauben. Bei einem Neustart gehen auch hier alle Modifikationen verloren und das System ist wieder im Ausgangszustand.
Cowloop
Bei nur lesbaren Blockgeräten bietet sich zudem Cowloop an (Copy on Write Loopback-Device, [2]). Es macht das Blockgerät beschreibbar, indem es Änderungen getrennt in einem so genannten Sparse File aufbewahrt (Abbildung 4), das sich wiederum in einer RAM-Disk befinden kann. Im Vergleich zu Union-FS, das selbst für kleinste Änderungen eine Datei komplett in die beschreibbare Schicht kopiert, geht Cowloop sparsam mit dem Platz um und vermerkt nur wirklich geänderte Blöcke.

Abbildung 4: Das Cowloop-Blockgerät bedient sich bei zwei Quellen: Ein nur lesbares Device (links) dient als Basis, alle geänderten Blöcke landen in einer getrennten Datei (rechts). Die Inhalte des ursprünglichen Blockgeräts bleiben unverändert.
Nach dem Entpacken, Kompilieren und Installieren der Quellen von [2] stehen ein Kernelmodul und ein Hilfsprogramm bereit. Zusammen mit NBD lässt sich Cowloop wie folgt einsetzen:
modprobe cowloop cowdev -a /dev/nbd0 /tmp/nbd.cow mkdir /mnt/nbd-rw mount /dev/cow/0 /mnt/nbd-rw
Dieses Beispiel verknüpft das Netzwerk-Blockgerät »nbd0« mit der beschreibbaren Datei »/tmp/nbd.cow« und mountet das neue Blockgerät. Schreibvorgänge in »nbd-rw« wirken sich nicht auf das Netzwerk-Blockgerät aus. Falls sich Cowloop über ein fehlendes »/dev/cow/ctl« oder »/dev/cow/0« beschwert, dann hilft:
mkdir /dev/cow mknod /dev/cow/ctl b 241 255 ln -s /dev/cowloop0 /dev/cow/0
Nach dem Unmounten des kombinierten Blockdevice entfernt »cowdev -d /dev/cow/0« auch das Cowloop.
Union-FS arbeitet dagegen auf der höheren Dateisystemebene. Es hält geänderte Dateien in einem weiteren Filesystem fest. Die Modifikationen sind daher leicht nachvollziehbar: Das beschreibbar überlagerte Filesystem ist mit Linux-Bordmitteln auch zu durchsuchen.
Merkhilfe
Ein Nachteil der Netzwerk-Blockgeräte zeigt sich beim Update der Clients. Änderungen auf dem Server erfordern einen Neustart der Clients oder einen komplizierten Remount-Mechanismus. In der Praxis sind Änderungen aber nur bei Sicherheitsupdates oder einem Umstieg auf eine neue Ausgabe der Distribution notwendig, daher hält sich der Aufwand in erträglichen Grenzen.
Diskless Clients bleibt ein weiteres Problem: Sie können sich lokal nichts merken und beziehen daher alle Informationen über das Netz. Normalerweise erfährt der Kernel vom Bootloader oder durch einen Kernelparameter, wo sein Root-Filesystem liegt. Wenn er dies jedoch übers LAN erhält, hat er zuvor einige Hürden zu überwinden. Allen voran stehen das Aktivieren des Netzwerk-Interface und die IP-Konfiguration – beide Schritte setzten gewöhnlich ein funktionierendes Root-Filesystem voraus. Dafür existieren drei Lösungen: Kernel-Root auf NFS, Initial RAM-Disk sowie Early Userspace.
Root-Filesystem auf NFS
Seit sehr frühen Versionen beherrscht Linux die Technik “Kernel-Root auf NFS”, bei der es sein Root-Dateisystem direkt von einem NFS-Server einbindet. Das Verfahren setzt jedoch voraus, dass alle notwendigen Komponenten fest in den Kernel einkompiliert sind, inklusive jener für die Netzwerkkarte und für den NFS-Mount. Dazu kommt die IP-Autokonfiguration, die einen DHCP-Client im Kernel erfordert.
Probleme treten in diesem Szenario auf, sobald der Rechner eine neue Netzwerkkarte erhält oder ein Bugfix des Treibers anfällt. In beiden Fällen ist Neukompilieren des kompletten Kernels nötig.
Initial RAM-Disk
Deutlich eleganter ist eine Initial RAM-Disk, technisch als Blockdevice im RAM realisiert, der ein formatiertes Dateisystem mit allen Tools, Skripten und Modulen für das Setup bis hin zum Mount des Root-Filesystems enthält.
Auch dieser Ansatz hat aber Nachteile: Hoher Aufwand beim Erstellen der Initial RAM-Disk, Overhead durch Blockdevice und Filesystem sowie der schwierige Übergang aus dem kleinen Root-Filesystem der Initial RAM-Disk auf das neu eingehängte Root-Filesystem des Clients. Den Übergang organisiert »pivot_root«, »freeramdisk« gibt am Ende den Speicher der RAM-Disk wieder frei.

Abbildung 5: Die im Artikel vorgestellten Techniken lassen sich durchaus kombinieren. Blockgeräte tragen meist blockbasierte Dateisysteme. Sonderfälle sind Cowloop oder Union-FS, die – basierend auf Blockgeräten oder Dateisystemen – eine neue beschreibbare Schicht bilden.
Early Userspace
Die jüngste Entwicklung namens Early Userspace wird mittelfristig sowohl Kernel-Root auf NFS als auch die Initial RAM-Disk ablösen. Es handelt sich um spezielle Kernelstrukturen (Initram-FS), die ein einheitliches Root-Filesystem abbilden. Es lässt sich beispielsweise als Temp-FS direkt in den Kernel einkompilieren oder (ähnlich wie die Initial RAM-Disk) als CPIO-Archiv auch separat halten und laden.
Für den Übergang zum einzubindenden Root-Filesystem sorgt seit Kernel 2.6.15 nicht mehr »pivot_root«. Es genügt nun, den Mountpoint nach »/« zu verschieben. Die Inhalte, die vorher an dieser Stelle lagen, löscht das kleine Programm »run-init« (zu finden in der Klibc).
Bordmittel allein genügen nicht, um NBD zu verwenden, das spätere Root-Filesystem aus mehreren Teilen zusammenzusetzen und alles mittels Union-FS zu verbinden. Die einzelnen Mount-Vorgänge und das Laden der entsprechenden Module fasst am besten ein Shellskript zusammen (gekürzte Fassung in Listing 3), das den Namen »init« erhält und im Initram-FS steht.
| Listing 3: Init-Skript |
|---|
01 #!/bin/sh
02
03 # IP-, NFS- und (D)NBD-Parameter aus der Kernel-Kommandozeile lesen
04 for opts in ${KCMDLINE} ; do
05 case ${KCMDLINE} in
06 ip=*)
07 # IP-Konfiguration: client-ip:server-ip:gateway:netmask
08 IPINFO=${opts#ip=};;
09 nfsroot=*)
10 # NFS-Server und Pfad
11 NFSROOT=${opts#nfsroot=};;
12 nbdroot=*)
13 # NBD-Konfiguration: server:port
14 NBD=nbd # name of kernel module
15 NBDOPT=${opts#nbdroot=};;
16 dnbdroot=*)
17 # DNBD-Konfiguration: server:port
18 NBD=dnbd # name of kernel module
19 NBDOPT=${opts#dnbdroot=};;
20 dcsize=*)
21 # RAM-Cache-Größe für DNBD
22 DNBDCACHESIZE=${opts#dcsize=};;
23 esac
24 done
25 RWDIR=/dev/shm
26
27 # Root-Filesystem via NBD/DNBD importieren
28 if [ -n "${NBD}" ] ; then
29 nbdhost=${NBDOPT%:*}
30 nbdopt=${NBDOPT#*:}
31 nbdport=${nbdopt%,*}
32 nbdfs=${nbdopt#$nbdport*}
33 if [ -z "$nbdfs" ]; then
34 RFST=ext2;
35 else
36 RFST=${nbdfs#*,};
37 fi
38 echo "Diskless client using ${NBD} on $nbdhost:$nbdport with $RFST"
39 modprobe ${RFST}
40 case "${NBD}" in
41 nbd)
42 # Network Block Device des Standardkernels
43 sleep 2
44 nbd-client $nbdhost $nbdport /dev/nbd0
45 RDEV=/dev/nbd0;;
46 dnbd)
47 # DNBD (Distributed Network Block Device)
48 if [ -z $nodnbdcache ] ; then # variable not really used yet
49 mkdir /dnbd
50 mount -n -o 'size=$dcsize' -t tmpfs tmpfs ${RWDIR}
51 cat /dev/zero > ${RWDIR}/cache 2>/dev/null
52 clientopt="-c ${RWDIR}/cache"
53 fi
54 sleep 2
55 while ! dnbd-client -b $nbdhost -d /dev/dnbd0 $clientopt ; do
56 sleep 1
57 done
58 RDEV=/dev/dnbd0;;
59 esac
60 RWRO="ro"
61 if [ -n "${COWLOOP}" -a -x /bin/cowdev ] ; then
62 modprobe cowloop
63 echo "Using Copy-on-Write block device for rw access"
64 mount -n -t tmpfs -o size=50% ramfs ${RWDIR}
65 mkdir /dev/cow && cp -a /tmp/ctl /dev/cow
66 ln -s /dev/cowloop0 /dev/cow/0
67 cowdev -a ${RDEV} ${RWDIR}/nbd.cow
68 usleep 10
69 RWRO="rw"
70 RDEV=/dev/cow/0
71 fi
72 sleep 2
73 mount -n -t $RFST -o $RWRO $RDEV /mnt
74
75 # Root-Filesystem via NFS importieren
76 elif [ -n NFS ]
77 modprobe nfs
78 portmap
79 NFSRO=nfs
80 mount -t nfs [...]
81 fi
82
83 # Union-FS aktivieren
84 echo "Using UnionFS for rw access"
85 RWDIR=/dev/shm
86 mkdir -p ${RWDIR}/union ${RWDIR}/uniontmp
87 mount -n -t tmpfs none ${RWDIR}/uniontmp
88 mount -n --move /mnt /root
89 mount -n -t unionfs -o dirs=${RWDIR}/uniontmp=rw:/root=${NFSRO}ro none /mnt
90 mkdir -p /mnt/uniontmp
91 mount -n --move ${RWDIR}/uniontmp /mnt/uniontmp
92 mount -n --move /root /mnt/mnt
93
94 # Pivoting nach neuer Art
95 exec run-init -c dev/console /mnt /sbin/init
|
NFS und NBD/DNBD im Initram-FS
Das Skript muss einige wichtige Konfigurationsdaten erhalten. Wer diese Parameter nicht in einer Datei im Initram-FS speichern möchte, weil er dann für jede bootende Maschine ein eigenes Filesystem-Image braucht, übergibt die Werte am Bootprompt an den Kernel. Hier kann er beliebige Strings hineinschreiben, die der Kernel zwar nicht versteht, die aber das Init-Skript anschließend auswertet. Diese Taktik verwendet beispielsweise auch Knoppix, um Konfigurationsoptionen von der Benutzerauswahl im Bootmenü in das später laufende Linux-System zu transportieren.
Die Zeilen 4 bis 24 von Listing 3 werten die Kommandozeile aus und schreiben die Parameter in Variablen. Der Kernelstring für NFS (»nfsroot= Server:Pfad«) dient als Vorbild für das Format von Root on NBD/DNBD. Bei NBD ist die Angabe der Portnummer Pflicht, die Option ist »nbdroot= Server:Port,Filesystem«. Bei DNBD heißt das Schlüsselwort »dnbdroot=…«. Die Portnummer dahinter darf entfallen, das Komma muss aber bleiben. Zeile 22 liest die erlaubte Größe für das DNBD-Cachefile. Es lassen sich noch weitere Parameter übergeben, etwa für das Diff-File von Cowloop.
Theoretisch könnte auch ein DHCP-Server die Konfigurationsdaten verteilen [11]. Dazu wäre jedoch ein DHCP-Client wie »dhclient« nötig, der benutzerdefinierte Variablen kennt. Bei der IP-Konfiguration im Initram-FS kommt aber meist ein recht simpler Client aus dem Klibc-Paket [15] zum Einsatz.
Ziel-Root
Die Zeilen 28 bis 59 von Listing 3 holen das Root-Filesystem vom Server per NBD oder DNBD. Bei NBD genügt der Aufruf in Zeile 44; DNBD verlangt etwas mehr Arbeit. Der Block in den Zeilen 61 bis 71 verwendet auf Wunsch Cowloop, um das Blockdevice beschreibbar zu machen. Diese Tatsache vermerkt Zeile 69 in der Variablen »RWRO«.
Zeile 73 mountet das Root-Filesystem vorübergehend nach »/mnt«; bei NFS erledigt Zeile 80 diesen Schritt. Über den Mountpoint legen die Zeilen 86 bis 92 ein Union-FS, sodass es nach dem Beenden des Initram-FS transparent über dem ganzen Root-Filesystem liegt. Zeile 95 räumt auf und übergibt die Kontrolle an den gewöhnlichen Bootprozess.
Für Init-Skripte mit wenigen einfachen Kommandos ist das Klibc-Paket eine gute Wahl. Es bringt neben der schlanken Kernel-C-Standardbibliothek eine einfache Shell mit und Befehle wie »mount«, »nfsmount« und »ipconfig«. Letzterer eignet sich für statische IP-Konfigurationen sowie als DHCP-Client. Das Initram-FS verpackt neben dem Init-Skript eine Shell, die gewünschten Hilfskommandos und alle verwendeten Bibliotheken.
Filesystem verpacken
Der Kernel erwartet »init« auf oberster Verzeichnisebene. Fürs Einpacken eignet sich folgendes Kommando:
cd Initramfs-Verzeichnis find . ! -name "*~" | cpio -H newc --create | gzip -9 > ../initramfs.gz
Der Find-Aufruf ignoriert Backup-Kopien, danach legt CPIO ein Archiv im portablen SVR4-Format an und Gzip komprimiert es nach Kräften.
Neben NBD gibt es weitere freie Implementierungen eines Netzwerk-Blockgeräts, etwa ENBD [4], GNBD [5] oder ANBD [6], siehe auch Tabelle 1. Deren Blockgerätetreiber sind noch kein Bestandteil des Kernels, auch die meisten Distributionen liefern sie nicht mit. Den Mehraufwand für eine Installation aus den Quellen belohnt ENBD mit Fehlerbehandlung, automatischem Wiederaufbau abgebrochener Verbindung, dem gleichzeitigen Benutzen mehrerer Kanäle und vielem mehr.
| Tabelle 1: NBD-Varianten |
|
|---|---|
| Name | Erklärung |
| NBD | Das Network Block Device ist Vorläufer aller Netzwerk-Blockgeräte für Linux. Es gibt derzeit einen aktiven Entwickler und eine Mailingliste mit überschaubarem Traffic. [3] |
| ANBD | Another Network Block Device ist eine kompatible NBD-Erweiterung aus dem Jahr 2003. Sie erlaubt Multithreading und verspricht bessere Fehlermeldungen als die Vorlage. [6] |
| ENBD | Das Enhanced Network Block Device wird von einem aktiven Entwickler vorangetrieben, es existiert eine Mailingliste, auf der ein paar Meldungen pro Monat zusammenkommen. ENBD erweitert NBD um einen automatischen Restart bei Verbindungsverlusten, Authentifizierung und die Unterstützung von Wechsellaufwerken. [4] |
| DNBD | Das Distributed Network Block Device arbeitet mit UDP als Transportprotokoll und kann damit auf Multicast, Client-seitiges Caching und Server-Redundanz setzen. Es unterstützt nur RO-Exporte. [7] |
| GNBD | Das Global Network Block Device ist die Basis für GFS (Global Filesystem). [5] |
ATA übers Netz
Mit AoE (ATA over Ethernet) gibt es eine Technik für blockorientierten Datenaustausch, die etwas an I-SCSI erinnert. Während Letzteres recht hohen Aufwand treibt beim Kapseln der SCSI-Aufrufe in IP-Paketen, was bei intensiver Plattenbenutzung eine erhebliche Last für die CPU bedeutet, begnügt sich AoE mit der Ethernet-Schicht. Es geht davon aus, dass die Verlagerung von Festplatten in das lokale Netzwerk nur in Bereichen mit ausreichend leistungsfähiger Ethernet-Infrastruktur stattfindet.
Funktauglich
Speziell für den Diskless-Betrieb in Funknetzen ist DNBD [7] gedacht (Distributed Network Block Device). In diesem Szenario assoziieren sich Clients mit einem Accesspoint und teilen sich die Sendefrequenz. Zu jedem Zeitpunkt sendet maximal ein Client. Bremsend wirkt auch die recht niedrige Bandbreite, bei den verbreiteten IEEE-Standards 802.11b/a/g sind es maximal 54 MBit/s. Weil sich alle Clients das Übertragungsmedium teilen, sinkt die verfügbare Datenrate mit jedem zusätzlichen Gerät, das gleichzeitig bootet.
DNBD versucht die anfallende Datenmengen so weit wie möglich zu minimieren und dabei die Besonderheiten eines Funknetzes auszunutzen. Es verzichtet dazu auf Locking-Mechanismen und erlaubt nur lesenden Zugriff. Zudem sehen alle angeschlossenen Clients den DNBD-Datenverkehr (Abbildung 6) und cachen jeden Block, den der Server ausliefert – egal für welchen Client er gedacht war. Die Chancen stehen gut, dass auch die anderen Clients diesen Block irgendwann brauchen.

Abbildung 6: Das Distributed NBD eignet sich besonders für Funknetze: Fordert ein Rechner einen Block an, sendet der Server ihn per Multicast gleichzeitig an alle Clients. Das spart Zeit und Bandbreite, wenn ein anderer denselben Block braucht.
Gut geteilt
Das gemeinsam benutzte Medium bei Funknetzen erlaubt es prinzipiell, die Kommunikation anderer Clients mitzuhören – leider unterstützen nicht alle WLAN-Karten den so genannten Promiscuous- oder Monitor-Modus. DNBD setzt deshalb IP-Multicast ein, um eine Gruppe von Clients gleichzeitig zu adressieren. Damit das klappt, ist UDP als verbindungsloses Protokoll vorgesehen. DNBD behandelt Paketverluste und andere Kommunikationsprobleme selbst. Es verwendet IP-Multicast-Adressen aus dem Netz 224.0.0.0/4 – für Experimente empfiehlt sich aber 239.0.0.0/8.
Bevor ein DNBD-Client auf ein Netzwerk-Blockgerät zugreift, versucht er vorhandene Server zu finden, indem er spezielle Pakete an die gegebene Multicast-Adresse schickt. Die Server liefern in ihren Antworten Informationen über das vorhandene Blockgerät, welche die Clients zur Konfiguration benutzen. Anschließend können sie Blöcke anfragen. Für Hochverfügbarkeit lassen sich DNBD-Server zudem replizieren. Die Clients finden neue Server automatisch. Sie sammeln fortlaufend statistische Daten, um den Server mit der niedrigsten Antwortzeit auszuwählen.
Gutes Caching setzt immer Lokalitätseigenschaften voraus. Die Clients sollten in einem begrenzten Zeitraum auf dieselben Blöcke zugreifen, um von dem Verfahren zu profitieren. Dies ist zum Beispiel bei einem gleichzeitigen Start vieler Diskless Clients in Funknetzen gegeben. Es klappt außerdem bei Multimedia-Inhalten: Mehrere Clients spielen eine DVD über ein WLAN ab, wobei der Cache auch bei zeitversetztem Abspielen funktioniert.
Die Projektseite enthält eine Installationsanleitung: DNBD per Subversion herunterladen und übersetzen. Kernelquellen oder -header müssen vorhanden und konfiguriert sein; gegebenenfalls ist in »kernel/Makefile« der Pfad zum Kernel anzupassen. Wie auch andere Netzwerk-Blockgeräte hat DNBD mit bestimmten Kombinationen von I/O-Scheduler und Dateisystem Probleme, vor allem im Diskless-Betrieb (siehe Kasten “Fallen”).
| Fallen |
|---|
Beim Testen der einzelnen Kombinationen von Dateisystem, Blockgerät und Netzwerkschicht traten etliche Probleme auf. Da fast alle Komponenten im Kernel ablaufen, führen Bugs in Programmen und Konfigurationen schnell zum Systemstillstand. Es empfiehlt sich deshalb, anfangs eigene Experimente in einer virtuellen Umgebung wie VMware, Qemu oder User-Mode-Linux (UML) durchzuführen. Damit lassen sich sogar virtuelle Netzwerke mit mehreren Clients aufsetzen.
|

Abbildung 7: Squash-FS patcht den Kernel – das Patch für Linux 2.6.15 passt auch noch für 2.6.16. Bei der Konfiguration versteckt sich der Eintrag unter »File Systems | Miscellaneous Filesystems | SquashFS 3.0«. Hier ist das Filesystem als Modul konfiguriert.
Booten mit Hindernissen
Während mit PXE-Linux (Pre-boot Execution Environment, [13]) und Etherboot [14] seit längerem PCs über Ethernet booten können, hat sich für den Start über ein Wireless LAN noch kein Standard herausgebildet. Statt den Kernel und die RAM-Disk auf dem Luftwege zur Maschine zu bringen, bieten sich USB-Sticks oder Compactflash-Karten am IDE-Port als Kernelträger an. Zudem erhält »init« im Initram-FS die Aufgabe, vor der IP-Konfiguration die WLAN-Parameter einzustellen.
Sparfimmel
Eine klassische Methode, um Datenmengen weiter zu verringern, ist die Kompression. Auf der Transportschicht nutzen Netzwerke diese Technik nur selten. Bei IPsec ist eine komprimierte Übertragung zwar möglich, jedoch nur mit erheblichem Aufwand. Sinnvoller ist es, an den Dateien oder dem Dateisystem anzusetzen.
Das Spezialdateisystem Squash-FS [8] ist zwar für Embedded Devices gedacht, die es nur lesen. Nach einigen Anpassungen passt aber auch die Standardinstallation eines Linux-Systems hinein, um den Container per Netzwerk-Blockgerät auf einem anderen Rechner verfügbar zu machen. Die Daten liegen dann in kompakter Form auf dem Server und belasten Server und Netz weniger. Zwar müssen die Clients die Daten wieder dekomprimieren, das bleibt aber ohne Folgen für die Skalierbarkeit. Auch Squash-FS lässt sich per Union-FS beschreibbar überlagern. Die Kombination mit Cowloop ist nicht brauchbar, da das Dateisystem per se nicht beschreibbar ist.
Squash-FS bauen
Squash-FS ist nicht so bequem zu kompilieren wie DNBD. Es verlangt nach Kernelquellen und patcht dort das neue Filesystem hinein: »patch -p1 < Pfad/squashfs3.0-patch«. Leider hinken Squash-FS-Version oft hinter den Kernelversionen her. So ist das aktuelle Squash-FS 3.0 für Linux 2.6.15 gedacht. Glücklicherweise passt das Patch auch auf den 2.6.16er Kernel. Anschließend enthält der Menüpunkt »File Systems | Miscellaneous« den Eintrag »SquashFS 3.0«, den man am besten als Modul anwählt (Abbildung 7) und dann mit »make prepare modules« baut.
Das Userspace-Programm »mksquashfs« ist separat zu kompilieren. Auf der Servermaschine genügt dieses Packprogramm bereits, es erzeugt einen Squash-FS-Container: »mksquashfs Quellverzeichnisse Zieldatei -e Exclude«. Erst für das Mounten des Containers sind die passenden Kernelmodule nötig.
Der Container darf maximal 4 GByte groß sein. Durch die Kompression fasst das Dateisystem in der Praxis 10 GByte und mehr – das genügt für eine typische Linux-Installation. Das Erzeugen des Squash-FS-Containers dauert aber recht lange. Ein normal belasteter 2-GHz-Opteron benötigte für 8,4 GByte Quelldaten 32 Minuten. Der Container war in einem Test anschließend 2,9 GByte groß. Ein zweites Ausgangssystem von 5,6 GByte Umfang schrumpfte auf derselben Maschine im unbelasteten Zustand binnen 26 Minuten auf 1,9 GByte.
Schlank und effizient
Das gute Ergebnis rechtfertigt also den hohen Zeitaufwand. Es ist auch nur selten nötig, ein neues Root-Filesystem für Clients einzupacken, dem steht eine überzeugende Reduktion der Netzwerkbelastung gegenüber, verglichen mit unkomprimiertem Ext 2 auf demselben Blockdevice oder gar NFS. Angesichts der Leistungen moderner CPUs bleibt der Dekompressionsaufwand auf Client-Seite vernachlässigbar – Squash-FS ist schließlich für Embedded Systems mit viel schwächeren CPUs ausgelegt.
Neben Squash-FS gibt es noch weitere Vertreter dieser Klasse. Dateisysteme wie Ziso-FS [9] komprimieren die Daten auf ähnliche Weise. Auch Cloop (Compressed Loopback Device, [10]) sollte in Kombination mit einem Netzwerk-Blockgerät funktionieren.
Gut geblockt
NFS produziert einen erheblichen Overhead, der sich besonders bei kleinen Dateien bremsend auswirkt. Gerade die typischen Runlevel-, Systemsetup- und Adminskripte eines Linux-Systems machen NFS das Leben schwer. Hinzu kommen die vielen neuen Spielereien der modernen Distributionen, die für Filesystem-Monitore und Ähnliches einen stetigen NFS-Datenstrom erzeugen.
Network Block Devices arbeiten besser mit den internen Caches des Kernels zusammen, sodass ein inaktives System so gut wie keinen Netzwerktraffic erzeugt. Nun liest Linux beim Öffnen einer Datei den Block nur einmal, um Permissions, Dateibesitzer oder das letzte Änderungsdatum zu bestimmen und ein Filehandle zu holen. Diese Vorgänge erfordern bei NFS eine ganze Reihe von RPC-Aufrufen (siehe Kasten “Benchmarks”).
Aber auch der NFS-Betrieb lässt sich optimieren. Wenn typische Setup- und Konfigurationsroutinen in einer Initial RAM-Disk stattfinden, reduziert sich der Abstand von NFS zu den Blockdevices. Interessant bleibt jedoch immer der Einsatz von Squash-FS: In Read-only-Szenarien reduziert die Vorkompression des Dateisystems den Netzwerktraffic und nutzt die Blockcaches effizienter.
Deutliche Auswirkungen hat der Einsatz von Union-FS oder Cowloop. Letzteres ist beschränkt auf die Zusammenarbeit eines Blockdevice mit einem beschreibbaren Dateisystem. Union-FS taugt für alle Szenarien und reduziert ebenfalls den Datenverkehr. Zudem hilft es gerade im Bereich der typischerweise kleinen Konfigurationsdateien dabei, das teure Temp-FS im RAM zu entlasten.
HA inklusive
Hochverfügbarkeit ist mit NFS schwer zu erreichen [12]. Das spezialisierte DNBD agiert hier deutlich souveräner und schaltet transparent für den Client auf einen anderen Server um. Der Admin kann Server sogar – von allen Benutzern unbemerkt – dynamisch hinzufügen und entfernen. So umgeht er elegant den für Diskless Clients kritischen Single Point of Failure bei einem Serverausfall.
Die Linux Network Block Devices sind nicht angetreten, um die klassischen Netzwerkdateisysteme aufs Altenteil zu schicken. Jedoch können sie erfolgreich einige Nischen besetzen und dort bessere und deutlich performantere Lösungen anbieten, als es NFS, Samba & Co. vermögen. (fjl)
| Infos |
|---|
| [1] Dirk v. Suchodoletz und Thorsten Zitterell, “Hochstapler – Überlagerte Dateisysteme in der Praxis”: Linux-Magazin 10/05, S. 34
[2] Cowloop, Copy on Write Loopback: [http://www.atconsultancy.nl/cowloop/] [3] NBD (Network Block Device): [http://nbd.sourceforge.net] [4] ENBD (Enhanced NBD): [http://www.it.uc3m.es/ptb/nbd/] [5] GNBD (Global NBD): [http://sources.redhat.com/cluster/gnbd/] [6] ANBD (Another NBD): [http://www.aros.net/~ldl/anbd/] [7] DNBD (Distributed NBD): [http://lp-srv02a.ruf.uni-freiburg.de/trac/dnbd/] [8] Squash-FS: [http://squashfs.sf.net] [9] Ziso-FS: [http://www.kernel.org/pub/linux/utils/fs/zisofs/] [10] Cloop (Compressed Loopback Device): [http://www.knoppix.net/wiki/Cloop] [11] K. Agouros, “Zahlen Meister – DHCP-Grundlagen”: Linux-Magazin 02/02, S. 54 [12] André Bonhôte, “Die Kraft der zwei Herzen – Hochverfügbarer NFS-Server mit Linux-Heartbeat”: Linux-Magazin 07/03, S. 46 [13] PXE: [http://syslinux.zytor.com/pxe.php] [14] Etherboot: [http://etherboot.sf.net] [15] Klibc: [http://www.kernel.org/pub/linux/libs/klibc/] |
| Die Autoren |
|---|
| Thorsten Zitterell ist Mitarbeiter am Lehrstuhl für Betriebssysteme der Uni Freiburg und beschäftigt sich mit Echtzeit-Betriebssystemen für eingebettete Mikrosysteme.
Dirk von Suchodoletz ist Assistent am Lehrstuhl für Kommunikationssysteme und ständig auf der Suche nach intelligenten Konzepten für Diskless Clients unter Linux. |








