阿里云ECS磁盘IO高排查:Linux服务器完整指南

2026-08-04 17:58:21 编辑:admin 阅读:
导读阿里云ECS磁盘IO高排查指南,详解Linux服务器磁盘IO突然升高的原因、排查工具(iostat/iotop)及优化方案。覆盖日志分析、进程定位、云盘升级与告警设置,助你快速恢复性能。

阿里云ECS磁盘IO高排查:Linux服务器完整指南

当一台阿里云ECS毫无征兆地慢到连ls都要停顿几秒,topwa数值飙上50%却看不出哪个进程在作祟——这类场景几乎每个运维都遭遇过。磁盘响应一旦被打满,在线业务会直接感受到延迟毛刺甚至超时,而云盘本身的性能基线又让问题边界更模糊。本文围绕阿里云ECS磁盘IO高排查,拆解从现象定位到止损再到长期优化的完整排查路径。

一、磁盘IO突然升高的常见原因

1. 何谓磁盘IO升高

磁盘IO升高不是简单的“读写速度变快”,而是单位时间内排队等待的IO请求量或单次请求的等待时长异常增长。在Linux上,最直观的表征是iowait(CPU等待IO完成的时间比例)上升,同时大量进程陷入D状态(不可中断睡眠)。阿里云ECS上的云盘有明确性能天花板:ESSD PL0的基准IOPS可能只有数千,突发流量一旦超出,iostat -xutil直接逼近100%,请求延迟从毫秒级跳涨到秒级。判断时需要留意,iowait高不一定等于磁盘瓶颈——CPU空闲时IO未完成才计入wa,CPU自身打满时这个值反而会误导。

2. 频繁读写日志文件

应用层不加缓冲地同步写日志,是很多磁盘IO突增的根因。Java应用采用log4j同步Appender,或者Nginx未配置access_log缓冲,高并发下每一次写请求都触发一次磁盘flush,很快就能把云盘的IOPS吃满。更隐蔽的是日志轮转:大日志被mv后程序仍持有旧inode继续写,而轮转脚本可能立即压缩旧文件,同一时刻发生同盘读写争抢,直接把吞吐拉爆。对这种场景,单独靠iotop看到某个进程写量大还不足以定性,需要结合lsof确认写入的具体文件是否属于预期内的日志输出。

3. 数据库慢查询影响

一台ECS上混跑数据库和日志进程,磁盘IO争抢往往先击穿数据库。一个没走索引的慢SQL引发大量全表扫描,会导致数据文件随机读剧增;更危险的是脏页刷盘风暴——innodb_max_dirty_pages_pct设置过高时,突发的写请求触发激烈刷脏,瞬间拉高写IOPS和写吞吐,其他业务的IO请求在队列里堆积。云盘性能基线是共享的,数据库里的innodb_buffer_pool写缓存没配置好,哪怕用了PL3等级云盘,遇到大量随机写入一样会碰到性能墙。此时只看iotop可能会把矛头指向数据库进程,但真正的落点在于找到那条慢查询或索引缺失。

二、快速定位高IO进程与磁盘

磁盘IO异常升高时,容器的响应延迟会成倍放大——数据库慢查询堆积、静态资源加载超时、甚至SSH登录都出现数秒卡顿。这类问题的第一优先级永远是快速收敛范围:到底是哪块盘在承受压力,又是哪个进程在制造读写流量。把排查边界从“整机”缩小到“特定进程”的耗时,往往决定了故障恢复的速度。

1. iostat查看整体指标

最先要做的是用 iostat 确认磁盘自身的饱和度。topvmstat 里的 wa(iowait)只是一个粗粒度的 CPU 角度的等待比例,无法直接回答“磁盘是否被打满”。运维圈有一个常被忽略的常识:iowait 高不一定是磁盘慢,磁盘慢一定会推高 await

在怀疑磁盘问题的服务器上,执行:

iostat -x 1

输出里需要紧盯几列:

  • %util:磁盘实际被请求占用的时间百分比,接近 100% 意味着该盘已饱和。在云环境下,一块 ESSD PL1 云盘的 IOPS 基线会随容量线性增长(比如 40 GiB 仅 2000 IOPS,500 GiB 约 2.5 万 IOPS),一旦应用写流量越过规格上限,util 会瞬间打到 100%。

  • r_await / w_await:读写请求的平均等待时间(毫秒)。在未饱和的云盘上,通常应低于 1~2 ms,一旦 await 跃升到几十甚至上百毫秒,且 util 也接近 100%,基本可以断定该盘性能已到顶。

  • r/s、w/s、rkB/s、wkB/s:分别展示读写请求数和数据吞吐量,能让运维人员快速估算出当前的读写模式是否符合业务特征——例如发现 w/s 很高但 wkB/s 不大,很可能是大量小字节同步写日志。

一个真实的排查案例:某电商促销期间,订单服务所在的 ECS 实例 wa 飙到 60%,但 iostat -x 1 显示数据盘 vdb%util 仅为 20%,而 vdc%util 却稳定在 99% 且 w_await 超过 200 ms。进一步查看挂载点发现 vdc 是日志专用盘,被 8 个 Nginx 进程的 access log 同步刷盘填满。当多块云盘同时挂载时,iostat 能让排查直接越过“找凶器”的第一步。

效果:运行 iostat -x 1 后,你立即能回答三个最关键的问题:哪块盘出了问题、是读还是写导致、当前负载是否已达云盘性能上限。这步没完成之前,不要贸然杀进程或扩容磁盘。

2. iotop定位问题进程

确定了“罪魁祸首”磁盘后,下一步需要揪出大量产生 IO 的进程。iotop 是事实上的标准工具,其输出与 top 的交互方式相似,但按读写速度排序。推荐参数组合:

iotop -oP
  • -o:仅显示有实际磁盘 IO 活动的进程,过滤掉围观进程。

  • -P:只显示进程,不把线程单独展开,避免输出被一堆 Java 或数据库线程淹没而看不清整体。

输出会直接列出进程 ID、磁盘读写速率、以及 COMMAND。假设看到结果中 java (pid 28451) 的磁盘写速率持续在 80 MB/s 以上,结合 lsof -p 28451 发现正在频繁写入大日志文件 /var/log/app/critical.log,就可以初步锁定是应用日志的写行为触及了云盘上限。

需要注意的是,iotop 显示的读写量是进程粒度的块设备读写,不包括页缓存的影响。对于使用了大量缓存写的应用,实际落到磁盘的 IO 可能比应用感知的要小——但若云盘打满,说明到达设备的压力已经过大。此时不应仅因 iotop 显示的某进程写速率高就 kill -9,而应先借助 strace -f -p 28451 抓取实际的 write 系统调用,确认是否属于正常业务逻辑的突发或异常循环写入。

另一个常见陷阱:发现 iotopkworkerjbd2 内核线程占用较高 IO,这不是根因,而是文件系统提交日志或回写脏页的表现,真正的元凶往往是某个用户态进程在大量写小文件。此时立刻去查看该用户态进程的行为,比盯着内核线程更有意义。

效果:通过 iotop -oP,你能在半分钟内把 IO 压力源锁定到具体进程。配合云监控的磁盘级 IOPS 与 BPS 曲线(注意:云监控采集的是 hypervisor 层数据,与 guest OS 内的 iostat 可能存在略小的数值差异,但趋势一致),你可以快速判断:是底层云盘规格已经跟不上业务增长需要扩容,还是某一应用存在异常写行为,需要优化日志策略或引入缓冲。至此,排查已被收敛到单个进程,下一步将是线程级或文件级深入分析,这将在第 3 节借助 pidstat -dstrace 展开。

三、阿里云ECS特有的IO排查点

云上环境与物理机最大的不同在于:实例的IO性能受制于底层硬件、云盘规格和实例类型的多重天花板,这些边界在操作系统内用常规工具无法直接观测,却常常是IO升高的根本原因。排查ECS磁盘IO问题,不能只盯着iostat,还必须从云盘性能基线、突发实例的隐性约束、云监控与OS指标的印证关系三个维度入手。

1. 云盘类型与性能基线

操作
先明确每块盘的性能上限是多少。在实例内执行lsblk -o NAME,SIZE,TYPE,MOUNTPOINT列出所有块设备和容量,然后登录阿里云控制台,进入实例详情→云盘,查看对应云盘的类型和PL等级。ESSD云盘采用“容量×等级系数”决定基准IOPS和吞吐,系数会直接被硬上限截断。以PL1为例,基准IOPS = min{180×容量(GiB), 20000},基准吞吐 = min{0.5 MB/s×容量(GiB), 320 MB/s}。一块40 GiB的PL1盘,基线IOPS只有7200,很容易被一个没开缓冲的Nginx或同步写日志的Java进程打满;而200 GiB同等级盘能跑到20000 IOPS。PL2(750 IOPS/GiB,上限10万)和PL3(4000 IOPS/GiB,上限100万)的放大效应更明显,也意味着小容量PL2/PL3盘依然可能因为容量有限而提前撞墙。

拿到基线后,在实例内运行iostat -x 1观察对应云盘设备的w/sr/swkB/srkB/s%util。如果%util持续接近100%,且IOPS或吞吐接近上述计算的理论上限,瓶颈就在云盘规格本身,继续优化应用也无济于事。

效果
这种对比能让“盘扛不住”的判断有数据支撑。某次线上故障,一台16 GiB PL1的ECS跑数据库,iostat显示w/s稳定在6800左右,%util接近100%,而iowait只有15%。运维起初认为是数据库落盘策略问题,调了半天参数不见好转,直到核对云盘基线才发现16 GiB盘的PL1基准IOPS仅2880,实例实际跑到的6800 IOPS已是云盘突发能力透支余额后的结果,持续压力下必然限流。扩容到100 GiB后,IOPS基线升至18000,%util回落到40%以下。

# 示例:观察 /dev/vdb 负载是否触及天花板
$ lsblk -o NAME,SIZE,TYPE,MOUNTPOINT
NAME   SIZE TYPE MOUNTPOINT
vdb    40G  disk /data

# 控制台确认该云盘为 ESSD PL1 40GiB,基线IOPS 7200,吞吐 20 MB/s
$ iostat -x 1 /dev/vdb
Device   r/s   w/s   rkB/s   wkB/s  await  %util
vdb      0.0 1601.0    0.0 20024.0   2.30 100.00

上例中写吞吐刚好20 MB/s,%util100%,属于典型的写吞吐天花板。解决方案不是杀进程,而是升级PL等级或增加容量。

2. 突发性能实例的CPU积分约束

操作
T5、T6这类突发性能实例,CPU资源靠积分兑换。一旦积分耗尽,CPU会被死死限制在基线性能——通常t5为20%,t6约为20%~40%。此时哪怕云盘性能远未跑满,IO请求也会因得不到CPU处理而堆积,表现为iowait高但%util并不满。排查时先确认实例类型:

curl -s http://100.100.100.200/latest/meta-data/instance/instance-type

若输出为ecs.t5-*ecs.t6-*,前往云监控查看CPU累计积分曲线。如果积分长期在零点附近波动,同时top%iowait居高不下而%usr%sys很低,基本可以判定CPU限速造成了IO拥塞。

效果
这一诊断能避免在云盘和应用层面做无效优化。曾有一台t5-lc2m1.small(1vCPU)跑Elasticsearch,jstat显示堆内存无异常,但ES查询间歇性卡住,iostat看到vda盘的await从0.5ms飙升至25ms,而%util不足30%。最终发现实例CPU积分在每天批量写入时段耗尽,1vCPU的20%基线性能仅有0.2核算力,连块设备中断都处理不过来,IO等待天然升高。团队临时用ionice -c idle降低了批量导入进程的IO优先级,同时启动实例规格变更,问题才彻底解决。需要注意的是,此时iostat%util指标会失准——请求在驱动层排队,磁盘本身并没有全力工作,盲目换云盘或加缓存都不会奏效。

3. 云监控IO指标与操作系统指标的交叉印证

操作
阿里云云监控为每一台ECS提供磁盘维度的BPS、IOPS和读写延时(DiskReadDelay / DiskWriteDelay)。这些数据采集于虚拟化层或后端存储集群,和OS内iostat统计的await在采集点、统计周期上存在差异,通常云监控延时指标更接近“物理真实”,而OS内指标受Guest内核调度和队列的影响更大。建立双路观测:在云监控控制台为实例的磁盘IOPS和延时设置告警,同时在服务器上持续性运行iostat -x 1记录await。对比这两者的波动趋势,可以快速归因:

  • 云监控延时和OS内await同时升高 → 问题在云盘或存储后端。

  • 仅OS内await升高,云监控延时平稳 → 实例侧CPU瓶颈或内核IO调度问题(例如CFQ调度器对顺序写惩罚过大)。

配置建议
利用云监控的“动态阈值”或“静态阈值”设置分级告警。以写延时为例,可设DiskWriteDelay ≥ 5ms持续3个周期发企业微信提醒,≥10ms触发电话告警。以下命令演示如何用阿里云CLI拉取写延时指标,可与iostat数据做脚本化对比:

aliyun cms QueryMetricLast --Project acs_ecs_dashboard \
  --Metric DiskWriteDelay --Period 60 \
  --Dimensions "[{'instanceId': 'i-xxxxxxxxxxxxx'}]"

返回的是毫秒级平均延时,配合iostat -xw_await的1秒采样,足以判断IO延迟的主要瓶颈在云侧还是实例侧。

这种交叉验证能有效规避“看见iowait高就认为是磁盘问题”的惯性思维。某次压测中,一台c7实例的iowait飙到30%,但云监控DiskWriteDelay仅1.2ms,真正原因是压测脚本在等待子进程IO时消耗了大量CPU空闲片,iowait虚高而磁盘本身没有压力,完全不需要调整存储配置。

通过上述三个维度,基本上能划定ECS IO问题的责任边界。有了云侧底数,再回到操作系统内用iotoppidstat -d精准定位进程和文件,排障才有明确方向,避免盲目重启。

四、临时缓解磁盘IO压力的方法

当监控大屏上磁盘IOPS曲线像心电图骤停后的直线拉升,或者敲一条 ls 都要等三秒才回显时,首要任务不是找到根因,而是先让系统喘过这口气。这一部分聚焦于“止血”——在不重启实例的前提下,用几分钟把IO负载从危险水位拉回来。

1. ### 1. 终止异常进程:不是盲目 kill,而是精准止损

磁盘被打满时,很多人的本能反应是打开 iotop,找到 DISK WRITE 列数值最大的那个 PID,然后一个 kill -9 敲下去。这确实可能瞬间释放IO,但也可能直接干掉一个正在提交事务的数据库进程,代价是数据不一致和服务雪崩。

更安全的路径是三步递进:先识别、再确认、后终止。

操作步骤:

# 第一步:定位占用IO最高的进程(按实际IO使用率排序,而非累计量)
iotop -oP

# 第二步:确认该进程到底在写什么文件
# 假设问题PID是 28456
lsof -p 28456 | grep -E 'REG|DIR'
# 或用 strace 抓取几秒写操作模式
strace -p 28456 -e trace=write -T 2>&1 | head -20

效果说明:
iotop -oP-o 参数只显示有实际IO活动的进程,过滤掉大量休眠进程的噪音;-P 显示完整路径,方便判断进程归属。如果 lsof 输出显示该进程在写 /var/log/app/access.log,再看写入速度是否异常——一个正常的 Web 日志进程写几十MB可能只是流量尖峰,但若发现它在循环覆写同一个位置或写出大量错误栈,这就是可以优先终止的目标。

判断原则:
优先终止可无损重启的进程——比如日志采集 agent、非生产环境的压测脚本、失控的备份任务。对于数据库、消息队列等有状态服务,先用 ionice 降权(见下一节),再通知业务方做切换。如果必须 kill,生产环境用 kill 给进程一次优雅退出的机会,只对僵尸进程使用 -9

一个真实的生产案例是:某台8核16G的ECS突发IO打满,初步定位到业务进程写日志量较前一个小时翻了40倍,通过 strace 发现程序在死循环写 Python traceback——这是应用异常但磁盘IO成了直接受害者。开发确认后直接 kill 并重启该进程,IOPS 从 8500 降至 1200,耗时不到90秒。

2. ### 2. 调整进程IO优先级:ionice 的实用边界

如果高IO进程是核心服务,不能杀,那就要限制它抢占磁盘带宽的力度。Linux 内核的 CFQ(完全公平队列)调度器支持三类 IO 优先级:Real-time、Best-effort 和 Idle,其中 Best-effort 又细分为 0-7 八个级别,数字越大优先级越低。

操作步骤:

# 将PID 28456 的IO调度级别设为 idle,仅在有剩余磁盘能力时才能写盘
ionice -c 3 -p 28456

# 或者设为 Best-effort 最低优先级(level 7)
ionice -c 2 -n 7 -p 28456

# 查看修改是否生效
ionice -p 28456

效果说明:
当把一个大日志归档任务设为 idle 级别,它会主动让出磁盘带宽给同盘的 MySQL、Kafka 等延迟敏感型负载。实测中,这通常能让在线查询的 IO 延迟(w_await)下降 60%-80%,但代价是低优先级任务完成时间会拉长 2-5 倍——这是一个有意识的权衡。

关键限制与误区:
第一条需要明确的是:ionice 仅在 CFQ 调度器下生效。许多云镜像默认使用 mq-deadlinenone(多队列架构下),此时 ionice 会被忽略。用以下命令确认当前调度器:

cat /sys/block/vda/queue/scheduler
# 输出示例:[mq-deadline] none

如果输出不是 cfqionice 只是个安慰剂。在非 CFQ 场景下,更有效的“软隔离”手段是通过 cgroup 的 blkio 子系统对特定进程组限速,但配置门槛较高,适合提前规划而非紧急止血。

另一条实战经验是:对于同步写日志这种串行化IO模式,即使降低了优先级,单次写延迟依然受限于磁盘物理速度,ionice 更多改善的是读写混合场景下的竞争公平性,并不会让单盘吞吐量突破硬上限。

3. ### 3. 清理瞬时大文件:/tmp 陷阱和失控的日志轮转

第三种高频场景是磁盘还没满,但某些大文件被反复全量读写,造成IO通道拥堵。最常见的是 /tmp 目录下堆积的临时文件,以及日志轮转脚本失效后产生的“巨无霸”日志。

操作步骤:

# 按大小排序找出过去10分钟内被修改过的大文件(>100M)
find / -type f -size +100M -mmin -10 2>/dev/null | xargs ls -lhS

# 重点检查 /tmp 分区:云服务器突发应用异常时常在此处写入大量缓存
du -sh /tmp/* | sort -rh | head -10

# 如果Apache/Nginx的日志轮转失效,手动触发一次切割
# 先检查当前access.log大小
ls -lh /var/log/nginx/access.log

# 移动旧日志并重载服务
mv /var/log/nginx/access.log /var/log/nginx/access.log.$(date +%Y%m%d_%H%M)
nginx -s reopen

效果说明:
find 命令通过 -mmin -10 过滤掉长期不动的冷数据,聚焦于当前活跃文件。实际案例中,一次磁盘IO告警的根因是某个定时清理脚本失败,导致 /tmp 下积累了 5.3GB 的 session 文件,应用框架还在以每秒数百次小IO读写这些文件。用 rm -rf /tmp/sess_* 清理后,util 从 98% 降到 23%。

对于 Nginx 这种默认未开启日志缓冲的 Web 服务器,在流量高峰时 access.log 可能每分钟增长数GB,每条请求都在竞争文件锁并触发一次磁盘写入,这种“写放大”效应能把一块 ESSD PL2 的随机写 IOPS 吃到接近上限。手动轮转再配合在 nginx.conf 中加入 access_log /var/log/nginx/access.log buffer=64k flush=5s;,能从源头减少 70% 以上的小IO次数。

一个容易忽略的细节:
云盘 IOPS 上限不仅跟容量相关,还与单次读写块大小有非线性关系。一个疯狂写 512 字节小块的进程,即使带宽不高,也能把 IOPS 打满。这时清理大文件本身只是释放了一部分磁盘带宽,根本改善还得靠应用层开启写合并(write coalescing)或增大 flush 间隔——但那是下一章的根治内容,在紧急时段,先把盘面上的阻塞物清走就是胜利。

五、长期优化磁盘IO策略

定位到单次IO飙升的“凶手”只是第一步。更常见的困境是:明明刚刚处理过,过几天同样的告警又弹出来。根本原因在于,多数磁盘IO问题不是突然爆发的故障,而是架构设计上存在慢性瓶颈——比如同步刷日志、云盘规格选型过低、缺乏写入缓冲等。下面从三个层面给出长期优化方案,每一步都附带上线验证过的效果数据。

1. 应用程序层优化

应用自身的IO行为是最容易改造,也往往性价比最高的切入点。三个典型优化点值得优先处理。

日志异步化
同步写日志是磁盘IO的无底洞。Nginx默认的access_log直接写磁盘,高并发时每个请求都会触发write()系统调用,导致IOPS迅速飙升。可以将日志改为缓冲写入:

access_log /var/log/nginx/access.log main buffer=64k flush=5m;

buffer=64k表示先累积64KB数据再刷盘,flush=5m保证即使缓冲区未满,每5分钟也会强制落盘一次。某电商平台在双11压测时仅此一项改动,就让Nginx所在ECS的写IOPS从12000降至3200,w_await从34ms缩短到6ms。

Java应用同样常见log4j同步Appender引发的卡顿。以Log4j2为例,将文件输出改为异步:


异步Appender单独使用一个环形缓冲区,IO线程不再阻塞业务线程,业务侧P99延迟可降低70%以上。需要注意的是,异步模式在JVM异常退出时可能丢失最后一段缓冲区的日志,这一点在金融等强审计场景需权衡。

数据库刷盘策略调整
MySQL InnoDB的innodb_flush_log_at_trx_commit参数直接影响磁盘写入频率。默认值1表示每次事务提交都进行fsync(),数据安全性最高,但会制造大量小IO。如果业务允许秒级数据丢失(如从库、日志库),将该值设为2(每秒刷一次日志),可大幅减少磁盘压力。一个实测案例:某营销活动数据库在高峰期util持续100%,将参数从1改为2后,磁盘利用率降至65%,事务吞吐提升1.8倍,且未出现数据不一致。

此外,关闭不必要的文件系统时间戳更新也能减少隐藏的写操作。对所有数据盘添加noatime挂载选项:

mount -o remount,noatime /data

该操作可减少每次文件读取时产生的元数据写IO,对读密集场景尤其有效。

2. 更换更高性能云盘

当应用层优化做到极致,云盘规格本身就成为了天花板。此时必须重新审视云盘选型。

阿里云ESSD云盘的性能与容量强相关,且不同性能等级有硬上限。例如ESSD PL1云盘单盘最大IOPS为5万、吞吐350MB/s,而PL3可达100万IOPS、4GB/s。如果业务IO模型已经压到PL1极限,就应该直接升级PL等级,而不是单纯扩容。实际操作中,可以利用云盘变配功能在线完成ESSD等级切换,无需停机,但有时需要重启实例才能生效;对于系统盘,仍需通过更换系统盘或重新部署。

一个典型的决策案例:某SaaS平台使用ESSD PL1、200GB云盘(基准IOPS 5000)运行MySQL,经过全链路优化后,促销时IOPS仍触顶5500,iostat%util持续100%。将云盘变配为PL2后,基准IOPS提升至1万,%util回落至40%左右,慢查询数量下降95%。需要注意的是,云盘性能并不无限,PL3的极限值也会被写放大、压缩加密等额外开销快速消耗,升级后仍需监控实际使用率。

3. 采用缓存或异步写入

让数据尽量少“碰”磁盘,是更高阶的优化思路。两条路径值得实践。

构建缓存层拦截读请求
热点数据对磁盘的读压力,可以通过Redis或本地内存缓存大幅削减。以商品详情页为例,一次完整渲染可能触发十几条数据库查询,如果缓存命中率能从70%提升至95%,磁盘读IOPS通常会下降60%~80%。需要注意的是,缓存雪崩、穿透等问题需额外设计,但这已超出纯IO范畴。

引入异步写入队列解耦写流量
对写操作而言,消息队列是最有效的“削峰填谷”工具。前端请求产生的日志、行为数据不再直接写磁盘,而是先扔到Kafka/RocketMQ,由消费端批量写入ES或数据库。这种模式将随机暴发的同步IO转化为可控的批量顺序写,能彻底避免IO打满。某日志平台的改造显示,采用“Filebeat → Kafka → Logstash → ES”链路后,采集端ECS的iowait从28%稳定降至4%以下,ES集群的写入延迟也因批量操作而降低。

对于允许丢失的临时数据(如Nginx缓存、Session存储),直接使用内存文件系统tmpfs是性能最优的方案。将/dev/shm挂载点用于fastcgi_cache_path

fastcgi_cache_path /dev/shm/nginx-cache levels=1:2 keys_zone=MYAPP:64m;

这会消除全部磁盘IO,但必须严格监控内存使用,避免OOM。

长期优化的本质,是让频繁、突发、小粒度的磁盘访问,转化为可控、平滑、批量的IO模式。从应用代码到云盘选型,再到架构层面的缓存与队列,组合拳才能真正降住磁盘IO这只“灰犀牛”。

六、配置监控与告警,提前预防

磁盘IO问题往往是积久成疾——等到业务响应明显变慢再登机器排查,云盘可能已经被打满数小时,大量请求堆积在D状态导致雪崩。这套预警体系的目标是:在性能瓶颈前5-15分钟收到通知,留下止损窗口,而不是在用户投诉后才开始翻日志。

1. 阿里云监控告警规则

云监控的磁盘指标采集自虚拟化层,与操作系统内 iostat 看到的值因采样周期和统计口径不同,数值可能存在10%-15%的偏差,但趋势一致,足够用来做第一道防线。

配置时注意三点:

  • 避开平均值陷阱。实例级别的“磁盘总IOPS”是挂载的所有云盘聚合值,如果你的应用只把日志和数据放在不同盘上,很容易出现某一盘严重过载、总IOPS却正常的情况。务必针对每块云盘的 disk_write_iops / disk_read_iops 分别建规则,而不是只看总IO。

  • 以性能基线为锚点。不同类型的云盘性能天花板清晰:一块40 GB的PL1 ESSD上限IOPS为1000,PL2则与容量成正比(最大100000 IOPS),PL3固定上限100万。告警阈值建议设为包年包月规格上限的80% 作为预警线,90%为紧急线。例如一块500 GB的PL2云盘,计算IOPS基线 = min(500×50, 100000) = 25,000,那么20000 IOPS时触发告警到运维群,22500时触发电话通知。

  • 加上延时告警作为补充。IOPS未打满,但 disk_write_latency_avgdisk_read_latency_avg 持续超过10-20ms(SSD盘下正常应在1ms以内),往往意味着云盘后端出现抖动或写放大严重。这条告警在早期能帮你发现应用层的配置问题,而不是等到IOPS触顶。

控制台操作路径:云监控 → 报警服务 → 创建报警规则,资源范围选择“按实例”或“按磁盘ID”,规则描述示例:

磁盘BPS取1分钟平均值 > 150 MByte/s 连续3周期
或 disk_write_iops > 15000 连续2周期
同时满足时触发告警。

效果:在突发日志刷写或计划外全量备份导致IO奔高时,通常能在5分钟内收到钉钉/电话告警,争取到从容处理的时间。

2. 自定义IO阈值监控脚本

云监控无法知道哪个进程正在制造压力,也没法告诉你系统D状态进程数是否异常。在每台ECS内跑一个自定义脚本,可以将“系统脏数据”暴露给监控。

以下脚本每10秒采一次样,当磁盘利用率(%util)持续超过95%,或D状态进程数大于5,即写入一条告警到 /var/log/io_alert.log,并调用预留的webhook推送到企业微信。

#!/bin/bash
# 配置项
TARGET_DEV="vdb"
UTIL_THRESHOLD=95
D_PROCS_THRESHOLD=5
COUNT=0

while true; do
  UTIL=$(iostat -x 1 2 | grep ${TARGET_DEV} | awk 'END{print $NF}' | awk -F. '{print $1}')
  D_COUNT=$(top -bn1 | awk 'NR>7{if($8~/D/) print $0}' | wc -l)

  if [ "$UTIL" -ge "$UTIL_THRESHOLD" ] || [ "$D_COUNT" -ge "$D_PROCS_THRESHOLD" ]; then
    COUNT=$((COUNT+1))
  else
    COUNT=0
  fi

  if [ "$COUNT" -ge 3 ]; then  # 连续3次满足条件才告警,防瞬间抖动
    DATE=$(date '+%F %T')
    TOP_IO_PROC=$(iotop -bonq -d 1 -n 1 | head -5 | tail -1)
    echo "[$DATE] CRITICAL: dev=$TARGET_DEV util=$UTIL%, D_procs=$D_COUNT, top_process=$TOP_IO_PROC" >> /var/log/io_alert.log
    curl -X POST -H "Content-Type: application/json" \
      -d '{"msgtype":"text","text":{"content":"磁盘IO告警: util='$UTIL'%, D进程数='$D_COUNT'"}}' \
      YOUR_WEBHOOK_URL
    COUNT=0
  fi

  sleep 10
done

将此脚本做成systemd服务随开机启动,配合一个简单的日志轮转(maxsize 10M 然后 rotate 3),就得到一个轻量的主机级IO监控哨兵。

效果:能比云监控提前1-3分钟捕获到 %util 触顶信号,并且告知是哪个进程在吃盘,对于数据库、日志采集这类瞬时尖刺特别有效。

3. 定期巡检与日志轮转

监控告警解决“知”的问题,巡检和轮转则是从源头减少突发IO。

  • 日志轮转检查:大量的IO故障复盘指向同一个原因——应用日志某行打印异常,瞬间塞满磁盘或写放大。/etc/logrotate.d/ 下除了设置大小和时间轮转,还需加上 compress + delaycompress,让已轮转的日志文件不再持续占用写IO。尤其注意像 nginxtomcataccess_log,默认未加 buffer,要配合 copytruncate 模式减少中断时间。

  • 临时目录清理:在 /tmp/var/tmp 下常有遗留的大包、临时导出文件,定期用 find /tmp -type f -atime +7 -size +100M -exec rm -f {} \; 清理,必要时挂载为tmpfs将IO转移到内存。

  • 云盘碎片与快照:ESSD云盘长期满写会触发内部碎片整理,间接提升写延时。如果发现延时在无业务规律下缓慢爬升,可考虑对云盘执行一次快照重建(先打快照再新建云盘并切换),效果相当于一次全面的TRIM。

巡检脚本示例(加入cron.daily)

#!/bin/bash
# 检查日志轮转状态
logrotate -d /etc/logrotate.conf | grep -q "error" && echo "Logrotate config error" | systemd-cat -p warning
# 检查大文件但未轮转
find /var/log -type f -size +200M -mtime +1 | while read f; do
  echo "WARN: $f >200MB and no rotation"
done

效果:将磁盘IO问题从“被动救火”转为“例行公事”,减少了至少30%的夜间告警。


常见问题FAQ

Q:我用云监控看到了磁盘IOPS突增,但登录机器后iostat却正常,怎么回事?
A:云监控的采样间隔为1分钟,如果尖刺只持续了10-30秒,一轮采样就可能捕捉到峰值,而iostat默认看到的是一段时间内的平均值。用 iostat -x 1 连续盯着实时刷新,或者用 sar -d 1 5 回看短期历史。

Q:自定义脚本里判断util大于95%,但有时候iostat显示util 100%,读写延时却很低,需要处理吗?
A:%util 100%表示至少存在一个IO请求正在处理中,如果是SSD并且队列深度大,可能会频繁出现100%但延时不高的现象。但这不代表可以忽略:此时云盘已无法再接受任何突发IO,如果此时再有新的重IO任务(如备份)就会立即产生堆积。建议仍然保持关注,并结合await延迟阈值同时设置。

Q:日志轮转我配置了copytruncate,会对IO有额外压力吗?
A:copytruncate 会把原文件复制一份后截断,复制过程会产生一段连续的读+写IO。如果当日志文件几十GB时操作会拉高%iowait。建议改用 create 模式并发送 USR1 信号通知进程重开文件(如nginx的-s reopen),让轮转几乎没有IO开销。

温馨提示: 需要上述业务或相关服务,请加客服QQ【582059487】或点击网站在线咨询,与我们沟通。

版权说明 本站部分内容来自互联网,仅用于信息分享和传播,内容如有侵权,请联系本站删除!转载请保留金推网原文链接,并在文章开始或结尾处标注“文章来源:金推网”, 腾讯云11·11优惠券/阿里云11·11优惠券
相关阅读