在 Moby 中用好 prometheus/procfs:Go 语言从 /proc 与 /sys 采集 Linux 系统与进程指标

发布时间:2026/9/8 21:33:22
在 Moby 中用好 prometheus/procfs:Go 语言从 /proc 与 /sys 采集 Linux 系统与进程指标 在 Moby 中用好 prometheus/procfsGo 语言从 /proc 与 /sys 采集 Linux 系统与进程指标【免费下载链接】mobyThe Moby Project - a collaborative project for the container ecosystem to assemble container-based systems项目地址: https://gitcode.com/GitHub_Trending/mo/moby导读procfs README 所描述的是 Prometheus 生态中一个以纯 Go 编写的核心基础库github.com/prometheus/procfs。它以只读的伪文件系统/proc与/sys为数据源把内核导出的文本文件解析成强类型的 Go 结构体为系统、内核与进程级指标采集提供了统一入口。在本仓库Moby / moby中它作为间接依赖以v0.21.1版本内嵌于 vendor/github.com/prometheus/procfs服务于容器生态中的监控与指标链路。读完本文你将掌握该库的组织方式、FS核心抽象、基于FS的典型用法、构建与测试流程以及如何在自己的 Linux 监控程序中安全使用它。一、库定位为什么容器生态离不开 /proc 与 /sys 的解析在 Linux 上内核并不通过常规 API 暴露大部分运行态信息而是把数据「导出」为两个只读伪文件系统/proc以进程为中心的系统与进程信息例如/proc/statCPU 汇总统计、/proc/meminfo内存、/proc/[pid]/stat单进程状态/sys设备、内核子系统等结构化信息例如块设备、网络与硬件相关的内核对象。prometheus/procfs的价值在于把这些以「空格分隔文本」形式存在的内核文件封装成类型安全的 Go API。其包注释doc.go这样概括它的职责Package procfs provides functions to retrieve system, kernel and process metrics from the pseudo-filesystem proc.需要特别留意的是该库自带的一条重要声明原文照录自 README 开头WARNING: This package is a work in progress. Its API may still break in backwards-incompatible ways without warnings. Use it at your own risk.即该库属于「进行中」状态API 可能在无通知的情况下发生不向后兼容的变更。这一点决定了工程接入时必须把版本锁定并随之升级正如本仓库在 go.mod 中固定github.com/prometheus/procfs v0.21.1 // indirect的方式。在 Moby 仓库中的角色在 go.mod 中可以看到moby 将github.com/prometheus/procfs v0.21.1标记为indirect间接依赖同时引入了github.com/prometheus/client_golang v1.24.1、github.com/prometheus/common v0.70.1等 Prometheus 家族依赖。也就是说moby 自身并不直接 import 该库而是由上游指标采集与 Prometheus 客户端链路传递引入这正是go mod vendor之后把完整库目录裁剪进 vendor/github.com/prometheus/procfs 的原因。vendor 目录中保留了库的源码、internal/内部包以及构建工具ttar但按依赖裁剪规则省略了测试夹具fixtures等开发期文件。二、核心抽象FS把挂载点变成类型安全的句柄整个库的设计围绕一个贯穿始终的抽象展开FS类型代表某个伪文件系统通常是/proc或/sys的路径。数据从哪来、如何拼接路径、如何校验挂载点都收敛在这一层。2.1 根包的FS结构以库根包的实现 fs.go 为例// FS represents the pseudo-filesystem sys, which provides an interface to // kernel data structures. type FS struct { proc fs.FS isReal bool }源码还导出了两个与 Linux 语义强相关的常量DefaultMountPointproc 文件系统的默认挂载点值为/procSectorSize 512单扇区字节数专用于 Linux 块 I/O 计算供读取磁盘统计的模块换算使用。构造函数有两个// NewDefaultFS 使用默认挂载点 /proc若该目录不可读或不是目录则返回错误。 func NewDefaultFS() (FS, error) { return NewFS(DefaultMountPoint) } // NewFS 使用给定的 proc 挂载点同样会校验路径合法性。 func NewFS(mountPoint string) (FS, error) { ... }NewFS内部首先调用内层 internal/fs/fs.go 的fs.NewFS后者通过os.Stat校验传入路径必须是一个可读的目录否则分别返回 could not read %q 或 mount point %q is not a directory 的错误随后还会通过isRealProc检查该挂载点是否指向真实的 proc 文件系统。内层internal/fs包统一定义了所有伪文件系统的默认挂载点可直接查阅 internal/fs/fs.goDefaultProcMountPoint /procDefaultSysMountPoint /sysDefaultConfigfsMountPoint /sys/kernel/configDefaultSelinuxMountPoint /sys/fs/selinux此外FS.Path(p ...string)通过filepath.Join把相对路径安全地拼接为形如/proc/stat的完整读取路径统一了目录拼接逻辑。2.2 典型用法读取 CPU 统计README 给出的是最小可运行范式先初始化挂载点再读取对应统计。以读取 CPU 汇总统计/proc/stat为例fs, err : procfs.NewFS(/proc) stats, err : fs.Stat()fs.Stat()返回的Stat结构体定义见 stat.go聚合了内核级统计主要包括字段来源含义BootTime/proc/stat的btime行自 Unix 纪元以来的启动时间秒CPUTotalcpu汇总行全核累计 CPU 时间CPU map[int64]CPUStatcpu0、cpu1… 行按 CPU 编号的独立统计IRQTotal/IRQ []uint64intr行中断总次数与按编号中断次数ContextSwitchesctxt行上下文切换次数ProcessCreatedprocesses行已创建进程总数ProcessesRunning/ProcessesBlockedprocs_running/procs_blocked当前运行 / 阻塞等待 I/O的进程数SoftIRQsoftirq行软中断分类型统计其中CPUStat定义了User / Nice / System / Idle / Iowait / IRQ / SoftIRQ / Steal / Guest / GuestNice共 10 个维度类型均为float64单位为秒内核原始单位是 USER_HZ 时钟滴答读取后除以常量换算而来详见下文第六节。CPU 读取的解析实现位于 stat.go 中的parseCPUStat若行首 token 为cpu表示聚合行CPU id 记为 -1否则取cpu前缀后的数字作为 CPU 编号。2.3 双文件系统场景同时需要 /proc 与 /sys 的子包并非所有指标都能只从/proc拿到。README 特别举例部分子包例如blockdevice需要同时访问 proc 与 sys 两个伪文件系统于是对应构造函数显式接收两个挂载点fs, err : blockdevice.NewFS(/proc, /sys) stats, err : fs.ProcDiskstats()这种「一个FS管一个挂载点、双挂载点则用两个参数」的设计让磁盘等横跨两类内核接口的指标也能以统一方式获取。需要说明的是moby 本次 vendor 的快照按依赖裁剪只保留了库根包与internal/包从 vendor/github.com/prometheus/procfs 的目录结构可见blockdevice等上游子包存在于完整版上游工程中这里作为官方用法示例引用。三、包组织结构按「数据来源 信息类型」两个维度划分README 明确指出库的组织遵循两条原则数据来源信息来自/proc、/sys还是两者都要信息类型是进程级信息、系统级统计还是块设备等子系统信息。3.1 根包procfs进程 系统两级指标「大多数进程信息都可以由根procfs包中的函数获得」README 原文。从 vendor/github.com/prometheus/procfs 的源码文件清单可以直观印证这一组织原则进程级process以proc_*.go命名的文件覆盖单个进程的各方面——proc.go进程句柄与目录枚举、proc_stat.go/proc/[pid]/stat、proc_status.go、proc_statm.go、proc_maps.go、proc_smaps.go、proc_limits.go、proc_environ.go、proc_io.go、proc_cgroup.go、proc_fdinfo.go、proc_ns.go、proc_psi.go、proc_interrupts.go、proc_snmp.go、proc_snmp6.go、proc_netstat.go、proc_sys.go等系统级kernel/systemstat.goCPU、中断、上下文切换、meminfo.go内存、loadavg.go负载、cpuinfo.goCPU 详情并按架构拆分出cpuinfo_x86.go、cpuinfo_armx.go、cpuinfo_riscvx.go、cpuinfo_s390x.go、cpuinfo_ppcx.go、cpuinfo_mipsx.go、cpuinfo_loong64.go等。doc.go中给出了一个可直接照搬的进程级示例演示如何拿到「当前进程」并读取其状态func main() { p, err : procfs.Self() if err ! nil { log.Fatalf(could not get process: %s, err) } stat, err : p.Stat() if err ! nil { log.Fatalf(could not get process stat: %s, err) } fmt.Printf(command: %s\n, stat.Comm) fmt.Printf(cpu time: %fs\n, stat.CPUTime()) fmt.Printf(vsize: %dB\n, stat.VirtualMemory()) fmt.Printf(rss: %dB\n, stat.ResidentMemory()) }procfs.Self()即解析/proc/self当前进程自身的快捷方式返回的ProcStat结构体从 proc_stat.go 可见依次携带PID、Comm可执行文件名、State进程状态、父进程 PID 等字段并派生CPUTime()、VirtualMemory()、ResidentMemory()等便捷方法。3.2 网络与子系统专项文件根包中还有大量针对具体内核子系统的独立.go文件几乎是一份「/proc 与 /sys 指标速查表」文件数据来源内容net_dev.go/proc/net/dev网卡收发流量net_tcp.go/net_udp.go/net_unix.go/proc/net/tcp等各协议套接字表net_sockstat.go/netstat.go/net_conntrackstat.go/proc/net/*socket、连接跟踪等mdstat.go/proc/mdstat软件 RAID 状态swaps.go/proc/swapsswap 设备buddyinfo.go、zoneinfo.go、slab.go/proc/buddyinfo等内存分配器细粒度信息fscache.go、ipvs.go、nfnetlink_queue.go、arp.go对应/proc条目文件缓存、IPVS、netfilter、ARP 等每个文件都遵循同一个「FS句柄 → 一次文件读取 → 类型化结构体」的模式学习成本极低。四、构建与测试无二进制产物、以 ttar 夹具驱动单元测试README 强调一个工程事实procfs 是库而非可执行程序没有可分发的二进制产物它总是作为其他应用的一部分被编译。其质量保障方式是「绝大部分 API 都带有单元测试通过make test运行」。4.1 测试夹具fixtures与 ttar测试需要一个关键前提CI 与开发机上不一定存在完整的/proc、/sys内容。因此项目维护了一套来自真实内核文件系统的样例文件fixtures并在测试运行时把它们当作/proc、/sys的快照喂给解析器。这些夹具以 ttar 中的规则如实呈现了这一机制%/.unpacked: %.ttar echo extracting fixtures $* ./ttar -C $(dir $*) -x -f $*.ttar touch $ fixtures: testdata/fixtures/.unpacked update_fixtures: rm -vf testdata/fixtures/.unpacked ./ttar -c -f testdata/fixtures.ttar -C testdata/ fixtures/ .PHONY: test test: testdata/fixtures/.unpacked common-test要点拆解make test先把testdata/fixtures/.unpacked作为前置依赖触发解包再执行Makefile.common中的common-testGo 测试套件解包动作依赖仓库自带的ttar可执行文件位于 vendor/github.com/prometheus/procfs/ttar-x表示解包、-c表示打包每次解包成功后通过touch生成.unpacked标记文件避免重复解包。4.2 更新测试夹具的标准流程当新功能需要新的样例内核文件时README 给出五步操作流确保testdata/fixtures目录是最新状态——先删除旧目录再重新解包rm -rf testdata/fixtures make test在解包出的testdata/fixtures中修改/新增样例文件例如为某个新解析函数补一条真实的/proc/xxx内容运行make update_fixtures由ttar -c根据目录内容重新生成fixtures.ttar用git diff testdata/fixtures.ttar复核改动是否符合预期再随代码一起提交。4.3 在 vendor 目录下的现实约束需要指出的是当前 moby 仓库的 vendor 裁剪版vendor/github.com/prometheus/procfs属于「只供编译使用的依赖快照」并没有携带testdata/fixtures与fixtures.ttar这些开发期资源。因此上述测试流程针对的是 procfs 自身作为独立仓库开发时的场景在 moby 中以 vendor 方式使用时验证重点转为「能通过编译 在目标 Linux 环境运行集成测试」而不是在 vendor 目录内跑make test。五、源码级纵深解析精度与 USER_HZ 之谜把 README 的「使用说明」落到源码上最能体现这个库工程取舍的地方是时间单位的换算。Linux 内核在/proc/stat的cpu行中导出的是以 USER_HZ 为单位的时钟滴答jiffies计数。procfs 为了让调用方拿到「秒」单位的浮点数在 stat.go 的parseCPUStat中用fmt.Sscanf按固定格式一次性解析 10 个字段count, err : fmt.Sscanf(line, %s %f %f %f %f %f %f %f %f %f %f, cpu, cpuStat.User, cpuStat.Nice, cpuStat.System, cpuStat.Idle, cpuStat.Iowait, cpuStat.IRQ, cpuStat.SoftIRQ, cpuStat.Steal, cpuStat.Guest, cpuStat.GuestNice)随后统一除以常量userHZcpuStat.User / userHZ ...关键问题随之而来USER_HZ到底是多少proc_stat.go 顶部的大段注释完整记录了这段工程史Originally, this USER_HZ value was dynamically retrieved via a sysconf call which required cgo... After much research it was determined that USER_HZ is actually hardcoded to 100 on all Go-supported platforms as of the time of this writing.也就是说最初该常量希望通过sysconf动态获取但这会引入cgo依赖严重阻碍交叉编译。经过调研作者最终确认在 Go 官方支持的所有平台上USER_HZ都被硬编码为 100于是在无 cgo 的前提下直接写死const userHZ 100注释同时坦承了最坏后果——如果真的存在异类平台最多导致两个指标数值失真。这是一个典型的「用精确常量换取零 cgo、支持交叉编译」的设计决策也是把 procfs 广泛嵌入容器监控链路跨架构交叉构建是刚需的技术前提。解析层之下internal/util子包internal/util承担通用的文本解析parse.go提供ParseUint32s、ParseUint64s、ParsePInt64s返回[]*int64用于内核可空缺字段等辅助函数valueparser.go提供键值对解析readfile.go/sysreadfile.go封装文件读取与错误包装。全部解析器都不依赖 cgo这保证了库的可移植编译性。六、接入指引在 Moby 及自研 Linux 程序中引入6.1 在 Go 工程中声明依赖在 moby 仓库里该库作为 Prometheus 监控链路的间接依赖出现go.modgithub.com/prometheus/procfs v0.21.1 // indirect对普通 Go 模块工程如需要直接使用则显式引入并通过go mod vendor拉取到本地 vendor 目录go get github.com/prometheus/procfsv0.21.1引入路径与 moby 内嵌的 vendor 快照保持一致的版本即可复用 vendor/github.com/prometheus/procfs 内的同一份代码。6.2 一个覆盖系统 进程的可运行示例综合前文一个典型的采集程序会同时覆盖系统级与进程级指标package main import ( fmt log github.com/prometheus/procfs ) func main() { // 系统级/proc/stat 的 CPU 与系统统计 fs, err : procfs.NewFS(/proc) if err ! nil { log.Fatalf(failed to open /proc: %s, err) } stats, err : fs.Stat() if err ! nil { log.Fatalf(failed to read /proc/stat: %s, err) } fmt.Printf(boot time: %d\n, stats.BootTime) fmt.Printf(ctx switches: %d\n, stats.ContextSwitches) fmt.Printf(cpu total: user%.2fs system%.2fs idle%.2fs\n, stats.CPUTotal.User, stats.CPUTotal.System, stats.CPUTotal.Idle) // 进程级当前进程的 stat p, err : procfs.Self() if err ! nil { log.Fatalf(failed to open self: %s, err) } pstat, err : p.Stat() if err ! nil { log.Fatalf(failed to read process stat: %s, err) } fmt.Printf(process: %s (pid%d)\n, pstat.Comm, pstat.PID) fmt.Printf(cpu time: %.2fs\n, pstat.CPUTime()) }6.3 使用前提与限制平台限制该库本质是对 Linux 伪文件系统的封装只能在挂载了/proc、/sys的 Linux含容器内环境中返回真实数据非 Linux 平台无法工作。挂载点可定制借助NewFS(自定义路径)与FS.Path可读取 chroot、容器根文件系统或测试快照中的伪文件系统这也是其测试夹具得以工作的基础。API 稳定性务必记住 README 开篇的警告——库处于演进期、API 可能破坏性变更升级版本时应回归验证所有字段与签名。七、小结prometheus/procfs以「一个FS句柄 一个伪文件系统挂载点」的简洁抽象把繁琐的/proc、/sys文本解析收敛为强类型结构体与方法调用并按「数据来源 信息类型」组织成易于扩展的包结构。它牺牲了一定 API 稳定性换来的是无 cgo、支持交叉编译与跨架构的纯 Go 实现因此在 Moby 这类容器平台中被 Prometheus 监控链路以间接依赖方式内嵌。理解其 fs.go 的挂载点抽象、stat.go 的字段语义、USER_HZ100的换算决策以及 Makefile 的 ttar 夹具驱动测试流程即可在自己的监控与系统工具中稳健地驾驭它。本文以 moby 仓库 vendor 快照vendor/github.com/prometheus/procfs/README.md中的库文档为主干撰写库本身的版本信息以 go.mod 为准v0.21.1读者可将文中代码直接用于自研项目的 Linux 主机监控场景。【免费下载链接】mobyThe Moby Project - a collaborative project for the container ecosystem to assemble container-based systems项目地址: https://gitcode.com/GitHub_Trending/mo/moby创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询