Creazione di un plugin
Questa pagina è destinata a coloro che sono interessati a sviluppare un proprio plugin (collettore dati indipendente) per Zabbix agent 2.
Un plugin personalizzato consente di estendere le funzionalità di Zabbix agent 2 oltre i plugin integrati e i plugin caricabili forniti.
Ogni plugin è un pacchetto Go che definisce la struttura e implementa una o più interfacce (Exporter, Configurator, Runner). Per ulteriori informazioni, vedere Interfacce dei plugin e Diagramma delle connessioni.
Questa guida ti aiuterà a creare un plugin caricabile personalizzato.
Per ulteriori indicazioni, vedere anche il repository di:
Cosa creerai
Questo è un tutorial passo passo per creare un semplice plugin caricabile chiamato MyIP.
Questo plugin implementerà una singola chiave item (myip) che restituisce l'indirizzo IP esterno dell'host dell'agent Zabbix.
Passaggio 1: Configurazione
- Un plugin è un modulo Go standard.
Inizia inizializzando il file
go.modnella directory del plugin per tenere traccia delle dipendenze del plugin:
cd path/to/plugins/myip # Passa alla directory del 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 l'hash del commit HEAD più recente dal repository golang.zabbix.com/sdk nel branch di rilascio appropriato.
Per esempio:
go get golang.zabbix.com/sdk@af85407
Tieni presente che il versionamento di golang.zabbix.com/sdk al momento non è supportato, ma questo potrebbe cambiare in futuro.
Le dipendenze aggiuntive possono essere installate secondo necessità usando go get.
- Crea un file
main.govuoto per il codice sorgente del plugin:
touch main.go
Ora la configurazione iniziale è completa 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.
- Impostare il flusso di esecuzione di base.
Iniziare definendo il flusso di esecuzione principale del plugin. Aggiungere il seguente codice 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 per il plugin. La funzione run conterrà in seguito la logica principale del plugin.
- Esplorare 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 della chiave 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; spesso usato 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, aggirando lo scheduler interno dell'agent; utile per il monitoraggio basato su trap o guidato da eventi.
- Configurator - definisce come il plugin legge e applica le proprie impostazioni di configurazione.
È possibile implementare queste interfacce autonomamente oppure utilizzare quelle predefinite fornite da Zabbix Go SDK, modificandole secondo necessità. Questo tutorial utilizza le implementazioni predefinite.
- Creare la struct del plugin.
Ora importare il pacchetto plugin e creare una struct myIP che incorpori la struct plugin.Base:
import "golang.zabbix.com/sdk/plugin"
type myIP struct {
plugin.Base
}
La struct myIP al momento soddisfa l'interfaccia Accessor.
Più avanti nel tutorial verrà aggiunto un metodo per implementare una delle interfacce funzionali del plugin, Exporter.
Passaggio 3: Definire le chiavi degli item
Il tuo plugin ha bisogno delle chiavi degli 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 degli item utilizzando la funzione
plugin.RegisterMetrics()all'interno della funzionerun():
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
}
Per registrare più chiavi degli item, ripeti i parametri nome della metrica e descrizione per ogni 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.
- Importare il pacchetto container:
import "golang.zabbix.com/sdk/plugin/container"
- All'interno della funzione
run()aggiungere il codice per creare e configurare un handler:
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
}
Passaggio 5: Implementare la raccolta dei dati
La raccolta dei dati viene eseguita tramite l'interfaccia Exporter, che descrive il metodo Export:
func Export(
key string, // La chiave item da raccogliere.
params []string, // Argomenti passati alla chiave item (`myip[arg1, arg2]`).
context ContextProvider // Metadati sulla raccolta dei dati della chiave item.
) (any, error)
- Importare i pacchetti richiesti per le richieste HTTP e la lettura della risposta:
import (
"io"
"net/http"
)
- Implementare il metodo
Exportper la structmyIP:
func (p *myIP) Export(
key string, params []string, context plugin.ContextProvider,
) (any, error) {
// Il plugin può utilizzare 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),
)
// Raccogliere i dati e restituirli.
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
}
Passaggio 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 utilizzare il plugin:
echo "Plugins.MyIP.System.Path=$PATH_TO_THE_MYIP_PLUGIN_EXECUTABLE" > /etc/zabbix_agent2.d/plugins.d/myip.conf
Sostituire $PATH_TO_THE_MYIP_PLUGIN_EXECUTABLE con il percorso di 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_agent2.conf -t myip
L'output dovrebbe contenere un indirizzo IP esterno del proprio host e avere un aspetto simile al seguente:
myip [s|192.0.2.0]
A questo punto, è stato creato un semplice plugin caricabile per Zabbix agent 2. Congratulazioni!
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 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.
// Si blocca 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ò utilizzare una logica diversa di raccolta dati in base al parametro `key`.
// Questa implementazione verifica solo che la `key` fornita sia supportata.
if key != "myip" {
return nil, errs.Errorf("chiave item sconosciuta %q", key)
}
// Il log verrà inoltrato all'agent 2.
p.Infof(
"ricevuta richiesta di 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
}