Um in hochkomplexe Monitoring-Umgebungen mittels Nagios die noetigen Anpassungen, wie beispielsweise dem Hinzufuegen von weiteren Servern oder Abaendern von bestehenden Konfigurationen, zeitlich moeglichst gering zu halten, ist eine feste, intelligente Struktur in der Konfiguration absolut unabdingbar. Monitoring ist schliesslich dazu da, Fehlerlokalisation zu erleichtern, vielleicht gar zu automatisieren und somit freie Zeitraeume fuer andere wichtige Tasks zu schaffen und eben nicht dazu, die freie Zeit damit zu verbringen, die Monitoring-Umgebung staendig pflegen zu muessen.
Nagios bringt von Hause aus nuetzliche, objektorientierte Prinzipien mit, die man sich in grossen, wachsenden Umgebungen zunutze machen muss.
Viele Systemadministratoren machen sich zu Beginn leider nicht die Muehe, genauer zu planen, was denn eigentlich ueberwacht werden soll oder welche Moeglichkeiten sich bei der Konfiguration ergeben. Das hat zur Folge, dass die Installation, die eigentlich zum Evaluieren gedacht war, spaeter auch produktiv eingesetzt wird und die vielen, kleinen Fehler, die man zu Beginn unweigerlich macht, in den produktiven Einsatz mit hinuebergefuehrt werden. Spaeter, wenn die Monitoring-Umgebung waechst, werden diese kleinen, unueberlegten Fehler meist zu einer zeitraubenden Angelegenheit, die aufgrund der Komplexitaet der Konfiguration auch nicht mehr ohne grossen Zeitaufwand zu beheben sind.
Ein Beispiel:
Die Nagios-Installation funktioniert. Man moechte nun einen ersten Host hinzufuegen. Der gemeine Administrator lernt also, wie man einen Host und dazu gehoerige Services definiert. In der Konfiguration sieht das dann meistens so aus:
hosts.cfg
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | define host{ host_name webserver-123 alias Webserver 123 address 192.168.1.42 parents switch-456 check_command check-host-alive check_interval 5 retry_interval 1 max_check_attempts 5 check_period 24x7 process_perf_data 0 retain_nonstatus_information 0 contact_groups webserver-admins notification_interval 30 notification_period 24x7 notification_options d,u,r } define service{ host_name webserver-123 service_description HTTP check_command check-http!80 max_check_attempts 5 check_interval 5 retry_interval 3 check_period 24x7 notification_interval 30 notification_period 24x7 notification_options w,c,r contact_groups webserver-admins } |
Wir sehen hier also den Host ‘webserver-123′ und den dazugehoerigen Service ‘HTTP’, der ueberprueft, ob der Webserver denn auch auf localhost:80 lauscht. Soweit so gut: 31 Zeilen Konfiguration lassen sich noch recht gut ueberschauen und im Zweifelsfall auch anpassen. Nun ist es aber nicht mit dem Ueberwachen eines einzigen Services erledigt. Neben dem Ueberpruefen, ob HTTP funktioniert sind vielleicht auch noch andere Informationen nuetzlich, wie beispielsweise die Auslastung der CPU oder RAM, dem freien Speicherplatz auf den HDDs, dem Load der Maschine, wieviele Prozesse am laufen sind, die Anzahl der eigenloggten User, dem Datendurchsatz auf den einzelnen Interfaces und vieles, vieles mehr.
700-1000 Zeilen pro zu ueberwachendem Host in der Konfiguration werden ohne grosse Probleme erreicht (inklusive dem Aerger, den man spaeter hat aufgrund des Zeitaufwands, den man einplanen muss, wenn Anpassungen anstehen). Dafuer gibt es jedoch eine Loesung.
Am Anfang war das Template
Nagios folgt einem strikt objektorientierten Ansatz. Man hat also die Moeglichkeit einen (oder auch mehrere) generische Templates zu definieren, von dem die tatsaechlich zu ueberwachenden Hosts und Services ihre Einstellungen erben. Man lagert also alle Optionen in das generische Template aus und definiert den jeweiligen Host oder Service nur mit den Optionen, die auch wirklich fuer die spezifische Maschine notwendig sind. Zuerst folgt die Definition des Templates, danach die des Hosts.
templates.cfg
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | define host{ name generic-host ; The name of this host template notifications_enabled 1 ; Host notifications are enabled event_handler_enabled 1 ; Host event handler is enabled flap_detection_enabled 1 ; Flap detection is enabled failure_prediction_enabled 1 ; Failure prediction is enabled process_perf_data 1 ; Process performance data retain_status_information 1 ; Retain status information across program restarts retain_nonstatus_information 1 ; Retain non-status information across program restarts check_period 24x7 ; By default, Linux hosts are checked round the clock check_interval 5 ; Actively check the host every 5 minutes retry_interval 1 ; Schedule host check retries at 1 minute intervals max_check_attempts 3 ; Check each Linux host 10 times (max) check_command check-host-alive ; Default command to check Linux hosts notification_period 24x7 ; Linux admins hate to be woken up, so we only notify during the day ; Note that the notification_period variable is being overridden from ; the value that is inherited from the generic-host template! notification_interval 30 ; Resend notifications every 2 hours notification_options d,u,r ; Only send notifications for specific host states contact_groups nagios-admins ; Notifications get sent to the admins by default register 0 ; DONT REGISTER THIS DEFINITION - ITS NOT A REAL HOST, JUST A TEMPLATE! } |
hosts.cfg
1 2 3 4 5 6 | define host{ use generic-host host_name webserver-01 alias Webserver 01 address 192.168.1.42 } |
Die Anzahl der Konfigurationsparameter fuer einen einzelnen Host ist somit von 17 auf 6 Zeilen reduziert worden. Das gleiche Prinzip laesst sich natuerlich auch auf Services anwenden:
templates.cfg
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | define service{ name generic-service ; The 'name' of this service template active_checks_enabled 1 ; Active service checks are enabled passive_checks_enabled 1 ; Passive service checks are enabled/accepted parallelize_check 1 ; Active service checks should be parallelized (disabling this can lead to major performance problems) obsess_over_service 1 ; We should obsess over this service (if necessary) check_freshness 0 ; Default is to NOT check service 'freshness' notifications_enabled 1 ; Service notifications are enabled event_handler_enabled 1 ; Service event handler is enabled flap_detection_enabled 1 ; Flap detection is enabled failure_prediction_enabled 1 ; Failure prediction is enabled process_perf_data 0 ; Process performance data retain_status_information 1 ; Retain status information across program restarts retain_nonstatus_information 1 ; Retain non-status information across program restarts is_volatile 0 ; The service is not volatile check_period 24x7 ; The service can be checked at any time of the day max_check_attempts 3 ; Re-check the service up to 3 times in order to determine its final (hard) state normal_check_interval 5 ; Check the service every 10 minutes under normal conditions retry_check_interval 1 ; Re-check the service every two minutes until a hard state can be determined contact_groups nagios-admins ; Notifications get sent out to everyone in the 'admins' group notification_options c,r ; Send notifications about warning, unknown, critical, and recovery events notification_interval 20 ; Re-notify about service problems every hour notification_period 24x7 ; Notifications can be sent out at any time register 0 ; DONT REGISTER THIS DEFINITION - ITS NOT A REAL SERVICE, JUST A TEMPLATE! } |
services.cfg
1 2 3 4 5 6 | define service{ use generic-service service_description HTTP check_command check_http!80 host_name webserver-01 } |
Der Vorteil liegt auf der Hand: Nehmen wir an, die IT-Abteilung ist gewachsen und es wurden einzelne Teams fuer spezifische Server eingeteilt. Jetzt haben wir also nicht mehr nur die Kontaktgruppe “nagios-admin”, sondern auch die Gruppe “webserver-admin”. Der objektorientierte Ansatz macht mir die Aenderungen leicht, indem ich die Kontaktgruppe lediglich in der Template-Definition hinzufuege und eben nicht in jeder einzelnen Host- und Service-Definition. Grade fuer Administratoren, die mit awk und sed (unverstaendlicherweise) auf Kriegsfuss stehen, ist Letzteres eine enorm zeitaufwendige Arbeit, die man sich so ersparen kann.
Jetzt ist die Monitoring-Umgebung also gewachsen. Wir ueberwachen eine Vielzahl verschiedenster Dienste und wurden letzte Nacht aufgrund einer Warnung, dass sich der Disk Space auf /dev/sda3 verringert, nachts um halb 4 aus dem Bett geklingelt. Das Problem ist nun nicht derart unternehmenskritisch, als das wir dafuer unseren wohlverdienten Schlaf unterbrechen muessten und koennte auch problemlos morgens um 9 vom Office aus behoben werden. In unserer generischen Service-Definition, unserem Template, haben wir jedoch eine 24×7 Benachrichtung definiert. Wir muessen uns also ueberlegen, wie wir erreichen koennen, das fuer einzelne Checks andere Bedingungen gelten. Dafuer bieten sich folgende zwei Loesungen an:
Ueberschreiben von Template-Definitionen
Wir erinnern uns: In unserem Service-Template haben wir folgendes definiert:
templates.cfg
1 | notification_period 24x7 ; Notifications can be sent out at any time |
Dies gilt fuer alle Services, die ihre Optionen von diesem Template erben. Um die Option des Templates zu ueberschreiben, koennen wir in der spezifischen Service-Definition die jeweilige Option wie folgt ueberschreiben:
services.cfg
1 2 3 4 5 6 7 | define service{ use generic-service service_description DISK3 check_command check_nrpe!5667!check_disk3 host_name webserver-01 notification_period 8x5 } |
Wir haetten somit unser Ziel erreicht und wuerden bezueglich etwaigiger Speicherplatz-Probleme nur noch waehrend der Arbeitszeit benachrichtigt werden. Allerdings ist diese Art und Weise eher eine “von hinten durch die Brust ins Auge”-Loesung. Wir haben schliesslich wieder die Service Definition aufgeblaeht und was passiert, wenn wir weitere Optionen aus dem Service-Template veraendern moechten? Richtig, die Konfiguration von spezifischen Hosts und Services waechst wieder auf ein unertraegliches, wartungsfeindliches Mass an. Problematisch ist jetzt nicht mehr nur der Zeitaufwand, sondern auch das uneinheitliche Bild in der Konfiguration. Man muss schlicht und ergreifend wissen, dass man die notification_period fuer den Check in der Service-Definition explizit veraendert hat.
Wuerde beispielsweise beschlossen werden, dass /dev/sda3 jetzt doch eher zu den unternehmenskritischen Diensten gehoert und unter keinen Umstaenden in Platznoete geraten darf, dann steht dem Administrator, der schon lange oder gar noch nie nichts mehr an der Konfiguration gemacht hat, zuerst einmal eine laengere Suche nach der Option im Heuhaufen bevor. Besser ist es daher, sich mehrere Service-Templates fuer einzelne Service-Kategorien zu erstellen und diese vom generischen Template erben zu lassen. Man kann also auch Templates von anderen Templates erben lassen.
Vererbung innerhalb von Templates
Bevor man damit beginnt, eine Vielzahl von verschiedenen Service-Templates zu erstellen, sollte man konsolidieren. Ich beispielsweise fahre sehr gut mit nur 4 weiteren Service-Templates, die allesamt verschiedene Gewichtungen beinhalten bezueglich der Intervalle, in denen der jeweilige Dienst ueberprueft wird und den Zeitraeumen, in denen dies geschehen soll und ihre Einstellungen vom oben beschriebenen Service-Template erben:
templates.cfg
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | define service{ name normal-service ; The name of this service template use generic-service ; Inherit default values from the generic-service definition max_check_attempts 3 ; Re-check the service up to 4 times in order to determine its final (hard) state normal_check_interval 5 ; Check the service every 5 minutes under normal conditions retry_check_interval 1 ; Re-check the service every minute until a hard state can be determined register 0 ; DONT REGISTER THIS DEFINITION - ITS NOT A REAL SERVICE, JUST A TEMPLATE! } define service{ name important-service ; The name of this service template use generic-service ; Inherit default values from the generic-service definition max_check_attempts 2 ; Re-check the service up to 4 times in order to determine its final (hard) state normal_check_interval 1 ; Check the service every 5 minutes under normal conditions retry_check_interval 1 ; Re-check the service every minute until a hard state can be determined register 0 ; DONT REGISTER THIS DEFINITION - ITS NOT A REAL SERVICE, JUST A TEMPLATE! } define service{ name minor-service ; The name of this service template use generic-service ; Inherit default values from the generic-service definition max_check_attempts 5 ; Re-check the service up to 4 times in order to determine its final (hard) state normal_check_interval 10 ; Check the service every 5 minutes under normal conditions retry_check_interval 5 ; Re-check the service every minute until a hard state can be determined notification_period 24x7 ; Notifications can be sent out at any time register 0 ; DONT REGISTER THIS DEFINITION - ITS NOT A REAL SERVICE, JUST A TEMPLATE! } define service{ name unimportant-service ; The name of this service template use generic-service ; Inherit default values from the generic-service definition max_check_attempts 10 ; Re-check the service up to 4 times in order to determine its final (hard) state normal_check_interval 60 ; Check the service every 5 minutes under normal conditions retry_check_interval 10 ; Re-check the service every minute until a hard state can be determined notifications_enabled 0 ; Service notifications are disabled register 0 ; DONT REGISTER THIS DEFINITION - ITS NOT A REAL SERVICE, JUST A TEMPLATE! } |
Man ist somit also problemlos in der Lage, bestimmte Services im Gegensatz zu anderen weniger zu gewichten. Wichtige, unternehmenskritische Dienste koennen im Minutenintervall ueberprueft werden, andere weniger wichtigere Dienste aber beispielsweise nur stuendlich. Wir erreichen somit die noetige Flexibilitaet, um Dienste entsprechend ihrer Bedeutung ueberpruefen zu koennen.
Hosts zu Hostgruppen zusammenfassen
Nun haben wir eine bereits sehr gute Basis erreicht. Wir sind in der Lage, Hosts und Services mittels einiger, weniger Zeilen in der Konfiguration anzulegen und abzuaendern. Allerdings muessen wir weiterhin fuer jeden Host die Services einzeln anlegen, was zur Folge hat, dass fuer die 6 Zeilen lange Host-Definition etwa 100 Zeilen Service-Definition folgen. Hier gilt es zu optimieren, indem wir zuerst einmal Hostgruppen nach folgendem Schema einrichten:
hostgroups.cfg
1 2 3 4 5 | define hostgroup{ hostgroup_name webserver alias Our company's webserver members webserver-01,webserver-02,webserver-03 } |
Hostgruppen-basierte Service-Definitionen
Der Weg zu Hostgruppen-basierten Services ist jetzt nicht mehr weit. Anstatt den HTTP Service-Check fuer jeden einzelnen Host mittels des Hostnames definieren, nehmen wir jetzt einfach den Namen der Hostgruppe. Das sieht dann wie folgt aus:
1 2 3 4 5 6 | define service{ use important-service service_description HTTP check_command check_http!80 hostgroup_name webserver } |
Anstatt jeden einzelnen Service fuer die entsprechenden Hosts konfigurieren zu muessen, koennen wir mit Hilfe der Hostgruppen die Service-Definitionen global abhandeln. Wir haben jetzt also nur noch eine Service-Definition anstatt eine fuer jeden Host!
Das hat nicht nur den Vorteil, dass man mit geringem Zeitaufwand die komplette Installation pflegen kann, sondern auch, dass einem der Ueberblick ueber die Umgebung nicht so leicht entgleitet. Mehrere hundert Hosts und tausende Services sind in einer hochkomplexen Umgebung keine Seltenheit, da ist Vereinheitlichung schlicht und ergreifend eine Notwendigkeit.
Performancegrenzen ausloten
Abschliessend noch ein, zwei Worte zur Performance von Nagios. Diese laesst sich, wer haette es geahnt, nicht so einfach in Zahlen ausdruecken. Eine Aussage, wie beispielsweise “Nagios kann 831 Hosts und 5.542 Services zeitnah ueberwachen” ist schlicht und ergreifend nicht moeglich. Zu komplex sind die Abhaengigkeiten von der Hardware, der Art und Anzahl der eingesetzten Checks, sowie der Intervalle, in denen die Dienste ueberprueft werden. Muss Nagios nach jedem Check noch ein externes Kommando aufrufen, sind weitere Verzoegerungen in Kauf zu nehmen.
Nun ist der gemeine Nagios-Administrator meist von der Idee angefixt, moeglichst alles zu ueberwachen: Laeuft der syslog-ng auf all meinen Servern? Kann ich den Hostnamen auf all meinen 10 eingesetzten Nameservern aufloesen? Und wann ist eigentlich mal wieder Towel-Day?
Nun spiegelt der Load des Nagios-Servers die Performance meist nur unzureichend wieder. Akkurate Aussagen lassen sich mittels des Load-Wertes jedenfalls nicht machen. Vielleicht laeuft ja auch noch ein anderer, performancekritischer Dienst auf der Maschine. Einen ersten Blick sollte man also ins Web-Interface werfen und sich die Performance-Werte zu Gemuete fuehren:
Interessant sind hier zuerst einmal die Werte “Check Latency - Maximum” und “Check Latency - Average” fuer unsere Services. Diese Werte spiegeln die Zeit wieder, wie lange ein auszufuehrerer Check in der Warteschlage von Nagios verzoegert wird. Wenn ich also einen Check habe, der alle fuenf Minuten ueberprueft werden soll und eine durchschnittliche Check Latency von ca. zehn Sekunden besteht, dann wird dieser Check eben erst wieder nach fuenf Minuten und zehn Sekunden ausgefuehrt.
Ein weiterer, guter Indikator fuer Performanceprobleme ist die Tabelle auf der linken Seite, die angibt, wieviele Checks der Nagios-Server in einer, in fuenf in 15 Minuten, sowie in einer Stunde ausfuehren konnte.
In grossen Installation wird es aufgrund der Masse an Checks, sowie der Vielzahl von verschiedenen Plugins kaum dazu kommen, eine Latenz im Millisekundenbereich zu erreichen. Zu Beginn jedoch mit wenigen hundert Services sollte dieser Wert sich entsprechend dort ansiedeln.
Performance-Monitoring
Eine weitere Moeglichkeit, Performancegrenzen von Nagios auszumachen ist das Monitoring des eigenen Nagios-Servers. Ich beispielsweise setze dazu ein kleines Bash-Script von Matteo Corti als Check ein, welches mir alle 10 Minuten die Check Latency inklusive Performancedaten zurueckgibt. Ich bin also in der Lage, diese grafisch ueber laengere Zeitraeume darzustellen, beispielsweise mittels PNP oder Nagiosgrapher und kann somit beim Einbinden neuer Checks auch leicht zeitversetzt Performance-Engpaesse ausmachen und diese ggf. beheben. Grad umfangreiche Bash-Scripte versetzten Nagios eine ungemein laestige Performancebremse. Ein Plugin in C oder Perl ist Shellscripts meist vorzuziehen.
Anhang: Performance-Monitoring Shellscript
Hier das Performance-Monitoring Shellscript zum Ueberwachen der Latenz von Matteo Corti, welches u.a. auch auf Nagiosexchange zum Download angeboten wird:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | #!/bin/bash # (c) Matteo Corti, ETH Zurich, 2007 # # check_nagios_latency # # Checks the Nagios latency VERSION=0.9.3 ################################################################################ # Functions ################################################################################ # Prints usage information # Params # $1 error message (optional) usage() { if [ -n "$1" ] ; then echo "Error: $1" 1>&2 fi echo echo "Usage: check_nagios_latency [-hvV?] -w warning -c critical [-n path]" echo echo " -c critical threshold" echo " -h, -? this help message" echo " -n path nagiostats path" echo " -v verbose output" echo " -w warning threshold" echo " -V version" echo echo "Report bugs to: Matteo Corti <matteo.corti@id.ethz.ch>" echo exit 3 } ################################################################################ # Checks if a given program is available and executable # Params # $1 program name check_prog() { if [ -z "$PROG" ] ; then PROG=`which $1` fi if [ -z "$PROG" ] ; then echo "LATENCY CRITICAL - cannot find $1" exit 2 fi if [ ! -x "$PROG" ] ; then echo "LATENCY CRICTICAL - $PROG is not executable" exit 2 fi } ################################################################################ # Main ################################################################################ # process command line options while getopts "vh?Vc:w:n:" opt; do case $opt in c ) CRITICAL=$OPTARG; ;; h | \? ) usage ; exit 3; ;; n ) PROG=$OPTARG ;; V ) echo "check_nagios_latency version ${VERSION}"; exit 3; ;; v ) VERBOSE=1; ;; w ) WARNING=$OPTARG; ;; esac done shift $(($OPTIND - 1)) ################################################################################ # sanity checks ############### # Check options if [ -z "${CRITICAL}" ] ; then usage "No critical threshold specified" fi if [ -z "${WARNING}" ] ; then usage "No warning threshold specified" fi ###################### # Check number formats if ! echo $WARNING | grep -qE '^[0-9]+(\.[0-9]+)?$' ; then echo "LATENCY UNKOWN - Wrong number: $WARNING" exit 3 fi if ! echo $CRITICAL | grep -qE '^[0-9]+(\.[0-9]+)?$' ; then echo "LATENCY UNKOWN - Wrong number: $WARNING" exit 3 fi ####################### # Check needed programs check_prog nagiostats LATENCY=`$PROG | grep "Active Service Latency" |cut -f3 -d'/' | awk '{print $1}' | tr -d '\n'`; if [ -n "${VERBOSE}" ] ; then echo "latency: ${LATENCY}"; fi #################### # Perform the checks PERF="Latency=${LATENCY};${WARNING};${CRITICAL};;" COMPARISON=`echo "if($LATENCY>$CRITICAL) 1 else 0;" | bc` if [ $COMPARISON -eq 1 ] ; then echo "LATENCY CRITICAL ${LATENCY}s | $PERF" exit 2 fi COMPARISON=`echo "if($LATENCY>$WARNING) 1 else 0;" | bc` if [ $COMPARISON -eq 1 ] ; then echo "LATENCY WARNING ${LATENCY}s | $PERF" exit 1 fi echo "LATENCY OK ${LATENCY}s| $PERF" exit 0; |
Rechtliches
Dieser Beitrag ist unter einer Creative-Commons Lizenz veroeffentlicht worden. Er darf frei kopiert, veroeffentlicht und veraendert werden, sofern der Name des Autors genannt wird und keine kommerziellen Absichten verfolgt werden (Google AdSense in Blogs, Foren und Wikis ausgenommen). Ueber Feedback freut sich der Autor genauso, wie ueber Verbesserungsvorschlaege und Benachrichtigungen im Falle von offensichtlichen Fehlern.


Add your comment below, or trackback from your own site.
Subscribe to these comments.
Be nice. Keep it clean. Stay on topic. No spam.
You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="">