uptime 命令显示 400 days。或者 800 days。甚至可能是 1,200 天。

你感到自豪。你的服务器看起来像个明星。

但只有生产环境宕机才能教会你一个真相:长时间的 uptime 往往掩盖的问题比它解决的更多。

我见过运行时间长达两年的服务器,在一次简单的重启后彻底崩溃。高运行时间 no 是健康的标志。它只说明 no 有人长时间仔细检查过它。

所以,让我分享一份完整的 Linux 服务器健康检查清单。下次你看到一台高 uptime 运行时间的服务器时,逐一执行这 25 项检查。有些很快。其他的需要一分钟。但它们共同揭示出表面之下实际发生的情况。

第 1 部分:系统资源(那些会撒谎的基础指标)

这些指标表面看起来 fi 好。但高 uptime 掩盖了底下的缓慢衰败。

1. 查看相对于 CPU 数量的 15 分钟负载均值

运行以下命令:

uptime
nproc

你会看到三个数字:1 分钟、5 分钟和 15 分钟。当 1 分钟的数字高于 15 分钟的数字时,表明负载呈上升趋势——负载累积的速度快于其被清除的速度。反过来(15 分钟高于 1 分钟)实际上意味着负载正在从过去的峰值下降。

不过,真正的问题 no 是比较这三个数字彼此之间的关系。而是将这些数字中的任何一个与你的 CPU 核心数进行比较。如果 15 分钟均值持续超过 nproc 返回的 CPU 数量,那么你的系统已经饱和,工作正在排队等待处理,速度超过了处理能力。

检查什么: 如果 1 分钟负载高于 15 分钟负载,则负载呈上升趋势。如果任何均值持续超过你的 CPU 核心数,你就遇到了饱和问题。将三个数字放在一起比较以获取趋势信息,但始终以你的核心数作为基准。


2. 检查 available 内存,而不是 free 内存

运行以下命令:

free -h

查看 available 列。忽略 free。

total        used        free      shared  buff/cache   available
Mem:            30Gi       8.7Gi        16Gi       1.2Gi       7.2Gi        22Gi
Swap:          975Mi          0B       975Mi

available 内存是系统实际可以分配给新进程而无需交换的内存。如果这个数字一周又一周地持续下降,你就遇到了缓慢的内存泄漏。

检查什么: 记下今天的 available 内存。一周后再检查一次。它降低了吗?

3. 统计僵尸进程数量

运行这些命令:

top -bn1 | grep "zombie"

或者直接检查僵尸进程计数:

ps aux | awk '$8=="Z" {print $0}' | wc -l

或者

ps -eo stat= | grep -c '^Z'

一个僵尸进程 no 足为奇。几个也 fi 没问题。但超过十个僵尸进程并且持续数天?那才是真正的问题。每个僵尸进程都占用一个进程表槽位。一旦该表被填满,no 任何新进程都无法运行。没有新的 SSH 会话。没有新的监控检查。什么都没有。

检查什么: 僵尸进程计数在大多数时候应该为零。偶尔出现几个是可以的。持续存在的僵尸进程 no 是好事。

过多的僵尸进程最终会耗尽 available 进程 ID 或进程表资源。

4. 验证 inode 使用情况,而不仅仅是磁盘空间

运行这个:

df -i

磁盘空间总是备受关注。但 inode(存储 fi 文件信息的数据结构)即使在你还有大量空间时也可能耗尽。当这种情况发生时,你 no 无法创建新 fi 文件。日志停止写入。服务静默崩溃。

检查什么: 查找任何 IUse% 超过 85% 的分区。那是你的警告信号。

udev            4041811     635  4041176    1% /dev
tmpfs           4060692    1209  4059483    1% /run
/dev/nvme0n1p2 30433280 2949390 27483890   10% /
tmpfs           4060692     434  4060258    1% /dev/shm
tmpfs           4060692      16  4060676    1% /run/lock
efivarfs              0       0        0     - /sys/firmware/efi/efivars
/dev/nvme0n1p1        0       0        0     - /boot/efi
tmpfs            812138     268   811870    1% /run/user/1000
/dev/sda1             0       0        0     - /media/ostechnix/SK_WD_SSD
/dev/fuse        262144      40   262104    1% /etc/pve

5. 检查处于不可中断睡眠状态(D 状态)的进程

运行这个:

ps aux | awk '$8=="D" {print $0}'

处于 D 状态的进程正在直接等待硬件。它们 no 无法被杀死。即使使用 -9 也不行。在磁盘 I/O 繁重期间,出现少量此类进程是 no 正常的。但如果它们持续超过几分钟,你的存储子系统肯定出了大问题。

检查什么: 零个卡住的 D 状态进程是目标。持续存在的 D 状态进程通常表明存储、fi 文件系统或网络挂载的 fi 文件系统存在问题。

6. 在日志中查找 OOM Killer 事件

运行这些:

sudo dmesg | grep -i -E "killed process|out of memory|oom"
journalctl -k | grep -iE "oom|out of memory|killed process"

OOM Killer 是 Linux 的最后手段。当内存完全耗尽时,内核会开始杀死进程以维持存活。这从来都不是一个好兆头。在一台高 uptime 运行时间的服务器上,你可能会 fi 发现几个月前的 OOM 事件,而你从未 no 注意到。

检查什么: 任何 OOM Killer 事件都意味着你曾有过内存问题。一次事件是一个警告。多次事件意味着你有一个模式。

第 2 部分:磁盘和文件系统(腐烂发生的地方)

高 uptime 的服务器通常从 fi 文件系统开始腐烂。这些检查能及早发现腐烂。

7. 查找最大的日志 fi 文件

运行这些:

find /var/log -type f -size +100M -exec ls -lh {} \;
du -sh /var/log/* | sort -hr | head -10

如果日志轮转失效,日志 fi 文件会无限增长。而日志轮转失效的频率比你想象的要高。一个配置错误的 cron 任务或六个月前的权限更改都可能静默地停止所有日志轮转。

检查什么: 任何单个日志 fi 文件超过 1GB 需要调查。超过 10GB 就已经是一次事故了。换句话说,无论大小如何,意外的增长都值得调查。

8. 验证 logrotate 是否正常工作

运行这些:

logrotate --debug /etc/logrotate.conf 2>&1 | head -30
journalctl -u logrotate --since "30 days ago" 2>/dev/null | grep -i "error\|fail"
grep -i "logrotate" /var/log/syslog 2>/dev/null | grep -i "error\|fail" | tail -10

日志轮转 可能会在无人 no 注意的情况下失败。[cron 任务](https://ostechnix.com/a-beginners-guide-to-cron-jobs/) 运行了。命令退出了。但权限问题或缺失的目录会导致静默失败。在一台高 uptime 运行时间的服务器上,日志轮转可能已经坏了多年。

注意: logrotate 默认 no 会在任何主流发行版上写入一个专用的 /var/log/logrotate.log 文件。其状态记录在 /var/lib/logrotate/status 和/或 /var/lib/logrotate/logrotate.status 中。它遇到的任何错误都可能出现在系统日志或 syslog 中,这就是第二和第三个命令查找的地方。

检查什么: 调试输出应显示每个日志 fi 文件被处理且没有错误。根据发行版检查 /var/lib/logrotate/status 和/或 /var/lib/logrotate/logrotate.status,以确认关键日志 fi 文件最近的轮转时间戳。


9. 测试每个 NFS 或网络挂载

运行这些:

mount | grep -E "nfs|nfs4|cifs"
for mount in $(mount | grep -E "nfs|nfs4|cifs" | awk '{print $3}'); do
timeout 5 ls $mount > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "STALE: $mount"
fi
done

陈旧的 NFS 挂载是阴险的。服务器保持运行。挂载看起来 fi 正常。但任何触及该挂载的进程都会挂起 60 秒或更长时间。高 uptime 的服务器会慢慢积累这些挂载,每个陈旧的挂载都会给所有检查它的操作增加延迟。

检查什么: 每个网络挂载都应在 2 秒内响应 ls。挂起意味着陈旧的挂载。

你也可以使用这个命令:

mount | grep -E "nfs|nfs4|cifs" | while read -r line; do
mountpoint=$(echo "$line" | awk '{print $3}')
timeout 5 ls "$mountpoint" >/dev/null 2>&1
done

10. 检查磁盘 I/O 等待时间

运行这个:

iostat -x 1 5

查看 %iowait 列和 await 列。

高 iowait 意味着你的 CPU 在等待磁盘上花费了时间。然而,在多 -c 核系统上,仅靠 iowait 可能会产生误导——一个 32 核机器上的单个 I/O 密集型进程可以饱和一个磁盘,而 iowait 却几乎不显示。始终将其与 iostat -x 中每个 -d 设备的 await 值相关联。

await 列显示从 I/O 请求提交到完成所需的平均时间(毫秒)。在生产负载下,SATA SSD 上高于 20ms 的值或 NVMe 驱动器上高于 5ms 的值值得调查。请记住,在轻负载下,现代 NVMe 驱动器通常远低于 1ms,SATA SSD 则低于 2ms。任何在这些负载阈值以上的持续峰值都指向设备饱和或硬件退化。

检查什么: 不要仅仅依赖一个 %iowait 数字。使用 iostat -x 检查每个 -d 设备的 await 值,并查找 %util 接近 100% 的设备。与应用程序响应时间相关联,以判断这些数字是否反映了你的工作负载的真正瓶颈。


11. 查找设置了 SUID/SGID 位的 fi 文件

运行这个:

find / -perm /6000 -type f 2>/dev/null | grep -v "^/proc"

SUID 和 SGID 位允许程序以提升的权限运行。任何系统上都应有一小部分此类文件——/bin/su、/usr/bin/passwd、/usr/bin/sudo 和 /usr/bin/newgrp 是常见的例子。但随着时间的推移,新的可能会出现。旧软件可能会添加它们。错误配置可能会创建它们。在一台高 uptime 运行时间的服务器上,这些可能会在无人 no 注意的情况下积累。

注意:在现代 Linux 发行版上,一些以前需要 SUID 的二进制文件(例如 ping)已被迁移为使用 Linux capabilities。如果你看到一个带有 SUID 但你 no 不期望的二进制文件——即使是一个熟悉的——也值得对照发行版的默认软件包进行确认。

检查什么: 审查列表。任何不熟悉的内容都需要调查。

12. 查找全局可写的 fi 文件和目录

运行这个:

find / -type f -perm -0002 -ls 2>/dev/null | grep -v "^/proc"

全局可写的 fi 文件是一个安全风险。系统上的任何用户都可以修改它们。在一个有许多用户的繁忙服务器上,一个全局可写的配置文件可能导致权限提升。

全局可写的目录通常比文件更危险。也运行这个来检查目录:

find / -type d -perm -0002 -ls 2>/dev/null

检查什么: 列表应该非常短。系统日志文件有时是全局可读的,但很少是全局可写的。/etc 或 /usr 中的任何内容都不应该是全局可写的。

第 3 部分:进程和服务(运行中的混乱)

一台高 uptime 运行时间的服务器会像冰箱积累旧食物一样积累混乱的进程。

13. 查找运行时间非常长的进程

运行这个:

ps -eo pid,etime,comm,args --sort=-etime | head -20

已经运行了数月的进程可能 fi 是正常的。或者它们可能正在挂起、泄漏内存,或者什么 no 有用的事都没做。在一台高 uptime 运行时间的服务器上,你经常会 fi 发现那些本应退出但从未退出的旧脚本。

检查什么: 对于每个旧进程,问:这个进程应该还在运行吗?它需要运行吗?它能被干净地重启吗?

14. 检查重叠的 cron 任务

运行这个:

# User-level crontabs
for user in $(cut -f1 -d: /etc/passwd); do
crontab -u $user -l 2>/dev/null
done
# System-level cron locations (these are where most production jobs actually live)
cat /etc/crontab
ls -la /etc/cron.d/
ls -la /etc/cron.daily/ /etc/cron.weekly/ /etc/cron.monthly/ /etc/cron.hourly/

一个需要 15 分钟才能完成但每 10 分钟运行一次的 [cron 任务](https://ostechnix.com/a-beginners-guide-to-cron-jobs/) 会创建重叠的进程。随着时间的推移,这些进程会堆积起来。系统保持运行,但每天都运行得更糟。高 uptime 完美地隐藏了这些调度错误。

在生产服务器上,大多数计划任务位于 /etc/cron.d/、/etc/crontab 和 cron.daily/weekly/monthly/hourly 目录中——no 在用户的 crontab 中。仅检查用户 crontab 会错过典型服务器上的大部分计划工作。

检查什么: 查找那些运行频率高于其完成所需时间的任务。添加日志记录以查看实际运行时间。


15. 验证 systemd 服务状态

运行这些:

systemctl list-units --state=failed
systemctl list-units --state=inactive --all | grep -v "not-found"

失败的服务是显而易见的。但那些本应运行却“成功”停止的服务更难发现。在一台高 uptime 运行时间的服务器上,一个关键服务可能早在几个月前就停止了,而无人 no 注意到。

检查什么: 每个应该运行的服务都应显示 active (running)。任何其他状态都需要一个理由。

注意: 大型系统可能有数百个有意不活跃的单元。只审查预期活跃的服务。

16. 查找持续重启的服务

运行这个:

# Find services with automatic restart counts above zero
systemctl list-units --type=service --state=running --no-pager | awk '{print $1}' | \
xargs -I{} sh -c 'count=$(systemctl show {} -p NRestarts 2>/dev/null | cut -d= -f2); \
[ "$count" -gt 0 ] 2>/dev/null && echo "NRestarts=$count {}"'
# Confirm with journal history for any service you find
journalctl -u service-name --since "7 days ago" | grep -E "Started|Stopped|Failed" | tail -20

有些服务会不断崩溃和重启。系统在某种意义上让它们保持“运行”。但持续重启意味着某些东西坏了。在一台高 uptime 运行时间的服务器上,这种情况可能持续数月而无人查看日志。

关键区别:systemctl list-units --state=activating 只显示在你运行命令时当前正在启动的服务——它永远不会显示一个崩溃后快速恢复的服务。NRestarts 属性才是实际记录 systemd 必须自动重启服务次数的属性,并且它在那些快速恢复周期中持续存在。

检查什么: 任何 NRestarts 大于零的服务都值得一看。与日志交叉引用,以查看它实际崩溃的频率以及在每次重启前记录了哪些错误。同时,调查那些重启次数异常高或持续增加的服务。

17. 检查失败的定时器(systemd 的 cron 替代品)

运行这个:

systemctl list-timers --all

Systemd 定时器可能会静默失败。定时器运行了。命令以错误码退出。但 no 没有警报触发。几个月下来,一个失败的备份定时器意味着 no 备份。一个失败的日志轮转定时器意味着磁盘会被填满。

--all 标志不带任何状态过滤器是正确的方法。它在一个视图中显示每个定时器及其 LAST 和 NEXT 运行时间。按 --state=waiting 或 --state=running 过滤可能会隐藏处于意外状态的定时器,而这正是你试图发现的。

检查什么: 每个定时器都应显示一个已填充的 LAST 列。任何 LAST 列为空的定时器都从未成功运行过。任何 LAST 日期远早于其配置间隔的定时器都已静默停止触发。

第 4 部分:安全(静默的积累)

安全问题在高 uptime 运行时间的服务器上缓慢积累。这些检查能发现攻击者喜欢的东西。

18. 检查待处理的安全更新

运行这个(Debian/Ubuntu):

apt list --upgradable 2>/dev/null | grep -i security

在 Ubuntu 系统上,还有一个专用命令用于此目的:

ubuntu-security-status

运行这个(RHEL/CentOS/Rocky):

dnf updateinfo list --security

一台高 uptime 运行时间的服务器通常意味着 no 重启。而 no 重启通常意味着 no 内核更新。而 no 内核更新意味着已知的漏洞会持续数月或数年未修补。

检查什么: 待处理的安全更新应在几天内应用,no 是几个月。内核更新需要重启。这就是残酷的现实。

19. 审查失败的 SSH 登录尝试

运行这些:

sudo grep "Failed password" /var/log/auth.log 2>/dev/null || sudo grep "Failed password" /var/log/secure
sudo lastb | head -20

许多现代系统仅将 SSH 身份验证日志存储在 journald 中。在这些系统中,你可以使用:

journalctl -u ssh

或者,

journalctl -u sshd

失败的 SSH 尝试表明存在暴力破解攻击。在一台暴露于互联网的高 uptime 运行时间服务器上,你可能会看到来自世界各地的数千次尝试。这是 no 正常的噪音。但突然的峰值或来自陌生 IP 的成功登录则 no 是好事。

检查什么: 查找来自意外 IP 的成功登录。查找来自同一 IP 的失败尝试模式。

20. 检查未经授权的用户帐户

运行这些:

grep -E "sh$|bash$" /etc/passwd
lastlog | grep -v "Never logged in"

对于 bash 以外的 shell,你可以使用这个命令:

getent passwd | awk -F: '$7 !~ /(nologin|false)$/'

用户帐户会随着时间的推移而积累。合同工离开了。旧管理员离开了。但他们的帐户有时会保留下来。在一台高 uptime 运行时间的服务器上,你可能会发现已经多年不活跃的帐户。

检查什么: 每个具有登录 shell 访问权限的用户都应是授权且活跃的。禁用或删除其余的。

21. 验证 SSH 配置安全性

运行这个:

sshd -T | grep -E "permitrootlogin|passwordauthentication"

使用密码进行 root 登录是危险的。通过互联网进行密码认证是有风险的。在一台老旧的高 uptime 运行时间服务器上,你可能会发现从未更新过的过时 SSH 配置。

检查什么: PermitRootLogin 应为 prohibit-password 或 no。如果你使用密钥,PasswordAuthentication 应为 no。注意:旧的 Protocol 指令已在 OpenSSH 7.6(2017)中被移除,因为 SSHv2 现在是唯一支持的协议——你 no 需要检查它。现代系统无需任何配置指令即可强制执行 SSHv2。

22. 扫描你未预料到的开放端口

运行这些:

ss -tulpn | grep LISTEN
nmap -sT -p- localhost 2>/dev/null

高 uptime 运行时间的服务器通常会随着时间推移积累服务。有人为测试安装了数据库。有人启动了一个调试用的 Web 服务器。然后所有人都忘记了。这些被遗忘的服务是完美的攻击目标。

检查什么: 每个开放端口都应有记录在案的用途。未知的端口意味着未知的服务。

了解如何使用防火墙规则和最佳实践在 Linux 中 检查开放端口 并 保护它们,请参阅下面的链接:

23. 检查 Rootkit

如果尚未安装,则安装(Debian/Ubuntu):

sudo apt install rkhunter chkrootkit

如果尚未安装,则安装(RHEL/CentOS/Rocky):

sudo dnf install rkhunter chkrootkit

注意: chrootkit 软件包的可用性取决于发行版和已启用的仓库。请查阅你发行版的官方文档。

然后运行:

sudo rkhunter --check --skip-keypress
sudo chkrootkit

Rootkit 很少见。但在一台多年未仔细检查的高 uptime 运行时间服务器上,值得检查一下。

在信任这些工具的输出之前,要理解它们能做什么和不能做什么。rkhunter 和 chkrootkit 都使用系统自身的二进制文件运行——而一个复杂的 rootkit 可能已经替换了这些二进制文件。一个干净的结果是一个微弱的信号,no 是一个保证。这两个工具也会在现代系统上产生大量误报,尤其是在频繁更新软件包的发行版上使用 rkhunter 时。每个警告都需要在得出结论之前进行手动验证。

为了更强的保证,考虑使用一个合适的文件完整性监控器,如 AIDE 或 Tripwire,它会将文件与系统已知干净时建立的加密基线进行比较。

检查什么: rkhunter 或 chkrootkit 的任何警告都需要调查,但预期会有误报。手动验证每个发现。不要将干净的扫描视为健康的保证。

第 5 部分:监控和验证(负责观察的系统)

你不能信任已经静默了 400 days 的监控系统。这些检查验证观察者本身。

24. 验证你的监控警报是否正常工作

运行这些:

# Check when the last alert was sent
grep "ALERT" /var/log/monitoring.log | tail -5
# Simulate a test alert
/usr/local/bin/send_test_alert.sh

这项检查很伤人。我见过太多次了。一台服务器显示 500 days 的 uptime。监控仪表板看起来一片绿。然后你发现警报脚本在 400 days 前就停止运行了。磁盘被填满了。CPU 过热了。但 no 没有人收到一个警报。

请注意,这些路径只是示例。请将它们替换为你实际的路径。

检查什么: 这台服务器上一次真正的警报是什么时候?你能现在就触发一个测试警报吗?有人收到它吗?

25. 提出重启问题(最重要的问题)

这不是一个命令。这是一次对话。

大声问出来:如果我们现在就重启这台服务器,它会完全按照应有的方式恢复吗?

每个服务都能正确启动吗?所有挂载都存在吗?网络配置正确吗?应用程序能在无需人工干预的情况下启动吗?

如果答案是“可能”或“我们不确定”或“让我检查一下操作手册”,那么 uptime 就只是一个数字。它 no 是可靠性。它 no 是安全性。它只是运气。

检查什么: 在预发布环境中测试重启。或者在维护窗口期间安排一次。真正的信心来自实践,no 来自一个计数器。

结论

长 uptime no 是荣誉的徽章。它是一个数据点。

一台拥有 30 days 的 uptime 并定期进行健康检查的服务器,比一台拥有 800 days 的 uptime 但 no 有验证的服务器更安全。问题 no 是“它运行了多久?”问题是“我们如何知道它工作正常?”

每月运行一次这份清单。不是因为期望发现问题。而是因为你没有发现的问题,才是最终会让你宕机的问题。

下一步

你找到了根本原因,接下来呢?在对任何 Linux 系统进行故障排除之前,运行一小组只读命令来捕获系统当前状态,以免任何干预措施破坏证据。我们在下面的链接中整理了一份推荐的 Linux 故障排除命令清单。

该指南解释了在你触碰任何东西之前,应该在 Linux 系统中检查什么。