Hero Image

# Skripte mit Zabbix beobachten

Oft melden Skripte per E-Mail, ob sie erfolgreich waren oder nicht. Das ist im Fehlerfall sehr praktisch, da diese E-Mails die Fehlermeldung oft mitschicken. Aber merkt dann jemand, dass z.B. ein Backup eine Woche lang nicht gelaufen ist? Hier kommt Zabbix ins Spiel. Eine sehr weit verbreitete Open Source Monitoring Lösung die auch bei uns im Einsatz ist.

Hintergrund

Bei einigen wenigen Systemen und Backup Aufgaben fällt das vielleicht irgendwann auf, aber verlässlich ist das nicht. Wir brauchen ein automatisches System, das mitverfolgt und damit auch protokolliert, wann ein Backup erfolgreich war. Dazu wird automatisch bei überfälligen Backups alarmiert.

Um Daten in Zabbix empfangen zu können, werden diese als "Item" einem Host zugeordnet und müssen vorher angelegt sein. Was machen wir denn, wenn z.B. 10 Backup Jobs (Items) für 5 verschiedene Server (Hosts) existieren? Alles von Hand anlegen? Als erstes brauchen wir ein neues Zabbix Template, welches folgende Funktionen bereitstellt:

  • Items, die noch nicht existieren, aber Daten empfangen, werden automatisch für einen Host erzeugt
  • Diese automatisch erstellten Items erhalten einen eindeutigen Namen und auch die gewünschten Alarme (Trigger)
    • Zusätzlich wird der Name des Skripts als Tag gespeichert. Dies erleichtert bei vielen Aufgaben das spätere Filtern
  • Es wird ein Item für den Erfolg und eines für die benötigte Zeit angelegt. So wissen wir auch immer wie lange es gedauert hat
  • Verschiedene Alarme sind vordefiniert:
    • Wenn seit 2 Tagen kein Status empfangen wurde, gibt es einen AVERAGE Alarm
    • Liegt der letzte Status eine Woche zurück, gibt es einen HIGH Alarm
    • Es gibt auch einen HIGH Alarm, wenn der Exit-Code des Skripts ungleich 0 ist
    • Ein WARNING wird ausgegeben, wenn die durchschnittliche Backup Dauer plötzlich stark abweicht (trendavg)

Bedingung

  • Euer Backup Shell Skript mit der Erweiterung
  • Der Host auf dme die Backups laufen wurde bereits angelegt
  • Das Zabbix Template wurde importiert und dem Host zugeordnet
  • Eine konfigurierte Zabbix-Agentd Installation

Das Template

Template name: Templates/Template App Backup Item Namen:

  • Backup success for {#JOBNAME}'
  • Backup duration for {#JOBNAME}'

Interessant sind hier die so genannten Key-Namen, quasi "technische Namen", die als eine Art "Zieladresse" im Zabbix für die Daten fungieren. Diese Keys lassen sich beliebig anlegen, eine gewisses Namensschema ist sicher von Vorteil und wird aus folgenden Variablen konstruiert:

  • backup ist der immer gesetzte Prefix
  • BACKUPSCRIPT = Skriptname als Unterscheidungsmerkmal. Wird automatisch ohne Pfad erkannt ${0##*/}
    • backup_archive.sh
  • JOBNAME = Name des Backup-Jobs. Wird mit Variablen aus dem Skript gefüllt.
    • /mnt/backup:vaultstr
  • Zusammengesetzt ({#BACKUPSCRIPT} {#JOBNAME}) könnten das dann so aussehen:
    • backup[{#BACKUPSCRIPT} {#JOBNAME},exit]' , z.B. backup[backup_archive.sh /mnt/backup:vaultstr,exit]
    • backup[{#BACKUPSCRIPT} {#JOBNAME},time]' , z.B. backup[backup_archive.sh /mnt/backup:vaultstr,time]

Zusammengefasst: Ein Zabbix Agent schickt Daten an einen Key, der zu einem Item auf einem Host auf einem Zabbix Server gehört. Und dieses Item mit allem Pipapo wird beim ersten Mal automatisch durch ein Template angelegt.

zabbix_export:
  version: '6.0'
  date: '2025-03-05T21:43:35Z'
  groups:
    - uuid: a571c0d144b14fd4a87a9d9b2aa9fcd6
      name: Templates/Applications
  templates:
    - uuid: 954ee6018f9d489c86af2c7c6a02efaf
      template: 'Template App Backup'
      name: 'Template App Backup'
      groups:
        - name: Templates/Applications
      discovery_rules:
        - uuid: 9c7e599e898d4ce49229d053c57d230a
          name: 'Backup Trigger Discovery'
          type: TRAP
          key: backup.discovery
          delay: '0'
          lifetime: 7d
          item_prototypes:
            - uuid: 8e730595e7fa4a0684bb1f0eea6b9324
              name: 'Backup success for {#JOBNAME}'
              type: TRAP
              key: 'backup[{#BACKUPSCRIPT} {#JOBNAME},exit]'
              delay: '0'
              valuemap:
                name: 'Exit code'
              tags:
                - tag: Application
                  value: Backup
                - tag: Script
                  value: '{#BACKUPSCRIPT}'
              trigger_prototypes:
                - uuid: b5d1fa43ae4b40af9542dac1a610c5d1
                  expression: 'last(/Template App Backup/backup[{#BACKUPSCRIPT} {#JOBNAME},exit])<>0'
                  name: 'Backup for {#JOBNAME} ({#BACKUPSCRIPT}) on {HOST.NAME} has exit code {ITEM.LASTVALUE}'
                  priority: HIGH
                - uuid: fe0c4a7b65b942b38cb7b37fdaa109e5
                  expression: 'count(/Template App Backup/backup[{#BACKUPSCRIPT} {#JOBNAME},exit],2d)=0'
                  name: 'Backup for {#JOBNAME} ({#BACKUPSCRIPT}) on {HOST.NAME} too long ago'
                  priority: AVERAGE
                - uuid: 549f39f7abc54f11aa0cabce56fc2bb5
                  expression: 'min(/Template App Backup/backup[{#BACKUPSCRIPT} {#JOBNAME},exit],7d)<>0'
                  name: 'Backup for {#JOBNAME} ({#BACKUPSCRIPT}) on {HOST.NAME} was longer than a week ago'
                  priority: HIGH
            - uuid: 1f2a6a5b5d834bfeb009d086c9f2ee33
              name: 'Backup duration for {#JOBNAME}'
              type: TRAP
              key: 'backup[{#BACKUPSCRIPT} {#JOBNAME},time]'
              delay: '0'
              units: s
              tags:
                - tag: Application
                  value: Backup
                - tag: Script
                  value: '{#BACKUPSCRIPT}'
              trigger_prototypes:
                - uuid: 3c617c82004048ae9a7611c78d35f52e
                  expression: 'trendavg(/Template App Backup/backup[{#BACKUPSCRIPT} {#JOBNAME},time],1M:now/M-2M)<0'
                  name: 'Backup for {#JOBNAME} ({#BACKUPSCRIPT}) on {HOST.NAME} run too long'
                  priority: WARNING
                  manual_close: 'YES'
      valuemaps:
        - uuid: 788b4f227a5744ba80c964f755fa6943
          name: 'Exit code'
          mappings:
            - value: '0'
              newvalue: OK
            - type: GREATER_OR_EQUAL
              value: '1'
              newvalue: NOK

Diesen Block einfach als yaml-Datei abspeichern und in Zabbix importieren (Configuration - Templates - Import)

Skript

Nachfolgend die nötigen Bausteine, die in das eigene Skript eingearbeitet werden müssen:

  • Zu Beginn hinterlegt ihr den Pfad zur Zabbix Agentd Konfiguration: ZABBIXCONF="/usr/local/etc/zabbix6/zabbix_agentd.conf"
  • Vor dem Start der eigentlichen Aufgabe im Skript wird die Zeit mit START=$(date +%s) erfasst
  • Am Ende der Aufgabe wird die Zeit mit END=$(date +%s) gestoppt und der Ergebnis mit DURATION=$(($END-$START)) in Sekunden berechnet
  • Schritt 1: Zabbix übergibt an den Key (backup.discovery) des Templates (Template App Backup) den Inhalt der beiden Zabbix-Variablen BACKUPSCRIPT und JOBNAME. Falls die Items noch nicht vorhanden sind, werden beide wie oben beschrieben auf Bases des Templates angelegt.

zabbix_sender -c ${ZABBIXCONF} -k "backup.discovery" -o '{"data":[{"{#BACKUPSCRIPT}":"'${0##*/}'","{#JOBNAME}":"'${DATA}'"}]}'

Beispiele um JOBNAME zu füllen: DATA dann mit sinnvollen Werten aus eurem Skript ersetzen.
Immer eine Kombination nutzen, damit z.B. die gleichen Pfade zu verschiedenen Zielen unterschiedlich heißen.

- DATA = ${RCLONEREMOTE}:${DATASET}, z.B. pcloud_coretwo:/mnt/data/home
    - pcloud_coretwo # Rclone Ziel
    - /mnt/data/home # Zu sicherndes ZFS Dataset/Pfad
- DATA = ${DESTINATION}:${JOBNAME}, z.B. /mnt/backup:git
    - /mnt/backup # Zielpfad für das tar.gz Archiv
    - git # Jailname
- DATA = ${TARGETHOST}@${SNAPNAME}, z.B. corebackup:month
    - corebackup # Zielserver
    - month # Snaphostname

Wenn ein Zabbix-Proxy zwischengeschaltet ist, können die beiden folgenden Befehle beim ersten Mal fehlschlagen, da ein Zabbix-Proxy nur einmal pro Stunde neu erstellte Items kennt und erst dann akzeptiert. Also warten oder den Proxy neu starten.

  • Schritt 2: Zabbix sendet den Exit Code (RC) der Aufgabe im Skript an den oben erzeugten Key.
zabbix_sender -c ${ZABBIXCONF} -k "backup[${0##*/} ${DATA},exit]" -o ${RC}

Beispiele nach "-k": 
"backup[backup_rclone.sh pcloud_coretwo:/mnt/data/home,exit]"
"backup[backup_archive.sh /mnt/backup:git,exit]"
"backup[backup_replication.sh pcloud_coretwo:/mnt/data/home,exit]"
  • Schritt 3: Zabbix sendet die Ausführungszeit in Sekunden an den oben erzeugten Key.
zabbix_sender -c ${ZABBIXCONF} -k "backup[${0##*/} ${DATA},time]" -o ${DURATION}

Beispiele nach "-k": 
"backup[backup_rclone.sh pcloud_coretwo:/mnt/data/home,time]"
"backup[backup_archive.sh /mnt/backup:git,time]"
"backup[backup_replication.sh corebackup:month,time]"

Wer diese Logik einmal verstanden hat, kann nun alle möglichen Skripte und Situationen mit einer Zabbix-Überwachung ausstatten.

Wenn Du diese Inhalte für wertvoll und nützlich findest oder einfach nur Hallo sagen möchtest,
dann freue ich mich über eine Rückmeldung per Mastodon oder schreibe mir eine E-Mail