这是一个分步教程,指导如何为 Zabbix agent 2 创建一个简单的可加载插件。
您也可以使用 示例仓库 作为模板或指南,来创建您自己的插件。
本教程演示如何创建一个新的可加载插件 MyIP。 该插件将实现一个监控项键值,myip,返回Zabbix agent 2运行所在主机的外部IP地址。
go.mod
文件以跟踪插件依赖项:golang.zabbix.com/sdk
):将 $LATEST_COMMIT_HASH
替换为 golang.zabbix.com/sdk
仓库中适当发布分支的最新 HEAD
提交哈希。 例如:
请注意,目前 golang.zabbix.com/sdk
的版本控制尚不支持,但未来可能会发生变化。
可以根据需要使用 go get
安装其他依赖项。
main.go
文件:现在初始设置已完成,插件已准备好进行开发。
在上一步中安装的 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
包中的接口:
您可以自行实现这些接口,或使用 Zabbix Go SDK 提供的默认接口,根据需要进行修改。 本教程使用默认实现。
现在,导入 plugin 包并创建一个 myIP
结构体,该结构体嵌入了 plugin.Base
结构体:
myIP
结构体当前满足 Accessor 接口。 在本教程的后续部分,将添加实现功能插件接口之一,即 Exporter 的方法。
您的插件需要监控项键值来收集数据并将其提供给 Zabbix server 或 proxy。
run()
函数中使用 plugin.RegisterMetrics()
函数注册监控项键值:func run() error {
p := &myIP{}
// 注册 `myip` 监控项键值。
err := plugin.RegisterMetrics(
p,
"MyIP", // 插件名称
"myip", // 监控项键值名称
"返回主机的 IP 地址。", // 监控项键值描述
)
if err != nil {
return errs.Wrap(err, "注册监控项失败")
}
return nil
}
要注册多个监控项键值,为每个监控项重复参数 metric name 和 description。 例如:
plugin.RegisterMetrics(&impl, "Myip", "metric.one", "Metric one description.", "metric.two", "Metric two description.")
"### 第 4 步:设置处理器
处理器促进了 agent 与插件之间的通信。
run()
函数内部添加代码以创建并设置处理器:func run() error {
p := &myIP{}
// 注册 `myip` 监控项键。
err := plugin.RegisterMetrics(
p,
""MyIP"", // 插件名称
""myip"", // 监控项键名称
""返回主机的 IP 地址。"", // 监控项键描述
)
if err != nil {
return errs.Wrap(err, ""注册监控项失败"")
}
// 创建一个新的处理器。
h, err := container.NewHandler(""MyIP"") // 插件名称
if err != nil {
return errs.Wrap(err, ""创建新处理器失败"")
}
// 设置日志记录,将插件的日志转发给 agent 。
// 可通过 p.Logger.Infof, p.Logger.Debugf 等访问。
p.Logger = h
// 启动插件执行。
// 阻塞直到从 agent 接收到终止请求。
err = h.Execute()
if err != nil {
return errs.Wrap(err, ""执行插件处理器失败"")
}
return nil
}
```"
### 步骤 5: 实现数据收集
数据收集通过导出器接口完成,该接口描述了 `Export` 方法:
```go
func Export(
key string, // 要收集的监控项键。
params []string, // 传递给监控项键的参数 (`myip[arg1, arg2]`)。
context ContextProvider // 关于监控项键数据收集的元数据。
) (any, error)
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("未知监控项键 %q", key)
}
// 日志将转发到 agent 2 日志。
p.Infof(
"收到处理 %q 键和 %d 个参数的请求",
key,
len(params),
)
// 收集数据并返回。
resp, err := http.Get("https://api.ipify.org")
if err != nil {
return nil, errs.Wrap(err, "获取 IP 地址失败")
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, errs.Wrap(err, "读取响应体失败")
}
return string(body), nil
}
这将在当前目录中创建一个可执行文件 myip
。
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
监控项,请运行:输出应包含主机的外部 IP 地址,看起来类似于以下内容:
myip [s|192.0.2.0]
至此,您已为 Zabbix agent 2 创建了一个简单的可加载插件。 恭喜!
"### 完整源代码
```go 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, ""注册指标失败"") }
// 创建一个新的处理器。 h, err := container.NewHandler(""MyIP"") // 插件名称 if err != nil { return errs.Wrap(err, ""创建新处理器失败"") }
// 设置日志记录,将插件的日志转发到 agent 。 // 可通过 p.Logger.Infof, p.Logger.Debugf 等访问。 p.Logger = h
// 启动插件执行。 // 阻塞直到收到 agent 的终止请求。 err = h.Execute() if err != nil { return errs.Wrap(err, ""执行插件处理器失败"") }
return nil }
func (p *myIP) Export( key string, params []string, context plugin.ContextProvider, ) (any, error) { // 插件可以基于 key
参数使用不同的数据收集逻辑。 // 此实现仅验证提供的 key
是否受支持。 if key != ""myip"" { return nil, errs.Errorf(""未知监控项键 %q"", key) }
// 日志将转发到 agent 的日志。 p.Infof( ""收到处理 %q 键的请求,参数数量为 %d"", key, len(params), )
// 收集数据并返回。
resp, err := http.Get(""https://api.ipify.org"") if err != nil { return nil, errs.Wrap(err, ""获取IP地址失败"") }
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body) if err != nil { return nil, errs.Wrap(err, ""读取响应体失败"") }
return string(body), nil } ```"