当你登录到一台出现性能问题的 Linux 服务器:在第一分钟内,你会检查些什么呢?

在 Netflix 我们有一个庞大的 EC2 Linux 云,并且有许多性能分析工具用于监控和研究其性能。其中包括用于全局云监控的 Atlas 和用于按需实例分析的 Vector。虽然这些工具能帮助我们解决了大部分问题,但有时我们还是需要登录实例并运行一些标准的 Linux 性能分析工具。

前 60 秒:总结

在这篇文章中,Netflix 性能工程团队将使用你应该可用的标准 Linux 工具,向你展示在命令行进行优化性能调查的前 60 秒。在 60 秒之内,通过运行以下 10 个命令,你可以获得对系统资源使用情况和正在运行的进程的深入了解。先查找错误和饱和指标,因为它们都很容易解释,然后再查找资源利用率。饱和指的是资源的负载超出它的处理能力,并且会从请求队列的长度或者等待时间上表现出来。

1
2
3
4
5
6
7
8
9
10
uptime
dmesg | tail
vmstat 1
mpstat -P ALL 1
pidstat 1
iostat -xz 1
free -m
sar -n DEV 1
sar -n TCP,ETCP 1
top

其中一些命令需要安装 sysstat 软件包。这些命令显示出来的指标将帮助你完成一些 USE 方法(一种定位性能瓶颈的方法)。这涉及检查所有资源(CPU、内存、磁盘等)的利用率、饱和度和错误指标。

下面的部分总结了这些命令,并附有来自生产系统的实例。要获取有关这些工具的更多信息,请查阅它们的手册页面。

1. uptime

1
2
$ uptime 
23:51:26 up 21:31, 1 user, load average: 30.02, 26.43, 19.02

这是快速查看平均负载的方法,平均负载指示想要运行的任务(进程)的数量。在 Linux 系统中,这些数字包括想要在 CPU 上运行的进程,以及阻塞在不可中断 I/O(通常是磁盘 I/O)中的进程。这个命令给出了关于资源负载(或需求)的高层次概念,但是不借助其他工具很难正确理解。仅值得快速查看。

这三个数字分别是具有1分钟、5分钟和15分钟固定时间间隔的指数衰减滑动和均值。这三个数字让我们了解负载是如何随时间变化的。例如,你被要求检查一个有问题的服务器,而 1 分钟的平均负载远低于 15 分钟的平均负载,那么你可能登录的太晚了,错过了问题的发生。

在上面的例子中,平均负载显示出最近有增加的趋势,相较于 15 分钟的值 19, 1 分钟内的平均负载值高达 30. 如此大的数字意味着某种情况的大量发生,很可能是CPU需求;该序列中的 3、4 条命令 vmstatmpstat 将用于确认。

2. dmesg | tail

1
2
3
4
5
6
$ dmesg | tail
[1880957.563150] perl invoked oom-killer: gfp_mask=0x280da, order=0, oom_score_adj=0
[...]
[1880957.563400] Out of memory: Kill process 18694 (perl) score 246 or sacrifice child
[1880957.563408] Killed process 18694 (perl) total-vm:1972392kB, anon-rss:1953348kB, file-rss:0kB
[2320864.954447] TCP: Possible SYN flooding on port 7001. Dropping request. Check SNMP counters.

这条命令用于查看最近 10 条系统消息(如果有)。请查找会引发性能问题的错误。上例包括 oom-killer(内存不足杀死进程)和 TCP 丢弃请求。

不要忽略这一步!dmesg 总是值得检查的。

3. vmstat 1

1
2
3
4
5
6
7
8
9
$ vmstat 1
procs ---------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
34 0 0 200889792 73708 591828 0 0 0 5 6 10 96 1 3 0 0
32 0 0 200889920 73708 591860 0 0 0 592 13284 4282 98 1 1 0 0
32 0 0 200890112 73708 591860 0 0 0 0 9501 2154 99 1 0 0 0
32 0 0 200889568 73712 591856 0 0 0 48 11900 2459 99 0 0 0 0
32 0 0 200890208 73712 591860 0 0 0 0 15898 4840 98 1 1 0 0
^C

vmstat(8) 是 visual memory stat 的缩写,是一种常用工具(在几十年前首次为 BSD 创建)。它在每一行打印关键服务器统计信息的摘要。

vmstat 带有参数 1 运行,以打印每秒摘要。输出的第一行(在此版本的 vmstat 中)有一些列显示自启动以来的平均值,而不是前一秒的值。现在先跳过第一行,除非你想了解并记住每一列代表的内容。

需要检查的列:

  • r: 在 CPU 上运行和等待执行的进程。因为不包括 I/O,所以它提供了一种比平均负载更好的用于确定 CPU 饱和的信号。解释:如果 “r” 值大于 CPU 核心数,则表示 CPU 饱和。
  • free: 可用内存,单位是 kB. 如果数字的位数多的数不过来的话,则表明你有足够的可用内存。第 7 条命令 free -m 能更好的解释可用内存的状态。
  • si, so: swap 的换入和换出。如果非零,则表示内存不足。
  • us, sy, id, wa, st: 这些是所有 CPU 的平均 CPU 时间的细分。分别是用户时间、系统时间(内核)、空闲时间、等待 I/O 的时间和被偷取时间(被其他虚拟机占用,或者在使用 Xen 时,由虚拟机的独立驱动域占用)。

CPU 时间的细分中,可以通过将用户时间和系统时间相加来确认 CPU 是否繁忙。持续的等待 I/O 表明存在磁盘瓶颈;这是因为 CPU 处于空闲状态,任务因为等待未决的磁盘 I/O 而被阻塞。可将等待 I/O 视为另一种形式的 CPU 空闲,这种空闲形式可以提供关于为什么 CPU 处于空闲状态的线索。

系统时间在 I/O 处理中是必要的。若系统时间的平均值较高(超过 20%),则可能值得进一步探索:也许内核在处理 I/O 时效率不高。

在上面的例子中,CPU 时间几乎完全集中在用户级别,指向应用程序级别的使用。CPU 平均利用率也远超 90%. 这不一定是问题;使用 “r” 列检查饱和的程度。

4. mpstat -P ALL 1

1
2
3
4
5
6
7
8
9
10
$ mpstat -P ALL 1
Linux 3.13.0-49-generic (titanclusters-xxxxx) 07/14/2015 _x86_64_ (32 CPU)

07:38:49 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
07:38:50 PM all 98.47 0.00 0.75 0.00 0.00 0.00 0.00 0.00 0.00 0.78
07:38:50 PM 0 96.04 0.00 2.97 0.00 0.00 0.00 0.00 0.00 0.00 0.99
07:38:50 PM 1 97.00 0.00 1.00 0.00 0.00 0.00 0.00 0.00 0.00 2.00
07:38:50 PM 2 98.00 0.00 1.00 0.00 0.00 0.00 0.00 0.00 0.00 1.00
07:38:50 PM 3 96.97 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 3.03
[...]

这个命令会打印每个 CPU 的 CPU 时间细分,可以用来检查是否存在不平衡。单个繁忙的 CPU 可以作为单线程应用程序的证据。

5. pidstat 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ pidstat 1
Linux 3.13.0-49-generic (titanclusters-xxxxx) 07/14/2015 _x86_64_ (32 CPU)

07:41:02 PM UID PID %usr %system %guest %CPU CPU Command
07:41:03 PM 0 9 0.00 0.94 0.00 0.94 1 rcuos/0
07:41:03 PM 0 4214 5.66 5.66 0.00 11.32 15 mesos-slave
07:41:03 PM 0 4354 0.94 0.94 0.00 1.89 8 java
07:41:03 PM 0 6521 1596.23 1.89 0.00 1598.11 27 java
07:41:03 PM 0 6564 1571.70 7.55 0.00 1579.25 28 java
07:41:03 PM 60004 60154 0.94 4.72 0.00 5.66 9 pidstat

07:41:03 PM UID PID %usr %system %guest %CPU CPU Command
07:41:04 PM 0 4214 6.00 2.00 0.00 8.00 15 mesos-slave
07:41:04 PM 0 6521 1590.00 1.00 0.00 1591.00 27 java
07:41:04 PM 0 6564 1573.00 10.00 0.00 1583.00 28 java
07:41:04 PM 108 6718 1.00 0.00 0.00 1.00 0 snmp-pass
07:41:04 PM 60004 60154 1.00 4.00 0.00 5.00 9 pidstat
^C

pidstat 有点类似于 top 命令的按进程的摘要,但它会打印滚动的摘要信息而不清屏。这对于随时间观察变化趋势以及将您所看到的内容(复制粘贴)记录到您的调查记录中非常有用。

上面的例子识别出了两个消耗 CPU 的 Java 进程。%CPU 列是所有 CPU 的总和;1591% 表明这两个 Java 进程几乎占用了16个 CPU 的资源。

6. iostat -xz 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ iostat -xz 1
Linux 3.13.0-49-generic (titanclusters-xxxxx) 07/14/2015 _x86_64_ (32 CPU)

avg-cpu: %user %nice %system %iowait %steal %idle
73.96 0.00 3.73 0.03 0.06 22.21

Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
xvda 0.00 0.23 0.21 0.18 4.52 2.08 34.37 0.00 9.98 13.80 5.42 2.44 0.09
xvdb 0.01 0.00 1.02 8.94 127.97 598.53 145.79 0.00 0.43 1.78 0.28 0.25 0.25
xvdc 0.01 0.00 1.02 8.86 127.79 595.94 146.50 0.00 0.45 1.82 0.30 0.27 0.26
dm-0 0.00 0.00 0.69 2.32 10.47 31.69 28.01 0.01 3.23 0.71 3.98 0.13 0.04
dm-1 0.00 0.00 0.00 0.94 0.01 3.78 8.00 0.33 345.84 0.04 346.81 0.01 0.00
dm-2 0.00 0.00 0.09 0.07 1.35 0.36 22.50 0.00 2.55 0.23 5.62 1.78 0.03
[...]
^C

这是一个很好的工具,用于理解块设备(磁盘),无论是应用的工作负载还是产生的性能结果。注意以下内容:

  • r/s, w/s, rkB/s, wkB/s:这些是传递给设备的每秒读取操作数、写入操作数、读取数据量和写入数据量(单位为 KB)。使用它们来表征工作负载。性能问题可能仅仅是由于施加了过多的负载导致的。
  • await:I/O 的平均时间(单位为毫秒)。这是应用程序所经历的等待时间,因为它既包括排队时间,也包括被服务时间。大于预期的平均时间可能表明设备饱和或设备出现问题。
  • avgqu-sz:发给设备的平均请求数。大于 1 的值可能表明饱和(尽管设备通常可以并行处理请求,尤其是位于多个后端磁盘前面的虚拟设备)。
  • %util:设备利用率。这实际上是一个忙碌状态百分比,显示设备每秒工作的时间。大于 60% 的值通常会导致性能不佳(应该可在 await 中看到),尽管这取决于设备。接近 100% 的值通常表示饱和。

如果存储设备是一个位于多个后端磁盘前面的逻辑磁盘设备,那么 100% 的利用率可能只是意味着某些 I/O 正在于 100% 的时间中被处理,然而,后端磁盘可能远未饱和,并且可以处理更多的工作。

请记住,磁盘 I/O 性能不佳不一定是一个应用上的问题。通常会使用许多技术来执行异步I/O,以使应用程序不会被阻塞和直接遭受延迟(例如,提前读入和写入缓冲)。

7. free -m

1
2
3
4
5
$ free -m
total used free shared buffers cached
Mem: 245998 24545 221453 83 59 541
-/+ buffers/cache: 23944 222053
Swap: 0 0 0

右侧两列显示:

  • buffers: 用于块设备 I/O 的缓冲区高速缓存
  • cached: 用于文件系统的页面缓存

我们只需要检查这些缓存的大小不是接近于 0,否则可能导致更高的磁盘 I/O(使用 iostat 进行确认)和更差的性能。上面的示例看起来很好,每个缓存中都有很多兆字节。

-/+ buffers/cache 提供了更少令人困惑的已用和可用内存值。Linux 会将空闲内存用于缓存,但如果应用程序需要,可以快速回收它。因此,在某种程度上,缓存内存应该包括在可用内存列中,这一行就是这样做的。甚至有一个网站 linuxatemyram 专门讨论这种困惑。

如果在 Linux 上使用了 ZFS,事情会变得更加混乱,我们的一些服务就是这样,因为 ZFS 有自己的文件系统缓存,在 free -m 列中无法正确反映。这会导致看上去系统的可用内存不足,但实际上可以根据需要从 ZFS 缓存中使用这些内存。

8. sar -n DEV 1

1
2
3
4
5
6
7
8
9
10
11
12
13
$ sar -n DEV 1
Linux 3.13.0-49-generic (titanclusters-xxxxx) 07/14/2015 _x86_64_ (32 CPU)

12:16:48 AM IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
12:16:49 AM eth0 18763.00 5032.00 20686.42 478.30 0.00 0.00 0.00 0.00
12:16:49 AM lo 14.00 14.00 1.36 1.36 0.00 0.00 0.00 0.00
12:16:49 AM docker0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00

12:16:49 AM IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
12:16:50 AM eth0 19763.00 5101.00 21999.10 482.56 0.00 0.00 0.00 0.00
12:16:50 AM lo 20.00 20.00 3.25 3.25 0.00 0.00 0.00 0.00
12:16:50 AM docker0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
^C

使用这个工具检查网络接口的吞吐量:rxkB/s 和 txkB/s,作为工作负载的度量,并检查是否已经达到了任何限制。在上面的示例中,eth0 接收速度达到了 22 MB/s,相当于 176 Mb/s(远低于 1 Gb/s 的限制)。

这个版本还有用于表征设备利用率的 %ifutil(全双工的两个方向的最大值),我们也使用 Brendan 的 nicstat 工具 来测量这一点。和 nicstat 一样,这个指标很难准确获取,在这个示例中貌似也无法正常工作(0.00)。

9. sar -n TCP,ETCP 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ sar -n TCP,ETCP 1
Linux 3.13.0-49-generic (titanclusters-xxxxx) 07/14/2015 _x86_64_ (32 CPU)

12:17:19 AM active/s passive/s iseg/s oseg/s
12:17:20 AM 1.00 0.00 10233.00 18846.00

12:17:19 AM atmptf/s estres/s retrans/s isegerr/s orsts/s
12:17:20 AM 0.00 0.00 0.00 0.00 0.00

12:17:20 AM active/s passive/s iseg/s oseg/s
12:17:21 AM 1.00 0.00 8359.00 6039.00

12:17:20 AM atmptf/s estres/s retrans/s isegerr/s orsts/s
12:17:21 AM 0.00 0.00 0.00 0.00 0.00
^C

这是一份关键 TCP 指标的汇总视图。其中包括:

  • active/s: 每秒本地发起的 TCP 连接数(例如,通过 connect())
  • passive/s: 每秒远程发起的 TCP 连接数(例如,通过 accept())
  • retrans/s: 每秒 TCP 重传的次数

active 和 passive 连接的计数通常对于估算服务器负载很有用:新接受的连接数(passive),和下游连接数(active)。可以将 active 视为出站,passive 视为入站,但这并不严格正确(例如,考虑本地主机到本地主机的连接)。

重传是网络或服务器问题的标志;可能是不可靠的网络(例如公共互联网),也可能是服务器过载并丢弃数据包。上面的示例显示每秒仅一个新的 TCP 连接。

10. top

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ top
top - 00:15:40 up 21:56, 1 user, load average: 31.09, 29.87, 29.92
Tasks: 871 total, 1 running, 868 sleeping, 0 stopped, 2 zombie
%Cpu(s): 96.8 us, 0.4 sy, 0.0 ni, 2.7 id, 0.1 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem: 25190241+total, 24921688 used, 22698073+free, 60448 buffers
KiB Swap: 0 total, 0 used, 0 free. 554208 cached Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
20248 root 20 0 0.227t 0.012t 18748 S 3090 5.2 29812:58 java
4213 root 20 0 2722544 64640 44232 S 23.5 0.0 233:35.37 mesos-slave
66128 titancl+ 20 0 24344 2332 1172 R 1.0 0.0 0:00.07 top
5235 root 20 0 38.227g 547004 49996 S 0.7 0.2 2:02.74 java
4299 root 20 0 20.015g 2.682g 16836 S 0.3 1.1 33:14.42 java
1 root 20 0 33620 2920 1496 S 0.0 0.0 0:03.82 init
2 root 20 0 0 0 0 S 0.0 0.0 0:00.02 kthreadd
3 root 20 0 0 0 0 S 0.0 0.0 0:05.35 ksoftirqd/0
5 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/0:0H
6 root 20 0 0 0 0 S 0.0 0.0 0:06.94 kworker/u256:0
8 root 20 0 0 0 0 S 0.0 0.0 2:38.05 rcu_sched

top 命令包含我们之前检查过的许多指标。运行它可以很方便地查看是否有任何内容与之前的命令有很大不同,这将表明负载是变化的。

然而,top 的一个不足之处是很难随着时间观察数据的变化趋势,而在像 vmstatpidstat 这样提供滚动输出的工具中这可能会更清晰。如果不及时暂停输出(使用Ctrl-S暂停,Ctrl-Q继续),屏幕会被清除,从而可能会丢失间歇性问题的证据。

进一步的分析

您可以应用更多的命令和方法来进行更深入地钻研。请参阅 Brendan 在 Velocity 2015 中的 Linux 性能工具教程 ,其中使用了 40 多条命令,涵盖了可观察性、基准测试、调优、静态性能调优、分析和跟踪等内容。

最初于 2015 年 11 月 30 日在 techblog.netflix.com 上发布。