Izveidot spraudni (pamācība)

Šī ir soli pa solim pamācība par to, kā izveidot vienkāršu ielādējamu spraudni Zabbix aģentam 2.

Varat arī izmantot piemēra repozitoriju kā veidni vai ceļvedi savu spraudņu izveidei.

Ko jūs izveidosiet

Šajā pamācībā ir parādīts, kā izveidot jaunu ielādējamu spraudni MyIP. Spraudnis ieviesīs vienu vienuma atslēgu myip, kas atgriezīs tā hosta ārējo IP adresi, kurā darbojas Zabbix aģents 2.

1. darbība: iestatīšana

  1. Spraudnis ir standarta Go modulis. Sāciet ar go.mod faila inicializēšanu spraudņa direktorijā, lai sekotu spraudņa atkarībām:
cd path/to/plugins/myip # Pārslēdzieties uz savu spraudņa direktoriju
go mod init myip
  1. Instalējiet obligāto atkarību Zabbix Go SDK (golang.zabbix.com/sdk):
go get golang.zabbix.com/sdk@$LATEST_COMMIT_HASH

Aizstājiet $LATEST_COMMIT_HASH ar jaunāko HEAD commit hash no golang.zabbix.com/sdk krātuves attiecīgajā laidiena zarā. Piemēram:

go get golang.zabbix.com/sdk@af85407

Ņemiet vērā, ka golang.zabbix.com/sdk versiju pārvaldība pašlaik netiek atbalstīta, taču nākotnē tas var mainīties.

Papildu atkarības pēc vajadzības var instalēt, izmantojot go get.

  1. Izveidojiet tukšu main.go failu spraudņa pirmkoda vajadzībām:
touch main.go

Tagad sākotnējā iestatīšana ir pabeigta, un spraudnis ir gatavs izstrādei.

2. darbība: spraudņa struktūra

golang.zabbix.com/sdk modulis, kas tika instalēts iepriekšējā darbībā, nodrošina nepieciešamo ietvaru spraudņu izstrādei un garantē, ka visiem spraudņiem ir vienota struktūra.

  1. Iestatiet pamata izpildes plūsmu.

Sāciet ar spraudņa galvenās izpildes plūsmas definēšanu. Pievienojiet šādu kodu failam main.go:

package main

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

func run() error {
    return nil
}

Tas izveido spraudņa pamata izpildes plūsmu. Funkcija run vēlāk saturēs spraudņa pamatloģiku.

  1. Iepazīstieties ar spraudņa interfeisiem.

Zabbix aģents 2 spraudnim jābūt attēlotam ar struktūru, kas īsteno interfeisus no golang.zabbix.com/sdk/plugin pakotnes:

  • Accessor - definē būtiskās metodes, kas jāīsteno visiem spraudņiem, piemēram, spraudņa nosaukuma iestatīšanu un vienuma atslēgas taimautu apstrādi.
  • Vienu vai vairākus no šiem funkcionālajiem spraudņa interfeisiem:
    • Exporter - veic aptauju un atgriež vērtību (vai vērtības), neko vai kļūdu; bieži tiek izmantots kopā ar Collector interfeisu.
    • Collector - pārvalda periodisku datu vākšanu.
    • Runner - definē spraudņa palaišanas un apturēšanas procedūras.
    • Watcher - ļauj īstenot neatkarīgu metriku aptauju, apejot aģenta iekšējo plānotāju; noderīgs uz trigeriem balstītai vai notikumu vadītai uzraudzībai.
    • Configurator - definē, kā spraudnis nolasa un piemēro savus konfigurācijas iestatījumus.

Jūs varat šos interfeisus īstenot paši vai izmantot Zabbix Go SDK nodrošinātos noklusējuma variantus, pielāgojot tos pēc vajadzības. Šajā pamācībā tiek izmantotas noklusējuma implementācijas.

  1. Izveidojiet spraudņa struktūru.

Tagad importējiet plugin pakotni un izveidojiet myIP struktūru, kas iegulda plugin.Base struktūru:

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

type myIP struct {
    plugin.Base
}

Pašlaik myIP struktūra atbilst Accessor interfeisam. Metode viena no funkcionālajiem spraudņa interfeisiem, Exporter, īstenošanai tiks pievienota vēlāk pamācībā.

3. darbība: Definējiet vienumu atslēgas

Jūsu spraudnim ir nepieciešamas vienumu atslēgas, lai savāktu datus un nodotu tos Zabbix serverim vai starpniekserverim.

  1. Importējiet errs pakotni kļūdu apstrādei:
import "golang.zabbix.com/sdk/errs"
  1. Reģistrējiet vienumu atslēgas, izmantojot funkciju plugin.RegisterMetrics() funkcijā run():
func run() error {
    p := &myIP{}

    // Reģistrē `myip` vienumu atslēgu.
    err := plugin.RegisterMetrics(
        p,
        "MyIP",                           // Spraudņa nosaukums
        "myip",                           // Vienumu atslēgas nosaukums
        "Atgriež hosts IP adresi.",        // Vienumu atslēgas apraksts
    )
    if err != nil {
        return errs.Wrap(err, "neizdevās reģistrēt metriku")
    }

    return nil
}

Lai reģistrētu vairākas vienumu atslēgas, atkārtojiet parametrus metric name un description katrai metrikai. Piemēram:

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

4. darbība: iestatiet apdarinātāju

Apdarinātājs nodrošina saziņu starp aģentu un spraudni.

  1. Importējiet pakotni container:
import "golang.zabbix.com/sdk/plugin/container"
  1. Funkcijā run() pievienojiet kodu, lai izveidotu un iestatītu apdarinātāju:
func run() error {
    p := &myIP{}

    // Reģistrējiet `myip` vienuma atslēgu.
    err := plugin.RegisterMetrics(
        p,
        "MyIP",                           // Spraudņa nosaukums
        "myip",                           // Vienuma atslēgas nosaukums
        "Atgriež hosts IP adresi.",        // Vienuma atslēgas apraksts
    )
    if err != nil {
        return errs.Wrap(err, "failed to register metrics")
    }

    // Izveidojiet jaunu apdarinātāju.
    h, err := container.NewHandler("MyIP") // Spraudņa nosaukums
    if err != nil {
        return errs.Wrap(err, "failed to create new handler")
    }

    // Iestatiet žurnālu pārsūtīšanu no spraudņa uz aģentu.
    // Pieejams, izmantojot p.Logger.Infof, p.Logger.Debugf utt.
    p.Logger = h

    // Sāciet spraudņa izpildi.
    // Bloķējas līdz no aģenta tiek saņemts pārtraukšanas pieprasījums.
    err = h.Execute()
    if err != nil {
        return errs.Wrap(err, "failed to execute plugin handler")
    }

    return nil
}

5. darbība: datu vākšanas ieviešana

Datu vākšana tiek veikta, izmantojot Exporter interfeisu, kas apraksta Export metodi:

func Export(
  key string,             // Vienuma atslēga, no kuras vākt datus.
  params []string,        // Argumenti, kas nodoti vienuma atslēgai (`myip[arg1, arg2]`).
  context ContextProvider // Metadati par vienuma atslēgas datu vākšanu.
) (any, error)
  1. Importējiet nepieciešamās pakotnes HTTP pieprasījumiem un atbildes nolasīšanai:
import (
    "io"
    "net/http"
)
  1. Ieviesiet Export metodi struktūrai myIP:
func (p *myIP) Export(
    key string, params []string, context plugin.ContextProvider,
) (any, error) {
    // Spraudnis var izmantot atšķirīgu datu vākšanas loģiku atkarībā no `key` parametra.
    // Šī ieviešana tikai pārbauda, vai norādītais `key` tiek atbalstīts.
    if key != "myip" {
        return nil, errs.Errorf("unknown item key %q", key)
    }

    // Žurnāla ieraksts tiks pārsūtīts uz aģents 2 žurnālu.
    p.Infof(
        "received request to handle %q key with %d parameters",
        key,
        len(params),
    )

    // Savāciet datus un atgrieziet tos.

    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
}

6. darbība: spraudņa izveide un konfigurēšana

  1. Lai izveidotu spraudni, izpildiet:
go mod tidy
go build

Tam vajadzētu izveidot izpildāmo failu myip pašreizējā direktorijā.

  1. Konfigurējiet Zabbix aģents 2, lai izmantotu spraudni:
echo "Plugins.MyIP.System.Path=$PATH_TO_THE_MYIP_PLUGIN_EXECUTABLE" > /etc/zabbix_agent2.d/plugins.d/myip.conf

Aizstājiet $PATH_TO_THE_MYIP_PLUGIN_EXECUTABLE ar ceļu uz myip, kas tika izveidots 5. darbībā.

Spraudņa nosaukumam konfigurācijas parametra nosaukumā (MyIP šajā pamācībā) ir jāsakrīt ar spraudņa nosaukumu, kas definēts funkcijā plugin.RegisterMetrics().

  1. Lai pārbaudītu spraudni un tā myip vienumu, izpildiet:
zabbix_agent2 -c /etc/zabbix_agent2.conf -t myip

Izvadē jābūt jūsu hosta ārējai IP adresei, un tai vajadzētu izskatīties līdzīgi šim:

myip                                          [s|192.0.2.1]

Tādējādi jūs esat izveidojis vienkāršu ielādējamu spraudni Zabbix aģents 2.

Apsveicam!

Pilns avota kods

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{}

    // Reģistrē `myip` vienuma atslēgu.
    err := plugin.RegisterMetrics(
        p,
        "MyIP",                           // Spraudņa nosaukums
        "myip",                           // Vienuma atslēgas nosaukums
        "Atgriež hosts IP adresi.",        // Vienuma atslēgas apraksts
    )
    if err != nil {
        return errs.Wrap(err, "neizdevās reģistrēt metriku")
    }

    // Izveido jaunu apstrādātāju.
    h, err := container.NewHandler("MyIP") // Spraudņa nosaukums
    if err != nil {
        return errs.Wrap(err, "neizdevās izveidot jaunu apstrādātāju")
    }

    // Iestata reģistrēšanu, lai pārsūtītu žurnālus no spraudņa uz aģentu.
    // Pieejams, izmantojot p.Logger.Infof, p.Logger.Debugf u. c.
    p.Logger = h

    // Sāk spraudņa izpildi.
    // Bloķējas, līdz no aģenta tiek saņemts pārtraukšanas pieprasījums.
    err = h.Execute()
    if err != nil {
        return errs.Wrap(err, "neizdevās izpildīt spraudņa apstrādātāju")
    }

    return nil
}

func (p *myIP) Export(
    key string, params []string, context plugin.ContextProvider,
) (any, error) {
    // Spraudnis var izmantot atšķirīgu datu vākšanas loģiku atkarībā no `key` parametra.  
    // Šī implementācija tikai pārbauda, vai norādītais `key` tiek atbalstīts. 
    if key != "myip" {
        return nil, errs.Errorf("nezināma vienuma atslēga %q", key)
    }

    // Žurnāls tiks pārsūtīts uz aģenta 2 žurnālu.
    p.Infof(
        "saņemts pieprasījums apstrādāt %q atslēgu ar %d parametriem",
        key,
        len(params),
    )

    // Savāc datus un atgriež tos.

    resp, err := http.Get("https://api.ipify.org")
    if err != nil {
        return nil, errs.Wrap(err, "neizdevās iegūt IP adresi")
    }

    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return nil, errs.Wrap(err, "neizdevās nolasīt atbildes pamattekstu")
    }

    return string(body), nil
}