We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
在谈监控Go程序的资源使用情况前,我们先来谈一谈有哪些指标是需要监控的,一般谈论进程的指标最常见的就是进程的没存占用率、CPU占用率、创建的线程数。因为Go语言又在线程之上自己维护了Goroutine,所以针对Go进程的资源指标还需要加一个创建的Goroutine数量。
Go
CPU
Goroutine
又因为现在服务很多都部署在Kubernetes集群上,一个Go进程往往就是一个Pod,但是容器的资源是跟宿主机共享的,只是在创建的时候指定了其资源的使用上限,所以在获取CPU和Memory这些信息的时候还需要具体情况分开讨论。
Kubernetes
Pod
Memory
获取Go程序的资源使用情况使用gopstuil库即可完成。
gopstuil
package main import ( "fmt" "github.com/shirou/gopsutil/process" "os" "runtime" "runtime/pprof" "time" ) func main() { // NewProcess 会返回一个持有PID的Process对象,方法会检查PID是否存在,如果不存在会返回错误 // 通过Process对象上定义的其他方法我们可以获取关于进程的各种信息。 p, err := process.NewProcess(int32(os.Getpid())) if err != nil { panic(err) } // 返回指定时间内进程占用CPU时间的比例 cpuPercent, err := p.Percent(time.Second) if err != nil { panic(err) } // 上面返回的是占所有CPU核心时间的比例,如果想更直观的看占比,可以算一下占单个核心的比例 cp := cpuPercent / float64(runtime.NumCPU()) // 获取进程占用内存的比例 mp, _ := p.MemoryPercent() // 创建的线程数 threadCount := pprof.Lookup("threadcreate").Count() // Goroutine数 gNum := runtime.NumGoroutine() time.Sleep(time.Second * 2) fmt.Println(cpuPercent, cp, mp, threadCount, gNum) }
上面获取进程资源占比的方法只有在虚拟机和物理机环境下才能准确。因为类似Docker这样的Linux容器是靠着Linux的Namespace和Cgroups技术实现的进程隔离和资源限制,加上现在基本上都是K8s集群部署,所以如果是在Docker中获取Go进程的资源使用情况需要根据Cgroups分配给容器的资源上限进行计算才准确。
Docker
Linux
Namespace
Cgroups
K8s
在Linux中,Cgroups给用户暴露出来的操作接口是文件系统,即它以文件和目录的方式组织在操作系统的/sys/fs/cgroup路径下,在 Ubuntu 系统里,可以用mount -t cgroup指令把它们展示出来。
/sys/fs/cgroup
mount -t cgroup
在/sys/fs/cgroup下面有很多诸如cpuset、cpu、 memory这样的子目录,每个子目录都代表系统当前可以被Cgroups进行限制的资源种类。
cpuset
cpu
memory
针对我们监控Go进程内存和CPU指标的需求,我们只要知道cpu.cfs_period_us、cpu.cfs_quota_us和 memory.limit_in_bytes这三项就行,其他的有兴趣自己去研究吧。
cpu.cfs_period_us
cpu.cfs_quota_us
memory.limit_in_bytes
cpu.cfs_period_us、cpu.cfs_quota_us这两个参数需要组合使用,可以用来限制进程在长度为cfs_period的一段时间内,只能被分配到总量为cfs_quota的CPU时间, 可以简单的理解为容器能使用的核心数 = cfs_quota / cfs_period。
cfs_period
cfs_quota
所以在容器里获取Go进程CPU的占比的方法,需要做一些调整,要能计算出分配给了容器的最大CPU时间占比。
cpuPeriod, err := readUint("/sys/fs/cgroup/cpu/cpu.cfs_period_us") cpuQuota, err := readUint("/sys/fs/cgroup/cpu/cpu.cfs_quota_us") cpuNum := float64(cpuQuota) / float64(cpuPeriod)
而容器的能使用的最大内存数,自然就是在memory.limit_in_bytes里指定的啦,所以Go进程在容器中占用的内存比例需要通过下面这种方法获取
memLimit, err := readUint("/sys/fs/cgroup/memory/memory.limit_in_bytes") memInfo, err := p.MemoryInfo mp := memInfo.RSS * 100 / memLimit
上面进程内存信息里的RSS叫常驻内存,是在RAM里分配给进程,允许进程访问的内存量。而读取容器资源用的readUint,是containerd组织在cgroups实现里给出的方法,拷贝过来直接用就行啦,拷贝链接再下方参考链接里。
RSS
RAM
readUint
containerd
cgroups
上面分析解释各种指标用的代码的比较晦涩,可以看自动采样监控工具holmes的实现,下面参考链接里有我读源码时记的一些关键代码。
holmes
cpu.cfs_period_us & cpu.cfs_quota_us cfs_period_us用来配置时间周期长度,cfs_quota_us用来配置当前cgroup在设置的周期长度内所能使用的CPU时间数,两个文件配合起来设置CPU的使用上限。两个文件的单位都是微秒(us),cfs_period_us的取值范围为1毫秒(ms)到1秒(s),cfs_quota_us的取值大于1ms即可,如果cfs_quota_us的值为-1(默认值),表示不受cpu时间的限制 cpu.cfs_quota_us = 250000, cfs_quota_us = 250000 ,每250ms能使用250ms的CPU时间, 即分配给的上限是1核心 memory.limit_in_bytes memory.limit_in_bytes 设置的是内存上限额度
cpu.cfs_period_us & cpu.cfs_quota_us
cfs_period_us用来配置时间周期长度,cfs_quota_us用来配置当前cgroup在设置的周期长度内所能使用的CPU时间数,两个文件配合起来设置CPU的使用上限。两个文件的单位都是微秒(us),cfs_period_us的取值范围为1毫秒(ms)到1秒(s),cfs_quota_us的取值大于1ms即可,如果cfs_quota_us的值为-1(默认值),表示不受cpu时间的限制
cpu.cfs_quota_us = 250000, cfs_quota_us = 250000 ,每250ms能使用250ms的CPU时间, 即分配给的上限是1核心
memory.limit_in_bytes memory.limit_in_bytes 设置的是内存上限额度
RSS: (Resident Set Size) 进程的内存常驻集大小,用于表示分配在RAM中给该进程的内存量,它包括所有分配给进程的栈内存和堆内存。不会包括已经Swap out的内存 VSZ: (Virtual Memory Size) 进程的虚拟内存大小。它包括进程可以访问的所有内存,包括换出的内存、已分配但未使用的内存以及来自共享库的内存
参考链接:
The text was updated successfully, but these errors were encountered:
No branches or pull requests
在谈监控
Go
程序的资源使用情况前,我们先来谈一谈有哪些指标是需要监控的,一般谈论进程的指标最常见的就是进程的没存占用率、CPU
占用率、创建的线程数。因为Go
语言又在线程之上自己维护了Goroutine
,所以针对Go
进程的资源指标还需要加一个创建的Goroutine
数量。又因为现在服务很多都部署在
Kubernetes
集群上,一个Go
进程往往就是一个Pod
,但是容器的资源是跟宿主机共享的,只是在创建的时候指定了其资源的使用上限,所以在获取CPU
和Memory
这些信息的时候还需要具体情况分开讨论。Go进程怎么获取自己的资源占比?
获取
Go
程序的资源使用情况使用gopstuil
库即可完成。上面获取进程资源占比的方法只有在虚拟机和物理机环境下才能准确。因为类似
Docker
这样的Linux
容器是靠着Linux
的Namespace
和Cgroups
技术实现的进程隔离和资源限制,加上现在基本上都是K8s
集群部署,所以如果是在Docker
中获取Go
进程的资源使用情况需要根据Cgroups
分配给容器的资源上限进行计算才准确。在容器里怎么计算资源占比
在
Linux
中,Cgroups
给用户暴露出来的操作接口是文件系统,即它以文件和目录的方式组织在操作系统的/sys/fs/cgroup
路径下,在 Ubuntu 系统里,可以用mount -t cgroup
指令把它们展示出来。在
/sys/fs/cgroup
下面有很多诸如cpuset
、cpu
、memory
这样的子目录,每个子目录都代表系统当前可以被Cgroups
进行限制的资源种类。针对我们监控
Go
进程内存和CPU
指标的需求,我们只要知道cpu.cfs_period_us
、cpu.cfs_quota_us
和memory.limit_in_bytes
这三项就行,其他的有兴趣自己去研究吧。cpu.cfs_period_us
、cpu.cfs_quota_us
这两个参数需要组合使用,可以用来限制进程在长度为cfs_period
的一段时间内,只能被分配到总量为cfs_quota
的CPU
时间, 可以简单的理解为容器能使用的核心数 = cfs_quota / cfs_period。所以在容器里获取
Go
进程CPU
的占比的方法,需要做一些调整,要能计算出分配给了容器的最大CPU
时间占比。而容器的能使用的最大内存数,自然就是在
memory.limit_in_bytes
里指定的啦,所以Go
进程在容器中占用的内存比例需要通过下面这种方法获取上面进程内存信息里的
RSS
叫常驻内存,是在RAM
里分配给进程,允许进程访问的内存量。而读取容器资源用的readUint
,是containerd
组织在cgroups
实现里给出的方法,拷贝过来直接用就行啦,拷贝链接再下方参考链接里。上面分析解释各种指标用的代码的比较晦涩,可以看自动采样监控工具
holmes
的实现,下面参考链接里有我读源码时记的一些关键代码。名词解释
参考链接:
The text was updated successfully, but these errors were encountered: