Направите додатак (водач)

Ово је корак-по-корак водич о томе како да направите једноставан додатак који се може учитати за Zabbix агент 2.

Такође можете користити пример репозиторијума као шаблон или водич за креирање сопствених додатака.

Шта ћете креирати

Овај туторијал показује како да креирате нови учитавајући додатак MyIP. Додатак ће имплементирати један кључ ставке, myip, који враћа екстерну IP адресу домаћина на којем ради Zabbix агент 2.

Корак 1: Подешавање

  1. Додатак је стандардни Go модул. Почните иницијализацијом датотеке go.mod у директоријуму додатка да бисте пратили зависности додатка:
cd path/to/plugins/myip # Пребаците се на директоријум вашег додатка
       go mod init myip
  1. Инсталирајте обавезну зависност Zabbix Go SDK (golang.zabbix.com/sdk):
go get golang.zabbix.com/sdk@$LATEST_COMMIT_HASH

Замените $LATEST_COMMIT_HASH са најновијим HEAD хешем коммита из golang.zabbix.com/sdk репозиторијума у одговарајућој грани за издање. На пример:

go get golang.zabbix.com/sdk@af85407

Имајте на уму да верзирање golang.zabbix.com/sdk тренутно није подржано, али се то може променити у будућности.

Додатне зависности се могу инсталирати по потреби помоћу go get.

  1. Направите празну датотеку main.go за изворни код додатка:
touch main.go

Сада је почетно подешавање завршено и додатак је спреман за развој.

Корак 2: Структура додатка

Модул golang.zabbix.com/sdk, инсталиран у претходном кораку, пружа неопходан оквир за развој додатака и осигурава да сви додаци имају конзистентну структуру.

  1. Подесите основни ток извршавања.

Почните дефинисањем главног тока извршавања додатка. Додајте следећи код у main.go:

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

Овим се успоставља основни ток извршавања за додатак. Функција run ће касније садржати основну логику додатка.

  1. Истражите интерфејсе додатка.

Додатак Zabbix агента 2 биће представљен структуром која имплементира интерфејсе из пакета golang.zabbix.com/sdk/plugin:

  • Accessor - дефинише основне методе које сви додаци морају да имплементирају, као што су подешавање имена додатка и руковање временским ограничењима кључа ставке.

  • Један или више следећих функционалних интерфејса додатка:

    • Exporter - врши анкету и враћа вредност (или вредности), ништа или грешку; често се користи уз интерфејс Collector.
    • Collector - управља периодичним прикупљањем података.
    • Runner - дефинише процедуре покретања и гашења додатка.
    • Watcher - омогућава имплементацију независног анкетирања метрика, заобилазећи интерни распоређивач агента; корисно за праћење засновано на замкама или догађајима.
    • Configurator - дефинише како додатак чита и примењује своја подешавања конфигурације.

Можете сами имплементирати ове интерфејсе или користити подразумеване које пружа Zabbix Go SDK, модификујући их по потреби. Овај туторијал користи подразумеване имплементације.

  1. Направите структуру додатка.

Сада, увезите пакет додатак и креирајте структуру myIP која уграђује структуру plugin.Base:

import "golang.zabbix.com/sdk/plugin"
       
       type myIP struct {
         plugin.Base
       }

Структура myIP тренутно задовољава интерфејс Accessor. Метод за имплементацију једног од функционалних интерфејса додатка, Exporter, биће додат касније у туторијалу.

Корак 3: Дефинисање кључева ставки

Вашем додатку су потребни кључеви ставки да би прикупљао податке и пружао их Zabbix серверу или проксију.

  1. Увезите errs пакет за обраду грешака:
import "golang.zabbix.com/sdk/errs"
  1. Региструјте кључеве ставки користећи функцију plugin.RegisterMetrics() унутар функције run():
func run() error {
         p := &myIP{}
       
         // Register the `myip` item key.
         err := plugin.RegisterMetrics(
           p,
           "MyIP", // Назив додатка
           "myip", // Назив кључа ставке
           "Returns the host's IP address.", // Item key description
         )
         if err != nil {
           return errs.Wrap(err, "failed to register metrics")
         }
       
         return nil
       }

Да бисте регистровали неколико кључева ставке, поновите параметре назив метрике и опис за сваку метрику. На пример:

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

Корак 4: Подешавање руковаоца

Руковалац олакшава комуникацију између агента и додатка.

  1. Увезите пакет контејнер:
import "golang.zabbix.com/sdk/plugin/container"
  1. Унесите функцију 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")
         }
       
         // 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
       }

Корак 5: Имплементација прикупљања података

Прикупљање података се врши путем интерфејса Експортера, који описује метод Export:

func Export(
         key string, // The item key to collect.
         params []string,  // Arguments passed to the item key (`myip[arg1, arg2]`).
         context ContextProvider // Metadata about the item key data collection.
       ) (any, error)
  1. Увезите потребне пакете за HTTP захтеве и читање одговора:
import (
         "io"
         "net/http"
       )
  1. Имплементирајте метод Export за структуру myIP:
func (p *myIP) Export(
         key string, params []string, context plugin.ContextProvider,
       ) (any, error) {
         // The plugin can use different data collection logic based on the `key` parameter.
         // This implementation only verifies that the provided `key` is supported.
         if key != "myip" {
           return nil, errs.Errorf("unknown item key %q", key)
         }
       
         // The log will get forwarded to the agent 2 log.
         p.Infof(
           "received request to handle %q key with %d parameters",
           key,
           len(params),
         )
       
         // Collect the data and return it.
       
         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: Направите и конфигуришите додатак

  1. Да бисте направили додатак, покрените:
go mod tidy
       go build

Ово би требало да креира извршну датотеку myip у тренутном директоријуму.

  1. Конфигуришите Zabbix агента 2 да користи додатак:
echo "Plugins.MyIP.System.Path=$PATH_TO_THE_MYIP_PLUGIN_EXECUTABLE" > /etc/zabbix_agent2.d/plugins.d/myip.conf

Замените $PATH_TO_THE_MYIP_PLUGIN_EXECUTABLE путањом до myip креиране у кораку 5.

Назив додатка у називу параметра конфигурације (MyIP у овом туторијалу) мора се подударати са називом додатка дефинисаним у функцији plugin.RegisterMetrics().

  1. Да бисте тестирали додатак и његову ставку myip, покрените:
zabbix_agent2 -c /etc/zabbix_agent2.conf -t myip

Излаз би требало да садржи екстерну IP адресу вашег хоста и да изгледа слично овоме:

myip [s|192.0.2.0]

Овим сте креирали једноставан додатак за Zabbix агент 2 који се може учитати. Честитамо!

Комплетан изворни код

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{}
       
          // 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 from the agent is received.
          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) {
            // The plugin can use different data collection logic based on the `key` parameter.
            // This implementation only verifies that the provided `key` is supported.
            if key != "myip" {
                 return nil, errs.Errorf("unknown item key %q", key)
            }
       
           // The log will get forwarded to the agent 2 log.
            p.Infof(
                  "received request to handle %q key with %d parameters",
                   key,
                   len(params),
            )
            // Collect the data and return it.
       
            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
       }