ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

宝塔面板IO延迟高?从概念到实战的排查流程与解决方案

宝塔面板IO延迟高?从概念到实战的排查流程与解决方案 晚上十点多正打算睡觉手机上跳出来一条宝塔面板的告警通知IO延迟非常高。登录后台一看监控曲线直接冲到顶红色告警刺眼。可是网站访问量并没有涨CPU和内存也都很平静就像一台看起来啥事没有的机器背地里却在疯狂挣扎。先说结论遇到这种情况第一反应千万不要是重启服务器。我见过太多人一看到IO延迟飙高就顺手reboot结果重启完延迟倒是正常了一会儿过几天又复发而且因为重启把现场破坏了真正的问题再也查不出来。还有一部分人第一反应是找服务商售后但问了一圈也问不出所以然最后还是要自己查。这篇内容适合所有用宝塔面板管理服务器的朋友——不管是个人博客站长、小公司运维还是手里跑着几个服务的新手。我会把这几年排查IO延迟高的完整思路、用到的命令、踩过的坑都摊开讲清楚看完你就可以照着一步步排查把真正的原因揪出来。1. 先说清楚宝塔面板显示的“IO延迟”到底是什么1.1 这个数字是怎么算出来的宝塔面板监控页面的指标其实是读取系统的 /proc/diskstats 内核文件后计算出来的。面板上显示的“IO延迟”对应的就是磁盘平均响应时间专业一点说就是 iostat 命令里的 await 指标——从发起一个IO请求到数据成功返回之间的耗时单位一般用毫秒。这里有个很容易被忽视的点它统计的是“平均延迟”不是“最大延迟”。平均延迟100ms意味着磁盘上已经有一堆请求在排队了真实用户的感知可能是点击之后两三秒才有反应。如果你看到监控显示的延迟经常跳动到几百毫秒那基本可以认定当前磁盘的负载已经非常不健康。怎么判断延迟数值是否正常以我自己的经验大致这样看个位数毫秒非常健康SSD盘正常应该在0.x到5ms之间十几毫秒到几十毫秒开始有压力需要持续观察100ms以上磁盘响应已经明显拖后腿页面卡顿、数据库慢查询这时候大概率已经出现了动不动几百毫秒甚至上千业务卡得像PPT数据库查询可能直接超时1.2 “突然变高”通常有几种特征从实战角度看“突然”这个词背后其实可以分成几种完全不同的模式。我做了几年运维发现只要看监控曲线的形状基本就能猜个七八分第一种是瞬时飙升几分钟后自己回落。这种一般是有一次性任务在跑比如某个计划任务到了执行时间、日志切割、备份脚本启动。问题不大但如果你频繁接收到这种告警就需要看看是不是任务执行得太频繁了。第二种是每天固定时间飙高。这种几乎可以断定是计划任务撞车了。比如凌晨2点同时跑数据库备份、网站全量打包、日志切割几个任务叠加在一起IO延迟不飙才怪。第三种是持续走高不回落从早到晚一直高。这种就需要认真对待了常见原因包括磁盘空间写满、云盘被限流、数据库出现严重的慢查询、甚至服务器被入侵挂了挖矿程序。第四种是一重启就恢复但过几天又复发。这种十有八九是日志或缓存类文件在持续堆积重启只是把当前堆积的文件清掉或释放了句柄但根源没有解决过几天又堆回来。搞清楚自己面对的是哪种模式排查方向就很清晰了。如果你是“每天定点击穿”的情况那我基本可以断定和定时任务脱不了干系。如果你属于“持续高不回落”那就得往下走完完整的排查流程了。2. 别急着动任何东西先把排查流程走一遍2.1 先用top、free、df把“现场”保存下来在动任何东西之前先把现场记录下来。我一般打开一个终端把下面这几组命令全部跑一遍然后截图或者复制输出top -bn1 free -h df -h df -itop -bn1 是以非交互模式输出一次进程情况适合保存现场。top要重点看load average后面的三个数字分别是过去1分钟、5分钟、15分钟的平均负载。如果数字高同时%Cpu(s)里waiowait等待IO的CPU占比很高说明有大量CPU时间花在等磁盘IO上这是一个非常典型的IO瓶颈信号。然后看进程列表里有没有大量D状态的进程。D状态是uninterruptible sleep也就是不可中断睡眠表示进程在等IO返回。CPU密集型的进程一般是R状态S状态是正常睡眠唯独这个D状态出现越多说明IO阻塞越严重。如果top里密密麻麻全是D不用看别的了磁盘这一层肯定有问题。free -h主要看swap的使用情况。如果你发现swap used不是0而且还在不断增长说明物理内存已经不够用系统正在把内存里的数据换到磁盘上。每一次换页都是一次磁盘读写对IO的消耗非常致命。这种情况的根因是内存不足光看磁盘本身是解决不了的。df -h和df -i这两条尤其重要。df -h看容量Use%到80%以上就该警惕100%满盘的话很多程序会直接异常。df -i看的是inode每个文件都要占用一个inode如果小文件数量爆炸inode会先耗尽系统同样会报No space left on device但df -h里容量却还有几十GB。这是新人很容易栽的坑前面排查了半天最后发现根本不是空间不够而是文件数量太多把inode吃光了。2.2 用iostat和pidstat锁定元凶全局信息拿到手之后该深入看磁盘了。如果系统里还没装sysstat工具包先装上# CentOS/Rocky yum install -y sysstat # Ubuntu/Debian apt install -y sysstat然后跑iostat -x 1 3这个命令会连续采样3次每次间隔1秒。-x参数输出扩展数据。重点看这么几个字段%util设备的繁忙程度接近100%代表设备基本被打满了awaitIO请求的平均处理时间包含排队和实际处理时间这个和宝塔的IO延迟最接近r/s和w/s每秒读写的请求次数如果w/s特别高说明主要压力在写入方向rkB/s和wkB/s实际读写的吞吐量只看磁盘整体还不够还要看到底是哪个进程在读写。这里有两个工具可以选。有iotop的环境可以直接iotop -o -P-o参数只显示有IO活动的进程-P显示的是线程PID比全量输出清爽很多。iotop跑起来需要root权限宝塔管理的机器一般都在root下操作没问题。但有些精简系统没装iotop这时候可以用sysstat包里自带的pidstatpidstat -d 1这个命令可以每秒打印每个进程的读写情况。第一列是PID后面是每秒读写的字节数。输出里哪个进程在疯狂读写一眼就能看到。我个人其实更常用pidstat因为它在堡垒机、容器环境里也能用而且不依赖root的交互式界面。2.3 一张输出示例教你读懂现场数据我举个例子假设iostat输出长这样Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await r_await w_await svctm %util vda 0.00 128.50 2.00 186.00 32.00 4000.00 21.40 4.20 28.80 3.50 29.90 5.30 100.10这份输出里信息量很大。%util已经100%说明设备繁忙程度到顶了。w/s是186写入请求占绝对主导而w_await是29.9ms同时r_await只有3.5ms说明读没什么压力问题出在写方向。再配合pidstat看是哪个进程在写是MySQL在刷redo日志还是PHP进程在写日志还是备份进程在打包方向立刻就明确了。还有一种常见的现场组合load average到了8.xiostat里%util是98%top里D状态进程一堆。这种组合基本可以断定磁盘被写请求塞满所有进程都在排队等IO表现出来就是整个站点像死了一样。但注意一点光看负载高不能直接断定为IO问题必须同时看到wa高、D状态多、磁盘%util高这三个特征才敢下这个结论。否则也可能是有进程陷入死循环在空转CPU。3. 我排查IO延迟高时遇到的几类真凶3.1 磁盘快满了这是最常见的第一道坑先看空间原因很简单操作成本最低但也最容易被忽略。很多机器不是突然IO变高而是磁盘已经悄悄满了很长时间快照、备份文件、日志文件堆积如山。宝塔环境里最占空间的位置我帮你圈出来/www/backup宝塔的自动备份默认放这里跑几个月几十G很常见/www/wwwlogs网站访问日志和错误日志被扫描工具或者恶意爬虫盯上的时候一天几个G也不稀奇/var/log系统日志、数据库日志所在位置/tmpPHP会话文件、上传临时文件、解压中间文件容易残留定位大文件我一般用一行命令搞定du -sh /www/* | sort -rh | head找到最占空间的目录之后再继续往下钻。清理的时候有两点特别提醒第一日志文件如果正在被进程写入直接rm之后空间可能不会释放。因为文件被进程占用着删除只是把文件名从目录里摘掉了但文件内容仍然被进程持有磁盘空间要等进程关闭这个文件才真正释放。遇到这种情况要么重启对应进程要么用截断而不是删除的方式 /www/wwwlogs/xxx.log这样文件还在空间立刻释放进程也不会中断日志写入。第二inode耗尽的问题。如果df -i显示Use%接近100%但df -h空间还很多那就得开始清理小文件了。可以先用find /www -type f | wc -l估算文件总数如果已经到几十万甚至上百万个说明确实有小文件风暴。常见的是CMS的缓存目录、opcache、或者某些应用不断生成的临时文件。定位到目录之后要么改应用配置调整缓存策略要么写个计划任务定期清理。3.2 日志暴涨和小文件风暴IO被“写”死了讲一个我印象很深的案例。有台客户机白天业务量不大但IO延迟从下午开始逐步攀升到晚上直接卡死。查过去发现/www/wwwlogs下的access.log一天就长了4个G。再翻日志内容同一个IP以每秒几十次的频率在请求站点页面明显是爬虫或扫描工具。日志每写一行就是一次磁盘写IO海量请求就把磁盘写满了。这种情况的处理思路有两个层面。短期先把流量堵住在Nginx配置里加一个请求频率限制limit_req_zone $binary_remote_addr zoneperip:10m rate5r/s;然后在server块里引用limit_req zoneperip burst20 nodelay;这样设置后单个IP每秒超过5次请求就会直接返回503日志量会瞬间降下来。虽然有可能会误伤正常爬虫但比起整个站点被IO拖垮这个是划算的。长期要做的是日志切割。宝塔面板里网站-设置-网站日志可以设置按天切割保留3到7天就够了。没有切割的日志会无限增长迟早把磁盘塞满。注意切割完之后还要检查一下权限别让日志文件变成root所有导致php-fpm写不进去那是另一个坑了。小文件风暴则比单一日志文件更隐蔽。它总容量可能不大但疯狂创建和删除文件会消耗大量IOPS。比如某些伪静态化插件每个页面生成一个静态文件放到缓存目录页面多了之后单次请求可能触发几十个文件的写入和删除。应对思路是把文件缓存换成内存缓存宝塔商店里装个Redis把CMS的缓存驱动改成Redis家里有矿的再给PHP开上opcache磁盘压力会小很多。3.3 数据库与任务型应用在下半夜“准时开打”如果你观察到的IO延迟曲线是每天固定时间飙高特别像凌晨2点到3点那基本不用查了必然是计划任务撞车。宝塔面板的计划任务功能确实是神器但它也带来了一个常见的坑默认大家都把任务设在凌晨于是数据库备份、网站全量备份、日志切割、证书续签全挤在同一时刻。几个任务同时跑磁盘读写双重叠加延迟不飙才怪。处理方法很简单把计划任务错峰分配。比如2点跑数据库备份3点半跑网站全量备份4点做日志切割证书相关的另找时间。这操作看起来粗暴但极其有效能把一个“IO延迟高”的报警直接消除掉。我处理过的机器里有三分之一以上是这个问题改完计划任务的执行时间就再没复发过。数据库本身的问题是另一个大头。MySQL在宝塔环境里默认配置偏保守一旦并发上来容易出现临时表落盘。什么叫临时表落盘MySQL执行GROUP BY或ORDER BY时如果排序的数据量超过了tmp_table_size的阈值它就会在磁盘上创建临时文件来排序。每一次落盘都是一次磁盘写IO。检查起来也很简单打开宝塔的MySQL慢查询日志看有没有大量创建临时表的记录。遇到这种情况可以适当调大tmp_table_size和max_heap_table_size。比如从默认的16M调到64M或128M。调的时候要理性不是越大越好因为每个连接都会分配内存调太大内存会不够。我一般从64M起步观察几天内存和IO变化再决定要不要继续往上调。另外定期执行OPTIMIZE TABLE对频繁更新的大表也很有帮助能减少索引碎片和随机IO。这里还要点名一下任务型应用比如很多人喜欢折腾的青龙面板搭建。这类应用的特点是并发任务多每个任务频繁读写配置文件、数据库、日志文件一旦多个任务同时跑磁盘IO会被吃得很干净。我见过一台同时跑网站和青龙面板的低配机器任务并发时IO延迟直接飙到几百毫秒。处理思路无非三种把任务调度时间错开、限制任务并发数、或者干脆把这类型的应用和网站业务拆到两台机器上。3.4 云盘被限流突发性能额度用完了这一类问题在云服务器上特别常见但又最容易被用户忽略。很多云厂商的服务器实例属于“突发性能型”设计CPU有积分池磁盘同样有IOPS基线。平时负载不高使用的IO能力低于基线就会积累“积分”。一旦持续高负载积分用光IO就被限制在一个很低的水平表现出来就是面板上的IO延迟居高不下。怎么判断是不是被限流我总结三个特征第一业务量没有明显增长但IO延迟就是缓慢爬升然后维持在高位第二iostat显示%util很高但实际的吞吐量却不高两个数据对不上第三云厂商的监控控制台里有突发额度或积分的统计这个指标如果见底了基本就坐实了。应对方式分为应急和长效两种。应急就是减少并发任务、停掉非关键服务给突发额度回血的时间。但这种方式治标不治本只要业务恢复正常额度又会很快消耗光。长期来看要么升级为固定性能规格的实例要么把磁盘换成更高级别的SSD或ESSD盘。尤其是跑数据库、搜索引擎这类吃IO的中间件这一步基本是必经之路。还有一种类似的情况是宿主机层面的IO问题。这在用户侧确实是个黑盒你无法主动解决。但如果你确认业务完全正常、磁盘没满、也没被限流IO延迟却长期高可以做个快照之后提交工单让云厂商检查。这种情况不多见但有我碰过两次。对面的工程师一般会做宿主机迁移处理之后就正常了。3.5 最不愿遇到的情况异常进程与挖矿木马这个场景虽然不常遇到但一遇到就是大事。特征很明显IO延迟高、CPU异常高、网络带宽被占满三个往往同时出现。为什么会牵扯到IO因为挖矿程序虽然主要吃CPU和GPU但它同样会持续写日志、写进程状态、下载更新文件这些都会产生磁盘IO。如果你的机器同时还有正常业务在跑两者叠加IO延迟自然爆表。排查方法从top开始top -bn1看CPU占用排名靠前的进程如果有个叫不出名字的进程占着200%甚至更高的CPU就很可疑了。再确认一下它的可执行文件路径ls -l /proc/PID/exe如果路径在/tmp、/var/tmp这类明显不该放可执行文件的地方基本可以确定有问题。接着查启动项看看有没有被写入计划任务crontab -l入侵者通常会通过计划任务实现持久化每次到点自动拉起挖矿进程。宝塔的面板任务也可以看一眼。处理原则我强调三遍先打快照先打快照先打快照。云控制台里创建快照通常就几分钟这一步等于给自己买了份保险。然后收紧安全组或者暂时停止公网访问再开始清理。清理的过程包括杀进程、删文件、去掉计划任务但更关键的是修复被入侵的入口——是SSH弱密码、Redis未授权访问还是某个Web应用有漏洞必须找到源头堵上否则清理完还会复发。如果自己拿不准能不能清干净最稳妥的做法是备份网站数据和数据库之后直接重装系统重新部署应用。重装虽然麻烦但比和木马斗智斗勇省时间得多也更干净。4. 从应急处置到长效优化的完整方案4.1 应急三步先保业务再断根因真遇到IO延迟把站点拖到接近不可用的时候第一步不是排查是保命。按顺序做三件事第一步打快照。云服务器的控制台基本都能秒级创建快照先创建好等于给这次排查买了份保险。操作前顺手把top、iostat的输出保存一份这是你排查的依据也是事后复盘的数据。第二步清理空间。把日志、临时文件、备份目录这些明显占空间的东西清一清。为什么要先清理因为只要不是满盘导致的问题清理完延迟多半会立刻降下来一大截这能帮你把磁盘空间这个因素排除掉。第三步定位进程并做最小化重启。如果清理完延迟还高用pidstat或iotop找到涉及IO最多的进程尝试单独重启那一个应用。比如IO占用最大的是MySQL就重启MySQL服务然后观察IO变化。恢复下来了说明问题在MySQL内部再去查慢查询和表结构。还是高继续看下一个进程。千万别一上来就reboot整机重启会让所有现场数据消失还可能让还没落盘的数据库数据面临丢失风险。除非IO高到连登录和执行命令都做不到否则不重启。4.2 长效优化把磁盘压力从源头降下来排查清楚之后还要防止下次再犯。我自己会把下面这些优化方案作为标准动作计划任务错峰备份、切割、清理、续签全部错开时间宁可拉长执行窗口也不要叠加执行日志切割宝塔面板默认支持日志切割检查是否都已开启保留天数设置3到7天足够缓存落内存装Redis把PHP的session、页面缓存、对象缓存尽量从文件转向Redis这一步能砍掉大量文件IO数据库定期维护用mysqltuner这类工具按建议调整MySQL参数定期执行OPTIMIZE TABLE打开慢查询日志方便追溯目录挂载拆分服务器有多块盘时把日志目录、备份目录、数据库目录挂到独立磁盘避免它们和系统盘互相争抢IO升级磁盘类型预算允许的情况下直接升级到SSD或ESSD云盘对IO敏感的应用来说这个投资在体验上的提升立竿见影4.3 把告警配置好让“突然变高”变成“提前预警”与其天天被“突然变高”折腾不如把告警配置前置让问题在刚开始冒头的时候就被发现。宝塔面板的告警配置在面板设置里推送渠道支持邮件、钉钉、企业微信、Server酱等。我建议阈值这样设磁盘使用率超过80%告警IO延迟平均超过100ms且持续5分钟告警系统负载超过CPU核心数的1.5倍告警前两个和IO高度相关第三个负载告警能帮你把握整体资源情况。这里的关键点是“持续5分钟”这个条件它会把单次瞬时抖动过滤掉只对持续异常发通知不然天天半夜被闹醒用不了几天你就把通知关了。同时云厂商自带的云监控也要开起来。底层的实例IO状态、带宽、网络丢包这些指标可以和宝塔面板的数据做交叉验证。有时候宝塔显示IO高云监控显示磁盘性能一切正常这种矛盾本身就是重要的线索往往意味着问题出在虚拟化层或者宿主机侧。5. 最后聊几句我的运维习惯我的原则是遇到IO延迟高先拍照记录现场再决定下一步动作。这是我第一次遇到时用代价换来的教训。那次没有记录现场直接重启了重启后暂时正常后面几天又反复最后才发现是日志切割任务和数据库备份任务撞在一起而且日志文件已经堆了几十GB。如果当时先停下来看看iostat和top十分钟就能定位到问题根本不用折腾好几天。另外还有一个经验排查完问题之后一定要花十分钟把处理过程写成笔记。不用写得多规范哪怕就是几条命令、几个数字、当时的判断逻辑都值得留下来。因为IO问题有一个特点它很少只出现一次。今天解决了日志堆积过两个月可能又出现数据库临时表落盘的问题你的笔记就是下次排查时最快的路径。最后再分享一个小技巧如果你用的是SSD平时IO延迟在1到3毫秒左右某天突然变高可以先看一眼云控制台有没有临时快照、迁移、扩容这类后台任务在跑。很多时候“突然”不等于故障而是有后台任务在占用磁盘。反正不管哪种原因先记录、再排查、最后动手这个顺序永远不会错。
返回列表