黑客与 / - Linux 故障排除,第一部分:高负载
本专栏是致力于我最喜欢的主题之一:故障排除的系列专栏中的第一篇。我白天是一名系统管理员,虽然我喜欢我工作的很多方面,但是没有什么比得上追踪复杂的服务器问题时,以美元计算停机时间的那种肾上腺素飙升的感觉。虽然确实停机的原因和 Linux 文本编辑器一样多,故障排除的方法也同样多,但多年来,我发现我执行相同的步骤来隔离问题。因为我的专栏通常更侧重于技巧和窍门,而不是哲学和设计,所以我不会过多谈论解决问题的总体方法。相反,在本系列中,我将描述您可能在 Linux 系统上发现的一些常见问题类型,然后我将讨论如何使用常用工具(其中大多数可能已在您的系统上)来隔离和解决每类问题。
在第一篇专栏中,我从您在 Linux 系统上最常遇到的问题之一开始。不,这不是让打印工作。我指的是一台运行缓慢的服务器,它可能负载很高。但在我解释如何诊断和修复高负载之前,让我们退后一步,讨论一下负载在 Linux 机器上意味着什么,以及如何知道何时负载过高。
当管理员提到高负载时,通常他们谈论的是 平均负载。当我诊断服务器速度缓慢的原因时,我登录系统后运行的第一个命令是 uptime
$ uptime 18:30:35 up 365 days, 5:29, 2 users, load average: 1.37, 10.15, 8.10
如您所见,今天是我的服务器运行时间纪念日。您还可以看到我的平均负载是1.37, 10.15, 8.10。这些数字分别代表我的系统在过去 1、5 和 15 分钟内的平均负载。从技术上讲,平均负载代表在过去 1、5 或 15 分钟内必须等待 CPU 时间的平均进程数。例如,如果我当前的负载为 0,则系统完全空闲。如果我的负载为 1,则 CPU 足够繁忙,以至于一个进程不得不等待 CPU 时间。如果我的负载确实为 1,然后我生成另一个通常会占用 CPU 的进程,则我的负载应变为 2。通过平均负载,系统将很好地让您了解过去 1、5 和 10 分钟内的持续繁忙程度。
在查看平均负载时,要记住的另一个重要事项是,它没有根据系统上的 CPU 数量进行标准化。一般来说,持续负载为 1 表示系统上的一个 CPU 被占用。简而言之,这意味着负载为 1 的单 CPU 系统与负载为 4 的四 CPU 系统大致一样繁忙。所以在上面的例子中,假设我有一个单 CPU 系统。如果我登录并看到上面的平均负载,我可能会认为服务器在过去 15 分钟内负载相当高 (8.10),大约在 5 分钟前达到峰值 (10.15),但最近,至少在过去 1 分钟内,负载已显着下降。如果我看到这种情况,我甚至可能会认为负载的真正原因已经消退。另一方面,如果平均负载为 20.68、5.01、1.03,我会得出结论,高负载很可能是在过去 5 分钟内开始的,并且正在变得更糟。
在您了解平均负载的含义后,下一个合乎逻辑的问题是“什么样的平均负载是好的,什么样的平均负载是坏的?” 答案是“视情况而定”。您看,很多不同的事情都可能导致负载过高,每种情况对性能的影响都不同。一台服务器的负载可能是 50,但仍然响应迅速,而另一台服务器的负载可能是 10,但登录却需要很长时间。我曾遇到过平均负载达到数百的服务器,它们当然很慢,但没有崩溃,我还遇到过一台服务器,其平均负载始终为 50,但仍然响应迅速,并且运行了多年。
当您对负载过高的系统进行故障排除时,真正重要的是 原因 负载过高。当您开始诊断高负载时,您会发现大多数负载似乎都属于三个类别:CPU 密集型负载、内存不足问题引起的负载和 I/O 密集型负载。我在下面详细解释了这些类别中的每一个,以及如何使用 top 和 iostat 等工具来隔离根本原因。
如果我登录运行缓慢的系统时使用的第一个工具是 uptime,那么我使用的第二个工具是 top。top 的优点在于它适用于所有主要的 Linux 系统,并且它在单个屏幕中提供了大量有用的信息。top 是一个非常复杂的工具,有很多选项,可以单独写一篇文章来介绍它。在本专栏中,我将重点介绍如何解释其输出来诊断高负载。
要使用 top,只需键入top在命令行中。默认情况下,top 将以交互模式运行,并每隔几秒钟更新其输出。列表 1 显示了终端中的 top 示例输出。
列表 1. top 示例输出
top - 14:08:25 up 38 days, 8:02, 1 user, load average: 1.70, 1.77, 1.68 Tasks: 107 total, 3 running, 104 sleeping, 0 stopped, 0 zombie Cpu(s): 11.4%us, 29.6%sy, 0.0%ni, 58.3%id, .7%wa, 0.0%hi, 0.0%si, 0.0%st Mem: 1024176k total, 997408k used, 26768k free, 85520k buffers Swap: 1004052k total, 4360k used, 999692k free, 286040k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 9463 mysql 16 0 686m 111m 3328 S 53 5.5 569:17.64 mysqld 18749 nagios 16 0 140m 134m 1868 S 12 6.6 1345:01 nagios2db_status 24636 nagios 17 0 34660 10m 712 S 8 0.5 1195:15 nagios 22442 nagios 24 0 6048 2024 1452 S 8 0.1 0:00.04 check_time.pl
如您所见,仅几行就包含了很多信息。第一行镜像了您从 uptime 命令获得的信息,并将每隔几秒钟更新一次最新的平均负载。在本例中,您可以看到我的系统很忙,但不能称之为负载过重。尽管如此,此输出很好地分解为我们的不同负载类别。当我对运行缓慢的系统进行故障排除时,我通常会按顺序排除 CPU 密集型负载、RAM 问题,最后是 I/O 问题,所以让我们从 CPU 密集型负载开始。
CPU 密集型负载是指当您同时运行太多 CPU 密集型进程时引起的负载。由于每个进程都需要 CPU 资源,因此它们都必须轮流等待。要检查负载是否为 CPU 密集型,请检查 top 输出中的 CPU 行
Cpu(s): 11.4%us, 29.6%sy, 0.0%ni, 58.3%id, .7%wa, 0.0%hi, 0.0%si, 0.0%st
这些百分比中的每一个都是 CPU 时间的百分比,用于执行特定任务。同样,您可以花一整篇文章来介绍 top 的所有输出,所以这里介绍其中一些值以及如何读取它们
us:用户 CPU 时间。通常,当您遇到 CPU 密集型负载时,这是由于系统上用户运行的进程引起的,例如 Apache、MySQL 或许是 shell 脚本。如果此百分比很高,则用户进程(如这些进程)很可能是负载的原因。
sy:系统 CPU 时间。系统 CPU 时间是内核和其他系统进程占用的 CPU 百分比。CPU 密集型负载应表现为用户或系统 CPU 时间的百分比很高。
id:CPU 空闲时间。这是 CPU 空闲的时间百分比。此处的数字越高越好!事实上,如果您看到非常高的 CPU 空闲时间,则很好地表明任何高负载都不是 CPU 密集型的。
wa:I/O 等待。I/O 等待值表示 CPU 花费在等待 I/O(通常是磁盘 I/O)的时间百分比。如果您负载很高且此值很高,则负载很可能不是 CPU 密集型的,而是由于 RAM 问题或高磁盘 I/O 引起的。
如果您确实看到用户或系统列中的百分比很高,则很可能您的负载是 CPU 密集型的。要追踪根本原因,请向下跳几行到 top 显示系统上当前正在运行的进程列表的位置。默认情况下,top 将根据 CPU 使用百分比对这些进程进行排序,CPU 使用率最高的进程位于顶部(列表 2)。
列表 2. 当前进程示例
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 9463 mysql 16 0 686m 111m 3328 S 53 5.5 569:17.64 mysqld 18749 nagios 16 0 140m 134m 1868 S 12 6.6 1345:01 nagios2db_status 24636 nagios 17 0 34660 10m 712 S 8 0.5 1195:15 nagios 22442 nagios 24 0 6048 2024 1452 S 8 0.1 0:00.04 check_time.pl
%CPU 列告诉您每个进程占用的 CPU 量。在本例中,您可以看到 MySQL 占用了我 53% 的 CPU。当您在 CPU 密集型负载期间查看此输出时,您可能会看到两种情况之一:要么您会有一个进程占用 99% 的 CPU,要么您会看到许多较小的进程都在争夺 CPU 时间的百分比。在任何一种情况下,相对来说都容易看到导致问题的进程。关于 CPU 密集型负载,我最后想补充一点:我见过系统负载非常高,仅仅是因为一个多线程程序在没有太多 CPU 的系统上生成了大量线程。如果您在单 CPU 系统上生成 20 个线程,您可能会看到平均负载很高,即使没有任何特定的进程似乎占用 CPU 时间。
高负载的下一个原因是系统已耗尽可用 RAM 并已开始进入交换空间。由于交换空间通常位于比 RAM 慢得多的硬盘驱动器上,因此当您用完可用 RAM 并进入交换空间时,每个进程都会随着磁盘的使用而急剧减速。通常,这会导致恶性循环,因为已交换的进程运行速度较慢,响应时间更长,并导致更多进程堆积,直到系统耗尽 RAM 或速度绝对慢下来。交换问题的棘手之处在于,由于它们对磁盘的冲击如此之大,因此很容易将它们误诊为 I/O 密集型负载。毕竟,如果您的磁盘被用作 RAM,则任何实际上想要访问磁盘上文件的进程都必须排队等待。因此,如果我在 top 的 CPU 行中看到高 I/O 等待,我会接下来检查 RAM 并排除它,然后再对任何其他 I/O 问题进行故障排除。
当我想诊断内存不足问题时,我首先查看 top 输出中的接下来几行
Mem: 1024176k total, 997408k used, 26768k free, 85520k buffers Swap: 1004052k total, 4360k used, 999692k free, 286040k cached
这些行告诉您 RAM 和交换的总量以及已用和空闲量;但是,请仔细查看,因为这些数字可能会产生误导。我见过许多新手甚至有经验的管理员查看上面的输出并得出结论,系统几乎耗尽了 RAM,因为只有 26768k 空闲。虽然这确实显示了当前有多少 RAM 未使用,但它并没有说明全部情况。
当您访问文件并且 Linux 内核将其加载到 RAM 中时,当您不再需要该文件时,内核不一定会卸载该文件。如果有足够的可用 RAM,内核会尝试将尽可能多的文件缓存到 RAM 中。这样,如果您第二次访问该文件,内核可以从 RAM 而不是磁盘检索它,从而提供更好的性能。随着系统持续运行,您会发现空闲 RAM 实际上会显得相当小。但是,如果进程需要更多 RAM,内核只需使用其文件缓存中的一些。事实上,我看到很多超频爱好者想要提高性能并创建一个 ramdisk 来存储他们的文件。他们没有意识到的是,通常情况下,如果他们只是让内核为他们完成工作,他们可能会看到更好的结果,并更有效地利用他们的 RAM。
要获得更准确的可用 RAM 量,您需要将 free 列中的值与 cached 列中的值结合起来。在我的示例中,我将有 26768k + 286040k,或超过 300Mb 的可用 RAM。在这种情况下,我可以安全地假设我的系统没有遇到内存不足问题。当然,即使是可用 RAM 非常少的系统也可能没有进入交换空间。这就是为什么您还必须检查 Swap: 行,看看是否使用了很高比例的交换空间。
如果您确实发现您的可用 RAM 不足,请返回到 top 的相同进程输出,只是这次查看 %MEM 列。默认情况下,top 将按 %CPU 列排序,因此只需键入 M,它将重新排序以显示哪些进程正在使用最高百分比的 RAM。在列表 3 的输出中,我按 RAM 对相同的进程进行了排序,您可以看到 nagios2db_status 进程使用的最多,为 6.6%。
列表 3. 按 RAM 排序的进程
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 18749 nagios 16 0 140m 134m 1868 S 12 6.6 1345:01 nagios2db_status 9463 mysql 16 0 686m 111m 3328 S 53 5.5 569:17.64 mysqld 24636 nagios 17 0 34660 10m 712 S 8 0.5 1195:15 nagios 22442 nagios 24 0 6048 2024 1452 S 8 0.1 0:00.04 check_time.pl
有时,I/O 密集型负载可能很难追踪。正如我之前提到的,如果您的系统正在交换,则可能会使负载看起来是 I/O 密集型的。但是,一旦您排除了交换,如果您确实有高 I/O 等待,下一步是尝试追踪哪个磁盘和分区正在获得大部分 I/O 流量。为此,您需要像 iostat 这样的工具。
像 top 一样,iostat 工具也是一个复杂且功能齐全的工具,可以写一篇文章来专门介绍它。与 top 不同,虽然 iostat 工具应该适用于您的发行版,但默认情况下可能未安装在您的系统上,因此您需要追踪哪个软件包提供了它。在基于 Red Hat 和 Debian 的系统中,您可以在 sysstat 软件包中获得它。安装完成后,只需运行不带参数的 iostat 即可获得磁盘 I/O 统计信息的良好总体视图
Linux 2.6.24-19-server (hostname) 01/31/2009 avg-cpu: %user %nice %system %iowait %steal %idle 5.73 0.07 2.03 0.53 0.00 91.64 Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn sda 9.82 417.96 27.53 30227262 1990625 sda1 6.55 219.10 7.12 15845129 515216 sda2 0.04 0.74 3.31 53506 239328 sda3 3.24 198.12 17.09 14328323 1236081
与 top 一样,iostat 为您提供了 CPU 百分比输出。在其下方,它提供了系统上每个驱动器和分区的细分,以及每个驱动器和分区的统计信息
tps:每秒事务数。
Blk_read/s:每秒读取的块数。
Blk_wrtn/s:每秒写入的块数。
Blk_read:读取的总块数。
Blk_wrtn:写入的总块数。
通过查看这些不同的值并将它们相互比较,理想情况下,您将能够首先找出哪个分区(或哪些分区)正在获得大部分 I/O 流量,其次,大部分流量是读取 (Blk_read/s) 还是写入 (Blk_wrtn/s)。正如我所说,追踪 I/O 问题的根源可能很棘手,但希望这些值将帮助您隔离哪些进程可能导致负载。
例如,如果您有 I/O 密集型负载,并且您怀疑您的远程备份作业可能是罪魁祸首,请比较读取和写入统计信息。因为您知道远程备份作业主要会从您的磁盘读取数据,所以如果您看到大部分磁盘 I/O 是写入,您可以合理地假设它不是来自备份作业。另一方面,如果您确实看到特定分区上有大量的读取 I/O,您可以运行 lsof 命令并 grep 备份进程,看看它是否确实在该分区上有一些打开的文件句柄。
如您所见,使用 iostat 追踪 I/O 问题并非易事。即使没有参数,也可能需要一些时间和经验才能理解输出。也就是说,iostat 确实有很多参数,您可以使用这些参数来获取有关不同类型 I/O 的更多信息,包括查找有关 NFS 共享详细信息的模式。如果您想了解更多信息,请查看 iostat 的手册页。
直到最近,像 iostat 这样的工具还是系统管理员工具箱中用于追踪 I/O 问题的极限工具,但由于内核的最新发展,查找每个进程级别的 I/O 原因变得更容易了。如果您有一个相对较新的系统,请查看 iotop 工具。与 iostat 一样,它可能默认未安装,但顾名思义,它的作用本质上类似于 top,只是用于磁盘 I/O。在列表 4 中,您可以看到此计算机上的 rsync 进程正在使用最多的 I/O(在本例中为读取 I/O)。
列表 4. iotop 工具输出示例
Total DISK READ: 189.52 K/s | Total DISK WRITE: 0.00 B/s TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND 8169 be/4 root 189.52 K/s 0.00 B/s 0.00 % 0.00 % rsync --server --se 4243 be/4 kyle 0.00 B/s 3.79 K/s 0.00 % 0.00 % cli /usr/lib/gnome- 4244 be/4 kyle 0.00 B/s 3.79 K/s 0.00 % 0.00 % cli /usr/lib/gnome- 1 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % init
Kyle Rankin 是旧金山湾区的系统架构师,也是许多本书的作者,包括 The Official Ubuntu Server Book、Knoppix Hacks 和 Ubuntu Hacks。他目前是 North Bay Linux Users' Group 的主席。