Monitoring-Projekt bei der htp GmbH

Die htp GmbH ist ein Provider im Bereich Telefon und Internet im Großraum Hannover und Braunschweig für geschäfts- und Privatkunden. Seit einiger Zeit unterstützen wir htp bei der Migration auf Icinga 2. Im Zuge dieses Umzuges wurde die neue Monitoring-Plattform verteilt mit einem mehrstufigen Satellitenkonzept realisiert. Herzstück sind zwei Satelliten an zwei unterschiedlichen Standorten der eigene RZ-Umgebung. Die beiden zu HA zusammengeschalteten Icinga 2 Instanzen sind je mit einem Braintower SMS Gateway gekoppelt, die das Mail-Benachrichtigungssystem um den Versand von SMS erweitern.

Diese beiden Knoten, die an den Icinga Master inklusive Icinga Web 2 angeschlossen sind, dienen ihrerseits als Kommunikationspartner für weitere Icinga 2 Satelliten zur Überwachung eigner Systeme oder dedizierten Kundenumgebungen.

Lennart Betz

Autor: Lennart Betz

Der diplomierte Mathematiker arbeitet bei NETWAYS im Bereich Consulting und bereichert seine Kunden mit seinem Wissen zu Icinga, Nagios und anderen Open Source Administrationstools. Im Büro erleuchtet Lennart seine Kollegen mit fundierten geschichtlichen Vorträgen die seinesgleichen suchen.

Icinga 2 – Monitoring automatisiert mit Puppet Teil 1: Installation

This entry is part 1 of 5 in the series Icinga 2 Monitoring automatisiert mit Puppet

Der erste Release vom Rewrite des Puppetmoduls zu Icinga 2 liegt nun auch schon einige Monate zurück und somit wird es Zeit sich im Zuge einer Blog-Serie genauer damit zu befassen. Zu beziehen ist das Module entweder über die Puppet Forge oder via GitHub.

Zu Beginn steht natürlich immer erstmal die “Installation” bzw. weil Puppet deklarativ arbeitet, die Beschreibung installiert zu sein. Schon hier zeigt sich das Modul flexibel und kann vom Benutzer angepasst benutzt werden. So greift puppet bei einer einfachen Deklaration ohne Parameter auf die auf dem System konfigurierten Repositories zurück. Es bietet jedoch auch die Möglichkeit für die unterstützten Plattformen RedHat, Debian, Ubuntu und Suse, die automatisch Einbindung der offiziellen Icinga-Repositories auf packages.icinga.com.

class { '::icinga2':
  manage_repo => true,
}

Betreibt man selbst ein internes Repository, z.B. als Spiegel des offiziellen, muss dieses vor der Deklaration der Klasse ::icinga2 erfolgen. Beispielhaft hier für RedHat-Systeme:

yumrepo { 'ICINGA-release':
  descr => 'ICINGA (stable release for epel)',
  baseurl => 'http://packages.icinga.org/epel/$releasever/release/',
  failovermethod => 'priority',
  enabled => '1',
  gpgcheck => '1',
  gpgkey => 'http://packages.icinga.org/icinga.key',
}
->
class { '::icinga2': }

Ein Problem beim Einsatz in verteilten Umgebungen und vor allem durch die Benutzung von Icinga als Agent ist die Software-Verwaltung. So sollte keine Instanzen miteinander kommunizieren die zwei Major Releases auseinander liegen, z.B. 2.6.x mit 2.5.x sollte noch ok sein, 2.6er mit 2.4.x und früher jedoch nicht. Besser ist jedoch die Benutzung ausschließlich von 2.6er Versionen. Setzt man hierfür kein Software-Management-Tool ein, kann Puppet helfen. Voraussetzung sollte jedoch ein Repository-Spiegel sein, den man nur zu gegebenen Zeitpunkten synchronisiert.

package { 'icinga2':
  ensure => latest,
}
->
class { '::icinga2':
  manage_package => false,
}

Damit wird bei jedem Puppetlauf dafür gesorgt, das die neuste Version installiert ist. Welche das ist, steuert man über den “händischen” Repo-Sync. Sind für einzelne Features zusätzliche Pakte erforderlich, müssen dann auch diese durch eigene Package-Resources verwaltet werden. Betroffen sind hier außer auf Windows oder FreeBSD die Features idomysql und idopgsql.
Auch das Handling des Icinga Services kann angeschaltet werden. Standardmäßig zieht jede Änderung an einer mit Puppet verwalteten Konfigurationsdatei einen Reload des icinga-Prozesses nach sich. Vor Version 1.2.0 von puppet-icinga2 wird allerdings noch ein Neustart ausgelöst. Für den Fall, dass lediglich zu bestimmten Zeiten eine neue Konfiguration eingelesen werden soll, kann man wie folgt vorgehen:

schedule { 'everyday':
  range  => '2 - 4',
  period => daily,
  repeat => 1,
}

class { '::icinga2':
  manage_service => false,
}
~>
service { 'icinga2':
  ensure => running,
  enable => true,
  schedule => 'everyday',
}

Zu bedenken ist hier nur der Nachteil, dass auch nur zwischen 2 und 4 nachts der Dienst wieder gestartet wird, falls er nicht laufen sollte. Aber dafür gibt es ja das Monitoring. Abschließend für Heute kann nicht unerwähnt bleiben, auch um benötigte Plugins darf sich der Benutzer selbst kümmern. Hierbei ist die Reihenfolge, ob erst die Plugins und dann Icinga oder umgekehrt, unerheblich.

Lennart Betz

Autor: Lennart Betz

Der diplomierte Mathematiker arbeitet bei NETWAYS im Bereich Consulting und bereichert seine Kunden mit seinem Wissen zu Icinga, Nagios und anderen Open Source Administrationstools. Im Büro erleuchtet Lennart seine Kollegen mit fundierten geschichtlichen Vorträgen die seinesgleichen suchen.

Icinga 2 Best Practice Teil 4: Host Templates und Services

This entry is part 4 of 5 in the series Icinga 2 Best Practice

Heute soll es um die Strukturierung von Services und deren Zuordnungen zu Gruppen von Hosts gehen. Ein Host Template kann zur Zusammenfassung von Informationen einer Gruppe von Hosts dienen und damit mehrere unterschiedliche Services für den jeweiligen Host anziehen. Wir wollen in den folgenden Beispielen Linux Hosts überwachen. Dort neben, wie schon in Teil 3 beschrieben, der Belegung der Dateisysteme auch in Abhängigkeit in welchem Netzsegment der Host angeschlossen ist, ob die Zeit synchron zum Zeitserver läuft.

template Host "linux-host" {
  import "generic-host"

  vars.os = "Linux"
  vars.disks["disk /"] = {
    disk_partition = "/"
  }
}

apply Service "time" {
  import "generic-service"

  check_command = "ntp_time"
  command_endpoint = host_name

  assign where host.vars.os == "Linux"
}

Es gibt zwei Netze mit je eigenem Zeitserver. Um dieses abzubilden, definieren wir für jedes Netz ein eigenes Host-Template:

template Host "dmz-net" {
  vars.ntp_address = "172.16.2.99"
}
template Host "lan-net" {
  vars.ntp_address = "172.16.1.99"
}

Diese beiden Templates enthalten nur netzspezifische Informationen, in unserem Beispiel auch nur den jeweilig zuständigen Zeitserver. Der Service-Check time mit dem Plugin check_ntp_time ermittelt die Differenz zwischen der lokalen Zeit des Hosts und der Zeit des NTP-Servers, der in ntp_address angegeben ist. Nun müssen wir für einen Host im internen Netzwerk lan-net nur noch beide Templates zusammen bringen:

object Host "host.example.org" {
  import "linux-host"
  import "lan-net"
  import "postgres-dbms"

  address = "172.16.1.11"
}

Habe wir weitere Services, die abhängig vom Netzsegment unterschiedlich zu konfigurieren sind, können diese Informationen den Netz-Templates hinzugefügt werden. Ein weiteres Beispiel wäre hier die Überwachung unterschiedlicher Domain Name Services. Diese Konzept der Stapelung von Host templates kann natürlich noch weitergeführt werden, z.B. auf Applikationen wie einen Postgresql basierendes Datenbank-Management-Systems bezogen. Ggf. muss jedoch auf die Reihenfolge der Importe geachtet werden, wenn Werte überschrieben werden sollen.

Lennart Betz

Autor: Lennart Betz

Der diplomierte Mathematiker arbeitet bei NETWAYS im Bereich Consulting und bereichert seine Kunden mit seinem Wissen zu Icinga, Nagios und anderen Open Source Administrationstools. Im Büro erleuchtet Lennart seine Kollegen mit fundierten geschichtlichen Vorträgen die seinesgleichen suchen.

Icinga 2 Best Practice Teil 3: Services überwachen

This entry is part 3 of 5 in the series Icinga 2 Best Practice

Nun in Teil 3 dieser Serie werden wir uns näher damit beschäftigen wie in Icinga 2 Services überwacht werden bzw. wie es zu konfigurieren ist, dass bestimmte Services nur auf bestimmten Hosts überwacht werden. Hier bietet Icinga 2 als Neuerung eine regelbasierte Zuweisung an Host-Objekte die definierten Eigenschaften genügen.

apply Service "ping4" {
  import "generic-service"

  check_command = "ping"

  assign where host.address || host.address6
}

So wird hier ein Service ping4 an alle Hosts “gebunden”, die das Attribut address oder address6 definiert haben. Nach diesem recht einfachem Beispiel wenden wir uns auch gleich etwas komplizierterem zu, der Überwachung von Dateisystemen auf einem Linux-System.

apply Service for (filesystem => config in host.vars.disks) {
  import "generic-service"

  check_command = "disk"
  command_endpoint = host.name

  vars += config

  assign where host.vars.os == "Linux"
  ignore where typeof(config) != Dictionary
}

Da hier über das CheckCommand disk, das Plugin check_disk zur Anwendung gelangt, das lokal auf dem zu überwachenden System laufen muss, wird es via command_endpoint auf genau diesem Endpoint angetriggert (siehe hierzu Teil 1 dieser Serie). Ein Host kann mehrere unterschiedlich Dateisysteme beherbergen, deshalb sind diese im Host-Objekt mit dem Custom-Attribute vars.disks zu definieren. Ausserdem muss zusätzlich, wie in dem assign-Statement gefordert, vars.os auf Linux gesetzt sein.

object Host "host.example.org" {
  ...
  vars.os = "Linux"

  vars.disks["disk /"] = {
    disk_partition = "/"
  }
  vars.disks|"disk /tmp"] = {
    disk_partition = "/tmp"
  }
}

Bekanntlich handelt es sich bei vars um ein Dictionary und vars.disks ist eine Darstellungsform eines Keys in diesem Dictionary. Eine andere Form einen Schlüssel anzusprechen ist der Index mit []-Klammern, wie in vars.disks[“disk /”]. Das heißt wir haben hier ein Dictionary in einem Dictionary. Und um es noch auf die Spitze zu treiben weisen wir den einzelnen Keys als Wert wieder jeweils ein Dictionary zu, Perl lässt grüßen. Wozu nun das Ganze? Mit apply Service for wird der Inhalt von vars.disks durchlaufen. Da es sich hierbei um ein Dictionary handelt, wird hier ein for-each verwendet, zu sehen an filesystem => config. Beides sind hier unsere Laufvariablen für die Schleife. Der Variablen filesystem wird jeweils der Key zu gewiesen, also beim ersten Durchlauf “disk /” und beim Zweiten “disk /tmp”, in config dann demnach der zugehörige Wert. Diesen Wert, selbst ein Dictionary, kann als Konfigurations-Dictionary bezeichnet werden, der den jeweiligen Pluginaufruf von disk parametrisiert. Die möglichen Parameter für disk sind sehr gut der Online-Dokumentation zu entnehmen. Wie dies funktioniert und warum die Zeile vars += config hierzu eine zentrale Rolle spielt, wird in Teil 4 erklärt werden. Der Name des Services entspricht standardmäßig dem Inhalt von filesystem.
Selbstverständlich besitzt jedes Linux-System ein Root-Dateisystem und sagen wir, bei uns auch ein eigenes für /tmp. Natürlich möchte man nun nicht für alle seine Hosts immer diese obigen 7 Zeilen angeben müssen, deshalb definieren wir mit diesen ein Host-Template mit der Bezeichnung linux-host. Nun haben Regeln die dumme Eigenheit ihre Ausnahmen zu haben, z.B. hat der Host host.example.org im Gegensatz zu allen anderen Hosts eben kein Dateisystem /tmp. Was dann?

object Host "host.example.org" {
  import "generic-host"

  vars.disks["disk /tmp"] = false
}

Hier wird nun für /tmp die Definition aus dem Template nachträglich überschrieben. Das ignore-Statement in unserer Service-Definition sorgt dafür, dass alle Dateisysteme, denen kein Konfiguration-Dictionary zugewiesen ist, auch nicht als Service in unserer Icinga-Konfiguration landen. Bei typeof handelt es sich um eine Funktion, die die Typesierung einer Variablen ermittelt.

Bis zum nächsten Mal, ihr müsst unbedingt schauen wie es weiter geht. In Teil 4 folgt die etwas theoretische Erklärung wie solche Services jeweils einzeln unterschiedlich parametrisiert werden.

Lennart Betz

Autor: Lennart Betz

Der diplomierte Mathematiker arbeitet bei NETWAYS im Bereich Consulting und bereichert seine Kunden mit seinem Wissen zu Icinga, Nagios und anderen Open Source Administrationstools. Im Büro erleuchtet Lennart seine Kollegen mit fundierten geschichtlichen Vorträgen die seinesgleichen suchen.

Icinga 2 Best Practice Teil 2: Konfiguration synchronisieren

This entry is part 2 of 5 in the series Icinga 2 Best Practice

Im heutigen Teil 2 dieser Serie, befassen wir uns mit Konfiguration und deren Synchronisation zwischen Icinga 2 Instanzen in verteilten Umgebungen. Hierbei ist eine solche verteilte Umgebung schon gegeben, wenn der Agent zum Einsatz kommt, also immer wenn Endpoints und Zones beteiligt sind. In Teil 1 wurde Tipps zur Konfiguration der Verbindungsdaten gegeben, heute vertiefen wir, was auf dem Agenten passiert und was dort zusätzlich zum Icinga 2 Core benötigt wird.
Zuerst werden die jeweiligen Plugins auf den Agenten benötigt, seien es für Linux die Monitoring-Plugins oder auf der Windows-Seite die nativen aus dem Icinga-Projekt oder die Plugins vom NSClient++. Bei Icinga, auch in der neuen Version 2, ist das Objekt vom Typ CheckCommand das Bindeglied zwischen Host- bzw. Service-Objekt und dem konfigurierten Plugin, was zur Statusermittlung aufzurufen ist. Wobei ein lokal auszuführender Host-Check üblicherweise nicht Praxisrelevant ist. Das CheckCommand beschreibt wie das Plugin aufzurufen ist, den Ort im Dateisystem wie auch mit welchen Optionen das Plugin ausgeführt wird.

object CheckCommand "mem" {
  command = [ PluginContribDir + "/check_mem.pl" ]

  arguments = {
    "-u" = {
      set_if = "$mem_used$"
      description = "Check USED memory"
    }
...

Für die Pfadangabe zum Plugin ist immer die Verwendung einer Konstanten zu empfehlen, da sich der Ort von Plattform zu Plattform unterscheiden kann und eine Konstante ist für jede Instanz und damit für jeden Agenten gesondert setzbar. Standardmäßig sollten Konstanten in constants.conf im Icinga-Konfigurationsverzeichnis definiert werden.
Beim Plugin check_mem.pl im obigen Beispiel handelt es sich nicht um eines vom Monitoring-Plugin-Projekt, sondern um eines was gesondert auf den Agenten zu installieren ist. Ausserdem benötigen die Icinga-Instanzen der Agenten obige Definition. Für das CheckCommand mem wird diese schon in der ITL (Icinga Template Library) mit geliefert und muss nicht selbst erstellt werden. Auf den jeweiligen Agenten ist lediglich die Definition in icinga2.conf als include einzubinden. Sie befindet sich im PluginContrib-Bereich der ITL.

include <plugins>
include <plugins-contrib>  

In plugins hingegen befinden sich die Definitionen zu Plugins aus dem Monitoring-Projekt. Bei Windows empfiehlt sich die Definitionen der nativen (windows-plugins) bzw. NSClient++-Plugins (nscp) einzubinden.
Was aber nun, wenn man nun für ein Plugin selbst ein CheckCommand-Objekt anlegen muss? Es jeweils auf allen Agenten in die ITL legen? Nein, der ITL-Bereich ist ausschließlich für vom Projekt gepflegte Objekte, eigene würden beim nächsten Update wieder verschwinden. Ausserdem ist eine zusätzliche Pflege von sich ändernden Konfigurationsdateien für jeden Agenten nicht erstrebenswert. Hier kommen nun globale Zonen ins Spiel, die einem Konfiguration auf beliebige Endpoints synchronisieren und automatisch laden.

object Zone "linux-commands" {
  global = true
}
object Zone "windows-commands" {
  global = true
}

Im Gegensatz zum Server bzw. Master und Satelliten-Systemen, ist auf den Windows- und Linux-Agenten nur die entsprechende Zone in der zones.conf einzutragen. In beiden werden auch wirklich nur CheckCommands hinterlegt, Services und Templates werden in der Regel nicht auf Agenten benötigt und sind damit Informationen die besser nicht leicht zugänglich sowie zentral gehalten werden.
Windows kennt leider keinen graceful restart bzw. reload wie Unixsysteme und somit ist hier die Trennung sinnvoll, da Windows einen Stop des Dienstes und nachgelagertem Start nur ausführen muss, wenn es wirklich nötig ist.

Teil 3 dieser Reihe wird sich dann mit praktischen Definition von Services beschäftigen und wie diese ausschließlich aus den zugehörigen Host-Objekten parametresiert werden.

Lennart Betz

Autor: Lennart Betz

Der diplomierte Mathematiker arbeitet bei NETWAYS im Bereich Consulting und bereichert seine Kunden mit seinem Wissen zu Icinga, Nagios und anderen Open Source Administrationstools. Im Büro erleuchtet Lennart seine Kollegen mit fundierten geschichtlichen Vorträgen die seinesgleichen suchen.