2018年5月1日星期二

golang disk free采集不准确问题

     工作中接手的golang程序运行一段时间后,出现了采集的主机信息free不准确的问题,golang采集的程序与df看到的结果不一致。追踪源码发现核心的采集逻辑是如下这段:
func getVfsStats(path string) (total uint64, free uint64, avail uint64, inodes uint64, inodesFree uint64, err error) {
var s syscall.Statfs_t
if err = syscall.Statfs(path, &s); err != nil {
return 0, 0, 0, 0, 0, err
}
total = uint64(s.Frsize) * s.Blocks
free = uint64(s.Frsize) * s.Bfree
avail = uint64(s.Frsize) * s.Bavail
inodes = uint64(s.Files)
inodesFree = uint64(s.Ffree)
return total, free, avail, inodes, inodesFree, nil
}
这段逻辑其实沿用的是cadvisor里面的代码,上层使用的时候,用这里的free(uint64(s.Frsize) * s.Bfree)作为返回的磁盘空间可用值free。要解决这个问题,得看看syscall.Statfs这个函数调用到底返回了些什么信息,也即syscall.Statfs_t各项的含义。



syscal.Statfs本质上这里是调用了Linux的系统调用statfs,查询文件系统的统计信息,对应结构如下:
type Statfs_t struct {
Type    int64  #文件系统类型,具体可见man statfs
Bsize   int64  #文件系统块大小 Blocks  uint64 #文件系统块总数 Bfree   uint64 #free blocks in fs, 总的空闲块数量 Bavail  uint64  #free blocks available to unprivileged user针对非root用户,实际可用块数量 Files   uint64   #total file nodes in file system
Ffree   uint64  #free file nodes in fs
Fsid    Fsid       #file system id实际上不同系统含义不一样,普遍用法是使用包含一些随机内容使得(fsid, inode)唯一标记一个文件。这里意义不大,忽略。
Namelen int64  #maximum length of filenames  文件名长度中的最大值。 Frsize  int64      #fragment size (since Linux 2.6) Flags   int64       Spare   [4]int64  }
这里的unprivileged user是相对于super user而言的。在Linux中,super user就是指root,而unprivileged user指的就是非root用户。非root用户在使用某些资源或者命令时往往会受限制。
从这里来看,Bfree=总块数-真正使用了的块数,而Bavail则是非root用户可用的块数。BfreeBavail明显的差异,是Bfree中包含了文件系统预留的空间,而Bavail看不到这不到这部分预留。

以下FS.godfext4文件系统下空间预留比例调整时的输出。其中FS.go使用了上面的getVfsStats,代码:
func main() {
total, free, avail, _, _, _ := getVfsStats("/u01") fmt.Printf("total:%#v\n", int64(total)/1024/1024/1024) fmt.Printf("free:%#v\n", int64(free)/1024/1024/1024) fmt.Printf("avail:%#v\n", int64(avail)/1024/1024/1024)}


df中各项的计算逻辑:
Size = f_blocks * f_bsize
Used = (f_blocks-f_bfree)*f_bsize
Avail = f_bavail*f_bsize
Use% = Used/(Used+Avail) * 100
进一步查看df内部调用statfs的取值:
#strace -e statfs df -h /u01
statfs("/u01", {f_type="EXT2_SUPER_MAGIC", f_bsize=4096, f_blocks=920597106, f_bfree=913048164, f_bavail=819489636, f_files=233889792, f_ffree=230001119, f_fsid={-787308303, -2023304367}, f_namelen=255, f_frsize=4096}) = 0
Filesystem      Size  Used Avail Use% Mounted on
/dev/sdb1       3.5T   29G  3.1T   1% /u01
+++ exited with 0 +++
针对我们程序遇到的问题,真正要看到的free,等同于df中看到的磁盘Avail信息,所以我们将上层获取的free的数据来源改成uint64(s.Frsize) * s.Bavail

ps:
statfsLinux上的系统变量,POSIX API提供的为statvfs,后者更通用,适用于任何支持POSIX的操作系统。

参考:


没有评论:

发表评论