Aktuelles in der Kategorie Howto

migration.pngDies ist der zweite Teil unseres Migrations-Howtos für Netzwerk-Überwachungs-Systeme von Nagios nach OpenNMS. Nachdem im ersten Teil die Integration von aktiven Nagios-Checks via NRPE (Nagios Remote Plugin Executor) diskutiert wurde, wenden wir uns in diesem zweiten Teil den passiven Nagios-Checks durch NSCA (Nagios Service Check Acceptor) zu.

Passive Nagios-Checks

Da es momentan keine spezielle OpenNMS-Unterstützung für passive NSCA-Checks gibt, müssen diese durch andere Mechanismen abgebildet werden. In diesem Howto verwenden wir für jeden passiven NSCA-Check einen eigenen Service, dessen Status durch externe Benachrichtigung geändert wird und der durch den PassiveStatusKeeper-Mechanismus von OpenNMS überwacht wird.

Einbinden der Nagios SNMP-Trap Informationen

Für die sinnvolle Behandlung von SNMP-Traps wird eine Registrierung des jeweiligen MIBs (Management Information Base) als OpenNMS-Event benötigt. OpenNMS kennt zwar von Haus aus eine Vielzahl von SNMP-Traps, die aus dem Nagios-MIB sind jedoch nicht darunter. Die benötigte Nagios.events.xml Datei wurde aus dem MIB erstellt und muss in das events/-Unterverzeichnis der OpenNMS Konfiguration kopiert werden und in der eventconf.xml Datei via <event-file>events/Nagios.events.xml </event-file> eingebunden werden. Der in der Nagios-MIB definierte SNMP-Trap für Service-Events ist nSvcEvent, welcher als numerische OID (Object Identifier) den Code .1.3.6.1.4.1.20006.1.7 besitzt.

Benachrichtigungen via SNMP-Trap

Das für passive NSCA-Checks verwendete Programm send_nsca kann etwa durch folgendes Shell-Skript ersetzt werden, welches die Benachrichtigung von OpenNMS via snmptrap durchführt:
  #!/bin/bash

  SNMPTRAP=/usr/bin/snmptrap
  COMMUNITY=public

  while getopts "H:d:p:" OPTION; do
    case $OPTION in
      H) HOST=$OPTARG;;
      d) DELIM=$OPTARG;;
      p) PORT=$OPTARG;;
      *) ;;
    esac
  done

  if [ "x$DELIM" == "x" ]; then
    DELIM="\t"
  fi

  if [ "x$HOST" == "x" ]; then
    echo "No host defined, exiting"
    exit
  fi

  array=(`awk -F"$DELIM" '{for(i=1;i<=3;i++){print $i};
                    for(i=4;i<=NF;i++){gsub(/ /,"_",$i); print $i}}'`);
  NODE=${array[0]}
  SERVICE=${array[1]}
  STATUS=${array[2]}
  REASON=`echo ${array[3]} | sed s/_/\ /g`"'"

  $SNMPTRAP -v 2c -c $COMMUNITY $HOST '' NAGIOS-NOTIFY-MIB::nSvcEvent \
    NAGIOS-NOTIFY-MIB::nHostname s "$NODE" \
    NAGIOS-NOTIFY-MIB::nHostStateID i 0 \
    NAGIOS-NOTIFY-MIB::nSvcDesc s "$SERVICE" \
    NAGIOS-NOTIFY-MIB::nSvcStateID i "$STATUS" \
    NAGIOS-NOTIFY-MIB::nSvcAttempt i 1 \
    NAGIOS-NOTIFY-MIB::nSvcDurationSec i 1 \
    NAGIOS-NOTIFY-MIB::nSvcGroupName s "" \
    NAGIOS-NOTIFY-MIB::nSvcLastCheck i 0 \
    NAGIOS-NOTIFY-MIB::nSvcLastChange i 0 \
    NAGIOS-NOTIFY-MIB::nSvcOutput s "$REASON"
Das Skript versteht die beiden nsca_send Optionen -H (Host) und -d (Feldtrenner). $HOST ist dabei der OpenNMS-Server, für den die Benachrichtigung vorgesehen ist. Der awk-Befehl zerteilt den per Feldtrenner separierten NSCA-String (z.B. „postgres-test; Vaccuum;0;Vaccuum OK 1276511175“) in seine Bestandteile, welche den Variablen $NODE, $SERVICE, $STATUS und $REASON zugeteilt werden. Diese werden schließlich beim Aufruf von snmptrap den nSvcEvent Parametern nHostname, nSvcDesc, nSvcStateID und nSvcOutput zugeteilt. Den übrigen Parametern nHosteStateID, nSvcAttempt, nSvcDurationSec, nSvcGroupName, nSvcLastCheck und nSvcLastChange werden allgemeine Werte zugeteilt, da sie bei der Umsetzung des nSvcEvent-Traps in ein OpenNMS-Event keine Rolle spielen und auch beim Aufruf von send_nsca nicht bekannt sind. Damit die Parameter des nSvcEvent-Traps von snmptrap aufgelöst werden können, muss das NAGIOS-MIB der Net-SNMP-Installation bekannt sein.

Umwandlung von Traps in Events

Die eingehenden SNMP-Traps werden in OpenNMS-Events vom Typ nSvcEvent umgewandelt. Um diese nun einzelnen Nagios-Checks bzw. OpenNMS-Services zzuuweisen, müssen sie vom translatord-Dämon in passiveServiceStatus Events umgewandelt werden. Folgender Abschnitt ist hierfür in der Datei translator-configuration.xml nötig:
<event-translation-spec 
  uei="uei.opennms.org/vendor/nagios/traps/nSvcEvent">
    <mappings>
      <mapping>
        <assignment type="parameter" name="passiveNodeLabel">
          <value type="parameter" 
            name=".1.3.6.1.4.1.20006.1.1.1.2" 
            matches=".*" 
            result="${0}" 
          />
        </assignment>
        <assignment type="parameter" name="passiveIpAddr">
          <value type="field" 
            name="interface" 
            matches=".*" 
            result="${0}"
          />
        </assignment>
        <assignment type="parameter" name="passiveServiceName">
          <value type="parameter" 
            name=".1.3.6.1.4.1.20006.1.3.1.6" 
            matches=".*" 
            result="${0}" 
          />
        </assignment>
        <assignment type="parameter" name="passiveStatus">
          <value type="sql" 
            result="SELECT CASE WHEN ?::integer = 0  
                                     THEN 'Up'        
                                     ELSE 'Down' 
                                     END;" >  
            <value type="parameter" 
              name=".1.3.6.1.4.1.20006.1.3.1.7" 
              matches=".*" 
              result="${0}" 
            />
          </value>
        </assignment>
        <assignment type="parameter" name="passiveReasonCode">
          <value type="parameter" 
            name=".1.3.6.1.4.1.20006.1.3.1.17" 
            matches=".*" 
            result="${0}" 
          />
        </assignment>
        <assignment type="field" name="uei">
          <value type="constant" 
            result="uei.opennms.org/services/passiveServiceStatus" 
          />
        </assignment>
      </mapping>
    </mappings>
</event-translation-spec>
Die uei in der zweiten Zeile bezeichnet dabei den SNMP-Trap, wie er in der jeweiligen events-XML Datei angegeben ist. Die Parameter passiveNodeLabel, passiveServiceName, passiveStatus und passiveReasonCode werden aus den jeweiligen Parametern $NODE, $SERVICE, $STATUS und $REASON des SNMP-Traps übernommen. Für die Ermittlung von passiveStatus ist dabei eine SQL-Abfrage nötig, um je nach Nagios-Status ein "Up" oder "Down" zu senden. Die IP-Adresse kann direkt aus dem "interface"-Feld des Events übernommen werden. Schließlich wird in der letzten Zuordnung die OpenNMS UEI (Unique Event Identifier) von nSvcEvent nach passiveServiceStatus umgewandelt.

Einbinden von Passiven Checks als Services

Passive NSCA-Checks können naturgemäß nicht mit capsd entdeckt werden, deshalb müssen sie per LoopPlugin für jede betreffende Node per ip-match Property in der capsd-configuration.xml Datei aktiviert werden:
    <protocol-plugin 
      protocol="NSCA-check1" 
      class-name="org.opennms.netmgt.capsd.plugins.LoopPlugin"
      scan="on">
        <property key="ip-match" value="192.168.178.158" />
        <property key="ip-match" value="172.16.17.213" />
        <property key="is-supported" value="true" />
    </protocol-plugin>
Dabei bezeichnet protocol den Namen des betreffenen OpenNMS-Service.

Zusätzlich ist auch ein entsprechender Poller in poller-configuration.xml nötig, zum einen der <service>-Anker:

    <service name="NSCA-check1" 
      interval="30000" 
      user-defined="false" 
      status="on">
    </service>
Sowie schließlich ein PassiveServiceMonitor:
<monitor service="NSCA-check1" 
  class-name="org.opennms.netmgt.poller.monitors.PassiveServiceMonitor"
/>
Mit diesen Änderungen und nach einem Neustart von OpenNMS sollten die NSCA-Checks für die entsprechende Node im OpenNMS-Webinterface erscheinen und sich wie normale OpenNMS Dienste in Bezug auf Ausfälle etc. verhalten.
migration.pngHier bei credativ setzen wir bevorzugt Nagios für das Monitoring ein, aber wenn es die Richtilinien bei Kunden verlangen, arbeiten wir natürlich auch mit anderen Überwachungs-Systemen. Dieses Howto beschreibt die Integration von aktiven und passiven Nagios-Checks in ein OpenNMS-System, so dass zwar die entwickelten Checks weiter verwendet, der Nagios-Server aber abgeschaltet werden kann.

Einleitung

OpenNMS ist ein Open-Source Netzwerk-Monitoring-System. Es ist in Java geschrieben und benutzt PostgreSQL als Datenbank. Die Konfiguration erfolgt über XML-Dateien, teilweise (vor allem Reports, Benachrichtigungen usw.) auch über die Web-Oberfläche. OpenNMS ist relativ stark auf SNMP fokussiert, vor allem was das Sammeln von Performance-Daten angeht. Ob Rechner (oder Dienste auf Rechnern) erreichbar sind, kann jedoch auch einfach anderweitig abgefragt werden.
Dieses Howto behandelt die Migration eines Netzwerk-Überwachungs-Systems von Nagios/Icinga zu OpenNMS. Für aktive Nagios-Checks beruht diese Migration auf dem NRPE Plugin (Nagios Remote Plugin Executor), welches von der stabilen OpenNMS-Version bereits unterstützt wird. Es erfordert also ein bereits vorhandenes NRPE-Setup für Nagios, bzw. eine Migration des Nagios-Setups zu NRPE. Passive Nagios-Checks via NSCA (Nagios Service Check Acceptor) werden durch SNMP-Traps in OpenNMS integriert und bedürfen keiner Änderung auf der Nagios-Seite, lediglich das send_nsca-Skript muss zentral ausgetauscht werden. Die Sammlung und Bearbeitung von Performance-Daten ist mit diesem Howto nicht möglich, hierfür wäre eine Erweiterung der OpenNMS Code-Basis nötig.

Aktive Nagios-Checks

Die aktiven NRPE-Checks werden durch den OpenNMS Service pollerd ersetzt. Dieser stellt das Modul NrpeMonitor zur Verfügung, welches im Prinzip dazu gedacht ist, einen NRPE-Service auf einem der bekannten Rechner zu überwachen. Dazu wird alle 5 Minuten (dieser Wert ist für jeden Dienst einzeln konfigurierbar) versucht, den NRPE-Dämon zu erreichen, wobei im Normalfall der Befehl „_NRPE“ abgefragt wird, welcher vom NRPE-Dämon als Spezialfall aufgefasst wird und bei normalen Betrieb ein „OK“ und die Versionsnummer von NRPE zurück liefert.
Alternativ kann jedoch in der Konfigurations-Datei des pollerd ein beliebiges Kommando für den NRPE-Dämon eingestellt werden, z. B. check_load. Wenn dieses Kommando dem NRPE-Dämon bekannt ist, wird es ausgeführt und liefert den üblichen Nagios-Status zurück (OK mit Rückgabewert 0, WARNING mit Rückgabewert 1, CRITICAL mit Rückgabewert 2 oder UNKNOWN mit Rückgabewert 3) sowie die Performance-Daten, welche jedoch momentan vom pollerd bzw. OpenNMS nicht ausgewertet werden.
Um die bestehenden Nagios-Checks in OpenNMS abzubilden, wird für jeden bisher verwendeten aktiven Nagios-Check ein NRPE-Service in OpenNMS definiert. Diese werden dann unabhängig voneinander in OpenNMS geführt. Hierfür sind zwei Konfigurations-Dateien von OpenNMS zu ändern:
  • Die Datei capsd-configuration.xml bestimmt, auf welche Dienste ein Rechner vom OpenNMS Service capsd beim Scannen untersucht werden soll.
  • Die Datei poller-configuration.xml bestimmt, welche bzw. in welcher Art die vom capsd gefundenen Dienste vom pollerd überwacht werden sollen.
Aus Sicherheitsgründen sollten dem NRPE-Dämon keine variablen Parameter übergeben werden; die Abfrage des gleichen Checks mit verschiedenen Parametern oder Schwellen-Werten sollte also als jeweils unterschiedliche Checks in NRPE definiert werden.

Anpassung der capsd Konfiguration

Für jeden NRPE-Check muss ein neuer protocol-plugin Abschnitt in capsd-configuration.xml definiert werden. Der class-name ist dabei stets org.opennms.netmgt.capsd.plugins.NrpePlugin, normalerweise müssen lediglich die Werte für protocol und der für das Attribut „command“ angepasst werden. Ein Beispiel wäre:
    <protocol-plugin 
      protocol="NRPE-load" 
      class-name="org.opennms.netmgt.capsd.plugins.NrpePlugin" 
      scan="on">
        <property key="banner" value="*" />
        <property key="port" value="5666" />
        <property key="timeout" value="3000" />
        <property key="retry" value="2" />
        <property key="command" value="check_load" />
    </protocol-plugin>
Der Wert für „protocol“ bezeichnet den Dienst, wie er intern bzw. im Web-Interface von OpenNMS geführt werden soll. Das Attribut „port“ bezeichnet den Port, auf dem der NRPE-Dämon zu erreichen ist, der Standard hierfür ist 5666. Das Attribut „command“ bezeichnet den vom NRPE-Dämon auszuführenden Check, wie er in /etc/nagios/nrpe.cfg (oder evtl. /etc/nagios/nrpe_local.cfg) definiert ist. In diesem Fall wird der übliche Nagios-Check „check_load“ für die Überprüfung der System-Auslastung in Bezug auf die Anzahl der laufenden Prozesse ausgeführt.
Zu beachten ist hierbei, dass OpenNMS vor Version 1.6.9 nicht genauer den Rückgabewert prüft und im Falle eines Checks, der nicht ein „OK“ (sondern vorübergehend ein „WARNING“ oder „CRITICAL“) zurück gibt den jeweiligen Check nicht erkennt. Hierfür wurde von uns ein Patch entwickelt, welcher ab Version 1.6.9 in OpenNMS enthalten ist. Falls eine frühere Version von OpenNMS eingesetzt wird, kann das Problem durch eine temporäre Anpassung der Schwellen-Werte in der NRPE-Konfigurations-Datei auf dem jeweiligen Rechner behoben werden, so dass zum Zeitpunkt der Erkennung NRPE den Dienst als „OK“ ansieht.
Falls in einem großen Rechner-Netzwerk eine Prüfung auf eine potentiell große Anzahl von NRPE-Checks nicht im allgemeinen erwünscht ist, können diese auch auf ein einzelnes Netzwerk-Segment oder einen IP-Bereich beschränkt werden. Dazu wird die Beispiel-Konfiguration in einen protocol-configuration Abschnitt eingebettet:
    <protocol-plugin 
      protocol="NRPE-load" 
      class-name="org.opennms.netmgt.capsd.plugins.NrpePlugin" 
      scan="off">
        <protocol-configuration scan="on" user-defined="false">
        <range begin="192.168.178.0" end="192.168.178.254"/>
        <specific>10.0.0.2</specific>
        <specific>10.0.0.7</specific>
        <property key="banner" value="*" />
        <property key="port" value="5666" />
        <property key="timeout" value="3000" />
        <property key="retry" value="2" />
        <property key="command" value="check_load" />
        </protocol-configuration>
    </protocol-plugin>
Wichtig ist hierbei der Tag <range begin=“$IP_ADRESSE“ end=“$IP_ADRESSE“/>, welcher den IP-Bereich definiert. Alternativ können auch über <specific>$IP_ADRESSE</specific> Tags bestimmte IP-Adressen festgelegt werden. Durch die Option scan= “off“ in der ersten Zeile wird auf allen anderen Rechnern nicht auf diesen Check geprüft.
Nach der Änderung von capsd-configuration.xml ist ein Neustart von OpenNMS nötig; alternativ (ein OpenNMS-Neustart kann je nach Größe des überwachten Netzwerks eine längere Zeit dauern) kann der capsd-Daemon per JMX RPC neu gestartet werden:
for i in stop init start; do
  wget --proxy=off -O /dev/null \
"http://manager:manager@localhost:8181/invoke?objectname=OpenNMS%3AName%3DCapsd&operation=$i"; 
done
Wenn die Änderung hauptsächlich für einen bestimmten Rechner veranlasst wurde und sofort aktiv sein soll, muss für diesen Rechner ein „Rescan“ ausgeführt werden; entweder per Web-Interface oder mit Hilfe des send-event.pl Skripts, welches das entsprechende Event (forceRescan) an OpenNMS (auf localhost laufend) übermittelt:
send-event.pl uei.opennms.org/internal/capsd/forceRescan localhost --interface 10.0.0.2

Anpassung der pollerd Konfiguration

Die Konfigurations-Datei für den pollerd-Service muss für jeden NRPE-Check an zwei Stellen geändert werden; zum einen muss ein <service> Absatz hinzugefügt werden, in dem der zu überwachende Service ähnlich wie in der capsd-Konfigurationsdatei definiert wird, außerdem muss eine <monitor service=[...]/> Zeile eingefügt werden um die Überwachung des jeweiligen Services zu aktivieren. Analog zu dem Beispiel im letzten Abschnitt wäre dies etwa folgendes:
    <service name="NRPE-load" 
      interval="300000" 
      user-defined="false" 
      status="on">
        <parameter key="retry" value="3" />
        <parameter key="timeout" value="3000" />
        <parameter key="port" value="5666" />
        <parameter key="command" value="check_load" />
        <parameter key="padding" value="2" />
    </service>
Jeweils zu ändern sind auch hier die Angaben für „service name“ und der Parameter „command“. Die Option „interval“ gibt das Überwachungs-Intervall in Millisekunden an, der Standard-Wert ist hier üblicherweise 300000, was 5 Minuten entspricht. Die entsprechende Zeile für die Aktivierung des NrpeMonitor ist
<monitor service="NRPE-load" 
 class-name="org.opennms.netmgt.poller.monitors.NrpeMonitor" 
/>
Auch beim Ändern der Datei poller-configuration.xml ist ein Neustart von OpenNMS nötig, bzw. ein analoger Aufruf der RPCs wie im letzten Abschnitt, mit Pollerd statt Capsd als Argument:
for i in stop init start; do
  wget --proxy=off -O /dev/null \
"http://manager:manager@localhost:8181/invoke?objectname=OpenNMS%3AName%3DPollerd&operation=$i"; 
done
Im demnächst folgenden zweiten Teil dieses Howtos wird die Integration von passiven Nagios-Checks behandelt.

tux.jpgUm Postfix als SMTP-Server für viele Nutzer anzubieten, muss er auf eine Nutzerdatenbank zurück greifen. Dieser Artikel zeigt am Beispiel der Groupware Zarafa, wie Postfix via libsasl mit einer MySQL-Nutzerdatenbank verknüpft wird.

Mit Hilfe von SASL können Nutzer sich gegen Postfix authentifizieren und E-Mails einliefern, damit dieser die weiter zustellt. Dies ist zum Beispiel interessant, wenn der Postfix als zentraler MTA für eine Firma oder größere Institution genutzt werden soll.

Die Schwierigkeit besteht darin, eine vorhandene Nutzerdatenbank einzubinden, hier die MySQL-DB der Groupware Zarafa. Häufig wird dabei auf PAM zurückgegriffen: Postfix authentifiziert gegen PAM, via PAM kann eine Vielzahl verschiedenster Plugins angebunden werden, um z.B. auch auf Datenbanken zurück zu greifen. Ein anderer Weg ist, die Bibliothek libsasl des Cyrus-Projekts zu nutzen, da dies ebenfalls auf verschiedene Plugins zurückgreifen kann.

Dafür muss im ersten Schritt Postfix in der main.cf für die Authentifizierung gegen SASL fit gemacht werden:

smtpd_sasl_auth_enable = yes
broken_sasl_auth_clients = yes
smtpd_sasl_path = smtpd
smtpd_sender_restrictions = permit_sasl_authenticated, ...

saslauthd wird nicht direkt aufgerufen, es wird nur die libsasl-Bibliothek genutzt. Daher kann /etc/sysconfig/saslauthd auf den Standardwerten bleiben. Der Dienst selbst braucht nicht gestartet werden!

Es fehlt aber noch die eigentliche Konfiguration, in der unter Anderem auch die Syntax der MySQL-Abfrage definiert wird. Diese Konfiguration wird in der smtpd.conf definiert, die standardmäßig unter /usr/lib/sasl2/ erwartet wird. Erst wenn sie dort *nicht* vorliegt, wird sie unter /etc/sasl2/smtpd.conf erwartet. Warum dort nicht zuerst gesucht wird, entzieht sich meinem Verständnis. Es bietet sich also an, einen symbolischen Link auf die Datei unter /etc/ zu erstellen...
So oder so muss der Inhalt wie folgt aussehen:

pwcheck_method: auxprop
auxprop_plugin: sql
mech_list: PLAIN LOGIN
allow_plaintext: true
sql_engine: mysql
sql_hostnames: 127.0.0.1
sql_user: zarafa
sql_passwd: ******
sql_database: zarafa
sql_select: select value from objectproperty where objectid=(select objectid from objectproperty where value='%s' limit 1) and propname='loginname'

Dabei müssen die natürlich die Sternchen für das Passwort gegen das tatsächliche Passwort ersetzt werden. Sind alle Komponenten neu gestartet worden, können E-Mails von Nutzern eingeliefert werden, die sich via libsasl gegen die MySQL-Datenbank von Zarafa authentifizieren.

Alle Artikel zum Thema CentOS stehen auch als eigene Kategorie mit eigenem Feed zur Verfügung. Falls Ihr tiefergehende Fragen zu Support und Services für Zarafa habt, oder euch Supportangebote für CentOS interessieren, seid ihr bei uns ebenfalls richtig.

Update:
Ursprünglich hatte ich in diesem Artikel auf saslauthd direkt aufgesetzt, dann aber fälschlicherweise nicht gegen die DB, sondern gegen den IMAP-Server von Zarafa (via rimap) authentifiziert. Danke an Uli in den Kommentaren für den Hinweis. Mehr Infos zu saslauthd, libsasl und den unterschiedlichen Einsatzszenarien finden sich im SASL-Readme von Postfix.

debianlogo.pngDas massenhafte Installieren von Debian-Maschinen lässt sich mit Hilfe von Preseeding und Netboot vereinfachen. Friedrich Weber hat in seinem Schülerpraktikum hier bei uns den entsprechenden Prozess gelernt - und in einem Howto fest gehalten.

Man stelle sich die folgende Situation vor: plötzlich finden sich einige zehn bis zwanzig fabrikneue Notebooks und eine wunderbare Idee, was man mit ihnen anstellen könnte: ein deutschsprachiges Debian installieren und das Ganze nach den eigenen Wünschen anpassen. Allerdings wird sofort klar, dass es nicht den geringsten Spaß macht, auf jedem Notebook manuell die Debian-Installation und -Konfiguration vorzunehmen. An dieser Stelle kommt Debian Preseed ins Spiel.

Das Konzept ist einfach und einleuchtend: Der normale Debian-Installer stellt während der Installation eine Reihe Fragen (zu Sprache, Partitionierung, Paketen, Bootloader etc.). Über Preseed kann man nun zu jeder zu stellenden Frage eine Antwort vorgeben. Nur Fragen, für die man nicht schon über Preseed eine Antwort vorgibt, stellt der Debian-Installer überhaupt noch. Im Idealfall werden nun nur noch am Anfang der Installation einige Fragen angezeigt, deren Antworten sich von Zielsystem zu Zielsystem unterscheiden und die der Administrator manuell abhandeln muss - nachdem diese beantwortet wurden, kann die Installation unbeaufsichtigt ablaufen.

Preseed arbeitet mit einer sehr einfach aufgebauten Konfigurationsdatei: der preseed.cfg. Sie beinhaltet, wie oben beschrieben, Antworten auf Fragen während der Installation, und das im debconf-Format. Eine solche Datei besteht aus mehreren Zeilen, von denen jede Zeile eine debconf-Konfigurationsoption - eine Antwort auf eine Frage - festlegt, zum Beispiel so:

    d-i debian-installer/locale	string de_DE.UTF-8

Diese Zeile beinhaltet als erstes Element den Namen des Paketes, das konfiguriert wird (d-i ist hier eine Kurzform für debian-installer), als zweites Element den Namen der Option, die gesetzt wird, als drittes Element den Typ der Option (hier string, eine Zeichenkette), und der Rest ist der Wert der Option. Mit diesem Beispiel wird also die Sprache auf deutsch mit UTF-8-Kodierung gesetzt.

Solche Zeilen kann man sich selbst zusammenbasteln, einfacher geht es aber mit dem Tool debconf-get-selections: Dieses Kommando gibt schlicht und einfach alle Optionen aus, die lokal gesetzt wurden. Aus der Ausgabe können die gewünschten Einstellungen herausgefischt, gegebenenfalls angepasst und in die preseed.cfg kopiert werden.

Hier ein Beispiel einer solchen preseed.cfg:

    d-i debian-installer/locale string de_DE.UTF-8
    d-i debian-installer/keymap select de-latin1
    d-i console-keymaps-at/keymap select de
    d-i languagechooser/language-name-fb select German
    d-i countrychooser/country-name select Germany
    d-i console-setup/layoutcode string de_DE

    d-i clock-setup/utc boolean true
    d-i time/zone string Europe/Berlin
    d-i clock-setup/ntp boolean true
    d-i clock-setup/ntp-server string ntp1

    tasksel tasksel/first multiselect standard, desktop, gnome-desktop, laptop
    d-i pkgsel/include string openssh-client vim less rsync

Mit diesen Optionen werden Spracheinstellungen und Zeitzone gesetzt, außerdem zu installierende Tasks und Pakete ausgewählt.
Vollkommen unbeaufsichtigt wird diese Installation nicht ablaufen, aber ein Anfang ist es auf jeden Fall.

Nun stellt sich die Frage, woher Preseed eigentlich seine Konfigurationsdatei bezieht. Grundsätzlich ist es möglich, Preseed mit CD- und DVD-Images oder USB-Sticks zu nutzen. Wesentlich komfortabler ist es aber, ein Debian-netboot-Image zu benutzen, also einen Installer, der über das Netzwerk gestartet wird und bei dieser Gelegenheit auch seine Preseed-Konfiguration beziehen kann. Dieses Booten über Netzwerk wird mit PXE realisiert und setzt ein System voraus, das von Netzwerkkarte booten kann.

Zunächst wird das System angewiesen, von der Netzwerkkarte zu booten. Dafür fordert es von einem DHCP-Server per Broadcast eine IP-Adresse an. Dieser DHCP-Server übermittelt aber nicht nur eine passende IP, sondern auch die IP eines sogenannten Bootservers. Ein Bootserver ist ein TFTP-Server, der einen Bootloader bereitstellt, mit dem der Administrator den gewünschten Debian-Installer auswählt. Gleichzeitig kann dem Debian-Installer hier über Boot-Optionen mitgeteilt werden, dass er Preseed benutzen soll und wo er die Preseed-Konfiguration finden kann. Hier ein Ausschnitt der PXELINUX-Konfigurationsdatei pxelinux.cfg/default:

    label i386
        kernel debian-installer/i386/linux
        append vga=normal initrd=debian-installer/i386/initrd.gz netcfg/choose_interface=eth0 domain=example.com locale=de_DE debian-installer/country=DE debian-installer/language=de debian-installer/keymap=de-latin1-nodeadkeys console-keymaps-at/keymap=de-latin1-nodeadkeys auto-install/enable=false preseed/url=http://$server/preseed.cfg DEBCONF_DEBUG=5 -- quiet 

Wenn der User also i386 eintippt, wird der Kernel debian-installer/i386/linux (zu finden auf dem TFTP-Server) heruntergeladen und gestartet, diesem werden außerdem eine ganze Menge Bootoptionen mit auf den Weg gegeben. Der Debian-Installer erlaubt das Angeben von debconf-Optionen als Bootparameter. Das ist sehr praktisch, denn dem Installer muss irgendwie mitgeteilt werden, wo die Preseed-Konfiguration im Netzwerk zu finden (preseed/url) ist. Damit er diese Preseed-Konfiguration herunterladen kann, muss er allerdings auch ins Netzwerk eingebunden sein. Dafür werden ihm die nötigen Optionen übergeben (die Option für den Hostnamen wurde hier bewusst ausgelassen, denn jedes Zielsystem hat natürlich einen anderen Hostname). auto-install/enable würde die Spracheinstellungen verzögern, sodass sie erst nach der Netzwerkkonfiguration gestellt würden, damit diese Einstellungen ebenfalls aus der preseed.cfg gelesen werden könnten. Das ist hier nicht notwendig, denn die Spracheinstellungen werden ebenfalls als Kerneloptionen übergeben, um zu gewährleisten, dass auch die Netzwerkkonfiguration deutschsprachig ist.

Die vorgestellten Beispiele und Konfigurationsauszüge sind natürlich sehr allgemein gehalten und stark gekürzt. Trotzdem sollte dieser Blog-Post eine Einführung in das Preseed-Konzept in Verbindung mit netboot geboten haben. Abschließend noch eine vollständigere Fassung der preseed.cfg:

    d-i debian-installer/locale string de_DE.UTF-8
    d-i debian-installer/keymap select de-latin1
    d-i console-keymaps-at/keymap select de
    d-i languagechooser/language-name-fb select German
    d-i countrychooser/country-name select Germany
    d-i console-setup/layoutcode string de_DE

    # Netzwerk
    d-i netcfg/choose_interface select auto
    d-i netcfg/get_hostname string debian
    d-i netcfg/get_domain string example.com

    # Paketmirror
    d-i mirror/protocol string http
    d-i mirror/country string manual
    d-i mirror/http/hostname string debian.example.com
    d-i mirror/http/directory string /debian
    d-i mirror/http/proxy string
    d-i mirror/suite string lenny

    # Zeitzone
    d-i clock-setup/utc boolean true
    d-i time/zone string Europe/Berlin
    d-i clock-setup/ntp boolean true
    d-i clock-setup/ntp-server string ntp.example.com

    # Root-Account
    d-i passwd/make-user boolean false
    d-i passwd/root-password password geheimespasswort
    d-i passwd/root-password-again password geheimespasswort

    # Weitere APT-Optionen
    d-i apt-setup/non-free boolean false
    d-i apt-setup/contrib boolean false
    d-i apt-setup/security-updates boolean true

    d-i apt-setup/local0/source boolean false
    d-i apt-setup/local1/source boolean false
    d-i apt-setup/local2/source boolean false

    # Tasks
    tasksel tasksel/first multiselect standard, desktop
    d-i pkgsel/include string openssh-client vim less rsync
    d-i pkgsel/upgrade select safe-upgrade

    # Popularity-Contest
    popularity-contest popularity-contest/participate boolean true

    # Kommando, das nach der Installation ausgeführt wird. `in-target` bedeutet, dass das folgende
    # Kommando in der installierten Umgebung ausgeführt wird, nicht in der Installationsumgebung.
    # Hier wird http://$server/skript.sh nach /tmp heruntergeladen, ausführbar gemacht und ausgeführt.
    d-i preseed/late_command string in-target wget -P /tmp/ http://$server/skript.sh; in-target chmod +x /tmp/skript.sh; in-target /tmp/skript.sh

Alle Howtos dieses Blogs werden auch als Kategorie Howto samt eigenem Feed angeboten - und falls ihr nach Support und Services für Debian sucht, seit ihr bei uns ebenfalls richtig.

postgreslogo.pngDer OOM-Killer kann auf stark ausgelasteten Maschinen für böse Überraschungen sorgen: Prozesse werden plötzlich und unerwartet beendet. Dieses Verhalten lässt sich aber mit Kernel-Bord-Mitteln sehr genau beeinflussen.

Administratoren auf Linuxmaschinen mit hoher RAM-Nutzung erleben oft eine Begegnung der unheimlichen Art: den Linux OOM-Killer (OOM = Out Of Memory). Der Administrator findet in diesem Szenario eine "abgestürzte" PostgreSQL-Instanz vor, im Serverlog finden sich dann einer oder meist mehrere Einträge der Form

Out of Memory: Killed process PID (Prozessname)

Doch was genau steckt dahinter?

Virtueller Speicher und Overcommit

Virtueller Speicher in Linuxsystemen wird auf vielfältige Weise adressiert: RAM, mmap(), Swap oder Shared Memory, um ein paar Beispiele zu nennen. Es ist möglich, durch das sogenannte Overcommit-Verhalten bei Allokieren von Speicher mehr Ressourcen anzufordern, als tatsächlich im System aktuell vorhanden ist. In solchen Situationen spricht man von einer OOM-Situation, das System hat alle Ressourcen aufgebraucht und ist nicht mehr in der Lage, mehr virtuellen Speicher zu adressieren. Hier wird der OOM-Killer aktiv, der Prozesse nach festgelegten Kriterien auswählt und diese terminiert, um dem System ein wenig Luft zu verschaffen. Dieses Verhalten ist insbesondere für Datenbanksysteme zu berücksichtigen, die nicht auf dedizierter Hardware laufen. Der OOM-Killer bevorzugt in solchen Umgebungen häufig PostgreSQL, da als Kandidaten zum Terminieren solche Prozesse ausgewählt werden, die mit aggressiver Speichernutzung auffallen. Da der OOM-Killer den gesamten Adressraum aller Kinder inklusive Shared Memory in Summe sieht, erkennt man recht schnell, dass PostgreSQL auf jeden Fall weit oben in der Liste der Kandidaten auftauchen wird.
Wie stark der zur Verfügung stehende Speicher genutzt wird, findet man am schnellsten über das /proc-Filesystem heraus:

$ grep Commit /proc/meminfo 
CommitLimit:    376176 kB
Committed_AS:   265476 kB

In diesem Beispiel sind aktuell als Obergrenze 376176 kB(CommitLimit) an Speichernutzung möglich, zugewiesen wurden 265476 kB (Committed_AS). Nähert sich CommitLimit sehr stark an Committed_AS an oder übersteigt diesen sogar, dann ist der Einsatz des OOM-Killers wahrscheinlich.

Der Linux-Kernel stellt einige Schnittstellen zur Verfügung, die das Verhalten des OOM-Killers gegenüber PostgreSQL beeinflusst.

Overcommit abschalten

Die radikalste Methode ist, Overcommit generell im Kernel abzuschalten. Allerdings kommt dies nur für dedizierte Datenbanksysteme in Frage, auf denen PostgreSQL exklusiv läuft. Das Overcommit-Verhalten lässt sich in modernen 2.6ern Kernel in drei Kategorien mit dem Parameter

vm.overcommit_memory = 0

konfigurieren. Die einzelnen Kategorien hierbei sind:

  • 0: Vorsichtiges Overcommitverhalten. Während gemäßigte Allokierungen erlaubt sind, werden extrem große Allokierungen, die zu übermäßigem Overcommit führen, abgelehnt. In diesem Modus kann root auch mehr Speicher allokieren als ein unprivilegierter Benutzer. Dieser Modus ist auch die Standardeinstellung des Kernels.
  • 1: Overcommit unterliegt keinen Einschränkungen
  • 2: Schaltet Overcommitverhalten ab. Generell bedeutet dies, dass der maximale allokierbare tatsächliche Adressraum nicht größer werden kann, als swap + ein konfigurierbarer Anteil an Prozent des physkalischen RAM.

Der Anteil des physikalischen RAM bei Modus 2 wird über den zusätzlichen Parameter

vm.overcommit_ratio = 50

kontrolliert.

Während vm.overcommit_memory=1 für Spezialanwendungen interessant sein könnte, wird es im Praxiseinsatz eher zum Einsatz für die Parameterwerte 0 oder 2 kommen. Wird Overcommit über vm.overcommit_memory=2 abgeschaltet, so wird ein Prozess (in Abhängigkeit von vm_overcommit_ratio) sofort eine "Out Of Memory"-Bedingung beim Allokieren von Speicher erhalten. Abhängig von der Distribution sollte man die Einstellungen permanent in die Datei /etc/sysctl.conf speichern, so dass diese auch nach einem Neustart des Systems aktiv sind:

$ echo "vm.overcommit_memory=2 >> /etc/sysctl.conf
$ echo "vm.overcommit_ratio=60 >> /etc/sysctl.conf
$ sysctl -p /etc/sysctl.conf

Die Änderungen wirken sich sofort auf den virtuellen Speicher aus, man kann dies erneut durch Abrufen von /proc/meminfo überprüfen:

$ grep Commit /proc/meminfo 
CommitLimit:    401440 kB
Committed_AS:   266456 kB

Die Maschine verfügt über 249848 kB Swap und 252656 kB physikalischen RAM. Nach der Formel Swap + vm.overcommit_ratio * RAM ergibt dies ein CommitLimit von 401440 kB.

OOM-Killer auf Prozessebene konfigurieren

Ist PostgreSQL nicht auf einem dedizierten Server installiert und wird mit einer speicherhungrigen Middleware (bspw. JBoss- oder Tomcat-Installation) auf demselben System betrieben, so ist es wünschenswert, Overcommit-Verhalten zwar zu erlauben, im Falle einer "Out Of Memory"-Situation aber PostgreSQL vom OOM-Killer auszunehmen. Seit Kernel 2.6.11 bietet Linux daher ein Interface an, um den OOM-Score eines Prozesses zu tunen, so dass dieser vom OOM-Killer weniger oder stärker berücksichtigt wird. Dies erlaubt ein sehr feinfühliges Einstellen des Systems auf die Speicherbedürfnisse einzelner Prozesse. Die Konfiguration wird über eine Datei im /proc-Filesystem des Kernel vorgenommen, beispielsweise hier für den PostgreSQL-Hauptprozess unter Debian (0 ist die Standardeinstellung für Prozesse):

$ cat /proc/$(cat /var/run/postgresql/8.4-main.pid)/oom_adj
0

Die erlaubten Werte sind von -17 bis +15, negative Werte verringern die Affinität des Prozesses gegenüber den OOM-Killer, positive Werte erhöhen diese. -17 schaltet den OOM-Killer für den jeweiligen Prozess komplett ab. Die Einstellung wird vom Parent an etwaige Kindprozesse weitervererbt. Da PostgreSQL sich für eine Datenbankverbindung forked, reicht es, diese Einstellung dem PostgreSQL-Hauptprozess mitzugeben:

$ echo -17 >> /proc/$(cat /var/run/postgresql/8.4-main.pid)/oom_adj
$ psql -q postgres
test=# SELECT pg_backend_pid();
 pg_backend_pid 
----------------
           3429
(1 Zeile)

test=# 
[1]+  Stopped                 psql -q test
$ cat /proc/3429/oom_adj
-17

Der Nachteil dieser Methode ist, dass dies nun für alle Kindprozesse des PostgreSQL-Hauptprozesses gilt, was eventuell vom DBA nicht mehr gewünscht ist. Beispielsweise möchte man zwar gerne die PostgreSQl-Systemprozesse wie Background Writer oder Autovacuum vor dem OOM-Killer schützen, nicht jedoch normale Datenbankverbindungen.

Das Setzen von /proc/PID/oom_adj erfordert jedoch einen privilegierten Benutzer, so dass man am Besten die Einstellung direkt im Startskript der PostgreSQL-Datenbank vornimmt.

Erweiterungen in PostgreSQL 9.0

PostgreSQL 9.0 wird hinsichtlich der Zusammenarbeit mit dem /proc-Interface ebenfalls einige Neuerungen mitbringen. Zum einen wurde das im Quelltext mitgelieferte Linux-Startskript dahingehend erweitert, zum anderen bietet das Backend nun auch Unterstützung, falls man die /proc-Einstellungen eben nicht an normale Datenbankverbindungen weitervererben möchte. Hierzu kann der PostgreSQL-Server mit dem Makro LINUX_OOM_ADJ=0 kompiliert werden, beispielsweise:

$ ./configure CC="ccache gcc" CFLAGS="-DLINUX_OOM_ADJ=0"

Diese Methode schützt dann die PostgreSQL-Systemprozesse effektiv, erlaubt aber dem OOM-Killer etwaige Amoklaufende Backends trotzdem zu terminieren.

Alternativen

Eine alternative Lösung gibt es auch in Form eines Kernelpatches. Dies ergänzt das /proc-Filesystem um eine Liste an Prozessnamen, die explizit vom OOM-Killer nicht berücksichtigt werden dürfen. Da dies jedoch eine inoffizielle Erweiterung des Kernels ist, muss man seinen eigenen Kernel damit pflegen, auch ist diese Erweiterung bei weitem nicht so flexibel wie das Interface über oom_adj. Des weiteren sind Prozessnamen relativ ungeeignet, um spezifische Prozesse eindeutig zu identifizieren (z.B. Java- oder Perlbasierte Prozesse).

Zusammenfassung

Der Linuxkernel bietet mittlerweile umfassende Möglichkeiten, die Speichernutzung von Prozessen an das Memory Management des Kernels anzupassen. Die flexibelste Lösung stellt das /proc-Filesystem mit dem oom_adj-Interface dar. PostgreSQL 9.0 ergänzt dies durch weitere Maßnahmen. Dedizierte Datenbanksysteme können vom Administrator dahingehend angepasst werden, gar kein Overcommit des virtuellen Speichers zuzulassen, hier muss jedoch sorgfältig abgewogen werden, welche Anforderungen die PostgreSQL-Instanz an die VM des Kernels stellt.

Alle Blog-Artikel zum Thema PostgreSQL werden auch als Kategorie PostgreSQL samt eigenem Feed angeboten - und falls ihr nach Support und Services für PostgreSQL sucht, seit ihr bei uns ebenfalls richtig.

tux.jpgNach der Einführung in RHCS geht es jetzt ins Eingemachte: die Installation von RHCS unter Debian, um einzelne KVM-Gäste als Dienst anzubieten.

Die Hintergründe von RHCS haben wir bereits erklärt. Die konkrete Umsetzung wird am Beispiel zweier Hosts mit einem Shared Storage erklärt, die als Service verschiedene KVM-Gäste anbieten.

Installation der Nodes

In diesem Setup sind die Nodes die Maschinen, auf denen KVM läuft. Jeder darauf laufende KVM-Gast ist wiederum ein über RHCS verwalteter Dienst. Bei der Installation der KVM-Hosts ist auf mehrere Punkte zu achten:
  • /tmp/ und /var/ sollten auf verschiedenen Partitionen liegen, das verbessert die Performance.
  • Es sollten Debian-Backports genutzt werden, insbesondere für die Kernel.
  • Alle IP-Adressen sollten via DNS in beide Richtungen auflösbar oder in /etc/hosts eingetragen sein.
  • Der Hostname darf nicht auf 127.0.0.1 zeigen, das führt zu Problemen mit dem Cluster Management System CMAN.
  • /etc/hosts/ und /etc/resolv.conf sollte auf allen Nodes gleich sein.
  • ssh-Keys sollten auf allen Nodes passwortlos für root bestehen und auf alle anderen Nodes verteilt werden.
  • Aus Performance-Gründen ist es besser, den aktuellesten stabilen Kernel zu installieren. Allerdings lässt linux-image-2.6.32-bpo.2-amd64 die Gast-Kernel >= 2.6.30 crashen, ein Patch ist aber verfügbar, siehe #573071.
  • Die Netzwerk-Geräte sollten für einen guten Überblick klar benannt sein - zum Beispiel rhcs-backbone und external anstatt eth0 und eth1.

Einrichtung des Shared Storage

Ein zentrales Element des RHCS ist das Shared Sotrage, auf das die Nodes gemeinsame zugreifen. In diesem Beispiel nehmen wir einen "normalen" Rechner, und installieren auf diesem ein iSCSI-Target:
apt-get install iscsitarget iscsitarget-source 
echo 'ISCSITARGET_ENABLE=true' > /etc/default/iscsitarget
m-a a-i iscsitarget


Hier ist zu beachten, dass das iscsi-Target korrekt bauen muss, siehe auch #566740. Eingerichtet wird die Maschine, welche das Shared Storage bereit stellt, via /etc/ietd.conf:

IncomingUser discovery_in YourSecurePwd1
OutgoingUser discovery_out YourSecurePwd2
Target YOURMACHINE:clvm1
       IncomingUser node_in YourSecurePwd1
       OutgoingUser node_out YourSecurePwd2
       Lun 0 Path=/dev/sdx1,Type=blockio


Auf den Nodes muss das Target korrekt angesprochen werden. Dies wird in der /etc/iscsi/iscsid.conf definiert:

discovery.sendtargets.auth.authmethod = CHAP
discovery.sendtargets.auth.username = discovery_in
discovery.sendtargets.auth.password = YourSecurePwd1
discovery.sendtargets.auth.username_in = discovery_out
discovery.sendtargets.auth.password_in = YourSecurePwd2
node.startup = automatic
node.session.auth.authmethod = CHAP
node.session.auth.username = node_in
node.session.auth.password = YourSecurePwd1
node.session.auth.username_in = node_out
node.session.auth.password_in = YourSecurePwd2


Gestartet wird der Dienst mit /etc/init.d/open-iscsi start. Vorhandene Targets werden mit den folgenden Befehlen gesucht, gelöscht oder hinzugefügt:

# discovering the targets
iscsiadm -m discovery -t st -p YOURMACHINE -P 1
# deleting target on wrong interface
iscsiadm -m node -p 192.168.0.100:3260,1 -o delete
# opening the portal
iscsiadm -m node --targetname "iqn.2010-03.YOURMACHINE:clvm1" --portal "YOURMACHINE:3260" --

VM setup

Die virtuellen Maschinen werden via KVM bereit gestellt. Dafür muss zuerst die passende Software installiert werden:
apt-get install linux-image-2.6.32-bpo.2-amd64 kvm libvirt-bin virtinst -t lenny-backports


Bei der Einrichtung der Bridge für die Gäste muss darauf geachtet werden, dass der Bridge-Name für alle Nodes gleich sein muss. Auch die libvirt-Konfiguration muss gleich sein, es ist daher hilfreich, auf git und vergleichbare Techniken zurück zu greifen. Danach können die Gäste mit

virt-install -n <NAME> -r 256 --vcpus=1 --disk path=/dev/vg_cluster#/<LV> \
  -c /root/debian-<VERSION>-amd64-netinst.iso --vnc --noautoconsole --os-type linux \
  --os-variant debianLenny --accelerate --network=bridge:bridge0 --hvm -k de


installiert und mit virt-viewer -c qemu+ssh://:/system angesehen werden.

RHCS setup

Der nächste Schritt ist das Aufsetzen von RHCS selbst - dafür müssen in erster Instanz die Programme installiert und aufgerufen werden: apt-get install redhat-cluster-suite. Die dadurch bereit gestellten NFS-Dienste werden aber nicht gebraucht:
invoke-rc.d nfs-kernel-server stop
invoke-rc.d nfs-common stop
invoke-rc.d portmap stop
update-rc.d -f nfs-kernel-server remove
update-rc.d -f nfs-common remove
update-rc.d -f portmap remove


Ein anderes Problem ist, dass das Programm system-config-cluster nicht unter Lenny zur Verfügung steht. Es wurde von credativ-Mitarbeiter Philipp Hübner paketiert und ist in Debian erst ab Squeeze enthalten.
Durch einen Backport kann system-config-cluster aber auch auf Lenny genutzt werden:

wget --no-check-certificate https://www.credativ.com/~phu/lenny-backports/system-config-cluster/system-config-cluster_1.0.53-1_all.deb
dpkg -i system-config-cluster_1.0.53-1_all.deb
apt-get -f install
apt-get install xauth


Damit LVM Locking Cluster-weit funktioniert, muss in der /etc/lvm/lvm.conf im Abschnitt global eine Anpassung vorgenommen werden:

 locking_type = 3


Falls wie empfohlen ein neuerer Kernel eingesetzt wird, liegt das Modul lock_dlm nicht mehr vor. Daher muss das Init-Script von CMAN angepasst werden, die Zeile modprobe lock_dlm 2>&1 || return 1 muss auskommentiert werden. Außerdem unterstützt RHCS 2 nur XEN, für libvirt-Unterstützung muss der Ressource Handler vm.sh geladen werden - er liegt für Debian Squeeze bereit:

wget --no-check-certificate https:///www.credativ.com/~phu/vm.sh -O /usr/share/cluster/vm.sh
chmod +x /usr/share/cluster/vm.sh

RHCS selbst wird gestartet mittels

/etc/init.d/cman start
/etc/init.d/clvm start
/etc/init.d/rgmanager start

Fencing

Fencing beschreibt das automatisierte Neutralisieren von Nodes, die nicht mehr reagieren. Wir setzen in unserem Beispiel dafür eine per Netz steuerbare Steckdose ein, NETIO-230A. Es liegt bisher kein fence agent für dieses Gerät vor, aber mit Hilfe der verfügbaren Python-Bibliothek kann ohne Weiteres ein solcher geschrieben werden.

Abschließende Worte

Dieses Howto zeigt, dass das Einrichten von RHCS unter Debian mit einfachen Schritten möglich ist - wenn auch je nach Einsatz weitere Anpassungen vorgenommen werden müssen. Dabei helfen wir übrigens gerne - Open Source Hochverfügbarkeits-Lösungen gehören zu unseren Spezialitäten, und Services und Support bei KVM-Virtualisierung ist unser Alltagsgeschäft.

bash.pngDer Text-Editor vim bietet viele Möglichkeiten für Automatismen. Im folgenden Artikel wird das automatische Einfügen von Textbausteinen vorgestellt.

Sehr häufig schreibt man beim Programmieren oder beim Administrieren immer wieder die gleichen Code-Teile. Dabei kann der Editor vim schon beim Anlegen von Dateien erkennen, um was für einen Datei-Typ es sich handelt, und entsprechende Textbausteine bereit stellen. Dies kann durch die Datei .vim/plugin/autoinsert.vim realisiert werden, die zum Beispiel wie folgt aussieht:

if has("autocmd")
augroup autoinsert
  au!
  autocmd BufNewFile *.c call s:Template("c")
  autocmd BufNewFile Makefile call s:Template("make")
  autocmd BufNewFile makefile call s:Template("make-simple")
augroup END
endif

function s:Template(argument)
        if (a:argument == "help")
                echo "Currently availible templates:"
                echo " c                - Plain C Template"
                echo " make             - Makefile Template"
                echo " make-simple      - Simple Variant of the Makefile Template"
        else
                " First delete all in the current buffer
                %d

                " The Makefile variants
                if (a:argument == "make")
                        0r ~/.vim/skeletons/template.make
                        set ft=make
                elseif (a:argument == "make-simple")
                        0r ~/.vim/skeletons/template.make_simple
                        set ft=make
                elseif (a:argument == "make-simple-cpp")
                        0r ~/.vim/skeletons/template.make_simple_cpp
                        set ft=make

                " Stuff for plain C
                elseif (a:argument == "c")
                        0r ~/.vim/skeletons/template.c
                        set ft=c
                endif

                silent %!~/.vim/do_header %
        endif
endfunction

command! -nargs=1 Template call s:Template(<f-args>)

Wie zu sehen ist, werden in den Zeilen 21-35 diverse Templates aufgerufen, die wiederum die Textbausteine beinhalten. Das Template für make_simple, ~/.vim/skeletons/template.make_simple fügt zum Beispiel die üblichen Compiler-Flags zum Bauen von C/C++-Programmen mit dem gcc ein:

CC := gcc
CFLAGS := -Wall -pedantic -O3
LDFLAGS :=

PROG := main
OBJS := main.o

all: $(PROG)

$(PROG): $(OBJS)
        $(CC) $(LDFLAGS) -o $@ $^

clean:
        rm -rf $(PROG) $(OBJS)

.PHONY: all clean

Das folgende Template ~/.vim/skeletons/template.c mit Textbausteinen für Dateien mit der Endung .c fügt neben dem obligatorischen GPL-Header auch ein C-Code-Gerüst ein:

/*
 * %%FILENAME%% - description
 *
 * Copyright (C) %%YEAR%% %%AUTHOR%%
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
 */

#include <stdio.h>

int
main (int argc, char **argv)
{
  return 0;
}

/**This must remain at the end of the file.**********
 * vim600:set sw=2 ts=8 fdm=marker fmr=«««,»»»:     *
 * vim600:set cindent cinoptions={1s,>2s,^-1s,n-1s: *
 ****************************************************/


Die im Code vorkommenden Variablen wie %%FILENAME%% oder auch %%AUTHOR%% können ebenfalls automatisch beim Aufruf durch ein kleines Shell-Skript ersetzt werden: das Skript ~/.vim/do_header, aufgerufen mit dem Dateinamen als Übergabewert, ermittelt über die glibc-Funktion getent bzw. aus der /etc/passwd den Namen des Autors und setzt diesen ein. Auch andere Variablen werden mit üblichen GNU-Tools ermittelt, wie das folgende Listing zeigt:

#!/usr/bin/env zsh

if which getent > /dev/null; then
        REALNAME=$(getent passwd $USER|awk -F : '{print $5}' | awk -F , '{print $1}')
else
        REALNAME=$(grep $USER /etc/passwd|awk -F : '{print $5}' | awk -F , '{print $1}')
        if which nidump > /dev/null && [ -z "$REALNAME" ]; then
                REALNAME=$(nidump passwd / | grep $USER|awk -F : '{print $8}')
        fi
fi
DATE=$(date)
YEAR=$(date +%Y)
FILENAME=$(echo $1 | sed 's/[^/]*\///')
FILE=$(echo $FILENAME | sed 's/\..*//')
FILEBIG=$(echo $FILE | tr '[:lower:]' '[:upper:]')
sed     "s/%%AUTHOR%%/$REALNAME/g;
        s/%%DATE%%/$DATE/g;
        s/%%YEAR%%/$YEAR/g;
        s/%%FILENAME%%/$FILENAME/g;
        s/%%FILE%%/$FILE/g;
        s/%%FILEBIG%%/$FILEBIG/g;"


Neben den hier gezeigten Beispielen können auch Templates für andere Programmiersprachen oder generell Datei-Typen erstellt werden, die Möglichkeiten sind fast endlos.

Dieses Howto zeigt nur eine kleine Auswahl der möglichen Automatismen des Editors vim, welche die tägliche Arbeit eines Admins erheblich erleichtern. Alle weiteren Howtos dieses Blogs werden als Kategorie Howtos samt eigenem Feed angeboten - falls ihr tiefer gehende Support- oder Service-Leistungen für GNU-Tools oder Linux allgemein sucht, seit ihr bei uns ebenfalls richtig.

light_logo_170px.pngLighttpd ist ein Webserver mit einer schnell wachsenden Nutzerbasis. Dieses Howto zeigt, wie bei Lighttpd Redirects auf Basis der Spracheinstellung des Nutzers im Browser erstellt werden.

Während des Wechsels unserer Blog-Software auf Movable Type wurde die Entscheidung getroffen, die Begrüßungsseite je nach Sprache des besuchenden Browsers entweder auf deutsch oder auf englisch anzuzeigen. Da Movable Type statische HTML-Seiten generiert, sollte dabei kein Workaround mit z.B. cgi-Skripten zum Tragen kommen.

Als Webserver wird bei der Maschine Lighttpd genutzt. Lighttpd kann das mächtige Modul mod_magnet einbinden, das eine Lösung für Sprach-Problematik bietet: mod_magnet kann Lua-Skripte ausführen, die nahezu alle Aspekte der Behandlung einer Anfrage ändern können. Die Anfragen an http://blog.credativ.com/ werden mit dem folgenden Lua-Schnipsel entsprechend umgeschrieben:

lighty.env["uri.path"] = "/en/index.html"
lang = lighty.request['Accept-Language']
if (lang) then
        if (string.sub(lang,1,2) == 'de') then
                lighty.env["uri.path"] = "/de/index.html"
        end
end
lighty.env["physical.rel-path"] = lighty.env["uri.path"]
lighty.env["physical.path"] = lighty.env["physical.doc-root"] .. lighty.env["physical.rel-path"]

In der Lighttpd-Konfiguration muss mod_magnet angeschaltet sein. Damit alle Anfragen für "/" entsprechend bearbeitet werden, muss außerdem folgender Schnipsel in der Konfiguration auftauchen:

$HTTP["url"] =~ "^/$" {
	magnet.attract-physical-path-to = ( "/path/to/your/script.lua" )
}

mod_magnet cached dabei das kompilierte Skript und führt es im Kern von Lighttpd aus, es sollte also keinen nennenswerten Einfluss auf die Auslieferungszeit von Webseiten haben.

Schreibt einen Kommentar, wenn Ihr Fragen dazu habt. Für professionelle Unterstützung und Beratung bei Lighttpd auf Geschäftsebene stehen wir wie üblich auch mit unserem Open Source Support Center bereit.

tux.jpgDer Werkzeugkasten eines System-Administrators sollte immer mit effektiven Werkzeugen gefüllt sein. Heute stellen wir das Paket sysstat vor.

Das Paket sysstat ist eine Sammlung von Kommandozeilen-Programmen, die dem Systemadministrator einen schnellen Überblick über die Leistungsfähigkeit des Systems verschaffen. In ihrer Arbeitsweise sind sie ein Frontend zu den Daten des Linux-Kernels, und können dementsprechend nur Daten ausgeben, die der Kernel selbst kennt - sie sammeln keine Daten darüber hinaus!

iostat

iostat liefert vor allen Dingen Status-Informationen über den Datendurchsatz der Festplatten, angeschlossener NFS-Laufwerke und aller CPUs. Ist ein System im Leistungsverhalten auffällig, kann iostat verwendet werden, um z.B. IO-Waits zu identifizieren.
Linux 2.6.31-19-generic (mymachine)         04.03.2010      _x86_64_        (2 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          11,82    0,29    3,44    1,25    0,00   83,20

Device:            tps   Blk_read/s   Blk_wrtn/s   Blk_read   Blk_wrtn
sda               9,39       161,19       168,44    4264806    4456696
Die zur Verfügung stehenden Optionen sind dabei umfangreich, die Wichtigsten dienen vor allen Dingen der Feststellung für spezialisierte Ausgaben:
-d
Anzeige nur der Festplatten-Daten.
-c
Anzeige der reinen CPU-Daten.
-p
Anzeige der IO-Daten je Partition.
-n
Anzeige der NFS-IO-Daten.
-x
Erweiterte Anzeige der Festplatten-Daten.
-t $NUM1
Gibt an, nach wie vielen Sekunden die Anzeige aktualisiert werden soll.

mpstat

Das Werkzeug mpstat dient der genaueren Analyse der Prozessor-Auslastung - ohne weitere Option wird eine Standard-Übersicht gezeigt, die an die Ausgabe von iostat erinnert.
Linux 2.6.31-19-generic (mymachine)         04.03.2010      _x86_64_        (2 CPU)

17:01:52     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest   %idle
17:01:52     all   11,96    0,29    3,26    1,23    0,10    0,11    0,00    0,00   83,06
Im Gegensatz zu iostat wird hier aber direkt schon die Arbeit mit aufgelistet, die der Prozessor an Soft- und Hardware-Interrupts verrichtet. Mit der Option -A wird die Ausgabe erweitert: die Statistiken werden pro Prozessor angezeigt, außerdem werden die Interrupts pro Sekunde pro Prozessor angezeigt.Eine Zahl $NUM hinter dem Befehl lässt diesen als Prozess laufen, und aktualisiert die Anzeige alle $NUM Sekunden.

pidstat

pidstat hilft bei der Analyse einzelner Prozesse - während der Aufruf ohne Optionen noch eine Liste aller Prozesse anzeigt, hilft die Option -C bei der Spezifizierung der zu untersuchenden Tasks:
Linux 2.6.31-19-generic (mymachine)         04.03.2010      _x86_64_        (2 CPU)

17:02:32          PID    %usr %system  %guest    %CPU   CPU  Command
17:02:32            1    0,00    0,00    0,00    0,00     1  init
17:02:32         2888    0,00    0,00    0,00    0,00     0  start_kdeinit
17:02:32         2889    0,00    0,00    0,00    0,00     0  kdeinit4
Mit der zusätzlichen Option -d werden I/O-Statistiken zu den Prozessen angezeigt. Mit -p kann die PID definiert werden statt des Prozess-Namens, -r verschafft einen Überblick über die Speicherauslastung. Eine Zahl $NUM hinter dem Befehl lässt diesen als Prozess laufen, und aktualisiert die Anzeige alle $NUM Sekunden.

sar

Die bisher vorgestellten Informationen geben jeweils nur einen Schnappschuss der Systemleistung wieder, echtes Status-Logging findet hier aber nicht statt. Dafür ist das Programm sar mit seinen Helfer-Programmen verantwortlich: es nimmt via Cron-Job alle 10 Minuten verschiedene Leistungsdaten des Systems auf, und sichert diese. Ein einfacher Aufruf gibt eine erste Idee des Performance-Verhaltens:
Linux 2.6.31-19-generic (mymachine)         04.03.2010      _x86_64_        (2 CPU)

09:30:30          LINUX RESTART

09:35:02        CPU     %user     %nice   %system   %iowait    %steal     %idle
09:45:01        all     17,38      1,02      5,10      3,87      0,00     72,63
09:55:01        all     11,90      0,27      2,86      0,75      0,00     84,23
10:05:01        all     10,20      3,52      3,46      2,55      0,00     80,27
10:15:02        all     12,96      0,32      3,18      0,65      0,00     82,89
10:25:01        all      7,94      0,18      3,17      2,42      0,00     86,30
10:35:01        all     12,41      0,89      4,55      0,56      0,00     81,60
10:45:02        all      8,97      0,09      3,51      0,89      0,00     86,55

Der Abruf aller Informationen mit sar -A sprengt aber leicht jede Bildschirmgröße. Die unterschiedlichen Einzel-Optionen sind zu zahlreich, um sie hier detailliert aufzulisten, einen Überblick gibt die Man-Page.
tux.jpgBei der Administration einer großen Zahl von Servern ist ein zentrales Konfigurations-Management irgendwann unabdingbar. Dieser Artikel beschreibt in einer ersten Einführung das in Ruby geschriebene Framework Puppet.

Einführung

Teil unseres Tagesgeschäfts ist es, beliebig große Server-Installationen zu verwalten und zu warten. Gerade bei großen Clustern heißt dies, eine Vielzahl von Maschinen mit fast identischer Konfiguration nebeneinander zu betreiben. Ohne eine zentralisierte, automatisierte Konfigurations-Verteilung ist dies kaum machbar - an dieser Stelle tritt Puppet auf den Plan.
Wie auch andere Konfigurations-Management-Werkzeuge greift Puppet auf einen zentralen Server zurück, der die Konfiguration verwaltet. Dort fragen die "Clients" verschlüsselt die Konfiguration ab, und spielen Sie gemäß der Vorgaben des Servers ein, verändern Rechte, führen Befehle aus, etc. Die Vorteile liegen auf der Hand:
  • Arbeitsschritte müssen unabhängig von der Rechner-Zahl nur einmal umgesetzt werden, unnötige Wiederholungen werden vermieden.
  • Für alle Rechner vereinheitlicht sich die Konfiguration zwangsweise - und wird damit einfach wartbar.
  • Die zentrale Infrastruktur ermöglicht einen schnellen Überblick an einer Stelle - ein "Rumlaufen", entfällt.
  • Der zentrale Konfigurationsbaum ermöglicht damit eine Versionierung: das Zurückspielen der Konfiguration für alle Rechner eines Netzwerkes z.B. auf den Stand "PRE-UPDATE" geht mit wenigen Befehlen, für das ganze Netzwerk!

Technische Arbeitsweise

Puppet besteht aus einem zentralen Server, Puppet-Master genannt, und den Clients, genannt Nodes. Diese melden sich beim Master an, und fragen dort nach der aktuellen Konfiguration. Der Master gibt diese an die Nodes weiter - die Möglichkeiten der Anweisungen des Masters sind schier unbegrenzt:
  • Der Server kann Dateien übergeben, die an bestimmte Orte kopiert werden.
  • Die Node kann angewiesen werden, Dateirechte zu prüfen und notfalls zu korrigieren.
  • Je nach Betriebssystem kann erzwungen werden, dass die Node prüft, ob bestimmte Dienste aktiv sind, oder ob bestimmte Pakete auch in der neuesten Version installiert sind.
  • Der Server kann die Node anweisen, bestimmte Befehle auszuführen.
  • usw.
Im Prinzip kann alles mit der Übergabe von Dateien vom Server an die Node erledigt werden, doch ist dies in komplexen Setups weder übersichtlich noch vereinfachend. Gerade die Abstraktion von System-Aufgaben (Dienste neu starten, Paket-Versionen sicherstellen, Nutzer einrichten, etc.) ohne das direkte Überschreiben von Dateien hilft ungemein beim Konfigurieren komplexer Systeme.

Installation

Für die Installation benötigt man einen zentralen Puppet-Master, der die Konfiguration verwaltet: apt-get install puppetmaster Puppet geht davon aus, dass alle beteiligten Rechner FQDNs haben, dies sollte in einem korrekt gewarteten Netzwerk aber eh der Fall sein!
Auf jedem zu verwaltenden Rechner wird ein Puppet-Client mit apt-get install puppet installiert.

Konfiguration von Puppet

Die Puppet-Nodes suchen automatisch nach dem Rechner, der auf den Namen puppet auflöst, so lange der Name korrekt auf den Hauptserver zeigt, brauchen sie nicht weiter konfiguriert werden.
Beim Puppet-Master muss der Dateiserver noch korrekt konfiguriert werden, der wie oben beschrieben Dateien an die Nodes übergeben kann. Je nach Anspruch können die Dateien dabei nah bei der weiteren Konfiguration gehalten werden, oder aber zentral in einem externen Archiv untergebracht werden. Für unser Beispiel werden wir die zu verteilenden Dateien nah bei der eigentlichen Konfiguration halten, ganz so, wie es auch im Best Practice Guide und in der Anleitung Module Configuration der Puppet-Dokumentation beschrieben wird.
Daher reicht es, in der Datei /etc/puppet/fileserver.conf folgende Konfiguration vorzunehmen:
[modules]
allow 192.168.0.1/24
allow *.credativ.de

Konfiguration der Konfiguration - Module

Um eine Server-Konfiguration durch Puppet erledigen zu lassen, ist es am Besten, diese in Aufgabenbereiche oder Themen zu unterteilen, wie zum Beispiel "ssh", "logs", "apache", etc. Diese Bereiche werden in Puppet als "Module" bezeichnet, und sind der Kern von Puppets Konfigurationsverwaltung. Den Aufbau eines Modules beschreiben wir hier anhand einer fiktiven SSH-Konfiguration und halten uns dabei eng an den Best Practice Guide.
Es sei angemerkt, dass die tatsächliche Konfiguration von SSH durch Puppet an einigen Stellen dynamischer wäre, hier wird nur ein vereinfachter Weg gezeigt.

Das SSH-Modul

Der Anspruch des SSH-Moduls ist:
  1. Das openssh-server-Paket soll in neuester Version installiert sein.
  2. Die Datei sshd_config soll der entsprechen, die im Puppet-Master hinterlegt ist.
  3. Falls die Datei sshd_config installiert wird, soll der sshd neu gestartet werden.
  4. Der Nutzer credativ soll bestimmte Dateien in seinem Verzeichnis $HOME/.ssh haben.
Um diesen Ansprüchen gerecht zu werden, erstellen wir die notwendigen Verzeichnisse des Moduls:
mkdir -p /etc/puppet/modules/ssh/manifests
mkdir -p /etc/puppet/modules/ssh/files
Der Ordner Mainfests enthält die eigentlichen Konfigurations-Anweisungen des Moduls, der Ordner files hält Dateien vor, die an die Nodes ausgeliefert werden sollen.
Die Konfigurations-Anweisungen des Modules finden sich in der Datei init.pp im Ordner manifests. Die Gruppe von Anweisungen, um die obigen Ziele 1.-4. zu erfüllen, wird dort als "Klasse" zusammen gefasst. Die Klasse enthält selbst wiederum Untersektionen, sogenannte Types. In unserem Fall findet sich für jedes vorher definierte Ziel ein Type:
class ssh{
        package { "openssh-server":
                 ensure => latest,
        }
        file { "/etc/ssh/sshd_config":
                owner   => root,
                group   => root,
                mode    => 644,
                source  => "puppet:///ssh/sshd_config",
        }
        service { ssh:
                ensure          => running,
                hasrestart      => true,
                subscribe       => File["/etc/ssh/sshd_config"],
        }
        file { "/home/credativ/.ssh":
                path    => "/home/credativ/.ssh",
                owner   => "credativ",
                group   => "credativ",
                mode    => 600,
                recurse => true,
                source  => "puppet:///ssh/ssh",
                ensure  => [directory, present],
        }
}
jeder Type setzt eine gänzlich andere Aktion auf dem Node um:
package
Hier wird sicher gestellt, dass das Paket openssh-server in der neuesten Version installiert ist.
file
Eine Datei auf der Node wird durch die Version vom Server überschrieben und mit entsprechenden Rechten versehen.
service
Der Dienst sshd muss laufen, und wird notfalls gestartet. Falls außerdem die Datei /etc/ssh/sshd_config aktualisiert wird, wird auch der Dienst neu gestartet.
file
Hier taucht noch einmal der Type "file" auf - es wird aber nicht eine einzelne Datei übertragen, sondern gleich ein ganzes Verzeichnis.
Damit das Modul auch korrekt arbeitet, müssen die beim Type "file" definierten Dateien und Verzeichnisse auch unter /etc/puppet/modules/ssh/files/ zu finden sein.

Nodes und Module

Wir haben nun drei Elemente: den Puppet-Master, die Nodes, und die Module. Nun muss die Zuweisung erfolgen, welche Nodes welche Module aufrufen sollen. Dafür muss zuerst das Modul in der Datei /etc/puppet/manifests/modules.pp aktiviert werden:
import "ssh"
Die Zuweisung zu den einzelnen Nodes erfolgt in der Datei /etc/puppet/manifests/nodes.pp. Diese legt für jede Node fest, welches Modul geladen wird. Außerdem gibt es für alle nicht weiter spezifizierten Nodes einen Default-Eintrag, und zu guter Letzt können auch Einträge von anderen abgeleitet werden. Um also für alle Nodes das Modul "rsyslog" zu laden, aber nur für die Node "external" das Modul "ssh", sieht der Eintrag wie folgt aus:
node default {
   include rsyslog
}

node 'external' inherits default {
  include ssh
}
Damit ist Puppet fertig konfiguriert, und nimmt sofort seine Arbeit auf.

Zertifikate - Sichere Kommunikation zwischen Node und Master

Die Kommunikation zwischen Master und Node verläuft verschlüsselt. Um dies zu gewährleisten, müssen Nodes auf dem Master zertifiziert werden. Dies ist möglich, nachdem ein Node das erste Mal eine Anfrage an den Master gestellt hat - der Master setzt diesen Node dann auf wait, und stellt ihm so lange keine Daten zur Verfügung. Erst, wenn die Node durch einen Admin verifiziert wurde, wird die Node frei geschaltet. Mit # puppetca --list wird die Liste der noch zu verifizierenden Nodes angezeigt, verifiziert wird mit: # puppetca --sign external.example.com Bei Bedarf kann dieser Prozess weiter verfeinert werden.

Abschließende Worte

Die hier vorgestellten Beispiele sind natürlich stark vereinfacht. Im Real-Betrieb würde die SSH-Konfiguration komplexer sein, und Schlüssel würden nicht gerade mit dem Type "file" statisch verteilt werden. Aus diesen Beispielen lassen sich aber leicht weitere Module ableiten, und die an vielen Stellen verlinkte Konfiguration tut ihr Übriges, damit der geneigte Leser sich tiefer in die Materie einarbeiten kann.
Wir hier bei credativ haben mit Puppet mittlerweile sehr umfangreiche und sehr gute Erfahrungen gemacht, leisten an vielen Ecken Support und Beratung für Puppet und merken, wie die Nachfrage steigt. Puppet ist derzeit auf der Überholspur, und es wird spannend sein zu beobachten, wie sich der Platzhirsch cfengine angesichts dieser Konkurrenz verhält.