Nexus Installation

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…

Sonatype 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.

Virtual Host

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.

Weitere Artikel: