Was ist Icinga2?
Die Icinga2-Konfigurationsoptionen sind reichhaltig und bieten Ihnen viele Möglichkeiten, das wiederzuverwenden, was Sie bereits haben. Sie leisten gute Dienste, aber sobald Ihre Überwachung groß genug wird, reichen sie einfach nicht mehr aus. Die Verringerung der Last auf den Überwachungshosts und den zu überwachenden Servern hat dann hohe Priorität. Eine Möglichkeit, dieses Problem zu lösen, ist die Nutzung der API und die Erstellung passiver Dienste, wo dies möglich ist.
Zum Zeitpunkt der Erstellung dieses Blogbeitrags gibt es nicht genügend Informationen für Leute, die lernen wollen, was passive Überprüfungen sind und wie man sie implementiert. Ich schreibe diesen Beitrag in der Hoffnung, dass er den Einstieg für diejenigen erleichtert, die wissen wollen, wie man anfängt.
Mehr zu Icinga2
Lesen Sie meinen Experten-Blog über die Feinabstimmung von Icinga2!
Passive Checks
Im Grunde genommen sind dies nichts anderes als Dienste, die keine Befehle auslösen. Stattdessen verlassen sie sich darauf, dass ihre Zustände, Ergebnisse und Leistungsdaten von einer anderen Quelle eingespeist werden. Der beste Weg, dies im Moment zu tun, ist die Verwendung der Icinga-API.
Der häufigste Anwendungsfall ist, wenn Sie in der Lage sind, Informationen von einer großen Anzahl von Servern oder Diensten zu sammeln, indem Sie ein einziges Skript ausführen. Dieses Skript kann entweder von Icinga als normaler Prüfbefehl ausgeführt werden, der einen kombinierten Status für alle Dienste zurückgibt, oder es kann auf jede andere Weise ausgelöst werden.
Das Plugin sammelt Daten für jeden Dienst, den es prüft, und injiziert sie dann direkt in einen bestehenden Dienst unter Verwendung der Icinga-API.
Ein passiver Dienst würde entweder den "dummy"-Befehl verwenden und "active_checks" deaktiviert haben oder sie aktiviert haben und sich auf die Dummy-Standardtextausgabe verlassen, die nur ausgegeben werden sollte, wenn die API-Injektion verzögert wird und das Prüfintervall für den Dienst überschreitet.
apply Service "ServiceName" {
max_check_attempts = 1
retry_interval = 5m
check_interval = 5m
enable_active_checks = false
check_command = "dummy"
vars.dummy_text = "No Passive Check Result Received"
vars.dummy_state = '3'
assign where host.vars.passive_host
}
Um die Frustration zu vermeiden, jeden Dienst vor dem Import seines Status manuell erstellen zu müssen, können Sie die API auch zum Erzeugen von Diensten verwenden. Sie können jedem vorhandenen Host zugewiesen werden, oder wenn Sie möchten, können die Hosts, denen sie zugewiesen sind, auch über die API erstellt werden.
Ich empfehle nicht, API und manuelle Konfiguration auf denselben Host-Objekten zu mischen, es sei denn, Sie haben eine gute Logik für die Erkennung und Beseitigung veralteter (Zombie-)Dienste implementiert, die nicht mehr existieren und daher keine Statusaktualisierungen erhalten. Eine Möglichkeit, dies zu tun, besteht darin, die API zu verwenden und Dienste mit der "Dummy"-Standardausgabe zu filtern und zu entfernen, die vorhanden sein sollte, wenn der Dienst keine externen Injektionen mehr erhält.
Wenn Ihr Host nur API-generierte Dienste enthält, kann er leicht zusammen mit allem, was mit ihm verbunden ist, entfernt und beim nächsten Auslösen Ihres Skripts neu erstellt werden.
ICINGA API
Die Icinga-API ist ein mächtiges Werkzeug, das Zugriff auf die gesamte Icinga-Konfiguration, Objekte und Zustände bietet. Die meisten der online gefundenen Beispiele verwenden den Linux-Shell-Befehl curl. Wenn Shell-Skripte für Sie geeignet sind, ist dies die einfachste Art der Interaktion, ich werde jedoch Beispiele für Python und curl bereitstellen, die das Gleiche tun. Python ist flexibler und läuft auch unter Windows, was leider manchmal erforderlich ist.
Beispiele für andere Sprachen finden Sie hier.
Erstellen eines Hosts
locken.
curl -k -s -u root:icinga -H 'Accept: application/json' -X PUT
'https://localhost:5665/v1/objects/hosts/example.localdomain'
-d '{ "templates": [ "generic-host" ],
"attrs": { "address": "192.168.1.1", "check_command": "hostalive", "vars.os" : "Linux" } }'
| python -m json.tool
Python
import requests, json
req_url = 'https://localhost:5665/v1/objects/hosts/example.localdomain
headers = { 'Accept': 'application/json', 'X-HTTP-Method-Override': 'PUT' }
data = { "templates": [ 'generic-host' ],
"attrs": { 'address': '8.8.8.8' }
}
resp = requests.get(req_url, headers=headers, auth=(username, password), data=json.dumps(data), verify=False)
if (resp.status_code == 200):
print "Result: " + json.dumps(resp.json(), indent=4, sort_keys=True)
else:
print resp.text
ERSTELLEN EINES DIENSTES
locken.
curl -k -s -u root:icinga -H 'Accept: application/json' -X PUT
'https://localhost:5665/v1/objects/services/example.localdomain!passive-service'
-d '{ "templates": [ "generic-service" ],
"attrs": { "check_command": "dummy", "check_interval": '300' ,"retry_interval": '300' } }'
Python
import requests, json
req_url = 'https://localhost:5665/v1/objects/services/example.localdomain!passive-service
headers = { 'Accept': 'application/json',
'X-HTTP-Method-Override': 'PUT' }
data = { "templates": [ 'generic-service' ],
"attrs": { 'display_name': passive-service,
'check_command': 'dummy',
'enable_active_checks': '1',
'vars.dummt_text': 'No passive check result received',
'vars.dummy_state': '3',
'max_check_attempts': '1',
'retry_interval': '300',
'check_interval': '300',
'host_name': example.localdomain }
}
resp = requests.get(req_url, headers=headers, auth=(username, password), data=json.dumps(data), verify=False)
if (resp.status_code == 200):
print "Result: " + json.dumps(resp.json(), indent=4, sort_keys=True)
else:
print resp.text
EINSPEISUNG DES ZUSTANDS EINES DIENSTES
locken.
curl -k -s -u root:icinga -H 'Accept: application/json' -X POST
'https://localhost:5665/v1/actions/process-check-result?service=example.localdomain!passive-ping6'
-d '{ "exit_status": 2, "plugin_output": "PING CRITICAL - Packet loss = 100%",
"performance_data": [ "rta=5000.000000ms;3000.000000;5000.000000;0.000000", "pl=100%;80;100;0" ],
"check_source": "example.localdomain" }' | python -m json.tool
Python
import requests, json
req_url = 'https://localhost:5665/v1/actions/process-check-result?service=example.localdomain!passive-ping6
headers = { 'Accept': 'application/json', 'X-HTTP-Method-Override': 'POST' }
data = { "exit_status": 0,
"plugin_output": 'OK: the service is OK',
"performance_data": [ val1=5%;80;90;0;100 ],
"check_source": example.localdomain }
resp = requests.get(req_url, headers=headers, auth=(username, password), data=json.dumps(data), verify=False)
if (resp.status_code == 200):
print "Result: " + json.dumps(resp.json(), indent=4, sort_keys=True)
else:
print resp.text
Das Importieren von PerfData-Variablen ist einfach mit einem Array möglich. Andernfalls parst Python die erforderlichen Anführungszeichen nicht korrekt, was dazu führt, dass Icinga die Daten nicht erkennt. Hier ist ein Beispiel, wie das gemacht wird:
perf_arr.append('Heap=' + str(heap_curr) + 'b;' + str(heap_warn) + ';' + str(heap_crit) + ';0;' + str(srv_heap_max[cnt]))
perf_arr.append('JVM_Threads=' + str(srv_act_thr[cnt]) + ';')
perf_arr.append('Physical_Memory=' + str(srv_phys_mem[cnt]) + 'b;')
perf_arr.append('JVM_Load=' + str(srv_jvm_load[cnt]) + ';')
perf_arr.append('ExitStatus=' + str(exit_status) + ';')
perf_data = perf_arr
data = { "exit_status": service_state,
...
"performance_data": perf_data,
...
}
REKURSIVES LÖSCHEN EINES HOSTS ODER DIENSTES UND ALLER MIT IHM VERBUNDENEN ELEMENTE
locken.
curl curl -k -s -u root:icinga -H 'Accept: application/json' -X DELETE
'https://localhost:5665/v1/objects/hosts/example.localdomain?cascade=1' | python -m json.tool
Python
import requests, json
req_url = 'https://localhost:5665/v1/objects/hosts/example.localdomain?cascade=1
headers = { 'Accept': 'application/json', 'X-HTTP-Method-Override': 'DELETE' }
resp = requests.get(req_url, headers=headers, auth=(username, password), verify=False)
if (resp.status_code == 200):
print "Result: " + json.dumps(resp.json(), indent=4, sort_keys=True)
else:
print resp.text
ABFRAGE DES AKTUELLEN ZUSTANDS EINES DIENSTES
locken.
/usr/bin/curl -k -s -u root:icinga 'https://localhost:5665/v1/objects/services?service=api_generated_host_1!13&attrs=name&attrs=last_check_result'|jq
Python
import requests, json
req_url = 'https://localhost:5665/v1/objects/services?service=api_generated_host_1!13&attrs=name&attrs=last_check_result'
resp = requests.get(req_url, auth=(username, password), verify=False)
if (resp.status_code == 200):
print "Result: " + json.dumps(resp.json(), indent=4, sort_keys=True)
else:
print resp.text
Automatisierung und Autodiscovery
Mit der Fähigkeit, Objekte automatisch zu erstellen, zu zerstören und zu aktualisieren, ermöglicht die Icinga-API die Implementierung einer flexiblen Automatisierung. Ein gut durchdachtes Skript kann Dienste nahtlos erscheinen und verschwinden lassen, wenn sie implementiert oder aus Ihrer Umgebung entfernt werden.
Die automatische Erkennung wird nur durch die Komplexität des Skripts begrenzt, das Sie verwenden.
Hier zeige ich ein Beispiel, in dem alle Dienste auf einem bestimmten Host aufgelistet werden, ihr aktueller Zustand und ihre Ausgabe überprüft werden und, wenn sowohl der Zustand als auch die Ausgabe mit denen übereinstimmen, die durch den Dummy-Befehl gesetzt wurden, diese gelöscht werden.
Normalerweise sollte der Dummy-Befehl nur seine eigene Ausgabe liefern, wenn die externe Injektion länger als das check_interval des Dienstes verzögert wird.
Python
req_url = 'https://localhost:5665/v1/objects/services?filter=match("' + args.host_name + '",host.name)'
resp = requests.get(req_url, auth=(args.username, args.password), verify=False)
json_str = json.dumps(resp.json())
j = json.loads(json_str)
if args.actionDebug == True:
if (resp.status_code == 200):
print "Dynamic result: 200"
else:
print "Dynamic result:" + resp.status_code
z_svc = []
for x in j.keys():
if x == "results":
for i in j[x]:
if i['attrs']['last_check_result']['exit_status'] == 3.0:
if i['attrs']['last_check_result']['output'] == "UNKNOWN: No passive check result received":
z_svc.append(i['attrs']['__name'])
else:
continue
cnt = 0
while (cnt < len(z_svc)):
req_url = 'https://' + args.api_address + ':' + args.api_port + '/v1/objects/services/' + z_svc[cnt] + '?cascade=1'
headers = { 'Accept': 'application/json',
'X-HTTP-Method-Override': 'DELETE' }
resp = requests.post(req_url, headers=headers, auth=(args.username, args.password), verify=False)
if args.actionDebug == True:
print "Deleting Zombie Service " + z_svc[cnt]
if (resp.status_code == 200):
print "Result: " + json.dumps(resp.json(), indent=2, sort_keys=True)
else:
print resp.text
cnt = cnt + 1
Leistungsdaten
Jede gute Überwachung geht Hand in Hand mit einer gut organisierten grafischen Darstellung des Ereignisverlaufs. Um dies zu erreichen, sollten alle Ihre Überwachungsskripte richtig strukturierte Leistungsdatenwerte zurückgeben. Die übliche Ausgabe eines Überwachungsskripts enthält eine Textmeldung und einen Prüfstatus. Zum Beispiel (python):
print 'OK: This service is funcitoning as expected'
exit(0)
Diese Ausgabe enthält jedoch keine Leistungsdaten. Sie kann für grundlegende Überprüfungen verwendet werden, aber wenn Sie jemals in die Geschichte dessen, was mit dem Dienst im vergangenen Tag/Monat/Jahr passiert ist, eindringen müssen, ist sie nicht sehr nützlich.
In Icinga2 werden Leistungsdaten durch die gedruckte Ausgabemeldung geliefert. Nach dem Statustext muss ein Pipe-Symbol | stehen und alles danach wird von Icinga als Leistungsdaten betrachtet.
Hier ist ein Beispiel für die reichhaltigste Art der Bereitstellung von Leistungsdaten für das Prüfergebnis.
print 'OK: This service is ok | disk_space_percent=25%;40;60;0;100 disk_space_mb=25GB;40;60;0;100'
Alles vor dem '=' ist der Name des Wertes. Zum Beispiel 'disk_space_percent'
Das erste Feld nach dem '=' ist für den aktuellen Zustand des Dienstes gedacht, immer gefolgt von ';'. Das ist die geringste Menge an Informationen, die erforderlich ist, um Leistungsdaten zu erhalten, die von der Überwachung interpretiert werden können. Zusätzlich können Sie den Typ des Wertes hinzufügen, um sicherzustellen, dass die Überwachung versteht, wofür der Wert steht. MB,GB,% usw.
Der zweite und dritte Abschnitt sind die vordefinierten Zustände Warnung und Kritisch für den Dienst. Wenn der Dienst diese Werte überschreitet, ändert er seinen Zustand entsprechend. Sie sind ebenfalls durch ';' getrennt
Die vierte und fünfte Zahl kann bei Diensten nützlich sein, die eine Vielzahl von Werten als Leistungsdaten zurückgeben. Sie bestimmen den Mindest- und Höchstwert, der für diese Leistungszahl möglich ist. Der letzte Wert wird unintuitiv nicht von einem Semikolon gefolgt. Dies erlaubt es Icinga, den aktuellen Zustand in einem schönen Tortendiagramm zu interpretieren, das in die icingaweb2-Schnittstelle eingebettet ist.
Zum Beispiel überwacht die Prüfung des freien Speicherplatzes normalerweise alle Partitionen auf Ihrem Rechner. Sie gibt einen Wert für jede von ihnen zurück, aber wenn eine den Schwellenwert für Warnung/Kritik überschreitet, ändert die gesamte Prüfung ihren Status. Um das Problem so schnell wie möglich zu finden, werden die oben gezeigten Tortendiagramme so umgeschaltet, dass Sie immer das mit dem schlechtesten Zustand als erstes sehen. Fahren Sie mit der Maus darüber, und Sie werden genügend Informationen sehen, um zu wissen, womit Sie es zu tun haben.