Creare un plugin (tutorial)
Questo è un tutorial passo passo su come creare un semplice plugin caricabile per Zabbix agent 2.
Puoi anche utilizzare il repository di esempio come modello o guida per creare i tuoi plugin.
Cosa creerai
Questo tutorial mostra come creare un nuovo plugin caricabile MyIP. Il plugin implementerà una singola chiave di elemento, myip, che restituisce l'indirizzo IP esterno dell'host su cui è in esecuzione Zabbix agent 2.
Passaggio 1: Setup
- Un plugin è un modulo Go standard.
Inizia inizializzando il file
go.modnella directory del plugin per tracciarne le dipendenze:
cd path/to/plugins/myip # Passa alla directory del tuo plugin
go mod init myip
- Installa la dipendenza obbligatoria Zabbix Go SDK (
golang.zabbix.com/sdk):
go get golang.zabbix.com/sdk@$LATEST_COMMIT_HASH
Sostituisci $LATEST_COMMIT_HASH con il più recente hash del commit HEAD dal repository golang.zabbix.com/sdk nel branch di rilascio appropriato.
Ad esempio:
go get golang.zabbix.com/sdk@af85407
Si noti che il versioning di golang.zabbix.com/sdk non è attualmente supportato, ma questo potrebbe cambiare in futuro.
È possibile installare dipendenze aggiuntive, se necessario, utilizzando go get.
- Crea un file
main.govuoto per il codice sorgente del plugin:
touch main.go
Ora il setup iniziale è completo e il plugin è pronto per lo sviluppo.
Passaggio 2: Struttura del plugin
Il modulo golang.zabbix.com/sdk, installato nel passaggio precedente, fornisce il framework necessario per lo sviluppo dei plugin e garantisce che tutti i plugin abbiano una struttura coerente.
- Configurare il flusso di esecuzione di base.
Iniziare definendo il flusso di esecuzione principale del plugin. Aggiungere il codice seguente a main.go:
package main
func main() {
err := run()
if err != nil {
panic(err)
}
}
func run() error {
return nil
}
Questo stabilisce il flusso di esecuzione di base del plugin. La funzione run conterrà in seguito la logica principale del plugin.
- Esaminare le interfacce del plugin.
Un plugin di Zabbix agent 2 deve essere rappresentato da una struct che implementa le interfacce del pacchetto golang.zabbix.com/sdk/plugin:
- Accessor - definisce i metodi essenziali che tutti i plugin devono implementare, come l'impostazione del nome del plugin e la gestione dei timeout delle chiavi degli item.
- Una o più delle seguenti interfacce funzionali del plugin:
- Exporter - esegue un polling e restituisce un valore (o più valori), nulla oppure un errore; viene spesso utilizzata insieme all'interfaccia Collector.
- Collector - gestisce la raccolta periodica dei dati.
- Runner - definisce le procedure di avvio e arresto del plugin.
- Watcher - consente di implementare il polling indipendente delle metriche, ignorando lo scheduler interno dell'agent; è utile per il monitoraggio basato su trap o guidato dagli eventi.
- Configurator - definisce il modo in cui il plugin legge e applica le proprie impostazioni di configurazione.
È possibile implementare autonomamente queste interfacce oppure utilizzare quelle predefinite fornite da Zabbix Go SDK, modificandole secondo necessità. Questa esercitazione utilizza le implementazioni predefinite.
- Creare la struct del plugin.
Ora importare il pacchetto plugin e creare una struct myIP che includa la struct plugin.Base:
import "golang.zabbix.com/sdk/plugin"
type myIP struct {
plugin.Base
}
La struct myIP soddisfa attualmente l'interfaccia Accessor.
Nel corso dell'esercitazione verrà aggiunto un metodo per implementare una delle interfacce funzionali del plugin, ovvero Exporter.
Passaggio 3: Definire le chiavi item
Il tuo plugin ha bisogno delle chiavi item per raccogliere i dati e fornirli a Zabbix server o proxy.
- Importa il pacchetto errs per la gestione degli errori:
import "golang.zabbix.com/sdk/errs"
- Registra le chiavi item usando la funzione
plugin.RegisterMetrics()all'interno della funzionerun():
func run() error {
p := &myIP{}
// Registra la chiave item `myip`.
err := plugin.RegisterMetrics(
p,
"MyIP", // Nome del plugin
"myip", // Nome della chiave item
"Restituisce l'indirizzo IP dell'host.", // Descrizione della chiave item
)
if err != nil {
return errs.Wrap(err, "failed to register metrics")
}
return nil
}
Per registrare più chiavi item, ripeti i parametri metric name e description per ciascuna metrica. Ad esempio:
plugin.RegisterMetrics(&impl, "Myip", "metric.one", "Metric one description.", "metric.two", "Metric two description.")
Passaggio 4: Configurare l'handler
L'handler facilita la comunicazione tra l'agent e il plugin.
- Importa il package container:
import "golang.zabbix.com/sdk/plugin/container"
- All'interno della funzione
run()aggiungi il codice per creare e configurare un handler:
func run() error {
p := &myIP{}
// Registra la chiave item `myip`.
err := plugin.RegisterMetrics(
p,
"MyIP", // Nome del plugin
"myip", // Nome della chiave item
"Restituisce l'indirizzo IP dell'host.", // Descrizione della chiave item
)
if err != nil {
return errs.Wrap(err, "failed to register metrics")
}
// Crea un nuovo handler.
h, err := container.NewHandler("MyIP") // Nome del plugin
if err != nil {
return errs.Wrap(err, "failed to create new handler")
}
// Configura il logging per inoltrare i log dal plugin all'agent.
// Disponibile tramite p.Logger.Infof, p.Logger.Debugf, ecc.
p.Logger = h
// Avvia l'esecuzione del plugin.
// Rimane in attesa fino a quando non viene ricevuta una richiesta di terminazione dall'agent.
err = h.Execute()
if err != nil {
return errs.Wrap(err, "failed to execute plugin handler")
}
return nil
}
Passaggio 5: Implementare la raccolta dati
La raccolta dati viene eseguita tramite l'interfaccia Exporter, che descrive il metodo Export:
func Export(
key string, // La chiave dell'item da raccogliere.
params []string, // Argomenti passati alla chiave dell'item (`myip[arg1, arg2]`).
context ContextProvider // Metadati sulla raccolta dati della chiave dell'item.
) (any, error)
- Importa i pacchetti necessari per le richieste HTTP e la lettura della risposta:
import (
"io"
"net/http"
)
- Implementa il metodo
Exportper la structmyIP:
func (p *myIP) Export(
key string, params []string, context plugin.ContextProvider,
) (any, error) {
// Il plugin può usare una logica di raccolta dati diversa in base al parametro `key`.
// Questa implementazione verifica solo che il `key` fornito sia supportato.
if key != "myip" {
return nil, errs.Errorf("unknown item key %q", key)
}
// Il log verrà inoltrato al log di agent 2.
p.Infof(
"received request to handle %q key with %d parameters",
key,
len(params),
)
// Raccogli i dati e restituiscili.
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
}
Passo 6: Compilare e configurare il plugin
- Per compilare il plugin, eseguire:
go mod tidy
go build
Questo dovrebbe creare un eseguibile myip nella directory corrente.
- Configurare Zabbix agent 2 per usare il plugin:
echo "Plugins.MyIP.System.Path=$PATH_TO_THE_MYIP_PLUGIN_EXECUTABLE" > /etc/zabbix/zabbix_agent2.d/plugins.d/myip.conf
Sostituire $PATH_TO_THE_MYIP_PLUGIN_EXECUTABLE con il percorso del myip creato nel passaggio 5.
Il nome del plugin nel nome del parametro di configurazione (MyIP in questo tutorial) deve corrispondere al nome del plugin definito nella funzione plugin.RegisterMetrics().
- Per testare il plugin e il relativo item
myip, eseguire:
zabbix_agent2 -c /etc/zabbix/zabbix_agent2.conf -t myip
L'output dovrebbe contenere un indirizzo IP esterno del vostro host e avere un aspetto simile a questo:
myip [s|192.0.2.1]
Con questo, avete creato un semplice plugin caricabile per Zabbix agent 2. Complimenti!
Codice sorgente completo
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{}
// Registra la chiave item `myip`.
err := plugin.RegisterMetrics(
p,
"MyIP", // Nome del plugin
"myip", // Nome della chiave item
"Restituisce l'indirizzo IP dell'host.", // Descrizione della chiave item
)
if err != nil {
return errs.Wrap(err, "impossibile registrare le metriche")
}
// Crea un nuovo handler.
h, err := container.NewHandler("MyIP") // Nome del plugin
if err != nil {
return errs.Wrap(err, "impossibile creare un nuovo handler")
}
// Configura la registrazione per inoltrare i log dal plugin all'agent.
// Disponibile tramite p.Logger.Infof, p.Logger.Debugf, ecc.
p.Logger = h
// Avvia l'esecuzione del plugin.
// Rimane bloccato finché non viene ricevuta una richiesta di terminazione dall'agent.
err = h.Execute()
if err != nil {
return errs.Wrap(err, "impossibile eseguire l'handler del plugin")
}
return nil
}
func (p *myIP) Export(
key string, params []string, context plugin.ContextProvider,
) (any, error) {
// Il plugin può usare una logica diversa di raccolta dati in base al parametro `key`.
// Questa implementazione verifica solo che il `key` fornito sia supportato.
if key != "myip" {
return nil, errs.Errorf("chiave item sconosciuta %q", key)
}
// Il log verrà inoltrato al log dell'agent 2.
p.Infof(
"ricevuta richiesta per gestire la chiave %q con %d parametri",
key,
len(params),
)
// Raccoglie i dati e li restituisce.
resp, err := http.Get("https://api.ipify.org")
if err != nil {
return nil, errs.Wrap(err, "impossibile ottenere l'indirizzo IP")
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, errs.Wrap(err, "impossibile leggere il corpo della risposta")
}
return string(body), nil
}