atop 是一款开源的单机性能监测工具,支持实时观测的同时、也支持读取历史文件排查问题。另外一个优点是除提供 CPU、MEM、DISK 等全局指标外,还提供进程、线程级别的各项指标监控数据。鉴于 atop 的这些优点,字节跳动基于社区的 atop 进行优化,目前已迭代 3 个版本,稳定运行接近三年。本文将和大家分享字节跳动内部 atop 工具的新特性及使用情况。
atop 本身是一个单机性能监测工具,每间隔 interval(实时监控默认间隔是 10s,而后台采集社区默认间隔是 600s,即 10min)时间去采集本机常用的全局指标和进程指标。直接执行atop
即可展示,大致分为上下两部分,分别展示全局指标和进程指标。同时支持快捷键切换多种“界面模式”,用于专门查看进程的 CPU、内存、磁盘等指标信息。
运行方式
根据用户使用需求,能够以多种形式运行。一种是直接运行 atop 二进制,显示实时数据;一种是以服务形式(systemd 或 SysV Init)部署在物理机、虚拟机或容器里,作为常驻进程,采集数据写入磁盘,以天为分割单位计入不同文件;一种是使用atop -r $atop_log_file
按需读取历史数据。是一款少见的能够支持查看详细历史记录的工具,这在定位非实时问题时尤其有帮助。
atop 记录了很多指标,如全局的 CPU、CPL、MEM、DISK、NET、PSI,进程的 PID、PPID、TID、CMD、SYSCPU、USRCPU、CPUNR、RSIZE、PSIZE、RDDSK、WRDSK 等,称得上一个大而全的工具。而 atop 本身占有资源很少,并不会带来额外的性能开销。因此很多场景都可以使用 atop 来查看各种指标,进而帮助用户更好的了解业务进程在操作系统中的呈现。举例如下:
目前上游还不支持 atop 指标在集群粒度的聚合,但字节内部提供 JSON 数据的输出,可通过数据处理一系列流程将隶属同一集群的所有机器的 atop 数据都入库存储(可选择性按时间对数据进行稀释)。并按照业务需求加以展示,如分别对全局常用指标和某类业务(多进程 PID 加以聚合)各项指标绘制成曲线图,用于观测 24h 时间周期内的数据变化;也可以用于统计基础服务的利用率。
在线排查
业务高峰期,如果发现抖动,可直接运行atop
综合查看全局指标和进程指标的哪项指标异常,进而定位排查问题。一般来说先观察全局指标来缩小范围,看是 CPU、MEM、磁盘读写、网络中的哪项引发的问题。常见问题有,如果总 CPU 的 sys 态及 user 态飙高、并且只有几个单 CPU 核很高,多半是绑核不均匀引起的;如果 CPL(CPU Load Average)不高但 CPU 的中断 irq 很高,多半是 IO 操作引发的。
历史定位
前文提到过,常用的监控工具中,能够提供详细历史数据并且能高效保存的几乎没有;如果需要存储,需要人工干预、打通整个流程。而 atop 本身提供了日志存储功能,并自带日志压缩机制,尽量确保存储无压力。为保证历史日志不会将系统盘打满,字节开发了定制化存储位置和存储天数的功能,将在下文详细说明。
功能上线
atop 有个 interval 参数,可通过配置,来定制化数据采集的间隔,如 10s、甚至 1s。如果业务有新功能上线,想自测新功能对系统带来的影响,可以将 interval 设置成 1 秒,实时观测各指标的动态变化。
性能优化
atop 提供细粒度的进程态各项指标值的展示,在业务调优过程中,可参考进程态的数据变化来衡量调优是否符合预期。
告警辅助
atop 的数据比较全,有很多其他监控工具不具备的指标,如 oom-kill、per NUMA 指标等。可用于告警数据来源的补充。
atop 针对全局指标和进程指标分别提供了很多快捷键,供用户查看更多的指标。接下来列举一些常见问题,详细参数可以通过man atop
进行查看。
用户可直接运行 atop 命令查看实时的信息,也可以通过 atop -r /var/log/atop/atop_$date 文件查看历史信息。读取文件时有多种交互方式,比如-b 代表从什么时刻开始读,-e 代表读取到什么时刻(在社区支持分钟级别读取的基础上,字节内部支持秒级别读取)。快捷键说明如下图:
快捷键 | 说明 |
---|---|
b | 输入时间,格式为"12:23"(时:分) |
t | 前进 10s |
T | 后退 10s |
g | 按 CPU 使用率排序,默认排序方式 |
m | 按内存使用率降序排列 |
d | 按磁盘使用率降序排列 |
y | 查看线程信息 |
j | 查看 container 聚合信息 |
c | 查看详细的 command line |
l | 定制化查看每个 CPU 核/每个磁盘/每个网卡的信息 |
f | 展示全局指标中 fixed 的指标 |
Q | 根据进程状态过滤,如 R|S|D|Z 等 |
I | 按照 PID 进行搜索(注:大写的 i) |
可以,默认有读权限。但是安装包、重启服务、修改日志存储天数等还是需要 root 权限。但非 root 用户按 d 无权限实时查看进程的磁盘信息。这是因为非 root 用户默认被禁止读取其他用户的/proc/$pid/io 文件,反映到 atop 代码里就是没有设置相关标志位,导致无法查看磁盘。
但查看历史文件数据可以打消这个魔咒,也就是说非 root 用户可以通过atop -r /var/log/atop/atop_$date
查看磁盘相关数据,毕竟 atop 写日志时是以 root 用户在运行。
默认只有进程信息展示,按 y 可查看线程。如下图,白色为进程,黄色为该进程的所有线程。再次按 y,线程展示消失。
进程态网络相关的指标是通过定制化安装 netatop kernel module 完成的。本质上是通过 netfilter hook 获取进程数据,额外有个 netatopd daemon 负责数据记录及压缩。在数据量比较大的场景,会影响到性能,因此默认不开启该功能。如果开启,可以按 n 查看 per 进程的网络指标。
全局指标中 DSK 行的 busy 代表什么?
busy=io_ms/per_cpu_ms,其中 io_ms 是处理 io 所花费的时间(ms 单位是毫秒),busy 就是过去 10s 内 io 处理的时间占单 CPU 时间的比例(其中 io wait 并不真实占用 CPU 资源,而是处于 sleep 状态)。
按 d 之后,查看到进程栏的 DSK 列 很高,表示什么?
DSK 列的数值是个相对比,表示该进程占当前展示的所有进程之和的比率,是个相对值; 换句话说,该列对于排查是哪个进程导致的 heavy load 并无意义。
查看进程的磁盘利用率,要看 RDDSK, WRDSK 这两列绝对值,表示所属进程的读/写字节数。
随着云原生场景的推进,各云厂商正尝试将不同特征的业务(如优先级不同、延迟敏感度不同的多个业务)混部在同一台物理机上,尽量充分地将机器的物理资源利用起来,以提高单机的资源利用率。常见的混部场景有在离线混部,而常用的混部手法是将不同业务绑定不同 NUMA,从物理上达到隔离。那么查看 NUMA 粒度而不是整机粒度的 CPU、MEM 等指标则更有意义。基于这迫切的需求,字节内部实现了 NUMA 的监控粒度,相关代码已被社区接收,展示效果如下:
https://github.com/Atoptool/atop/pull/163
支持 per NUMA 粒度内存相关信息查看
例:一台 4NUMA 的机器,其 NUM(Memory per NUMA)展示如下图
支持查看 per NUMA 内存碎片化,使用百分比展示
例:如上图 NUM 的 frag 字段。
支持 per NUMA 粒度 CPU 相关指标的聚合
例:一台 4NUMA,运行了虚拟机的 arm 机器上,其 NUC(CPU per NUMA)展示如下图
前文提到过,目前社区不支持 atop 指标在集群粒度的聚合;除此之外,atop 作为一个单机工具,如果业务想查看某台服务器的监控,需要申请权限登录到机器上,这在某些场景下比较受限。因此想到有没有一种方式,针对拥有某个集群机器权限的同学来说,可以直接查看该集群内所有机器的 atop 数据,而无需再走工单申请单台机器权限,即网页版 atop。
考虑到数据处理过程中 JSON 数据的强兼容性,敲定将 atop 的原始数据以 JSON 数据输出:支持输出到终端,以及写本地 unix domain socket。针对前者,主要是用于本机的一些数据调试,也可以与其他组件打通,如数据报警等。针对本地 UDS,atop 作为 server 端,提供一种数据输出的能力;另外需要引入 client 端、监听并获取 atop 数据,同时作为数据源端、与数据处理打通、将本机 atop 数据流过数据通路落入数据库,供业务方使用。整体流程如下:
用法如下:
Currently we support three types of output:
1. atop -O stdio
2. atop -O only
3. atop -O unixsock -w /path/to/file 10 //Make unixsock non-block to make sure this will not block main engine. And if the unix remote server re-launches, atop will re-connect and continue to work.
Usage examples:
./atop
./atop -P ALL
./atop -O only // overwrite parseout, show json to stdio only
./atop -O stdio -P ALL // both parseout and json stdio
./atop -O stdio -w atop.log // print to stdio, as well as file
./atop -O unixsock // overwrite parseout, show json to unixsock
./atop -O unixsock -P ALL // both parseout and json unixsock
./atop -O unixsock -w atop.log // write json to unixsock and file
And the detail JSON output format is as follows:
{
"ip": "a.b.c.d",
"timestamp": 1565256314,
"CPU": {
"hertz": 100,
...
"cpu_nums": 40
},
"cpu": [
{
"hertz": 100,
...
"cpu_id": 0
},
...
{
"hertz": 100,
...
"cpu_id": 39
}
],
...
"PRC": [
{
"pid": 1,
"p_name": "(systemd)"
},
...
{
"pid": 73,
"p_name": "(migration/12)"
}
]
}
虽然 atop 的指标很全,且有历史记录可以查询,但依旧有不少声音提到 atop 做的还有欠缺。比如无法将所有 CPU 的使用情况同时展示,即使将显示器横过来也不能解决,这在高达 128 核的物理机上尤其头痛。为解决这个痛点,参考类似 htop 的展示方式,结合 atop 本身代码,从零开始用 ncurses 加以绘制,展示效果如下。目前只支持 CPU 和 MEM 的展示,如有其他需求,欢迎联系我们。
支持类似 htop 直观的展示所有 CPU 和内存的使用情况,但新增 NUMA 粒度的支持。一是解决因 CPU 核数太多展示不全的问题;进一步可以纵览所有 CPU 的负载,直观判断绑核是否有问题
双 NUMA 使用 bar 展示
双 NUMA 使用百分比展示
//注:上面两张图为压测场景
user态:stress -c 20
sys态:iperf -s -i 1 && iperf -c $ip -i 6 -t 600
//numactl -H的结果如下
4NUMA 使用 bar 展示
4NUMA 使用百分比展示
前面提到,atop 支持历史日志的记录,这在定位排查非实时问题时非常有帮助。然而在字节内部,曾因 atop 日志过大引发过比较严重的问题。起因是 atop 默认会将日志写到/var/log/atop 目录下,并且记录所有的进程、线程信息到日志里。有些业务服务器(如 Java 业务)动辄每 10 秒几万个线程,一天的日志量高达几 G,7 天高达几十 G,如果服务器本身的系统盘存储空间很紧张,就会造成系统盘打满、登录不上机器的严重后果。
为解决日志相关的问题,字节内部在新版本中推出了以下特性:
默认只存储每个进程的 top100 线程
根据日常排查问题来看,一般只关心 topM 的进程或每个进程的 topN 线程。观察每个进程,通常超过 top100 线程的 CPU 或 MEM 都处于不活跃状态,对于排查问题来说意义不大,没必要记录到日志。据此字节内部引入-H 参数,通过指定-H 100 可以过滤掉 top100(按照 CPU/MEM/DISK 比例之和倒叙排列)之外的线程,大大减少了日志存储量,尤其适用于线程数目多达几千或几万的 Java 业务场景。
使用 systemd timer 控制 daily 日志重定位
引用上游最新的 atop-rotate.timer 和 atop-rotate.service,取代之前的 cronjob。避免某些机器修改时区后未重启 cron.service,导致 cron 服务重启时间未遵守本机时间,写 atop 日志时间出错。
支持用户修改 atop 日志存储时间,默认存储为最近 7 天
上游默认存储天数为 30 天,在实际排查问题时必要性不大,反而浪费了系统盘的存储空间。如有需要,可以通过 atop-json 机制将本机日志传输到统一的存储池子中,采取数据稀释的方式(如保留 topM 进程、按时间顺序增大记录间隔等)进行保存。字节内部在 atoprc 中引入generations
变量,表示存储天数为generations+2
天。
例:echo 'generations 1' >> /etc/atoprc && systemctl restart atop
代表只保留存储最近 3 天的日志
支持用户修改 atop 日志存储位置,默认是/var/log/atop
有些服务器虽然系统盘存储空间小,但数据盘有多块,容量高达几 T,针对这种场景,用户可以通过修改 atop 日志存储位置,来缓解存储压力。字节内部在 atoprc 中引入 logpath 变量,标识日志存储目录。
例:echo 'logpath /data00/log/atop' >> /etc/atoprc && systemctl restart atop
将日志存储到/data00/log/atop
目录下
限制 atop 日志存储总量为 10G,超过 10G 会按照时间倒序删除日志文件
解决因 systemd 没能 daily 重启 atop 服务,导致持续写相同文件,导致单个文件过大的问题
通过-b/-e 参数指定时间范围时支持精确到秒
新增 compact_stall、allocastall、InCsumErrors 等指标
关闭 perfevent 采集 instr 和 cycle 两项指标的功能,释放 PMU 给其它专用工具,如 perf
修复低于 4.19 内核,用户态读取/proc/$pid/cpuset 文件遇到 css offline、导致永久陷入内核态的问题
修复/run/pacct_source 文件过大,导致 tmpfs 被占满的问题
到目前为止,字节已稳定运行接近三年,覆盖公司全量服务器,推出 3 个版本。
字节内部目前在用代码已 push 到相关分支,欢迎使用:
https://github.com/bytedance/atop/commits/bytedance-internal-v2.6.0%2Bbyted2
相关 repo 请访问:
https://github.com/bytedance/atop
其中 master 分支紧跟社区 atop 的 master 分支,其余分支是一些已推社区但尚未被合入的特性或 bug fix(如 Fix atop stuck when reading offline css 虽然本质上是内核的 bug,但可以通过修改 atop 代码加以规避,避免重启服务器)。
新版本 atop 的部分特性已经推社区并被上游接收,接下来还会将其余特性继续推上游,更好的回馈社区。
关于字节跳动系统部 STE 团队:
字节跳动系统部 STE 团队 (STE=System Technologies & Engineering,系统技术与工程) 一直致力于操作系统内核与虚拟化、系统基础软件与基础库的构建和性能优化、超大规模数据中心的系统稳定性和可靠性建设、新硬件与软件的协同设计等基础技术领域的研发与工程化落地,具备全面的基础软件工程能力,为字节上层业务保驾护航。同时,团队积极关注社区技术动向,拥抱开源和标准。
更多招聘信息,可邮件联系 [email protected] 获取。