news 2026/9/14 7:11:59

Telegraf unbound 输入插件:采集 Unbound DNS 解析器统计指标的完整配置与实现解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Telegraf unbound 输入插件:采集 Unbound DNS 解析器统计指标的完整配置与实现解析

Telegraf unbound 输入插件:采集 Unbound DNS 解析器统计指标的完整配置与实现解析

【免费下载链接】telegrafAgent for collecting, processing, aggregating, and writing metrics, logs, and other arbitrary data.项目地址: https://gitcode.com/GitHub_Trending/te/telegraf

本文基于 Telegraf 仓库中plugins/inputs/unbound/README.md及其配套源码 unbound.go、sample.conf、unbound_test.go 整理。该插件自 Telegraf v1.5.0 引入,适用于所有平台(server、network 类插件)。它通过调用unbound-control命令行工具,周期性采集 Unbound 递归 DNS 解析器的查询量、缓存命中率、递归时延、内存占用、DNSSEC 验证结果等统计指标,并可开启线程级指标与递归查询时延直方图,帮助你在 DNS 基础设施监控中定位解析性能退化、缓存异常与验证失败等问题。读完本文,你可以完成插件的配置、权限设置(组成员或 sudo 两种方式),理解全部配置项的实际行为,以及从源码层面弄清指标是如何从unbound-control的原始输出解析成 Telegraf 测量值的。

工作原理:shell out 到 unbound-control

从源码看,该插件并非通过 socket 直接读取统计数据,而是在每次采集时"shell out"执行unbound-control二进制,参数固定为stats_noreset(读取统计但不重置计数器)。核心采集逻辑在 Gather 方法与 unboundRunner 中,调用链为:

  1. init()将插件以名称unbound注册到输入插件注册表(默认值:Binary=/usr/sbin/unbound-controlTimeout=1sUseSudo=falseServer=""ThreadAsTag=falseConfigFile=""Histogram=false),注册入口见 init 函数;
  2. 若配置了server,先解析出host:port。由于unbound-control要求 IP 地址,插件会用net.Resolver对该主机名做一次带超时(timeout配置)的 DNS 查询,取第一个 IP,再拼成-s IP@port参数(未显式指定端口时,不追加端口参数);
  3. 若配置了config_file,追加-c参数指向 unbound 配置文件;
  4. use_sudo = true,则改用sudo <binary> ...形式执行;
  5. 通过 internal.RunTimeout 启动命令并按timeout限制运行时间,超时会尝试杀死进程;标准输出即key=value形式的统计行。

stats_noreset意味着采集是无副作用的:计数器保持累计值,插件重启或采集间隔变化都不会清零历史统计,这对计算速率型指标(如每秒查询量)是必要的。

配置

插件的完整示例配置如下(与仓库中 sample.conf 一致):

# A plugin to collect stats from the Unbound DNS resolver [[inputs.unbound]] ## Address of server to connect to, read from unbound conf default, optionally ':port' ## Will lookup IP if given a hostname server = "127.0.0.1:8953" ## If running as a restricted user you can prepend sudo for additional access: # use_sudo = false ## The default location of the unbound-control binary can be overridden with: # binary = "/usr/sbin/unbound-control" ## The default location of the unbound config file can be overridden with: # config_file = "/etc/unbound/unbound.conf" ## The default timeout of 1s can be overridden with: # timeout = "1s" ## When set to true, thread metrics are tagged with the thread id. ## ## The default is false for backwards compatibility, and will be changed to ## true in a future version. It is recommended to set to true on new ## deployments. thread_as_tag = false ## Collect metrics with the histogram of the recursive query times: # histogram = false

各配置项的含义与源码行为对照如下:

配置项默认值说明
server""(读取 unbound 自身配置)unbound-control连接的地址,可带:port。给主机名时插件会先做 DNS 解析再传 IP 给unbound-control(源码强制要求 IP)
use_sudofalsetrue时以sudo前缀执行二进制,用于受限用户场景
binary/usr/sbin/unbound-controlunbound-control可执行文件路径
config_file""传给unbound-control -c的 unbound 配置文件路径,默认读取系统默认配置
timeout1s命令执行超时,同时也是主机名解析的上下文超时;超时后进程会被尝试终止
thread_as_tagfalsetrue时,逐线程统计从unbound测量中拆出,写入带thread标签的unbound_threads测量。默认false是向后兼容考虑,官方建议新部署设为true
histogramfalsetrue时额外采集递归查询时延直方图字段。注意:直方图统计要求 unbound 侧开启extended-statistics: yes

除插件自身配置外,它同样支持 Telegraf 的全局与插件级配置选项(字段/标签过滤、别名、插件排序等),参见 docs/CONFIGURATION.md 的 Plugins 章节;README 中该部分由 docs/includes/plugin_config.md 生成嵌入。

权限设置

文档特别强调:插件依赖unbound-control,该工具通常需要额外权限才能成功执行。取决于执行插件的 telegraf 用户/组权限,你可能需要调整组成员、设置 ACL,或使用 sudo。文档给出两种方案。

方案一:组成员(推荐)

将 telegraf 用户加入 unbound 组:

$ groups telegraf telegraf : telegraf $ usermod -a -G unbound telegraf $ groups telegraf telegraf : telegraf unbound

这是官方文档推荐的方式,配置文件中无需任何额外项。

方案二:sudo 授权

配置中开启:

[[inputs.unbound]] use_sudo = true

并在 sudoers 中追加(visudo):

$ visudo # Add the following line: Cmnd_Alias UNBOUNDCTL = /usr/sbin/unbound-control telegraf ALL=(ALL) NOPASSWD: UNBOUNDCTL Defaults!UNBOUNDCTL !logfile, !syslog, !pam_session

从源码看,use_sudo = true时命令会被重组为sudo /usr/sbin/unbound-control [-s IP@port] [-c config] stats_noreset(见 unboundRunner),因此 sudoers 中放行的可执行文件路径必须与binary配置一致。文档最后建议"选择你认为最合适的方案"。

输出指标

插件输出的字段与 unbound 配置相关:基础统计随unbound-control stats_noreset输出,扩展统计(extended statistics)需要在 unbound 配置中开启extended-statistics: yes才会出现。原始统计名中的点号会被替换为下划线。下面列出 README 给出的完整字段清单。

unbound(整机级测量)

- unbound - fields: total_num_queries total_num_cachehits total_num_cachemiss total_num_prefetch total_num_recursivereplies total_requestlist_avg total_requestlist_max total_requestlist_overwritten total_requestlist_exceeded total_requestlist_current_all total_requestlist_current_user total_recursion_time_avg total_recursion_time_median time_now time_up time_elapsed mem_total_sbrk mem_cache_rrset mem_cache_message mem_mod_iterator mem_mod_validator num_query_type_A num_query_type_PTR num_query_type_TXT num_query_type_AAAA num_query_type_SRV num_query_type_ANY num_query_class_IN num_query_opcode_QUERY num_query_tcp num_query_ipv6 num_query_flags_QR num_query_flags_AA num_query_flags_TC num_query_flags_RD num_query_flags_RA num_query_flags_Z num_query_flags_AD num_query_flags_CD num_query_edns_present num_query_edns_DO num_answer_rcode_NOERROR num_answer_rcode_SERVFAIL num_answer_rcode_NXDOMAIN num_answer_rcode_nodata num_answer_secure num_answer_bogus num_rrset_bogus unwanted_queries unwanted_replies

字段大致分组:total_*为查询/缓存/递归总量与请求队列水位;time_*为时间戳与运行时长;mem_*为内存模块占用;num_query_*按查询类型、class、opcode、TCP/IPv6、DNS 头部标志位与 EDNS 维度计数;num_answer_*为应答 rcode 与 DNSSEC 验证结果(secure/bogus);unwanted_*为被拒绝的查询与应答。

unbound_threads(thread_as_tag = true 时)

- unbound_threads - tags: - thread - fields: - num_queries - num_cachehits - num_cachemiss - num_prefetch - num_recursivereplies - requestlist_avg - requestlist_max - requestlist_overwritten - requestlist_exceeded - requestlist_current_all - requestlist_current_user - recursion_time_avg - recursion_time_median

histogram 字段(histogram = true 时)

开启histogram后,额外采集递归查询时延直方图,字段名表示每个 bin 的下界:

- unbound: - fields: histogram_.000000 histogram_.000001 histogram_.000002 histogram_.000004 histogram_.000008 histogram_.000016 histogram_.000032 histogram_.000064 histogram_.000128 histogram_.000256 histogram_.000512 histogram_.001024 histogram_.002048 histogram_.004096 histogram_.008192 histogram_.016384 histogram_.032768 histogram_.065536 histogram_.131072 histogram_.262144 histogram_.524288 histogram_1.000000 histogram_2.000000 histogram_4.000000 histogram_8.000000 histogram_16.000000 histogram_32.000000 histogram_64.000000 histogram_128.000000 histogram_256.000000 histogram_512.000000 histogram_1024.000000 histogram_2048.000000 histogram_4096.000000 histogram_8192.000000 histogram_16384.000000 histogram_32768.000000 histogram_65536.000000 histogram_131072.000000 histogram_262144.000000

示例输出

unbound,host=localhost total_requestlist_avg=0,total_requestlist_exceeded=0,total_requestlist_overwritten=0,total_requestlist_current_user=0,total_recursion_time_avg=0.029186,total_tcpusage=0,total_num_queries=51,total_num_queries_ip_ratelimited=0,total_num_recursivereplies=6,total_requestlist_max=0,time_now=1522804978.784814,time_elapsed=310.435217,total_num_cachemiss=6,total_num_zero_ttl=0,time_up=310.435217,total_num_cachehits=45,total_num_prefetch=0,total_requestlist_current_all=0,total_recursion_time_median=0.016384 1522804979000000000 unbound_threads,host=localhost,thread=0 num_queries_ip_ratelimited=0,requestlist_current_user=0,recursion_time_avg=0.029186,num_prefetch=0,requestlist_overwritten=0,requestlist_exceeded=0,requestlist_current_all=0,tcpusage=0,num_cachehits=37,num_cachemiss=6,num_recursivereplies=6,requestlist_avg=0,num_queries=43,num_zero_ttl=0,requestlist_max=0,recursion_time_median=0.032768 1522804979000000000 unbound_threads,host=localhost,thread=1 num_zero_ttl=0,recursion_time_avg=0,num_queries_ip_ratelimited=0,num_cachehits=8,num_prefetch=0,requestlist_exceeded=0,recursion_time_median=0,tcpusage=0,num_cachemiss=0,num_recursivereplies=0,requestlist_max=0,requestlist_overwritten=0,requestlist_current_user=0,num_queries=8,requestlist_avg=0,requestlist_current_all=0 1522804979000000000

注意示例输出中的total_tcpusagetotal_num_queries_ip_ratelimitednum_zero_ttl等字段:README 的静态清单描述的是较基础的统计集,而实际输出还包含 unbound 版本相关的扩展字段(如 TCP 使用率、IP 限速、零 TTL 计数)。这是因为插件对stats_noreset的输出生成式解析——任何形如key=value且值可解析为数字的行都会成为字段,所以新版 Unbound 增加统计项时无需改动插件代码即可自动出现。

源码级实现细节

输出解析与容错

Gather 用bufio.Scanner逐行读取命令输出,按第一个=切分键值:

  • 每行必须恰好切出两段(len(cols) == 2),否则整行静默跳过;
  • 值用strconv.ParseFloat(value, 64)解析,非数字时通过acc.AddError记录错误(expected a numerical value for ...)并跳过该行——注意这不会使整次采集失败,而是部分容错;
  • 其余统计名统一strings.ReplaceAll(stat, ".", "_")后进入unbound测量。

这种"宽松解析"与测试文件中的断言一致:TestParseFullOutput 用一段真实的unbound-control输出(63 个字段)验证默认模式;TestParseFullOutputHistogram 验证开启直方图后字段数变为 103(恰好多出 40 个histogram_*字段);TestParseFullOutputThreadAsTag 与 TestParseFullOutputThreadAsTagHistogram 验证线程拆分后的两个测量。

thread_as_tag 的拆分规则

ThreadAsTagtrue时,凡以thread开头的统计名(如thread0.num.queries)会被拆分:去掉thread前缀的部分(0)作为thread标签值,剩余 token 用下划线连接作为字段名(num_queries),归入unbound_threads测量(见 Gather 中的线程分支 与 unbound_threads 写入)。有两点值得注意:

  1. 线程号必须能解析为整数,否则该行留在unbound测量中按普通点号替换处理;
  2. 该分支只在ThreadAsTag开启时生效。默认false时,thread0.num.queries这类字段会以thread0_num_queries形式平铺在unbound测量里——测试期望值(如thread0_num_queries)证实了这一点。这也解释了 README 中"默认 false 为向后兼容,未来版本将改为 true,新部署建议设为 true"的含义。

histogram 的命名归一化

Unbound 扩展统计中直方图行形如histogram.000000.000512.to.000000.001024=5503(区间下界.to.上界)。插件的处理是(见 histogram 分支):

  • 先判断是否以histogram.开头;只有histogram = true时才转换,否则该行被完全丢弃(不会以其他形式出现);
  • .to.之前的部分作为 bin 下界,再TrimLeft(suffix, "0")去掉前导零;若结果以.开头(原下界全零,如000000.000000)则补回一个0,得到histogram_.000000
  • 因此histogram.000000.000512.to.000000.001024→ 字段histogram_.000512histogram.000001.000000.to.000002.000000histogram_1.000000。测试期望表 parsedFullOutputHistogram 中histogram_.000000=20histogram_1.000000=136等值可以直接对照验证。

另外,直方图行即使histogram = false也会被识别为该分支(只是不采集),不会混入unbound普通字段——从分支顺序看,histogram.前缀判断先于通用的点号替换分支执行。

超时控制

命令执行统一走 internal.RunTimeout:先Start,再WaitTimeout;文档注释明确"超时会尝试杀死进程"。因此timeout同时约束 DNS 主机名解析(resolver 上下文)与命令本身运行时长,对守护进程卡死的unbound-control起到兜底作用。

采集失败的表现

unboundRunner返回的错误(主机名解析失败、无 IP、命令执行失败/超时)会被Gather包装为error gathering metrics: ...向上返回,导致本次采集记为错误并进入插件错误计数,而不是部分写入。这一点与行级解析错误(AddError后继续)不同:进程级失败整次丢弃,行级失败只丢该行。

验证采集

可以使用--test参数只运行输入插件并输出到 stdout(见 docs/COMMANDS_AND_FLAGS.md 中的说明),快速验证插件配置与权限是否正确:

telegraf --config /etc/telegraf/telegraf.conf --test

若权限不足,通常会看到error running unbound-control ...之类的错误;若server主机名无法解析,则会看到error looking up ip for server ...error no ip for server ...(均出自 unboundRunner)。正常时输出形如上文"示例输出"中的unbound,host=...行。

小结与注意事项

  • 插件本质是unbound-control stats_noreset的包装器:二进制路径、配置文件、sudo 前缀、目标地址都是对这条命令的参数拼装,理解命令即理解插件;
  • server填主机名可以,但插件会替你做一次 DNS 解析,若该解析本身依赖被监控的 Unbound,要留意采集链路不形成自我依赖故障放大;
  • 新部署建议thread_as_tag = true,以获得带thread标签的unbound_threads测量,避免线程字段平铺在unbound上;
  • 需要直方图时在 unbound 配置中开启extended-statistics: yes,并在插件中设置histogram = true,字段数会从 63 增至 103(以测试期望为准);
  • 权限二选一:加入 unbound 组(推荐)或按文档配置 sudoers 白名单,后者需保证binary路径与 sudoers 中放行路径一致;
  • 解析是"所见即所得"的宽松模式,Unbound 版本升级带来的新统计项会自动映射为字段(点换下划线),排查字段缺失时优先核对 unbound 侧extended-statistics配置,而不是插件本身。

【免费下载链接】telegrafAgent for collecting, processing, aggregating, and writing metrics, logs, and other arbitrary data.项目地址: https://gitcode.com/GitHub_Trending/te/telegraf

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 7:11:23

ToF相机技术全解:从测距原理到工业应用落地

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 7:10:18

移动硬盘提示需要格式化怎么办?数据恢复原理与实操指南

移动硬盘插上电脑&#xff0c;屏幕弹出一句“使用驱动器中的光盘之前需要格式化”&#xff0c;这一瞬间的心跳加速&#xff0c;我太熟悉了。做了这么多年数据恢复&#xff0c;几乎每个月都有朋友被这行字吓得语无伦次&#xff0c;急急忙忙来问我&#xff1a;“我是不是应该点格…

作者头像 李华
网站建设 2026/9/14 7:08:37

Cursor接入统一API网关:从多Key混乱到模型路由实践

我最初是在一个团队内部被问到“你们到底给 Cursor 配了多少个 API Key”这种问题&#xff0c;才开始正视 AI 编程工具已经严重碎片化的现实。桌面端有 Cursor、Continue 和一堆 VS Code 插件&#xff0c;终端里有各种 Agent CLI&#xff0c;每个工具都要求填一个模型服务的 Ke…

作者头像 李华
网站建设 2026/9/14 7:06:58

Libvio.link影视爬虫实战:反爬策略与架构设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华