NFS und SCM – GIT vs Hg vs SVN

13. April 2010, 16:21 Uhr
Allgemein,Versionsverwaltung
von Uk

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.