Se encuentra viendo la documentación de la versión en desarrollo, puede estar incompleta.
Esta página fue traducida automáticamente. Si detectas un error, selecciónalo y presiona Ctrl+Enter para informarlo a los editores.

Crear un plugin (tutorial)

Este es un tutorial paso a paso sobre cómo crear un plugin cargable simple para el agente Zabbix 2.

También puede utilizar el repositorio de ejemplo como plantilla o guía para crear sus propios plugins.

Qué crearás

Este tutorial demuestra cómo crear un nuevo plugin cargable MyIP. El plugin implementará una única clave de métrica, myip, que devuelve la dirección IP externa del equipo donde se está ejecutando el agente Zabbix 2.

Paso 1: Configuración

  1. Un plugin es un módulo estándar de Go. Comience inicializando el archivo go.mod en el directorio del plugin para rastrear las dependencias del plugin:
cd path/to/plugins/myip # Cambie al directorio de su plugin
       go mod init myip
  1. Instale la dependencia obligatoria Zabbix Go SDK (golang.zabbix.com/sdk):
go get golang.zabbix.com/sdk@$LATEST_COMMIT_HASH

Reemplace $LATEST_COMMIT_HASH con el último hash de commit HEAD del repositorio de golang.zabbix.com/sdk en la rama de lanzamiento correspondiente. Por ejemplo:

go get golang.zabbix.com/sdk@af85407

Tenga en cuenta que actualmente no se admite la gestión de versiones de golang.zabbix.com/sdk, pero esto puede cambiar en el futuro.

Las dependencias adicionales se pueden instalar según sea necesario usando go get.

  1. Cree un archivo vacío main.go para el código fuente del plugin:
touch main.go

Ahora la configuración inicial está completa y el plugin está listo para el desarrollo.

Paso 2: Estructura del plugin

El módulo golang.zabbix.com/sdk, instalado en el paso anterior, proporciona el marco necesario para el desarrollo de plugins y garantiza que todos los plugins tengan una estructura coherente.

  1. Configurar el flujo básico de ejecución.

Comience definiendo el flujo principal de ejecución del plugin. Añada el siguiente código a main.go:

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

Esto establece el flujo básico de ejecución para el plugin. La función run contendrá más adelante la lógica principal del plugin.

  1. Explorar las interfaces del plugin.

Un plugin de Zabbix agent 2 debe estar representado por una estructura que implemente interfaces del paquete golang.zabbix.com/sdk/plugin:

  • Accessor - define los métodos esenciales que todos los plugins deben implementar, como establecer el nombre del plugin y gestionar los timeouts de las claves de métricas.
  • Una o más de las siguientes interfaces funcionales de plugin:
    • Exporter - realiza una consulta y devuelve un valor (o valores), nada o un error; a menudo se utiliza junto con la interfaz Collector.
    • Collector - gestiona la recopilación periódica de datos.
    • Runner - define los procedimientos de inicio y apagado del plugin.
    • Watcher - permite implementar la consulta independiente de métricas, omitiendo el planificador interno del agente; útil para la monitorización basada en traps o eventos.
    • Configurator - define cómo el plugin lee y aplica su configuración.

Puede implementar estas interfaces usted mismo o utilizar las predeterminadas proporcionadas por el Zabbix Go SDK, modificándolas según sea necesario. Este tutorial utiliza las implementaciones predeterminadas.

  1. Crear la estructura del plugin.

Ahora, importe el paquete plugin y cree una estructura myIP que incluya la estructura plugin.Base:

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

La estructura myIP actualmente cumple con la interfaz Accessor. Más adelante en el tutorial se añadirá un método para implementar una de las interfaces funcionales del plugin, el Exporter.

Paso 3: Definir las claves de las métricas

Tu plugin necesita las claves de las métricas para recopilar datos y proporcionarlos al servidor o proxy de Zabbix.

  1. Importa el paquete errs para el manejo de errores:
import "golang.zabbix.com/sdk/errs"
  1. Registra las claves de las métricas usando la función plugin.RegisterMetrics() dentro de la función run():
func run() error {
           p := &myIP{}
       
           // Registrar la clave de la métrica `myip`.
           err := plugin.RegisterMetrics(
               p,
               "MyIP",                           // Nombre del plugin
               "myip",                           // Nombre de la clave de la métrica
               "Devuelve la dirección IP del equipo.", // Descripción de la clave de la métrica
           )
           if err != nil {
               return errs.Wrap(err, "no se pudo registrar las métricas")
           }
       
           return nil
       }

Para registrar varias claves de métricas, repite los parámetros nombre de la métrica y descripción para cada métrica. Por ejemplo:

plugin.RegisterMetrics(&impl, "Myip", "metric.one", "Descripción de la métrica uno.", "metric.two", "Descripción de la métrica dos.")

Paso 4: Configurar el handler

El handler facilita la comunicación entre el agente y el plugin.

  1. Importe el paquete container:
import "golang.zabbix.com/sdk/plugin/container"
  1. Dentro de la función run() agregue el código para crear y configurar un handler:
func run() error {
           p := &myIP{}
       
           // Registrar la clave de la métrica `myip`.
           err := plugin.RegisterMetrics(
               p,
               "MyIP",                           // Nombre del plugin
               "myip",                           // Nombre de la clave de la métrica
               "Devuelve la dirección IP del equipo.", // Descripción de la clave de la métrica
           )
           if err != nil {
               return errs.Wrap(err, "falló el registro de métricas")
           }
       
           // Crear un nuevo handler.
           h, err := container.NewHandler("MyIP") // Nombre del plugin
           if err != nil {
               return errs.Wrap(err, "falló la creación de un nuevo handler")
           }
       
           // Configurar el registro para reenviar los logs del plugin al agente.
           // Disponible a través de p.Logger.Infof, p.Logger.Debugf, etc.
           p.Logger = h
       
           // Iniciar la ejecución del plugin.
           // Se bloquea hasta que se recibe una solicitud de terminación del agente.
           err = h.Execute()
           if err != nil {
               return errs.Wrap(err, "falló la ejecución del handler del plugin")
           }
       
           return nil
       }

Paso 5: Implementar la recopilación de datos

La recopilación de datos se realiza a través de la interfaz Exporter, que describe el método Export:

func Export(
         key string,             // La clave de la métrica a recopilar.
         params []string,        // Argumentos pasados a la clave de la métrica (`myip[arg1, arg2]`).
         context ContextProvider // Metadatos sobre la recopilación de datos de la clave de la métrica.
       ) (any, error)
  1. Importe los paquetes necesarios para solicitudes HTTP y lectura de respuestas:
import (
           "io"
           "net/http"
       )
  1. Implemente el método Export para la estructura myIP:
func (p *myIP) Export(
           key string, params []string, context plugin.ContextProvider,
       ) (any, error) {
           // El plugin puede usar diferentes lógicas de recopilación de datos según el parámetro `key`.
           // Esta implementación solo verifica que la `key` proporcionada sea compatible.
           if key != "myip" {
               return nil, errs.Errorf("clave de métrica desconocida %q", key)
           }
       
           // El registro se enviará al registro del agente 2.
           p.Infof(
               "se recibió una solicitud para manejar la clave %q con %d parámetros",
               key,
               len(params),
           )
       
           // Recopile los datos y devuélvalos.
       
           resp, err := http.Get("https://api.ipify.org")
           if err != nil {
               return nil, errs.Wrap(err, "no se pudo obtener la dirección IP")
           }
       
           defer resp.Body.Close()
       
           body, err := io.ReadAll(resp.Body)
           if err != nil {
               return nil, errs.Wrap(err, "no se pudo leer el cuerpo de la respuesta")
           }
       
           return string(body), nil
       }

Paso 6: Compilar y configurar el plugin

  1. Para compilar el plugin, ejecute:
go mod tidy
       go build

Esto debería crear un ejecutable myip en el directorio actual.

  1. Configure el agente Zabbix 2 para usar el plugin:
echo "Plugins.MyIP.System.Path=$PATH_TO_THE_MYIP_PLUGIN_EXECUTABLE" > /etc/zabbix_agent2.d/plugins.d/myip.conf

Reemplace $PATH_TO_THE_MYIP_PLUGIN_EXECUTABLE con la ruta al myip creado en el paso 5.

El nombre del plugin en el parámetro de configuración (MyIP en este tutorial) debe coincidir con el nombre del plugin definido en la función plugin.RegisterMetrics().

  1. Para probar el plugin y su métrica myip, ejecute:
zabbix_agent2 -c /etc/zabbix_agent2.conf -t myip

La salida debe contener una dirección IP externa de su equipo y verse similar a esto:

myip                                          [s|192.0.2.0]

Con esto, ha creado un plugin simple y cargable para el agente Zabbix 2. ¡Felicidades!

Código fuente 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{}
       
           // Registrar la clave de métrica `myip`.
           err := plugin.RegisterMetrics(
               p,
               "MyIP",                           // Nombre del plugin
               "myip",                           // Nombre de la clave de métrica
               "Devuelve la dirección IP del equipo.", // Descripción de la clave de métrica
           )
           if err != nil {
               return errs.Wrap(err, "no se pudo registrar la métrica")
           }
       
           // Crear un nuevo handler.
           h, err := container.NewHandler("MyIP") // Nombre del plugin
           if err != nil {
               return errs.Wrap(err, "no se pudo crear el nuevo handler")
           }
       
           // Configurar el registro para reenviar los logs del plugin al agente.
           // Disponible a través de p.Logger.Infof, p.Logger.Debugf, etc.
           p.Logger = h
       
           // Iniciar la ejecución del plugin.
           // Bloquea hasta que se recibe una solicitud de terminación del agente.
           err = h.Execute()
           if err != nil {
               return errs.Wrap(err, "no se pudo ejecutar el handler del plugin")
           }
       
           return nil
       }
       
       func (p *myIP) Export(
           key string, params []string, context plugin.ContextProvider,
       ) (any, error) {
           // El plugin puede usar diferentes lógicas de recopilación de datos según el parámetro `key`.
           // Esta implementación solo verifica que la `key` proporcionada sea compatible.
           if key != "myip" {
               return nil, errs.Errorf("clave de métrica desconocida %q", key)
           }
       
           // El log se reenviará al log del agente 2.
           p.Infof(
               "se recibió una solicitud para manejar la clave %q con %d parámetros",
               key,
               len(params),
           )
       
           // Recopilar los datos y devolverlos.
       
           resp, err := http.Get("https://api.ipify.org")
           if err != nil {
               return nil, errs.Wrap(err, "no se pudo obtener la dirección IP")
           }
       
           defer resp.Body.Close()
       
           body, err := io.ReadAll(resp.Body)
           if err != nil {
               return nil, errs.Wrap(err, "no se pudo leer el cuerpo de la respuesta")
           }
       
           return string(body), nil
       }