创建插件
本页面面向有兴趣为 Zabbix agent 2 开发自有插件(独立数据采集器)的用户。
自定义插件可将 Zabbix agent 2 的功能扩展到内置插件和可加载插件所提供范围之外。
每个插件都是一个 Go 软件包,用于定义结构并实现一个或多个接口(Exporter、Configurator、Runner)。更多信息请参见插件接口和连接图。
本指南将帮助您创建一个自定义可加载插件。
如需更多指导,另请参见以下仓库:
您将创建的内容
这是一个分步教程,用于创建一个名为 MyIP 的简单可加载插件。
该插件将实现一个单独的监控项键(myip),用于返回 Zabbix agent 主机的外部 IP 地址。
第 1 步:设置
- 插件是一个标准的 Go 模块。
首先在插件目录中初始化
go.mod文件,以跟踪插件依赖项:
cd path/to/plugins/myip # 切换到您的插件目录
go mod init myip
- 安装必需的依赖项 Zabbix Go SDK(
golang.zabbix.com/sdk):
go get golang.zabbix.com/sdk@$LATEST_COMMIT_HASH
将 $LATEST_COMMIT_HASH 替换为 golang.zabbix.com/sdk repository 相应发布分支中最新 HEAD 提交的哈希值。
例如:
go get golang.zabbix.com/sdk@af85407
请注意,当前 golang.zabbix.com/sdk 不支持版本控制,但未来可能会有所变化。
可以根据需要使用 go get 安装其他依赖项。
- 为插件源代码创建一个空的
main.go文件:
touch main.go
现在初始设置已完成,插件已准备好进行开发。
第 2 步:插件结构
在上一步中安装的 golang.zabbix.com/sdk 模块为插件开发提供了必要的框架,并确保所有插件都具有一致的结构。
- 设置基本执行流程。
首先定义插件的主要执行流程。将以下代码添加到 main.go 中:
package main
func main() {
err := run()
if err != nil {
panic(err)
}
}
func run() error {
return nil
}
这为插件建立了基本的执行流程。
run 函数稍后将包含插件的核心逻辑。
- 了解插件接口。
Zabbix agent 2 插件应由一个结构体表示,该结构体实现 golang.zabbix.com/sdk/plugin 包中的接口:
- Accessor - 定义所有插件都必须实现的基本方法,例如设置插件名称以及处理监控项键超时。
- 以下一个或多个功能性插件接口:
- Exporter - 执行一次轮询并返回一个值(或多个值)、不返回任何内容,或返回错误;通常与 Collector 接口一起使用。
- Collector - 管理数据的周期性采集。
- Runner - 定义插件的启动和关闭过程。
- Watcher - 允许实现独立的指标轮询,绕过 agent 的内部调度器;适用于基于 trap 或事件驱动的监控。
- Configurator - 定义插件如何读取并应用其配置设置。
您既可以自行实现这些接口,也可以使用 Zabbix Go SDK 提供的默认实现,并根据需要进行修改。
本教程使用默认实现。
- 创建插件结构体。
现在,导入 plugin 包并创建一个嵌入 plugin.Base 结构体的 myIP 结构体:
import "golang.zabbix.com/sdk/plugin"
type myIP struct {
plugin.Base
}
目前,myIP 结构体满足 Accessor 接口。
在本教程的后续内容中,将添加一个用于实现功能性插件接口之一 Exporter 的方法。
第 3 步:定义监控项键值
您的插件需要监控项键值来收集数据,并将其提供给 Zabbix 服务器或 proxy。
- 导入 errs 包以进行错误处理:
import "golang.zabbix.com/sdk/errs"
- 在
run()函数中使用plugin.RegisterMetrics()函数注册监控项键值:
func run() error {
p := &myIP{}
// 注册 `myip` 监控项键值。
err := plugin.RegisterMetrics(
p,
"MyIP", // 插件名称
"myip", // 监控项键值名称
"Returns the host's IP address.", // 监控项键值描述
)
if err != nil {
return errs.Wrap(err, "failed to register metrics")
}
return nil
}
要注册多个监控项键值,请为每个指标重复使用参数 metric name 和 description。
例如:
plugin.RegisterMetrics(&impl, "Myip", "metric.one", "Metric one description.", "metric.two", "Metric two description.")
第 4 步:设置处理程序
处理程序用于促进 agent 与插件之间的通信。
- 导入 container 包:
import "golang.zabbix.com/sdk/plugin/container"
- 在
run()函数中添加用于创建和设置处理程序的代码:
func run() error {
p := &myIP{}
// 注册 `myip` 监控项键。
err := plugin.RegisterMetrics(
p,
"MyIP", // 插件名称
"myip", // 监控项键名称
"返回主机的 IP 地址。", // 监控项键描述
)
if err != nil {
return errs.Wrap(err, "failed to register metrics")
}
// 创建新的处理程序。
h, err := container.NewHandler("MyIP") // 插件名称
if err != nil {
return errs.Wrap(err, "failed to create new handler")
}
// 设置日志记录,将日志从插件转发到 agent。
// 可通过 p.Logger.Infof、p.Logger.Debugf 等方式使用。
p.Logger = h
// 启动插件执行。
// 在收到来自 agent 的终止请求之前会一直阻塞。
err = h.Execute()
if err != nil {
return errs.Wrap(err, "failed to execute plugin handler")
}
return nil
}
第 5 步:实现数据采集
数据采集通过 Exporter 接口完成,该接口描述了 Export 方法:
func Export(
key string, // 要采集的监控项键值。
params []string, // 传递给监控项键值的参数(`myip[arg1, arg2]`)。
context ContextProvider // 有关监控项键值数据采集的元数据。
) (any, error)
- 导入 HTTP 请求和响应读取所需的软件包:
import (
"io"
"net/http"
)
- 为
myIP结构体实现Export方法:
func (p *myIP) Export(
key string, params []string, context plugin.ContextProvider,
) (any, error) {
// 插件可以根据 `key` 参数使用不同的数据采集逻辑。
// 此实现仅验证所提供的 `key` 是否受支持。
if key != "myip" {
return nil, errs.Errorf("unknown item key %q", key)
}
// 日志将被转发到 agent 2 日志中。
p.Infof(
"received request to handle %q key with %d parameters",
key,
len(params),
)
// 采集数据并返回。
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:构建并配置插件
- 要构建插件,请运行:
go mod tidy
go build
这将在当前目录中创建一个可执行文件 myip。
- 配置 Zabbix agent 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 替换为步骤 5 中创建的 myip 的路径。
配置参数名称中的插件名称(本教程中为 MyIP)必须与 plugin.RegisterMetrics() 函数中定义的插件名称一致。
- 要测试该插件及其
myip监控项,请运行:
zabbix_agent2 -c /etc/zabbix_agent2.conf -t myip
输出应包含您的主机的外部 IP 地址,并且看起来类似于:
myip [s|192.0.2.0]
至此,您已经为 Zabbix agent 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{}
// 注册 `myip` 监控项键。
err := plugin.RegisterMetrics(
p,
"MyIP", // 插件名称
"myip", // 监控项键名称
"返回主机的 IP 地址。", // 监控项键描述
)
if err != nil {
return errs.Wrap(err, "failed to register metrics")
}
// 创建一个新的处理程序。
h, err := container.NewHandler("MyIP") // 插件名称
if err != nil {
return errs.Wrap(err, "failed to create new handler")
}
// 设置日志记录,将插件中的日志转发到 agent。
// 可通过 p.Logger.Infof、p.Logger.Debugf 等使用。
p.Logger = h
// 启动插件执行。
// 在收到来自 agent 的终止请求之前会一直阻塞。
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) {
// 插件可以根据 `key` 参数使用不同的数据采集逻辑。
// 此实现仅验证提供的 `key` 是否受支持。
if key != "myip" {
return nil, errs.Errorf("unknown item key %q", key)
}
// 日志将被转发到 agent 2 日志中。
p.Infof(
"received request to handle %q key with %d parameters",
key,
len(params),
)
// 采集数据并返回。
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
}