Erstellen eines Plugins

Diese Seite richtet sich an alle, die daran interessiert sind, ihr eigenes Plugin (eigenständigen Datensammler) für Zabbix Agent 2 zu entwickeln.

Mit einem benutzerdefinierten Plugin lässt sich die Funktionalität von Zabbix Agent 2 über die bereitgestellten integrierten Plugins und ladbaren Plugins hinaus erweitern.

Jedes Plugin ist ein Go-Paket, das die Struktur definiert und eine oder mehrere Schnittstellen (Exporter, Configurator, Runner) implementiert. Weitere Informationen finden Sie unter Plugin-Schnittstellen und Verbindungsdiagramm.

Diese Anleitung hilft Ihnen dabei, ein benutzerdefiniertes ladbares Plugin zu erstellen.

Weitere Hinweise finden Sie auch in den Repositories von:

Was Sie erstellen werden

Dies ist eine Schritt-für-Schritt-Anleitung zum Erstellen eines einfachen ladbaren Plugins mit dem Namen MyIP. Dieses Plugin implementiert einen einzelnen Datenpunktschlüssel (myip), der die externe IP-Adresse des Hosts des Zabbix Agent zurückgibt.

Schritt 1: Einrichtung

  1. Ein Plugin ist ein standardmäßiges Go-Modul. Beginnen Sie damit, die Datei go.mod im Plugin-Verzeichnis zu initialisieren, um Plugin-Abhängigkeiten zu verwalten:
cd path/to/plugins/myip # Wechseln Sie in Ihr Plugin-Verzeichnis
go mod init myip
  1. Installieren Sie die erforderliche Abhängigkeit Zabbix Go SDK (golang.zabbix.com/sdk):
go get golang.zabbix.com/sdk@$LATEST_COMMIT_HASH

Ersetzen Sie $LATEST_COMMIT_HASH durch den neuesten HEAD-Commit-Hash aus dem repository golang.zabbix.com/sdk im entsprechenden Release-Branch. Zum Beispiel:

go get golang.zabbix.com/sdk@af85407

Beachten Sie, dass die Versionierung von golang.zabbix.com/sdk derzeit nicht unterstützt wird, sich dies jedoch in Zukunft ändern kann.

Zusätzliche Abhängigkeiten können bei Bedarf mit go get installiert werden.

  1. Erstellen Sie eine leere Datei main.go für den Plugin-Quellcode:
touch main.go

Damit ist die anfängliche Einrichtung abgeschlossen, und das Plugin ist bereit für die Entwicklung.

Schritt 2: Plugin-Struktur

Das im vorherigen Schritt installierte Modul golang.zabbix.com/sdk stellt das notwendige Framework für die Plugin-Entwicklung bereit und sorgt dafür, dass alle Plugins eine konsistente Struktur haben.

  1. Grundlegenden Ausführungsablauf einrichten.

Beginnen Sie damit, den Hauptausführungsablauf des Plugins zu definieren. Fügen Sie den folgenden Code zu main.go hinzu:

package main

func main() {
    err := run()
    if err != nil {
        panic(err)
    }
}

func run() error {
    return nil
}

Dadurch wird der grundlegende Ausführungsablauf für das Plugin festgelegt. Die Funktion run wird später die Kernlogik des Plugins enthalten.

  1. Die Plugin-Schnittstellen kennenlernen.

Ein Zabbix Agent 2-Plugin soll durch eine Struktur dargestellt werden, die Schnittstellen aus dem Paket golang.zabbix.com/sdk/plugin implementiert:

  • Accessor – definiert wesentliche Methoden, die alle Plugins implementieren müssen, z. B. das Festlegen des Plugin-Namens und die Verarbeitung von Zeitüberschreitungen für Datenpunktschlüssel.
  • Eine oder mehrere der folgenden funktionalen Plugin-Schnittstellen:
    • Exporter – führt eine Abfrage aus und gibt einen Wert (oder mehrere Werte), nichts oder einen Fehler zurück; wird häufig zusammen mit der Schnittstelle Collector verwendet.
    • Collector – verwaltet die periodische Erfassung von Daten.
    • Runner – definiert die Start- und Herunterfahrverfahren des Plugins.
    • Watcher – ermöglicht die Implementierung einer unabhängigen Metrikabfrage unter Umgehung des internen Schedulers des Agenten; nützlich für trap-basierte oder ereignisgesteuerte Überwachung.
    • Configurator – definiert, wie das Plugin seine Konfigurationseinstellungen liest und anwendet.

Sie können diese Schnittstellen entweder selbst implementieren oder die vom Zabbix Go SDK bereitgestellten Standardschnittstellen verwenden und sie bei Bedarf anpassen. In diesem Tutorial werden die Standardimplementierungen verwendet.

  1. Die Plugin-Struktur erstellen.

Importieren Sie nun das Paket plugin und erstellen Sie eine Struktur myIP, die die Struktur plugin.Base einbettet:

import "golang.zabbix.com/sdk/plugin"

type myIP struct {
    plugin.Base
}

Die Struktur myIP erfüllt derzeit die Schnittstelle Accessor. Eine Methode zur Implementierung einer der funktionalen Plugin-Schnittstellen, Exporter, wird später im Tutorial hinzugefügt.

Schritt 3: Datenpunktschlüssel definieren

Ihr Plugin benötigt die Datenpunktschlüssel, um Daten zu erfassen und sie an den Zabbix Server oder Proxy bereitzustellen.

  1. Importieren Sie das Paket errs für die Fehlerbehandlung:
import "golang.zabbix.com/sdk/errs"
  1. Registrieren Sie Datenpunktschlüssel mit der Funktion plugin.RegisterMetrics() innerhalb der Funktion run():
func run() error {
    p := &myIP{}

    // Register the `myip` item key.
    err := plugin.RegisterMetrics(
        p,
        "MyIP",                           // Plugin name
        "myip",                           // Item key name
        "Returns the host's IP address.", // Item key description
    )
    if err != nil {
        return errs.Wrap(err, "failed to register metrics")
    }

    return nil
}

Um mehrere Datenpunktschlüssel zu registrieren, wiederholen Sie die Parameter Metrikname und Beschreibung für jede Metrik. Zum Beispiel:

plugin.RegisterMetrics(&impl, "Myip", "metric.one", "Metric one description.", "metric.two", "Metric two description.")

Schritt 4: Den Handler einrichten

Der Handler erleichtert die Kommunikation zwischen dem Agent und dem Plugin.

  1. Importieren Sie das Paket container:
import "golang.zabbix.com/sdk/plugin/container"
  1. Fügen Sie in der Funktion run() Code hinzu, um einen Handler zu erstellen und einzurichten:
func run() error {
    p := &myIP{}

    // Register the `myip` item key.
    err := plugin.RegisterMetrics(
        p,
        "MyIP",                           // Plugin name
        "myip",                           // Item key name
        "Returns the host's IP address.", // Item key description
    )
    if err != nil {
        return errs.Wrap(err, "failed to register metrics")
    }

    // Create a new handler.
    h, err := container.NewHandler("MyIP") // Plugin name
    if err != nil {
        return errs.Wrap(err, "failed to create new handler")
    }

    // Setup logging to forward logs from the plugin to the agent.
    // Available via p.Logger.Infof, p.Logger.Debugf, etc.
    p.Logger = h

    // Start plugin execution.
    // Blocks until a termination request is received from the agent.
    err = h.Execute()
    if err != nil {
        return errs.Wrap(err, "failed to execute plugin handler")
    }

    return nil
}

Schritt 5: Datenerfassung implementieren

Die Datenerfassung erfolgt über die Exporter-Schnittstelle, die die Methode Export beschreibt:

func Export(
  key string,             // Der zu erfassende Datenpunktschlüssel.
  params []string,        // An den Datenpunktschlüssel übergebene Argumente (`myip[arg1, arg2]`).
  context ContextProvider // Metadaten zur Datenerfassung des Datenpunktschlüssels.
) (any, error)
  1. Importieren Sie die erforderlichen Pakete für HTTP-Anfragen und das Lesen von Antworten:
import (
    "io"
    "net/http"
)
  1. Implementieren Sie die Methode Export für die Struktur myIP:
func (p *myIP) Export(
    key string, params []string, context plugin.ContextProvider,
) (any, error) {
    // Das Plugin kann abhängig vom Parameter `key` unterschiedliche Logik zur Datenerfassung verwenden.
    // Diese Implementierung prüft nur, ob der angegebene `key` unterstützt wird.
    if key != "myip" {
        return nil, errs.Errorf("unknown item key %q", key)
    }

    // Das Protokoll wird an das Agent-2-Protokoll weitergeleitet.
    p.Infof(
        "received request to handle %q key with %d parameters",
        key,
        len(params),
    )

    // Erfassen Sie die Daten und geben Sie sie zurück.

    resp, err := http.Get("https://api.ipify.org")
    if err != nil {
        return nil, errs.Wrap(err, "failed to get IP address")
    }

    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return nil, errs.Wrap(err, "failed to read response body")
    }

    return string(body), nil
}

Schritt 6: Das Plugin erstellen und konfigurieren

  1. Führen Sie zum Erstellen des Plugins Folgendes aus:
go mod tidy
go build

Dadurch sollte im aktuellen Verzeichnis eine ausführbare Datei myip erstellt werden.

  1. Konfigurieren Sie Zabbix Agent 2 so, dass es das Plugin verwendet:
echo "Plugins.MyIP.System.Path=$PATH_TO_THE_MYIP_PLUGIN_EXECUTABLE" > /etc/zabbix_agent2.d/plugins.d/myip.conf

Ersetzen Sie $PATH_TO_THE_MYIP_PLUGIN_EXECUTABLE durch den Pfad zu myip, das in Schritt 5 erstellt wurde.

Der Plugin-Name im Namen des Konfigurationsparameters (MyIP in diesem Tutorial) muss mit dem Plugin-Namen übereinstimmen, der in der Funktion plugin.RegisterMetrics() definiert ist.

  1. Führen Sie zum Testen des Plugins und seines myip-Datenpunkts Folgendes aus:
zabbix_agent2 -c /etc/zabbix_agent2.conf -t myip

Die Ausgabe sollte eine externe IP-Adresse Ihres Hosts enthalten und in etwa wie folgt aussehen:

myip                                          [s|192.0.2.0]

Damit haben Sie ein einfaches ladbares Plugin für Zabbix Agent 2 erstellt. Herzlichen Glückwunsch!

Vollständiger Quellcode

package main

import (
    "io"
    "net/http"

    "golang.zabbix.com/sdk/errs"
    "golang.zabbix.com/sdk/plugin"
    "golang.zabbix.com/sdk/plugin/container"
)

var _ plugin.Exporter = (*myIP)(nil)

type myIP struct {
    plugin.Base
}

func main() {
    err := run()
    if err != nil {
        panic(err)
    }
}

func run() error {
    p := &myIP{}

    // Registriert den `myip`-Datenpunktschlüssel.
    err := plugin.RegisterMetrics(
        p,
        "MyIP",                           // Plugin-Name
        "myip",                           // Name des Datenpunktschlüssels
        "Gibt die IP-Adresse des Hosts zurück.", // Beschreibung des Datenpunktschlüssels
    )
    if err != nil {
        return errs.Wrap(err, "failed to register metrics")
    }

    // Erstellt einen neuen Handler.
    h, err := container.NewHandler("MyIP") // Plugin-Name
    if err != nil {
        return errs.Wrap(err, "failed to create new handler")
    }

    // Richtet die Protokollierung so ein, dass Protokolle vom Plugin an den Agent weitergeleitet werden.
    // Verfügbar über p.Logger.Infof, p.Logger.Debugf usw.
    p.Logger = h

    // Startet die Plugin-Ausführung.
    // Blockiert, bis eine Beendigungsanforderung vom Agent empfangen wird.
    err = h.Execute()
    if err != nil {
        return errs.Wrap(err, "failed to execute plugin handler")
    }

    return nil
}

func (p *myIP) Export(
    key string, params []string, context plugin.ContextProvider,
) (any, error) {
    // Das Plugin kann abhängig vom Parameter `key` unterschiedliche Datenerfassungslogik verwenden.  
    // Diese Implementierung prüft nur, ob der angegebene `key` unterstützt wird. 
    if key != "myip" {
        return nil, errs.Errorf("unknown item key %q", key)
    }

    // Das Protokoll wird an Agent 2 weitergeleitet.
    p.Infof(
        "received request to handle %q key with %d parameters",
        key,
        len(params),
    )

    // Erfasst die Daten und gibt sie zurück.

    resp, err := http.Get("https://api.ipify.org")
    if err != nil {
        return nil, errs.Wrap(err, "failed to get IP address")
    }

    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return nil, errs.Wrap(err, "failed to read response body")
    }

    return string(body), nil
}