NFS und SCM – GIT vs Hg vs SVN
Ha! 5 Akronyme in 8 Wörtern. Das muss man erst mal hin bekommen.
Nicht Verpassen: Unseren Artikel über Repository Hosting.
Worum geht es ?
Wir nutzen selber massiv Versionsverwaltungssysteme (=Version Control Systems = VCS = SCM = Source Code Management) für die Verwaltung unserer eigenen Projekte und möchten das auch in unserer Hosting-Umgebung im Kontext einer Live & Staging Umgebung für Entwickler zur Verfügung stellen. Innerhalb unserer verteilten Serverstruktur setzen wir unter anderem NFS für große Storages ein. So weit so gut. Schmeißt man nun beides Zusammen: NFS als NAS Dateisystem und z.B. SVN (=Subversion) als VCS, dann stößt man schnell auf Probleme.
Ich habe mir mal drei gängige VCS (GIT, SVN, Hg aka Mercurial) und ihr Verhalten in einer NAS Umgebung angesehen. Folgend das Resultat dieses Ausfluges..
Welches NAS ?
NFS – alt aber verlässlich
NFS ist unter *NIX nach wie vor ein sehr verbreitetes Netzwerkdateisystem. Seine Stabilität wird seit langen Jahren durch abertausenden Installationen bewiesen und es muss sich in Sachen Geschwindigkeit keineswegs verstecken – zumindest wenn es um Durchsatz geht. Leider ist nicht nur positives darüber zu sagen. Hauptkritikpunkt ist aus meiner Perspektive die fehlende Ausfallsicherheit welche neuere, verteilte Dateisysteme inzwischen liefern. Zwar gibt es Bestrebungen hier auch wieder “mitspielen” zu können, aber noch ist das zumindest nicht offiziell Teil von NFS. Als nächstes wird häufig die fehlende Sicherheit genannt, die inzwischen aber – wenn sorgfältig konfiguriert – kein Problem mehr darstellen sollte. Das Problem welches mir im täglichen Arbeiten mit NFS am meisten auf-stößt ist die leider sehr, sehr mäßige Geschwindigkeit in Sachen Dateioperationen. Das macht sich besonders stark bemerkbar (wie auch im folgenden Test), wenn mit sehr vielen kleinen – z.B. leeren – Dateien gearbeitet wird. Nichts desto trotz sehe ich leider (noch!) keine stabile Konkurrenz.
An dieser Stelle vorweg: Es gibt einige bekannte lock-Probleme mit SVN + NFS, die man aber umgehen kann.
Samba – leider ein “anderer Ansatz”
Aufgrund eines Videocasts über Loadbalancing mit Sebastian Heinisch über seine Website feuerwache.net hab ich mir Samba nochmal genauer für den NAS Einsatz angesehen – aber so wie ich es sehe, prallen da zwei nicht ganz vereinbare Ideen aufeinander. Ein User-Verzeichnis mit dessen Rechten beim Login einbinden oder ein ganzes Root-Verzeichnis mit multiplen (Web)Usern und deren UIDs/GIDs zu mounten scheint dann doch leider was ganz anders zu sein und nicht mit den Zielen der Samba Entwickler vereinbar. Leider. Gerade mit vielen Dateioperationen ist Samba nach meinen Benchmarks deutlich NFS überlegen (im Durchsatz hinkt es dann aber wieder deutlich hinterher).
Und was es sonst noch gibt
Es gibt einige viel versprechende “neue” Netzwerkdateisysteme (GlusterFS, XtreemFS und einige andere) allerdings konnte ich mich mit keinem von denen in Sachen Stabilität oder Konfigurationsaufwand anfreunden – eine umfangreiche Beschreibung der Pros und Cons würde an dieser Stelle den Rahmen sprengen.
Benchmark – die nackten Fakten
Durchgeführt habe ich das Ganze auf unserem Büro Server, Dual Core 3Ghz, genügend RAM. Der per NFS exportierte Speicher liegt in einer virtuellen Maschine (Xen). Mein Rechner hat den Client gemimt (ein bisschen langsamer, da voll verschlüsseltes Dateisystem). Die Werte selber sind natürlich in Relation zueinander interessant – nicht absolut.
Angebunden ist der NFS Client über ein schnelles lokales Gbit.
SVN wird in der Version 1.6.4. eingesetzt, GIT 1.6.5 und Mercurial 1.3.1 (die beiden letzteren sind aus dem aktuellen debian backports, da die “stable” Versionen leider sehr unhandlich sind, Subversion habe ich der “Fairness”-halber dann auch in der Backport Versionierung genommen).
Aufsetzen der Umgebung
Zu erst einmal ein paar Dummy-Daten zum spielen:
#> mkdir /tmp/dummy && cd /tmp/dummy
#> for i in $( seq 1 20 ); do \
mkdir dir$i; \
for j in $( seq 1 100 ); do \
echo "$i/$j"; touch "dir$i/file$j"; touch "file-$i-$j"; \
done; \
done
Das erstellt 4000 Dateien in 20 Verzeichnissen. Ein nicht unwahrscheinlicher Arbeitssatz (eher klein).
Um einen möglichst extremen Fall zu schaffen werden sowohl die Clients als auch die Repositories im NFS liegen.
#> mkdir -p /mnt/nfs-test/svn-client && cp -r dummy/* /mnt/nfs-test/svn-client/ #> mkdir -p /mnt/nfs-test/git-client && cp -r dummy/* /mnt/nfs-test/git-client/ #> mkdir -p /mnt/nfs-test/hg-client && cp -r dummy/* /mnt/nfs-test/hg-client/
Danach die jeweiligen Repositories.
#> mkdir -p /mnt/nfs-test/svn-repo && svnadmin create /mnt/nfs-test/svn-repo #> mkdir -p /mnt/nfs-test/git-repo && cd /mnt/nfs-test/git-repo && git init #> mkdir -p /mnt/nfs-test/hg-repo && cd /mnt/nfs-test/hg-repo && hg init
Als letztes kommt der initiale Checkout der Clients (geht bei allen sehr schnell ohne Daten, daher sind die Werte hierfür nicht interessant).
#> cd /mnt/nfs-test/svn-client && svn co file:///mnt/nfs-test/svn-repo/ . #> cd /mnt/nfs-test/git-client && git clone file:///mnt/nfs-test/git-repo/ . #> cd /mnt/nfs-test/hg-client && hg clone file:///mnt/nfs-test/hg-repo/ .
Initialisierung, erster Commit
Die Kommandos werden alle in den jeweiligen Client-Verzeichnissen ausgeführt. Einmal via NFS (auf dem Client) und einmal “lokal” auf dem NFS Server selber zum Vergleich.
| add | status | commit | ||||
|---|---|---|---|---|---|---|
| SVN NFS |
svn add * |
7m34s |
svn status |
0m4s |
svn commit -m 'Init' |
22m27s |
| SVN local |
svn add * |
0m8s |
svn status |
0m0.2s |
svn commit -m 'Init' |
0m29s |
| GIT NFS |
git add * |
0m5s |
git status |
0m2s |
git commit --allow-empty -a -m 'Init' && git push origin master |
0m2s |
| GIT local |
git add * |
0m0.2s |
git status |
0m0.4s |
git commit --allow-empty -a -m 'Init' && git push origin master |
0m0.1s |
| HG NFS |
hg add * |
0m1s |
hg status |
0m2s |
hg commit -A -m 'Init' && hg push file:///../ |
5m52s |
| HG local |
hg add * |
0m0.8s |
hg status |
0m0.2s |
hg commit -A -m 'Init' && hg push file:///../ |
0m4s |
Ich denke hier sieht man schnell, dass SVN erhebliche Probleme in einer NFS Umgebung hat. Ein strace -p zeigt schnell woran das liegt – eine Unmenge an Dateioperatioen, z.B. die ganzen lock-Dateien die vor jedem commit erstellt und danach wieder gelöscht werden. Desgleichen beim commit via Mercurial über NFS.
Nun zu ein paar anderen – zum einen erklärenden, zum anderen interessanten – statistischen Daten über die erstellten Repository- und Clientverzeichnisse.
| Dateien | Verzeichnisse | Speicherplatz (KB) | |
|---|---|---|---|
| orginal | 4000 | 20 | 152 |
| SVN repo | 30 | 10 | 1256 |
| SVN client | 8021 | 188 | 1640 |
| GIT repo | 20 | 17 | 160 |
| GIT client | 4025 | 41 | 652 |
| HG repo | 4009 | 23 | 16364 |
| HG client | 8011 | 43 | 16740 |
Wie man sehen kann, belegt Mercurial überproportional viel Speicher (das 100 fache des Ausgangswertes). SVN verschwendet immerhin noch 10 mal so viel Platz wie die original Daten und fällt negativ durch fast den selben Faktor für Verzeichnisse auf (nicht gut für NFS). GIT verhält sich relativ schlank.
Arbeiten
Nun mal ein paar “real life” Aufgaben:
- Auschecken des nun nicht mehr leeren Repositories von einem neuen Client (2)
- Löschen von ein paar Arbeitsdaten (dir1-5) auf Client 1
- Danach wieder ein Update von Client 2
Jeweils wieder via NFS und lokal.
| checkout (client2) | del (client1) | commit (client1) | update (client2) | |||||
|---|---|---|---|---|---|---|---|---|
| SVN NFS |
svn co file:///./ . |
9m18s |
svn del dir1 dir2 dir3 dir4 dir5 |
0m53s |
svn commit -m 'Loeschen' |
1m0s |
svn up |
1m4s |
| SVN local |
svn co file:///./ . |
0m4s |
svn del dir1 dir2 dir3 dir4 dir5 |
0m0.5s |
svn commit -m 'Loeschen' |
0m2s |
svn up |
0m0.5s |
| GIT NFS |
git clone file:///./ . |
2m31s |
git rm -r dir1 dir2 dir3 dir4 dir5 |
0m12s |
git commit -a -m 'Loeschen' && git push .. |
0m4s |
git pull |
0m9s |
| GIT local |
git clone file:///./ . |
0m2s |
git rm -r dir1 dir2 dir3 dir4 dir5 |
0m0.3s |
git commit -a -m 'Loeschen' && git push .. |
0m0.2s |
git pull |
0m1s |
| HG NFS |
hg clone file:///./ . |
2m22s |
hg remove dir1 dir2 dir3 dir4 dir5 |
0m10s |
hg commit -A -m 'Loeschen' && hg push .. |
0m3s |
hg pull && hg update |
0m15s |
| HG local |
hg clone file:///./ . |
0m2s |
hg remove dir1 dir2 dir3 dir4 dir5 |
0m0.2s |
hg commit -A -m 'Loeschen' && hg push .. |
0m0.4s |
hg pull && hg update |
0m1s |
Auch hier sieht man leider, dass SVN aufgrund der vielen generierten Dateioperationen gar nicht mit NFS klar kommt. Als letzten Test versuche ich eine reguläre “Änderung -> Commit -> Änderung -> Commit ..” Situation zu simulieren indem ich 100 Änderungen an jeweils 10 Dateien durchführen lassen. Nach jeder Änderung folgt ein Commit. Gemessen wird dann die Gesamtdauer aller Commits. Hier ein Script-Schnipsel welches für den SVN Client genutzt wurde als Beispiel:
#!/bin/bash
for i in $( seq 1 10 ); do
for j in $( seq 1 10 ); do
date >> file-1-$j
done
svn commit -m "Changeset $i"
done
Und das sind die Resultate:
| SVN NFS | 12m50s |
| SVN local | 1m46s |
| GIT NFS | 2m31s |
| GIT local | 0m7s |
| HG NFS | 3m48s |
| HG local | 0m36s |
Fazit
Wie es aussieht schließt der Einsatz von NFS leider Subversion fast komplett aus (die Repos werden ja über die Zeit nicht kleiner). Übrig bleiben GIT und Mercurial wobei ich insgesamt GIT als “Sieger” in Sachen Geschwindigkeit und Schlankheit sehe. Wie immer gilt aber natürlich: Jeder Topf findet einen Deckel. Ich denke das hier skizzierte Szenario ist sehr speziell und man muss die Resultate auch in diesem Rahmen betrachten.
Dieses Blog läuft auf dem tollen Wordpress.
Pingback: Foaa » Blog Archive » Non-destructive migration from Mantis to Redmine
Pingback: • fortrabbit | Blog | Repository Hosting