Das Dependency Management und Build System Maven ist in der Java Welt derzeit ein de facto Standard. Daher ist es nicht wirklich überraschend, dass auch wir in unseren Projekten darauf aufsetzen.
Allerdings ist die Vielzahl der genutzten Frameworks und Artefakte in der Kombination mit langsamen Internet-Verbindungden (DSL 2000 der Telekom) nicht geeignet, mal eben so ein Projekt zu bauen. Die Download-Zeiten können mich dem Wahnsinn verfallen lassen…
Doch es gibt Abhilfe, denn nicht nur ich habe unter diesen Bedingungen gelitten. Mit dem Sonatype Nexus gibt es einen Repository Manager, der Abhilfe schaffen kann.
Wenn auch der Repository Manager als Ablageort für die genutzten Framework bereits eine erhebliche Verbesserung der Situation bringt, ist er auch als Ablage für die binären Ergebnisartefakte eigener Bibliotheken, Komponenten und Anwendungen ein fester Bestandteil unserer Entwicklungsinfrastruktur. Daher sind die aktuellen Projekte ohne den Nexus nicht mehr nutzbar. Die Ruhe, die mein häusliches Arbeitszimmer mir bietet, ist aber sinnvoll, wenn aufwändigere Entwicklungen anstehen und ich den Stress der Autobahnfahrten einsparen möchte.
Damit ist das heutige Projekt vorgegeben: ich installiere einen Sonatype Nexus auf meinem Entwicklungsserver unter Mountain Lion Server.
Zunächst einmal ist es sinnvoll, eine Gruppe und einen Benutzer für den Nexus auf dem Server anzulegen. Ich bevorzuge dabei den Weg über die Command-Line Tools.
merlin:~ ralf$ sudo dscl
Entering interactive mode... (type "help" for commands)
> cd /Local/Default/
/Local/Default > ls Groups gid
Es werden nun alle bereits vergebenen Gruppen dargestellt. Auf meinem Server ist die Gruppe 301 noch nicht vergeben. Diese wähle ich daher als numerische Kennung für die neu erstellte _nexus Benutzergruppe.
/Local/Default > create Groups/_nexus
/Local/Default > create Groups/_nexus PrimaryGroupID 301
Entsprechend liste ic mir nun die bereits vergebenen Benutzer auf. Damit stelle ich sicher, dass auch die Benutzerkennung 301 bisher noch nicht vergeben wurde.
/Local/Default > ls Users uid
Da diese Benutzerkennung tatsächlich noch ungenutzt ist, kann nun mit einigen wenigen Befehlen der entsprechende Funktionsnutzer angelegt werden:
/Local/Default > create Users/_nexus UserShell /bin/bash
/Local/Default > create Users/_nexus UniqueID 301
/Local/Default > create Users/_nexus PrimaryGroupID 301
/Local/Default > create Users/_nexus NFSHomeDirectory /Users/_nexus
/Local/Default > append Groups/_nexus GroupMembership _nexus
/Local/Default > exit
Goodbye
Da nun er Funktionsbenutzer angelegt ist, kann für diesen Nutzer nun das Benutzerverzeichnis erzeugt werden. Aus organisatorischen Gründen lege ich noch ein zweites Verzeichnis auf einer zusätzlichen Festplatte an. Dort werden die eigentlichen Dateien des Nexus gespeichert.
merlin:~ ralf$ cd /Users/
merlin:Users ralf$ sudo mkdir _nexus
merlin:Users ralf$ sudo chown _nexus:_nexus _nexus
merlin:Users ralf$ ls -al
total 0
drwxr-xr-x+ 10 root admin 340 24 Okt 10:29 .
drwxr-xr-x 32 root wheel 1156 24 Okt 09:17 ..
-rw-r--r-- 1 root wheel 0 20 Jun 22:00 .localized
drwxrwxrwt 5 root wheel 170 13 Sep 15:03 Shared
drwxr-xr-x+ 17 _minecraft _minecraft 578 22 Okt 19:16 _minecraft
drwxr-xr-x+ 2 _nexus _nexus 68 24 Okt 10:29 _nexus
merlin:Users ralf$ mkdir /Volumes/Server/_nexus
merlin:Users ralf$ cd /Volumes/Server/_nexus
Nun kann die aktuelle Version des Nexus geladen und an dieser Stelle installiert werden. Um hier einige sudo
Aufrufe einzusparen, werden die nächsten Schritte erst einmal unter der normalen Benutzerkennung durchgeführt. Allerdings sollte man nicht vergessen, vor dem Start des Systems die Besitzrechte dem _nexus
Benutzer zu übertragen.
merlin:_nexus ralf$ curl http://www.sonatype.org/downloads/nexus-2.1.2-bundle.tar.gz -O nexus-2.1.2-bundle.tar.gz
merlin:_nexus ralf$ tar xvzf nexus-2.1.2-bundle.tar.gz
x nexus-2.1.2/
x nexus-2.1.2/nexus/
x nexus-2.1.2/LICENSE.txt
x nexus-2.1.2/nexus/LICENSE.html
...
x nexus-2.1.2/nexus/WEB-INF/plugin-repository/nexus-capabilities-plugin-2.1/dependencies/nexus-capabilities-model-2.1.jar
x nexus-2.1.2/nexus/WEB-INF/plugin-repository/nexus-capabilities-plugin-2.1/dependencies/guice-assistedinject-3.1.0.jar
merlin:_nexus ralf$
Mit einem symbolische Link stelle ich nun noch sicher, dass bei zukünftigen Updates die Anpassungen für die neue Version nur minimal sind.
merlin:_nexus ralf$ rm nexus-2.1.2-bundle.tar.gz
merlin:_nexus ralf$ ln -s nexus-2.1.2 nexus
merlin:_nexus ralf$ sudo chown -R _nexus:_nexus .
merlin:_nexus ralf$ ls -l
total 16
lrwxr-xr-x 1 _nexus _nexus 11 24 Okt 10:52 nexus -> nexus-2.1.2
drwxr-xr-x 10 _nexus _nexus 340 17 Aug 23:37 nexus-2.1.2
drwxr-xr-x 4 _nexus _nexus 136 24 Okt 10:50 sonatype-work
merlin:_nexus ralf$
Nun ist es wünschenswert, dass dieser neue Serverprozess auch beim Start des Systems automatisch gestartet wird. Dazu muss unter OSX eine Konfiguration für den launchd
angelegt werden - eine immer wieder etwas hakelige Geschichte.
Da dabei (mir) immer mal wieder etwas schief läuft, erstelle ich zuerst einmal ein Shell-Script als .command
, mit dem ich das Starten und Stoppen des jeweiligen Systems testen kann. Erst wenn dieses Script zu meiner Zufriedenheit funktioniert, hinterlege ich eine entsprechende launchd
Konfiguration, die dieses Script auch verwendet. Dieses Script lege ich unter dem Namen nexus.command
im Heimatverzeichnis des Benutzers _nexus
an:
#!/bin/bash
export NEXUS_HOME=/Volumes/Server/_nexus/nexus
DIST_OS=macosx
DIST_BITS=64
DIST_ARCH=universal
WRAPPER="$NEXUS_HOME/bin/jsw/$DIST_OS-$DIST_ARCH-$DIST_BITS/wrapper"
WRAPPER_CONF=$NEXUS_HOME/bin/jsw/conf/wrapper.conf
PIDDIR="$NEXUS_HOME/bin/jsw/$DIST_OS-$DIST_ARCH-$DIST_BITS"
PIDFILE="$PIDDIR/nexus.pid"
if [ -f "$PIDFILE" ]; then
if [ -r "$PIDFILE" ]; then
pid=`cat "$PIDFILE"`
if [ "X$pid" != "X" ]; then
p=`/bin/ps -ww -p $pid -o command | grep "wrapper.pidfile" | tail -1`
if [ "X$p" = "X" ]; then
rm -f "$PIDFILE"
pid=""
fi
fi
fi
fi
if [ "X$pid" != "X" ]; then
kill $pid
fi
ipconfig waitall
# Nexus benötig einige offene Files...
if [ `ulimit -n` -lt 1024 ]; then
ulimit -n 1024
fi
exec $WRAPPER $WRAPPER_CONF wrapper.syslog.ident=nexus wrapper.pidfile=$PIDFILE wrapper.daemonize=false
Die Aufgabe dieses .commands
ist es, eine eventuell bereits aktive Instanz des Nexus zu stoppen und diese dann neu zu starten. Eigentlich sollte die Limits für die Anzahl offener Files über den launchd
gesetzt werden. Für den Fall, dass diese Zahl jedoch nicht den Erwartungen entspricht, passt das Script den Wert entsprechend an.
Nun muss noch die passende Konfiguration für den launchd
erstellt werden. Hierzu lege ich (des Backups und Wiederfindens wegen) eine Datei namens org.sonatype.nexus.plist
im Benutzerverzeichnis an:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN http://www.apple.com/DTDs/PropertyList-1.0.dtd >
<plist version="1.0">
<dict>
<key>Label</key>
<string>org.sonatype.nexus</string>
<key>ProgramArguments</key>
<array>
<string>./nexus.command</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>WorkingDirectory</key>
<string>/Users/_nexus</string>
<key>UserName</key>
<string>_nexus</string>
<key>HardResourceLimits</key>
<dict>
<key>NumberOfFiles</key>
<integer>1024</integer>
</dict>
</dict>
</plist>
Nun ist noch ein wenig Housekeeping notwendig und dem Start des Nexus steht dann nichts mehr im Wege…
merlin:_nexus ralf$ cd /Users/_nexus
merlin:_nexus ralf$ sudo chown _nexus:_nexus nexus.command
merlin:_nexus ralf$ sudo chmod 744 nexus.command
merlin:_nexus ralf$ sudo cp org.sonatype.nexus.plist /Library/LaunchDaemons/
merlin:_nexus ralf$ sudo launchctl load /Library/LaunchDaemons/org.sonatype.nexus.plist
merlin:_nexus ralf$ sudo launchctl start org.sonatype.nexus
Nach wenigen Sekunden sollte der Nexus nun zur Verfügung stehen und für die laufenden Entwicklungen genutzt werden können.
In der Standardkonfiguration ist der Nexus unter dem URL localhost:8081/nexus erreichbar. Natürlich wäre es nun schöner, wenn er auch in die Konfiguration des serverweiten Apache eingetragen ist. Zudem sind auch innerhalb des Nexus noch weitere Dinge zu konfigurieren, diese Schritte spare ich mir aber erst einmal für einen späteren Zeitpunkt auf.
Update: Apache Integration
Bislang ist unser neu erstellter Nexus nur unter seiner eigenen, lokalen Adresse erreichbar. Aus dem Interner heraus ist aber nur eine Instanz des Apache http-Servers erreichbar, der für die intern installierten Anwendungen als Edge-Server fungiert. Auf diesem Apache http-Server sind zusätzliche virtuelle Hosts eingerichtet, die jeweils unabhängige, zweckgebundene Systeme repräsentieren.
Für den Betrieb des Nexus benötigen wir in diesem Fall einen virtuellen Host mit dem Hostnamen nexus.it-performance.net
. Dieser zusätzliche Hostname ist im DNS als CNAME
des tatsächlichen Eintrags für das Serversystem angelegt.
Der virtuelle Host wird einfach mit der Server
Anwendung angelegt.
Sobald diese “Website” in der Server
Anwendung angelegt ist, kann bereits mit dem Browser darauf zugegriffen werden. Es wird daraufhin die Standard Startseite von Mountain Lion Server angezeigt. Aber hier werden wir nachher noch Abhilfe schaffen.
Zunächst einmal müssen wir den bereits installierten Nexus als zusätzliche Webapplikation anlegen. Wie bereits bei der Integration der Git Repositories hinterlegen wir das Konfigurationsverzeichnis in einer Environment Variablen, da wird später noch einmal darauf zurückgreifen wollen.
merlin:_nexus ralf$ cd /Library/Server/Web/Config/apache2
merlin:apache2 ralf$ export SERVER_INSTALL_PATH_PREFIX=/Applications/Server.app/Contents/ServerRoot
Im Unterverzeichnis webapps
muss nun eine Konfigurationsdatei für unsere zusätzliche Webapp hinterlegt werden. Diese Datei namens org.sonatype.nexus.plist
verweist unter anderen auch auf die eigentlichen launchd
Eintrag des Nexus Servers. Damit wird durch diese Steuerdatei gleichzeitig garantiert, dass mit der Installation der Webapp auch der eigentliche Nexus Server im System installiert und gestartet wird.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>name</key>
<string>org.sonatype.nexus</string>
<key>displayName</key>
<string>Sonatype Nexus</string>
<key>launchKeys</key>
<array>
<string>org.sonatype.nexus</string>
</array>
<key>includeFiles</key>
<array>
<string>/Library/Server/Web/Config/apache2/webapp_scripts/httpd_nexus.conf</string>
</array>
<key>proxies</key>
<dict>
<key>/nexus</key>
<dict>
<key>keysAndValues</key>
<string/>
<key>path</key>
<string>/nexus</string>
<key>urls</key>
<array>
<string>http://localhost:8081/nexus</string>
</array>
</dict>
</dict>
</dict>
</plist>
Wir benötigen zusätzlich ein kleines Konfigurationsfragment um den extern genutzten Hostnamen an den Serverprozess des Nexus weiterzuleiten. Nur so ist dieser in der Lage, die korrekten URLs für die Anwender zu generieren und die Navigation innerhalb des Programms korrekt herzustellen. Es ist mir eigentlich unklar, warum die Konfigurationsanwendung des OSX Servers diese zusätzliche Konfigurationsoption nicht auch unterstützt.
So ist es leider derzeit noch notwendig, eine Datei httpd_nexus.conf
im Verzeichnis webapp_scripts
anzulegen.
# Config file for the integration of Sonatype Nexus as a OSX webapp
#
# see also: webapps/org.sonatype.nexus.plist
# http://reswi.de/octo/blog/2012/10/24/nexus-installation/
#
ProxyPreserveHost On
ProxyRequests Off
Nun ist der Zeitpunkt gekommen, diese Anwendung für den virtuellen Host zu registrieren, denn bei einer zukünftigen Veränderung der Einstellungen soll auch unsere neue Anwendung immer wieder berücksichtigt werden.
merlin:apache2 ralf$ sudo webappctl start org.sonatype.nexus nexus.it-performance.net
Damit ist die neue Konfiguration für den Apache erstellt. Sie ist aber in diesem Moment noch nicht aktiv, und das ist auch gut so. Denn zuerst einmal möchte ich diese Konfiguration auf ihre prinzipielle Korrektheit überprüfen lassen. Ein Fehler an dieser Stelle würde den Neustart des Apache http-Servers verhindern. Damit würden alle auf dem System gehosteten Inhalte und Anwendungen erst einmal, bis zur Korrektur der Einstellungen, vom Netz genommen. Unglückliche Benutzer sind damit garantiert.
Mit diesem Befehl können wird den Apache bitten, seine Konfiguration zu testen:
merlin:apache2 ralf$ sudo apachectl -t -D FOREGROUND -D WEBSERVICE_ON -f `pwd`/httpd_server_app.conf
Wird ein Konfigurationsfehler gemeldet, kann die Anwendung mit einem Stop-Befehl (äquivalent zur Installation) aus der Konfiguration entfernt und dann neu konfiguriert werden. Bei mir hat alles funktioniert und daher kann der Apache mit den neuen Einstellungen gestartet werden.
merlin:apache2 ralf$ sudo launchctl stop org.apache.httpd
In das Document-Root Verzeichnis der virtuellen Web-Servers, es befindet sich im Verzeichnis /Library/Server/Web/Data/Sites/
, lege ich nun noch eine eigene Indexdatei index.html
an, um die als Default verwendete Begrüßungsseite zu ersetzen.
<html>
<head>
<title>Sonatype Nexus</title>
<script type="text/javascript">
<!--
window.location = "http://nexus.it-performance.net/nexus/index.html"
//-->
</script>
</head>
<body>
</body>
</html>
Da der Nexus nicht direkt in der Wurzel des Namensraums installiert ist, können weitere Anwendungen in das URI Schema dieser Webseite integriert werden. Da jedoch auf der Nexus-Webseite auch die Anwendung dieses Namens in der Regel genutzt wird, löst diese Seite per Javascript eine Weiterleitung auf die eigentliche Nexus Applikation aus.