Это перевод страницы документации с английского языка. Помогите нам сделать его лучше.

Создание плагина (руководство)

Это пошаговое руководство по созданию простого загружаемого плагина для Zabbix Agent 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

Теперь начальная настройка завершена, и плагин готов к разработке.

Step 2: Plugin structure

The golang.zabbix.com/sdk module, installed in the previous step, provides the necessary framework for plugin development and ensures all plugins have a consistent structure.

  1. Set up basic execution flow.

Start by defining the main execution flow of the plugin. Add the following code to main.go:

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

This establishes the basic execution flow for the plugin. The run function will later contain the core logic of the plugin.

  1. Explore the plugin interfaces.

A Zabbix agent 2 plugin shall be represented by a struct that implements interfaces from the golang.zabbix.com/sdk/plugin package:

  • Accessor - defines essential methods all plugins must implement, such as setting the plugin name and handling item key timeouts.
  • One or more of the following functional plugin interfaces:
    • Exporter - performs a poll and returns a value (or values), nothing, or an error; often used alongside the Collector interface.
    • Collector - manages the periodic collection of data.
    • Runner - defines plugin startup and shutdown procedures.
    • Watcher - allows to implement independent metric polling, bypassing the agent's internal scheduler; useful for trap-based or event-driven monitoring.
    • Configurator - defines how the plugin reads and applies its configuration settings.

You can either implement these interfaces yourself or use the default ones provided by the Zabbix Go SDK, modifying them as needed. This tutorial uses the default implementations.

  1. Create the plugin struct.

Now, import the plugin package and create a myIP struct that embeds the plugin.Base struct:

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

The myIP struct currently satisfies the Accessor interface. A method for implementing one of the functional plugin interfaces, the Exporter, will be added later in the tutorial.

Step 3: Define item keys

Your plugin needs the item keys to collect data and provide it to Zabbix server or proxy.

  1. Import errs package for error handling:
import "golang.zabbix.com/sdk/errs"
  1. Register item keys using the plugin.RegisterMetrics() function within the run() function:
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
       }

To register several item keys, repeat the parameters metric name and description for each metric. For example:

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

Step 4: Set up the handler

The handler facilitates communication between the agent and the plugin.

  1. Import the container package:
import "golang.zabbix.com/sdk/plugin/container"
  1. Indise the run() function add code to create and set up a 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
       }

Step 5: Implement data collection

Data collection is done via the Exporter interface, which describes the Export method:

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. Import the required packages for HTTP requests and response reading:
import (
           "io"
           "net/http"
       )
  1. Implement the Export method for the myIP struct:
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
       }

Step 6: Build and configure the plugin

  1. To build the plugin, run:
go mod tidy
       go build

This should create an executable myip in the current directory.

  1. Configure Zabbix agent 2 to use the plugin:
echo "Plugins.MyIP.System.Path=$PATH_TO_THE_MYIP_PLUGIN_EXECUTABLE" > /etc/zabbix_agent2.d/plugins.d/myip.conf

Replace $PATH_TO_THE_MYIP_PLUGIN_EXECUTABLE with the path to the myip created in step 5.

The plugin name in the configuration parameter name (MyIP in this tutorial) must match the plugin name defined in the plugin.RegisterMetrics() function.

  1. To test the plugin and its myip item, run:
zabbix_agent2 -c /etc/zabbix_agent2.conf -t myip

The output should contain an external IP address of your host and look similar to this:

myip                                          [s|192.0.2.0]

With that, you have created a simple loadable plugin for Zabbix agent 2. Congrats!

Complete source code

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
       }