Totales Monitoring mit Nagios und Puppet

Ueber die totale Ueberwachung mit Nagios habe ich mich ja in einen der letzten Artikel ausgelassen. Es ging um die Konfiguration mit Puppet und Zusammentragen der verschiedenen Teile der Konfiguration mit exportierten Resourcen.

Posted by eumel8 on April 16, 2014 · 11 mins read

Ueber die totale Ueberwachung mit Nagios habe ich mich in einen der letzten Artikel ausgelassen. Es ging um die Konfiguration mit Puppet und Zusammentragen der verschiedenen Teile der Konfiguration der zu ueberwachenden Hosts mit exportierten Resourcen. Das war schon alles ganz nett, aber der Aufwand schon recht hoch. Aufgrund der Erfahrungen und neuer Anforderungen gibt es jetzt eine neue Version von Puppet Nagios.


Unser Puppetmodul zerfaellt immer noch in 3 Teile:

  1. NRPE Dienst fuer die zu ueberwachende Hosts
  2. Nagios Server (Monitor)
  3. Nagios Service (Kollektor)

Fuer den Kollektor hatten wir vorher die exportierten Resourcen. Alles mit @@ wurde zusammengesammelt. In der neuen Version wurde @@ durch einen Hash ersetzt.

nd => {
 'ab.beispiel.de' => {
 'ip' => '192.168.0.100',
 'domain' => 'beispiel.de',
 'services' => {
 'Ping' => { check => 'check_ping!200.0,60%!500.0,95%', notes => 'group_sms' },
 'Load' => { check => 'check_nrpe!check_load!15 10 5 30 25 20', notes => 'group_email,group_sms,group_voice'},
 },
 },
 'ns.beispiel.de' => {
 'ip' => '192.168.0.10',
 'domain' => 'beispiel.de',
 'services' => {
 'Ping' => { check => 'check_ping!200.0,60%!500.0,95%'},
 },
 },
 'gw.beispiel.de' => {
 'ip' => '192.168.0.1',
 'domain' => 'beispiel.de',
 'services' => {
 'Ping' => { check => 'check_ping!400.0,80%!500.0,95%'},
 },
 },
 }

Was haben wir denn da? Es werden 3 Hosts definiert: ab, ns und gw. Alle Hosts haben eine IP-Adresse und eine Domain. DIe Domain ist zwar als FQDN schon im Hostnamen enthalten, aber so kann man noch einfacher auf den Wert zugreifen und man kann auch unterschiedliche Domains verarbeiten. Denn wenn es zweimal denselben Hostnamen mit unterschiedlichen Domains gaebe, wuerde der erste Key in dem Hash fuer gueltig erklaert und der zweiten verworfen werden. Das hat in etws groesseren Umgebungen schon mal gedauert das rauszufinden ;-)
Dann sind fuer jeden Host Services in einem Hash definiert. Die Service-Description ist wieder der Key fuer einen weiteren Hash. Das ist so verschachtelt, weil ich zu dem einzelnen Service noch andere Eintraege hab als nur das Check-Kommando.Im zweiten Check oben habe ich noch den Parameter "notes", der eine andere Notifizierung fuer den Service definiert. Default werden alle Checks per Email notifiziert. Fuer bestimmte Checks moechte ich aber eine SMS haben oder angerufen werden. Dazu werden wieder die Dienste von Twilio verwendet wie in der urspruenglichen Version des Puppet-Modules.
Aus dem Hash "nd" werden groesstenteils die Nagios-Konfigurationsdateien erstellt. Das geht bei Host-Eintraegen ganz einfach:

<% @nd.each_pair do |k, v| -%>
define host {
 use cloud-host
 host_name <%= k %>
 address <%= v['ip'] %>
}
<% end -%>

Bei Servicegroups wird es bischen kniffliger:

<% @nagios_servicegroups = [ ] -%>
<% @nd.each_pair do |k, v| -%>
<% if v.class == Hash -%>
<% v.each_pair do |s, l| -%>
<% if s == 'services' -%>
<% l.each_pair do |e, c| -%>
<% @nagios_servicegroups << e -%>
<% end -%>
<% end -%>
<% end -%>
<% end -%>
<% end -%>
# autogenerated by puppet. do not touch
<% @nagios_servicegroups.uniq.each do |x| -%>
<% @vhosts = [ ] -%>
<% @nd.each_pair do |k, v| -%>
<% if v.class == Hash -%>
<% v.each_pair do |s, l| -%>
<% if s == 'services' -%>
<% l.each_pair do |e, c| -%>
<% if e == x -%>
<% @vhosts << k -%>
<% end -%>
<% end -%>
<% end -%>
<% end -%>
<% end -%>
<% end -%>
<% if @vhosts.length > 0 %>
define servicegroup {
 servicegroup_name <%= x %>
 members <%= @vhosts.join(',' + x + ',') + ',' + x %>
}
<% end -%>
<% end -%>

</ k></ e>

Die NRPE-Konfiguration fuer die zu ueberwachenden Hosts wartet mit zahlreichen Checks aus den Standard-Plugins auf. Die erforderlichen Parameter wurden alle im Hash "nd" definiert:

command[check_load]=/usr/lib/nagios/plugins/check_load -w $ARG1$,$ARG2$,$ARG3$ -c $ARG4$,$ARG5$,$ARG6$
command[check_users]=/usr/lib/nagios/plugins/check_users -w $ARG1$ -c $ARG2$
command[check_memory]=/usr/lib/nagios/plugins/check_mem -u -w $ARG1$ -c $ARG2$
command[check_logarimem]=/usr/lib/nagios/plugins/check_logaricheck mem
command[check_conn]=/usr/lib/nagios/plugins/local/check_conn -w $ARG1$ -c $ARG2$
command[check_disk_root]=/usr/lib/nagios/plugins/check_disk -w $ARG1$ -c $ARG2$ -p /
command[check_disk]=/usr/lib/nagios/plugins/check_disk -w $ARG1$ -c $ARG2$ -p $ARG3$
command[check_zombie_procs]=/usr/lib/nagios/plugins/check_procs -w $ARG1$ -c $ARG2$ -s Z
command[check_total_procs]=/usr/lib/nagios/plugins/check_procs -w $ARG1$ -c $ARG2$
command[check_local_time]=/usr/lib/nagios/plugins/check_ntp_time -H localhost
command[check_local_tcp]=/usr/lib/nagios/plugins/check_tcp -H 127.0.0.1 -p $ARG1$ -w $ARG2$ -c $ARG3$
command[check_tcp]=/usr/lib/nagios/plugins/check_tcp -H $ARG4$ -p $ARG1$ -w $ARG2$ -c $ARG3$
command[check_dns]=/usr/lib/nagios/plugins/check_dns -s $ARG1$ -H $ARG2$
command[check_log]=/usr/lib/nagios/plugins/check_log -F $ARG1$ -O $ARG2$ -q $ARG3$
command[check_http]=/usr/lib/nagios/plugins/check_http -I $ARG1$ -p $ARG2$ -u $ARG3$ -s $ARG4$
command[check_http_type]=/usr/lib/nagios/plugins/check_http -I $ARG1$ -p $ARG2$ -u $ARG3$ -T $ARG4$
command[check_http_ssl]=/usr/lib/nagios/plugins/check_http -I $ARG1$ -p $ARG2$ -u $ARG3$ --ssl
command[check_mysql]=/usr/lib/nagios/plugins/check_mysql -h 127.0.0.1 -u '$ARG1$' -p '$ARG2$' -d '$ARG3$'
command[check_mysql_replication]=/usr/lib/nagios/plugins/local/check_replication.pl -s 127.0.0.1 -m '$ARG1$' -u '$ARG2$' -p '$ARG3$'
command[check_proc]=/usr/lib/nagios/plugins/check_procs -w $ARG1$ -c $ARG2$ -a '$ARG3$ $ARG4$ $ARG5$ $ARG6$'
command[check_proc_1arg]=/usr/lib/nagios/plugins/check_procs -w $ARG1$ -c $ARG2$ -a $ARG3$
command[check_proc_cpu]=/usr/lib/nagios/plugins/check_procs -w $ARG1$ -c $ARG2$ --metric=CPU -C '$ARG3$'
command[check_proc_user]=/usr/lib/nagios/plugins/check_procs -w $ARG1$ -c $ARG2$ -a $ARG3$ -u $ARG4$
command[check_mailq]=/usr/lib/nagios/plugins/check_mailq -w $ARG1$ -c $ARG2$ -M $ARG3$
command[check_file_content]=/usr/lib/nagios/plugins/local/check_file_content.pl -f $ARG1$ -i $ARG2$ -e $ARG3$ -n $ARG4$
command[check_ntp_peer]=/usr/lib/nagios/plugins/check_ntp_peer -H $ARG1$ -w $ARG2$ -c $ARG3$
command[check_ntp_time]=/usr/lib/nagios/plugins/check_ntp_time -H $ARG1$ -w $ARG2$ -c $ARG3$

SIcher fehlen noch ein paar, aber das wird demnaechst noch erweitert. Wer ausgefallene Sachen sucht, wird meistens auf Github fuendig. Ich verweise deswegen auf Git, weil diese Module von dort sehr leicht als Submodule in dieses Module eingebunden werden koennen:

[submodule "files/plugins/remote/nagios-plugin-mongodb"]
 path = files/plugins/remote/nagios-plugin-mongodb
 url = https://github.com/mzupan/nagios-plugin-mongodb

<

Mit den Kommandos "git submodule init" und "git submodule update" hat man immer die neueste Version der zusaetzlichen Plugins und brauch keine Files nachtraeglich auf die Hosts zu verteilen. Es genuegt das Laden der Submodule und das EInbinden der Check-Templates in die nrpe.erb.Im Unterverzeichnis "local" koennen natuerlich noch selbst gebaute Checks oder Checks fuer die es keine Versionierung gibt, eingelagert werden. .

Wir wird das Modul nun eingebunden?
In der Node-Definition der zu ueberwachenden Host:

 class {'nagios::nrpe':
 nrpe_allowed_hosts => '192.168.0.10',
 timeserver => '192.168.0.15',
 nrpe_conf_overwrite => 1,
 }

Ich definiere als nrpe_allowed_host die IP des Nagiosservers, die IP-Adresse eines Timeservers fuer ntp-checks (ich glaube, das ist nicht mehr aktuell), und gebe an, ob die Original-nrpe.cfg erhalten werden soll, indem die generierte nur inkludiert wird oder ob sie ueberschrieben werden kann. Unter Umstaenden hat man ja eine nicht puppet gemanagte nrpe.cfg und moechte deren Inhalt nicht veraendern (obwohl NRPE doppelte Werte sowieso mit dem letzten gueltigen ueberschreibt):

Konfiguration des Nagios-Servers in nodes.pp:

class {'nagios::server':
 engine => 'icinga',
 http_users => {
 admin => { 'password' => '$apr1$x6DQznUt$hh05hGiXnBzfi4m0iKlty1' },
 peter => { 'password' => '$apr1$Pm4kjpYB$8KGIuRB49Skdf/5/nWfUN1' },
 }
 twilio_account =>; 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', # your twilio account number
 twilio_identifier => 'abcdefghijklmnopqrstuvqxyz', # your twilio secret identifier
 twilio_from => '%2B1123456790', # your urlenode twilio number, e.g. +1 123 456 7890
 twilio_to => '%2B491721234567', # your urlencode oncall number, e.g +49 172 123 4567
 notification => ['root@localhost','nagios@localhost'], # single or multiple email addresses
 }

Der Nagios-Server laeuft also mit Icinga. Es gibt admin und peter als User fuer das Webfrontend. Die Passwoerter sind als Hashs hinterlegt, die vom Webserver verarbeitet werden koennen. Ueblicherweise werden sie mit htpasswd generiert. Twilio benoetigt eine Account-Nummer und einen Account-Identfier (steht beides oben in der Twilio-Seite, wenn man sich eingeloggt hat). Die zugeteilte Telefonnummer kommt urlencoded nach twilio_from und nach twilio_to kommt die Nummer, die bei Alarmen angerufen werden soll oder an die die SMS geschickt wird.
Bei "notification" werden die Email-Adressen hinterlegt, an die Notifizierungs-Emails ueber den hauseigenen Mailserver verschickt werden.

Der besondere Clou ist noch das verteilte Monitoring mit NSCA. Ich habe also irgendwo eine Instanz mit Nagios laufen und irgendo anders eine andere. An einer dritten Stelle moechte ich die ersten beiden Instanzen zusammenfassen. Dazu genuegt in der Server-Konfiguration folgender Eintrag:

distribution => {
 member => 'client', # possible values: client, master
 host => '192.168.0.110', # master host ip address
 nsca_password => '12345678', # password for communication
 },

Mit dem Wert "member" im Hash "distribution" wird festgelegt, ob es sich um Nagios 1, 2 oder 3 in unserem Beispiel handelt. Durch den dort angegebenen Wert wird Nagios (oder Icinga) passend konfiguriert. Drei Bedingungen muessen noch erfuellt sein:

  • DIe IP des Master-Host muss stimmen und die Instanzen 1 und 2 muessen ihn auf TCP-Port 5667 erreichen.
  • Das Passwort muss ueberall gleich definiert sein
  • Die Hosts und Services muessen ueberall gleich sein. Das heisst, der Hash "nd" wird von Nagios 1 auf 3 kopiert und der Inhalt des Hash von Nagios 2 nach 3 inkludiert.

Durch Passive Host- und Service-Checks wird dem Master vorgegaugelt, er haette den totalen Ueberblick. Dabei bekommt er bloss von den unteren Instanzen die Informationen gepuscht. Damit er nicht mit veralteten Daten arbeitet, hat er noch einen Freshness-Mechanismus eingebaut, wo er zyklisch ueberprueft, ob die Werte noch zeitgemaess sind. Ansonsten wird der Status auf rot gesetzt wegen fehlender Daten.
Notfizierung kann man wahlweise auf allen 3 Instanzen aktivieren (das ist dann wirklich die totale Ueberwachung), oder nur auf dem Master, wobei dann dort der Check, der besonders alarmiert werden soll, doch noch im Hash angepasst werden muss mit dem Parameter "notes". Aber auch dafuer hat Ruby eine Loesung parat. Wie ich weiter oben schon beschrieb, werden sich wiederholende Werte im Hash aussortiert. Ich kann also "nd" von Nagios 1 nach 3 kopieren und kann auf dem Master 3 dann einen bestimmten Wert mit dem zusaetzlichen Alarming drueberschreiben. Dieser sollte dann in der endgueltigen Konfiguration gueltig sein und den zweiten Key verwerfen.

Und nun viel Spass mit der Ueberwachung!

https://github.com/eumel8/nagios/tree/without_exported_resources