记一次 PDF 处理 Bug 引发的磁盘爆满事故
记一次 PDF 处理 Bug 引发的磁盘爆满事故
一个看似无害的异常日志,因定时任务的疏忽差点让服务器沦陷。
一、背景
最近线上运行着一个处理 PDF 文件的服务,通过定时任务(Crontab)周期性执行,负责解析用户上传的 PDF 并提取关键信息。某天突然收到服务器磁盘告警,登录服务器时卡顿严重,甚至 SSH 连接都反复超时,凭借经验——大概率是磁盘写满了。
二、排查过程
1. 艰难地登入服务器
在网络延迟和频繁超时的夹缝中,终于以“龟速”登上服务器。第一件事就是确认磁盘:
df -h
输出如下(类似):
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 40G 40G 0 100% /
40G 的系统盘被吃干榨净。接下来找出元凶。
2. 定位大文件
du -sh /* | sort -hr | head -5
很快,/tmp 目录以几十 G 的体量高居榜首。进入 /tmp,发现大量以日期时间命名的日志文件,类似 pdf_error_20240515.log,单文件竟有十几 G。
3. 日志内容分析
查看日志,清一色的 PDF 解析异常堆栈:
java.io.IOException: Error reading PDF ...
Caused by: org.apache.pdfbox.exception.WrappedIOException ...
而且日志时间间隔非常密集,几乎每秒都在疯狂写入。至此,初步判断是 PDF 解析服务在定时任务中反复重启运行,且把异常日志定向到了 /tmp 下。
4. 追踪定时任务
查看 crontab:
crontab -l
发现了类似这样的一条:
*/1 * * * * cd /opt/pdf-processor && python process.py
再查看 process.py,其内部调用了一个 Java PDF 解析命令,大致逻辑如下:
import os
os.system("java -jar pdf-parser.jar /data/pdfs/ > /tmp/pdf_error_$(date +%Y%m%d).log")
问题顿时明朗——
三、根因分析
-
没有使用
&或nohup守护
定时任务每分钟触发一次,但由于脚本内命令是前台阻塞运行且耗时不短,上次任务未结束,下次任务又启动,导致多个实例同时写同一个日志文件。 -
异常日志无限追加
>重定向每次打开都会截断,但这里用了带日期的动态文件名,导致每天生成一个文件。然而当 PDF 批量处理时,单个异常日志就足以撑满磁盘。 -
PDF 解析本身存在 Bug
对于某些损坏或非标准格式的 PDF,解析库持续抛出异常,进程并没有退出,而是继续“卡死”在异常循环中,持续写日志,形成恶性循环。 -
日志未轮转,目录选择不当
/tmp一般是系统盘,空间有限;且没有配置 logrotate 或脚本内部的日志清理逻辑。
四、解决方案
应急处理
# 删除 /tmp 下的大日志文件(紧急时可直接 rm)
rm -rf /tmp/pdf_error_*.log
# 立即杀掉所有残留进程
pkill -f pdf-parser
磁盘恢复后,服务恢复正常。
根治方案
1. 修复 PDF 解析 Bug
- 完善异常处理,捕获所有
IOException并记录有限次错误后主动退出。 - 增加超时机制(如处理单文件超过 5 分钟则跳过)。
2. 优化定时任务脚本
- 使用
flock或 lock file 确保单实例运行:
*/5 * * * * flock -n /tmp/pdf.lock -c 'cd /opt/pdf-processor && python process.py'
- 将任务间隔从每分钟延长至合理周期(如 5 分钟),并增加任务完成检查。
3. 日志规范化
- 不要将日志写在
/tmp下,应放在独立挂载点或应用目录。 - 使用
>>追加 + logrotate 轮转,或直接集成到 Log4j / Logback 等框架。 - 当错误日志达到一定阈值(如 100M)时,主动清空或停止写。
4. 监控与告警
- 添加磁盘使用率告警(如 >80% 通知)。
- 对关键进程数量、日志文件大小进行监控。
- 实施
/tmp目录大小上限策略(如 systemd tmpfiles.d 限制)。
五、经验总结
- 任何会写日志的定时任务都必须考虑日志爆炸:磁盘 I/O 不仅能拖慢系统,还能让服务器直接不可达。
- 避免在
/tmp下存储长期运行的结果:很多 Linux 发行版会对/tmp定期清理,但不足以预防突发写入。 - “有 & 无 &”的教训:虽然本次不是 & 的问题,但类似场景中,如果不将长任务放入后台或使用 nohup,Crontab 会因等待而阻塞后续触发,引发进程堆积。
- 异常处理不是可有可无的装饰:生产中任何一个未捕获的异常都可能成为压死骆驼的最后一根稻草。
这次的磁盘爆满事件,幸好发生在测试环境(或是及时被发现),但足以让我们警醒——魔鬼总是在细节中。分享出来,希望大家不要再踩同一个坑。

浙公网安备 33010602011771号