返回博客
2026年8月31日Sergei Solod10 分钟阅读

VPS 明明在线,Linux 却开始为一次磁盘读取等待 18 秒

一次真实的 Linux 生产环境排障经历:VPS 一直在线,但有效工作几乎停止。I/O 压力接近 100%,读取延迟达到 18.7 秒,EXT4 和 Nginx 被阻塞,而 backend 仍能在约 3 ms 内响应。

LinuxDevOpsVPS性能调试

在进入排障过程之前,我想先说明一点:我对 FDCServers 的总体印象并不差。这篇文章不是劝人远离这家服务商的评测,也不是试图评价他们全部基础设施的质量。我只是有一台 VPS,在某个时期遇到了一个非常棘手、并不常见的问题。

此前相当长一段时间里,这台 VPS 一直正常工作,也在稳定处理真实的生产流量。后来情况发生了变化。普通请求开始耗费异常长的时间,页面先是打开很慢,随后有时干脆完全打不开。另一些时候,机器又会在我来得及认真检查故障之前恢复成看起来完全正常的状态。

最严重时,我记录到:

CPU iowait:       97–100%
I/O PSI full:     ~95–98%
read latency:     up to 18.7 seconds
flush latency:    up to 53.6 seconds
I/O queue depth:  128+

Nginx 还在运行。Backend 也还在运行。VM 仍然在线。但几乎没有任何真正有用的工作能够继续完成。

生产故障当然令人沮丧,但排障过程本身却非常有意思。我喜欢 Linux 的一个原因正是这种场景:机器从外部看起来还活着,但多个彼此独立的内核接口可以逐步告诉你,有效进展究竟停在了哪里。这篇文章讲的是如何沿着这些线索定位问题,而不是要判断 FDCServers 是好还是坏。

active (running) 原来几乎说明不了什么

我先从最常见的检查开始:

top
free -h
df -h
systemctl status nginx

健康时大概是这样:

D-state processes: 0
CPU iowait:        ~0%
disk latency:      ~2–4 ms
I/O PSI some:      0.04
I/O PSI full:      0.04

如果我只在这种时刻登录,很容易得出 VPS 完全健康的结论。但正常流量一回来,机器的状态就可能彻底改变。对于间歇性故障,健康状态下的一张快照几乎无法说明故障发生时的真实情况。必须在问题正在出现时收集证据。

改变排查方向的 iostat 样本

r_await = 18744 ms
f_await = 53561 ms
aqu-sz  = 128.54
util    ≈ 100%
read    = 88 KB/s

完成的读取平均需要约 18.7 秒,flush 操作约 53.6 秒。平均队列长度超过 128,但真正的读取吞吐量却只有 88 KB/s

这不像是 storage 因为高效处理高负载而正常繁忙。虚拟块设备路径几乎已经饱和,却完成不了多少有效工作。单纯 utilization 高并不代表故障,但如果同时出现巨大的 latency、很长的 queue、被卡住的 task、极低的 throughput 和失败的 request,那就是完全不同的信号。

我不再把 iowait 当成诊断结论

blocked processes: 4–9
CPU iowait:        97–100%
CPU idle:          0%

很容易把它概括成“CPU 100% 的时间都在等磁盘”。作为直觉理解,这种说法有帮助,但 Linux 的 accounting 更复杂,而且 iowait 不是对磁盘本身的直接测量。因此我只把它当作一个症状,再去寻找独立证据。

PSI 表明 I/O 正在阻止有效工作继续

cat /proc/pressure/io

某个严重时段的数值是:

some avg10=99.14
full avg10=95.55

后续复现中,full 一度接近 98%。在 I/O pressure 中,some 表示至少有一部分 non-idle 工作因 I/O 而 stalled 的时间;full 表示所有 non-idle task 同时因 I/O 而 stalled 的时间。接近 100% 的值远不只是“磁盘很忙”,而是 workload 几乎没有机会继续前进。

D-state 让我停止怀疑某一个进程

ps -eo state,pid,ppid,etime,wchan:50,comm,args | awk 'NR==1 || $1 ~ /^D/'

单个进程短暂进入 D-state 并不能证明 storage 有问题。真正重要的是哪些进程同时被阻塞。我看到 jbd2systemd-journald、Nginx worker、Nginx cache process 以及其他 filesystem activity 同时卡住。

如果只有 backend 卡住,我就查 backend;如果只有 Nginx 卡住,我就查 Nginx。但当 Nginx、system journal 和 EXT4 journal thread 同时无法继续推进时,它们共享的 dependency 就比某一个进程更值得关注。这个案例中,共同点就是 filesystem 以及下面的 storage path。

Kernel stack 指向了下一层

EXT4 journal thread 出现在这些路径中:

wait_on_buffer
jbd2_log_wait_commit
jbd2_journal_commit_transaction

Nginx worker 则出现在普通 filesystem read path 中:

folio_wait_bit_common
filemap_read
generic_file_read_iter
ext4_file_read_iter
vfs_read
pread64

有一次 kernel 直接报告:

INFO: task nginx blocked for more than 122 seconds.

与此同时,systemctl status nginx 仍然可以显示 active (running)。这两件事都是真的:进程存在,但某个 Nginx task 已经超过两分钟无法完成有效工作。进程在运行,并不等于服务健康。

最清晰的对照实验只用了大约 3 毫秒

通过 Nginx 发起的请求失败:

HTTP=000
SSL connection timeout

随后我绕过 Nginx,直接请求本地 backend:

connect = 0.000423 s
TTFB    = 0.003063 s
total   = 0.003139 s

大约 3 ms。这个测试里具体的 HTTP status 并不重要。Backend 能接受连接、执行 request,并几乎立即返回 response。大致同一时间,Nginx worker 还停在 EXT4 read path 中。

application execution       → progressing normally
filesystem-backed web path  → not progressing normally

越往下查,application-level 原因就越不可信。

同一台 VPS 可以在大约 30 秒内从正常变成不可用

一次复现前:

HTTP:          200
D-state:       0
CPU iowait:    3%
r_await:       ~1.18 ms
I/O PSI full:  ~2.95%

大约半分钟后:

D-state:       4
CPU iowait:    91%
CPU idle:      0%
I/O PSI some:  86.11%
I/O PSI full:  78.02%
r_await:       236.50 ms
HTTP:          000

随后进一步恶化:

CPU iowait:    96–100%
I/O PSI full:  ~98%
HTTPS queue:   512
HTTP:          000

移除 workload 后,反向恢复也可能很快:

D-state:      0
CPU iowait:   6%
r_await:      ~0.98 ms
queue depth:  ~0.07
HTTP:         200

这就是间歇性基础设施故障难排查的原因。十分钟后,别人检查同一台 VM,完全可能诚实地看到低于 1 ms 的 disk latency。他不一定错,只是看到的是另一个 state。

Latency 为 0 有时反而没什么参考价值

我也遇到过 iostat 显示 r_await = 0,而系统明显不健康的情况:iowait 很高、有进程处于 D-state、存在 outstanding I/O、几乎没有 completed read,throughput 也接近零。

如果 sampling interval 内几乎没有操作完成,那么基于 completed operation 计算的平均值就会失去很多信息。0 并不一定证明读取瞬间完成,也可能只是没有足够的 completion 去描述那些仍然卡住的操作。

从那以后,我不再单独看某一个 storage 指标,而是把 latency、IOPS、throughput、queue depth、in-flight I/O、D-state、PSI 和实际 request completion 放在一起看。

一次 incident 最终变成了漫长的 support 调查

第一次严重故障恰好与原基础设施上的 scheduled backup 重叠,FDCServers 也确认当时 backup 正在运行。作为最初解释,这很合理。但之后 backup 已经结束,而且不在原 backup window 内时,我仍然复现了同一类型的 storage stall。

问题在不同日期重复出现。某次 incident 中,根据我的 service log,VPS 随后有 4 小时 41 分 15 秒无法使用。我无法证明 storage stall 本身导致 VM 进入那个 state;要证明这一点需要我拿不到的 host-side 信息。

application
    ↓
Linux VFS
    ↓
EXT4
    ↓
virtual block device
    ↓
?

问号后面可能是 virtualization、host queue、storage network、distributed storage、physical media、scheduler 以及 guest 看不到的其他系统。我能看到 failure 在哪一层表现出来,但看不到 physical root cause。

FDCServers 在内部 escalation 了问题,最终把 VPS migration 到另一个 node。迁移后我又从 guest 侧记录到一次严重的 storage stall。这并不能证明 FDCServers 的所有 node 都有 storage 问题,只能说明从我的角度看,我那台 VPS 的问题没有被彻底消除。

为什么我仍然不把这写成一篇负面的 FDCServers 文章

把一次 infrastructure incident 变成对整个 provider 的判决非常容易。我不想这么做。

一种情况是 service 的 normal operating model 本身就与我的 workload 不兼容;另一种情况是 intermittent、难以复现、需要很长时间才能 isolate 的 infrastructure problem。我的 FDCServers 经历更像第二种。

Incident 之前,VPS 一直正常工作,也承载真实的 production traffic。Support 调查了问题并尝试解决。最后我已经积累了足够的 evidence,决定不再让 production 依赖这台特定的 VPS。

我要求取消服务并退款。FDCServers 给我退了款。这一点会影响我的整体评价。

我没有重新测试他们现在的基础设施,所以不能告诉你今天的 FDCServers VPS 表现如何。我也没有证据证明我的 instance 遇到的问题代表整个 fleet。基础设施一直在变化。我不会把一台 VPS 上的一次困难 incident 变成对整个 provider 的永久判断。这里我也不是在推荐 FDCServers,只是在描述自己的经历。

我最享受的部分其实是 Linux 本身

Downtime 很令人沮丧,但 investigation 很有意思。我真的很享受找到问题边界的过程。

我没有足够的 visibility 去识别 physical root cause。我真正想回答的问题更简单:有效工作在哪一层停止完成?

Backend 大约 3 ms 就能响应。Nginx 显示 active,但 kernel stack 显示它正在 EXT4 read 中等待。vmstat 显示 blocked process 和极端的 I/O wait。PSI 显示 I/O stall 几乎占用了整个 workload。iostat 显示巨大的 latency 和 queueing。D-state 显示多个彼此无关的进程同时等待。Kernel 甚至报告某个 Nginx task 被 block 超过 122 秒。

没有某一个 metric 单独解决 incident。真正有价值的是多个独立 signal 指向同一个方向。这正是我喜欢 Linux 的原因之一:你可以从“网站有时打不开”这种模糊现象开始,逐步走到对“有效工作究竟在哪一层停止”的精确描述。

我现在使用的 debugging workflow

top
free -h
df -h

date -u
uptime
cat /proc/pressure/io
cat /proc/pressure/memory
cat /proc/pressure/cpu
vmstat 1 10
iostat -x 1 10
ps -eo state,pid,ppid,etime,wchan:50,comm,args | awk 'NR==1 || $1 ~ /^D/'
ss -lntp
journalctl -k --since "30 min ago" --no-pager

只要条件允许,我还会把 request path 拆开逐层测试:

public request
      ↓
reverse proxy
      ↓
direct backend
      ↓
filesystem
      ↓
block device

我现在不只问“为什么 server 很慢?”,而是问:有效工作在哪一层停止完成?这个问题会带来更好的实验。

最后一条规则:reboot 之前先收集 evidence

Reboot 可能正是 production 需要的恢复手段,但它也可能把你能看到的最有价值 diagnostic state 一并清除。

Before reboot:
D-state:     high
I/O PSI:     ~97%
iowait:      ~100%
queues:      large
requests:    failing

After reboot:
D-state:     0
latency:     milliseconds
requests:    healthy

如果 availability 和 business impact 允许,我会先保存 UTC timestamp、PSI、vmstatiostatD-statewchan、kernel message、socket queue 和 request timing,再恢复机器。

Server 在运行,workload 却没有真正运行

我最终也不知道究竟是哪一个 host-side component 导致了 incident。我不能说某块具体 SSD 坏了,也无法指出某个特定 storage node,更不能证明 virtual block device 背后具体发生了什么。

但从 Linux 内部能够确认的事实已经足够:

read latency:       up to 18.7 s
flush latency:      up to 53.6 s
I/O PSI full:       almost 100%
iowait:             almost 100%
I/O queue:          128+
Nginx:              blocked in filesystem reads
EXT4/jbd2:          blocked waiting for I/O
direct backend:     ~3 ms
HTTP through Nginx: timing out

这些证据足以把 application 与出问题的 layer 分开,也足以让我做出运维决策。FDCServers 退还了 VPS 的费用,我迁移到了别处,也没有把一次困难的 infrastructure incident 当作对整个 provider 的结论。

真正留下来的经验更有用:process 可以是 running,service 可以是 active,VM 可以是 online,ping 也可以正常,但机器仍然可能几乎无法完成任何有效工作。

Linux 提供了足够的 evidence 来看清这种区别。关键是在 failure 仍然存在时,向系统提出正确的问题。