Telegraf kernel_vmstat 输入插件实战指南:从 /proc/vmstat 采集 Linux 内核虚拟内存统计
【免费下载链接】telegrafAgent for collecting, processing, aggregating, and writing metrics, logs, and other arbitrary data.项目地址: https://gitcode.com/GitHub_Trending/te/telegraf
本指南以 Telegraf 内置的kernel_vmstat输入插件为核心,讲解它如何通过读取 Linux 内核虚拟文件系统/proc/vmstat获取虚拟内存、NUMA、页回收、内存压缩(compaction)、透明大页(THP)等 90 余项内核级计数器,并转换为kernel_vmstat测量(measurement)输出。读完本文,你将掌握该插件的启用配置、字段含义分类、底层采集原理与异常处理行为,并能在内存压力、内存碎片化、swap 抖动等场景中利用这些指标进行监控与告警。该插件自 Telegraf v1.0.0 起提供,属于system分类,且仅支持 Linux 平台。
插件概述与数据来源
kernel_vmstat插件是一个"零配置"的系统级输入插件。它不做任何网络请求、不执行外部命令,唯一的数据来源就是内核虚拟文件系统中的/proc/vmstat文件。该文件由内核在运行时维护,每一行都以统计项名称 数值的形式呈现,例如:
nr_free_pages 78730 pgpgin 219717626 pswpin 2092关于/proc/vmstat中可用字段的完整列表,可查阅 proc 手册页(proc(5) 的/proc/vmstat一节);而各字段的详细语义说明则参见 vmstat 手册页(vmstat(8))。插件本身的作用是:读取该文件、逐行解析、将每个计数器转换为整数类型的 field,并以kernel_vmstat为测量名写入 accumulator。
安装与启用
在 Telegraf 中,kernel_vmstat与所有内置输入插件一样,通过注册机制挂载到插件系统。其注册入口位于 kernel_vmstat.go 的init()函数中:
func init() { inputs.Add("kernel_vmstat", func() telegraf.Input { return &KernelVmstat{ statFile: "/proc/vmstat", } }) }在默认(未裁剪)构建中,插件由 plugins/inputs/all/kernel_vmstat.go 自动导入并注册;若使用custom_builder定制构建,则需要确保构建标签包含inputs或inputs.kernel_vmstat。启用后,Telegraf 会按[agent]段配置的interval(默认 10s)周期性调用该插件的Gather方法。
配置方法
该插件没有任何专属配置项,配置节几乎为空。完整的配置样例见 sample.conf,可直接复制使用:
# Get kernel statistics from /proc/vmstat # This plugin ONLY supports Linux [[inputs.kernel_vmstat]] # no configuration需要注意的是,插件与所有 Telegraf 插件一样,还支持一组全局通用配置,用于修改指标、标签和字段,创建别名以及配置插件执行顺序,详情参见 CONFIGURATION.md。常用做法包括:
- 通过
name_override重命名测量; - 通过
tags为该插件的指标统一附加标签(如host); - 通过
fieldpass/fielddrop只保留或剔除部分字段——由于本插件输出字段多达 90 余项,若只需关注其中某几项(如内存压力相关指标),利用fieldpass可以显著降低指标基数; - 通过
interval覆盖全局采集周期,例如在高频抖动排查场景下单独调高采样频率。
配置文件写好之后,通过--config指定配置文件、或放入--config-directory指向的目录(Linux 下默认/etc/telegraf/telegraf.d)即可生效,具体可参考 CONFIGURATION.md。
底层实现原理
从源码结构看,插件的核心实现非常精简,整个采集逻辑集中在 kernel_vmstat.go 中,可概括为以下三步:
1. 读取 /proc/vmstat
getProcVmstat()先通过os.Stat检查目标文件是否存在。由于该文件路径被硬编码为/proc/vmstat(在init()中赋值给statFile字段),如果文件不存在,会返回形如kernel_vmstat: /proc/vmstat does not exist的错误;随后通过os.ReadFile一次性读入文件全部内容。从源码结构看,statFile字段仅被测试代码替换为临时文件,生产运行中始终指向系统真实路径。
2. 解析键值对
Gather使用bytes.Fields(data)按空白字符切分整个文件内容,得到一个交替排列的数组:{"stat1_name", "stat1_value", "stat2_name", "stat2_value", ...}。代码只取偶数下标作为字段名,并取紧邻的下一个元素作为值,通过strconv.ParseInt(..., 10, 64)将值解析为 64 位整数(见 kernel_vmstat.go):
dataFields := bytes.Fields(data) for i, field := range dataFields { if i%2 == 0 { m, err := strconv.ParseInt(string(dataFields[i+1]), 10, 64) if err != nil { return err } fields[string(field)] = m } }这意味着:所有字段均为整型(integer),且字段名与/proc/vmstat中的原始计数器名称完全一致,不做任何重命名、换算或单位转换。
3. 写入 accumulator
解析完成后,通过acc.AddFields("kernel_vmstat", fields, make(map[string]string))输出一个不带标签的测量。正因为不附加任何标签,该测量的 series 基数非常低,适合长时间高频采集。
非 Linux 平台行为
插件通过 Go 构建标签区分平台:在非 Linux 平台编译的是 kernel_vmstat_notlinux.go,其Init()会输出一条Current platform is not supported的警告日志,Gather直接返回 nil 而不产生任何指标。这印证了 README 中"本插件仅支持 Linux"的声明——在 macOS、Windows 等平台即使配置了该插件也不会报错,但不会有数据输出。
指标字段(Metrics)完整清单
kernel_vmstat测量包含以下字段(全部为 integer 类型),字段名与/proc/vmstat原始名称一致。依据内核语义可大致分为几类:
内存状态(nr_* 系列)
| 字段名 | 说明 |
|---|---|
| nr_free_pages | 空闲页数 |
| nr_inactive_anon / nr_active_anon | 匿名页活跃/非活跃链表页数 |
| nr_inactive_file / nr_active_file | 文件页活跃/非活跃链表页数 |
| nr_unevictable / nr_mlock | 不可回收页 / 被 mlock 锁定的页 |
| nr_anon_pages / nr_mapped | 匿名页 / 映射到用户空间的页 |
| nr_file_pages / nr_dirty | 文件页 / 脏页 |
| nr_writeback / nr_writeback_temp | 正在回写 / 临时回写的页 |
| nr_slab_reclaimable / nr_slab_unreclaimable | 可回收 / 不可回收的 slab 页 |
| nr_page_table_pages / nr_kernel_stack | 页表占用页数 / 内核栈页数 |
| nr_unstable / nr_bounce | 不稳定页 / bounce buffer 页 |
| nr_vmscan_write | 页回收过程中被写回的页 |
| nr_isolated_anon / nr_isolated_file | 处于隔离状态的匿名/文件页 |
| nr_shmem | tmpfs/shmem 页 |
| nr_anon_transparent_hugepages | 匿名透明大页数量 |
NUMA 统计
| 字段名 | 说明 |
|---|---|
| numa_hit / numa_miss | 分配命中所期望节点 / 未命中 |
| numa_foreign | 分配给其他节点的次数 |
| numa_interleave / numa_local / numa_other | 交错分配 / 本地节点分配 / 远程节点分配 |
分页与交换
| 字段名 | 说明 |
|---|---|
| pgpgin / pgpgout | 从磁盘读入 / 写出的页数 |
| pswpin / pswpout | 从 swap 换入 / 换出的页数 |
| pgalloc_dma / pgalloc_dma32 / pgalloc_normal / pgalloc_movable | 各内存 zone 的页分配数 |
| pgfree / pgactivate / pgdeactivate | 释放页 / 激活 / 去激活 |
| pgfault / pgmajfault | 缺页中断总数 / 主缺页中断数 |
| pgrefill_dma / pgrefill_dma32 / pgrefill_normal / pgrefill_movable | 各 zone 页回收填充数 |
| pgsteal_dma / pgsteal_dma32 / pgsteal_normal / pgsteal_movable | 各 zone 回收窃取数 |
| pgscan_kswapd_dma / pgscan_kswapd_dma32 / pgscan_kswapd_normal / pgscan_kswapd_movable | kswapd 在各 zone 扫描数 |
| pgscan_direct_dma / pgscan_direct_dma32 / pgscan_direct_normal / pgscan_direct_movable | 直接回收在各 zone 扫描数 |
| zone_reclaim_failed | zone 回收失败次数 |
| pginodesteal | 节点间窃取的页数 |
| slabs_scanned | 扫描的 slab 数量 |
内存回收(kswapd / 直接回收)
| 字段名 | 说明 |
|---|---|
| kswapd_steal / kswapd_inodesteal | kswapd 窃取页 / 窃取 inode 相关页 |
| kswapd_low_wmark_hit_quickly / kswapd_high_wmark_hit_quickly | 快速触及 low/high 水位线次数 |
| kswapd_skip_congestion_wait | kswapd 跳过拥塞等待次数 |
| pageoutrun | 触发页回收的次数 |
| allocstall | 分配因内存不足而停滞的次数 |
| pgrotated | 页回收时被旋转(重新激活)的页数 |
内存压缩(Compaction)
| 字段名 | 说明 |
|---|---|
| compact_blocks_moved / compact_pages_moved | 移动的块 / 页数 |
| compact_pagemigrate_failed | 页迁移失败次数 |
| compact_stall | 因分配触发压缩而停滞的次数 |
| compact_fail / compact_success | 压缩失败 / 成功次数 |
| htlb_buddy_alloc_success / htlb_buddy_alloc_fail | 大页伙伴分配成功 / 失败 |
不可换页(Unevictable)与透明大页(THP)
| 字段名 | 说明 |
|---|---|
| unevictable_pgs_culled / unevictable_pgs_scanned / unevictable_pgs_rescued | 不可换页剔除 / 扫描 / 拯救 |
| unevictable_pgs_mlocked / unevictable_pgs_munlocked | mlock 锁定 / 解锁页数 |
| unevictable_pgs_cleared / unevictable_pgs_stranded / unevictable_pgs_mlockfreed | 清除 / 遗落 / mlock 释放页数 |
| thp_fault_alloc / thp_fault_fallback | THP 缺页成功分配 / 回退 |
| thp_collapse_alloc / thp_collapse_alloc_failed | THP 折叠成功 / 失败 |
| thp_split | THP 拆分数 |
字段清单的完整权威版本以 plugins/inputs/kernel_vmstat/README.md 的 Metrics 一节为准。
示例输出
在某台运行中的 Linux 主机上,插件的输出形如下方 line protocol(注意所有值均带i后缀,即整数类型;末尾为纳秒时间戳):
kernel_vmstat allocstall=81496i,compact_blocks_moved=238196i,compact_fail=135220i,compact_pagemigrate_failed=0i,compact_pages_moved=6370588i,compact_stall=142092i,compact_success=6872i,htlb_buddy_alloc_fail=0i,htlb_buddy_alloc_success=0i,kswapd_high_wmark_hit_quickly=25439i,kswapd_inodesteal=29770874i,kswapd_low_wmark_hit_quickly=8756i,kswapd_skip_congestion_wait=0i,kswapd_steal=291534428i,nr_active_anon=2515657i,nr_active_file=2244914i,nr_anon_pages=1358675i,nr_anon_transparent_hugepages=2034i,nr_bounce=0i,nr_dirty=5690i,nr_file_pages=5153546i,nr_free_pages=78730i,nr_inactive_anon=426259i,nr_inactive_file=2366791i,nr_isolated_anon=0i,nr_isolated_file=0i,nr_kernel_stack=579i,nr_mapped=558821i,nr_mlock=0i,nr_page_table_pages=11115i,nr_shmem=541689i,nr_slab_reclaimable=459806i,nr_slab_unreclaimable=47859i,nr_unevictable=0i,nr_unstable=0i,nr_vmscan_write=6206i,nr_writeback=0i,nr_writeback_temp=0i,numa_foreign=0i,numa_hit=5113399878i,numa_interleave=35793i,numa_local=5113399878i,numa_miss=0i,numa_other=0i,pageoutrun=505006i,pgactivate=375664931i,pgalloc_dma=0i,pgalloc_dma32=122480220i,pgalloc_movable=0i,pgalloc_normal=5233176719i,pgdeactivate=122735906i,pgfault=8699921410i,pgfree=5359765021i,pginodesteal=9188431i,pgmajfault=122210i,pgpgin=219717626i,pgpgout=3495885510i,pgrefill_dma=0i,pgrefill_dma32=1180010i,pgrefill_movable=0i,pgrefill_normal=119866676i,pgrotated=60620i,pgscan_direct_dma=0i,pgscan_direct_dma32=12256i,pgscan_direct_movable=0i,pgscan_direct_normal=31501600i,pgscan_kswapd_dma=0i,pgscan_kswapd_dma32=4480608i,pgscan_kswapd_movable=0i,pgscan_kswapd_normal=287857984i,pgsteal_dma=0i,pgsteal_dma32=4466436i,pgsteal_movable=0i,pgsteal_normal=318463755i,pswpin=2092i,pswpout=6206i,slabs_scanned=93775616i,thp_collapse_alloc=24857i,thp_collapse_alloc_failed=102214i,thp_fault_alloc=346219i,thp_fault_fallback=895453i,thp_split=9817i,unevictable_pgs_cleared=0i,unevictable_pgs_culled=1531i,unevictable_pgs_mlocked=6988i,unevictable_pgs_mlockfreed=0i,unevictable_pgs_munlocked=6988i,unevictable_pgs_rescued=5426i,unevictable_pgs_scanned=0i,unevictable_pgs_stranded=0i,zone_reclaim_failed=0i 1459455200071462843单元测试与行为边界
插件的行为在 kernel_vmstat_test.go 中有系统性的验证,测试通过构造临时文件替换statFile来模拟不同输入,共覆盖四类场景:
- 完整文件:
TestFullVmStatProcFile用与 README 示例输出一致的全量数据(vmStatFileFull)验证所有字段均被正确解析并写入kernel_vmstat测量; - 部分字段:
TestPartialVmStatProcFile证明插件对内核版本差异有天然兼容性——不同内核版本/proc/vmstat中的字段集合可能不同,插件只解析文件实际存在的项,不依赖固定字段清单; - 非法数值:
TestInvalidVmStatProcFile将thp_split的值替换为非数字字符串abcd,验证解析失败会向上返回包含invalid syntax的错误;此时Gather整体失败,该采集周期不产生数据; - 文件缺失:
TestNoVmStatProcFile删除临时文件后,验证返回的错误信息包含does not exist,对应getProcVmstat()中的显式检查。
对使用者而言,这意味着:在正常运行的内核上插件几乎不会出错;一旦出现解析错误,应优先排查/proc/vmstat是否被篡改或处于异常状态。
实战应用建议
- 内存压力预警:组合观察
pswpin/pswpout(换页)、allocstall(分配停滞)、pgmajfault(主缺页)三组计数器在单位时间内的增量,可判断系统是否因内存不足而频繁换页、分配受阻; - 内存碎片化与 THP:
compact_stall、compact_fail与thp_fault_fallback的持续增长通常意味着内存碎片化加剧、大页分配回退频繁; - 降低指标基数:由于输出字段极多,建议结合
fieldpass只保留关心的字段,同时该测量不带标签,series 基数极低,非常适合长期落库; - 区分瞬时量与累计量:
/proc/vmstat中绝大多数计数器是自开机以来的累计值(如pgpgin、pgmajfault),直接绘图往往只见单调增长。在 InfluxDB 中建议配合derivative()等函数计算速率后再用于告警与趋势分析; - 平台限制:该插件仅支持 Linux,在多平台部署的采集端请通过条件化配置避免在非 Linux 主机上启用(虽然不会报错,但不会产生任何数据)。
【免费下载链接】telegrafAgent for collecting, processing, aggregating, and writing metrics, logs, and other arbitrary data.项目地址: https://gitcode.com/GitHub_Trending/te/telegraf
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考