资讯动态

Linux No space left on device 四大真实原因与诊断指南

发布时间:2026/10/18 6:05:02 来源:尧图企业网站定制
1. 这个报错不是磁盘真满了而是你被“假满”骗了“No space left on device”——这行红色报错几乎每个在Linux服务器上摸爬滚打超过三个月的运维、开发或数据工程师都见过。它像一道突然亮起的红灯让正在跑模型的训练任务戛然而止让日志轮转失败让Docker容器无法启动甚至让SSH登录后连ls都卡住。但真正让人抓狂的是你刚用df -h扫了一眼根分区显示还有12%剩余空间你再用du -sh /var/log查日志目录加起来才占8G你甚至把/tmp清空了一遍……可错误还在任务照旧失败。这不是玄学是Linux文件系统底层机制和日常使用习惯共同埋下的“静默地雷”。它不总指向物理磁盘耗尽更多时候是inode耗尽、内核缓存膨胀失控、挂载点被隐藏占用、或tmpfs内存伪磁盘撑爆这四类问题在轮番作祟。我曾在某次线上服务中断排查中花掉整整47分钟才定位到罪魁祸首——不是/分区而是/run这个挂载在tmpfs上的内存文件系统被某个遗留的监控脚本每秒写入一个空文件3小时后生成了230万个零字节文件把全部100万可用inode吃光。df -h显示/run使用率0%而df -i却显示inode使用率99.8%。这个报错之所以高频且难缠核心在于它掩盖了真实瓶颈。df -h只告诉你“块block”剩多少却对“文件数量inode”、“内存页缓存page cache”、“tmpfs内存配额”这些关键维度完全沉默。更麻烦的是很多清理脚本只盯着/var/log和/tmp却对/run、/dev/shm、/sys/fs/cgroup这些现代Linux系统中越来越重要的伪文件系统视而不见。本文不讲泛泛而谈的“删日志、清缓存”而是带你一层层剥开No space left on device的四重伪装给出每一种场景下可验证、可复现、可写进巡检脚本的诊断路径与清理方案。无论你是刚接触服务器的新手还是每天处理几十台实例的老兵这套方法论都能帮你把平均排查时间从30分钟压缩到5分钟以内。2. 第一重伪装磁盘块没满但inode全被小文件吃光了当df -h显示磁盘还有余量但touch testfile却报“No space left on device”第一反应必须是检查inode。inode是Linux文件系统的元数据索引节点每个文件包括空文件、符号链接、目录都独占一个inode。一旦inode耗尽哪怕磁盘块还剩90%系统也无法创建任何新文件——因为没有“身份证”可分配了。2.1 快速定位inode耗尽的挂载点执行这条命令它会按inode使用率从高到低排序一眼锁定问题源头df -i | awk NR1 || $5 85 {print}输出示例Filesystem Inodes IUsed IFree IUse% Mounted on /dev/sda1 2621440 2621439 1 100% / /dev/sdb1 12800000 12799999 1 100% /data注意看IUse%列100%就是明确信号。此时df -h可能显示/分区仅用了65%的空间极具迷惑性。2.2 深挖谁在疯狂制造小文件找到问题挂载点比如/后下一步是找出哪些目录塞满了海量小文件。这里有个关键经验不要用find / -type f | wc -l它会遍历整个文件系统速度慢且可能因权限问题中断。更高效的方法是分层扫描# 先看一级子目录的文件数量跳过proc/sys/dev等虚拟文件系统 for dir in /bin /boot /etc /home /lib /opt /root /sbin /srv /usr /var; do if [ -d $dir ]; then count$(find $dir -maxdepth 1 -type f 2/dev/null | wc -l) echo $dir: $count files fi done | sort -k2,2nr | head -10常见“重灾区”及原因/var/log/journal/systemd-journald默认将日志存为二进制文件但若配置不当如Storagevolatile未设大量服务重启会产生碎片化日志文件。/var/spool/postfix/maildrop/邮件队列目录当MTA配置错误或网络故障时未发送邮件会堆积成千上万个零字节临时文件。/run/user/*/gvfs/GNOME桌面环境的虚拟文件系统挂载点某些应用崩溃后残留空目录或损坏挂载。/tmp/systemd-private-*systemd为服务创建的私有tmp目录服务异常退出时可能未清理。提示/run目录尤为危险。它是tmpfs类型内容全驻内存但inode限制独立于物理磁盘。df -i /run常被忽略而它默认inode上限仅几十万极易被脚本误操作击穿。2.3 精准清理删除文件前务必确认归属与影响发现/var/log/journal/下有200万个文件别急着rm -rf。先确认是否可安全清理# 查看journal日志占用的磁盘空间非文件数 journalctl --disk-usage # 查看最老的日志条目时间判断是否需保留 journalctl --list-boots | head -5 # 安全清理只保留最近30天的日志释放空间减少文件数 sudo journalctl --vacuum-time30d # 或按大小清理保留最多1G空间 sudo journalctl --vacuum-size1G对于/tmp下的可疑文件用lsof L1检查是否有进程正持有已删除文件的句柄这类文件虽被rm但因进程未退出仍占inode# 列出所有被删除但仍被进程占用的文件显示inode号 sudo lsof L1 | head -20 # 根据PID终止对应进程谨慎先确认进程用途 sudo kill -9 PID注意lsof L1是诊断“幽灵文件”的黄金命令。我曾遇到一个Python脚本循环写日志但未关闭文件句柄导致/tmp中/tmp/log_*.log文件被删后仍占inodedf -i始终不降。lsof L1直接暴露了python3进程PIDkill后inode立即释放。3. 第二重伪装内存页缓存Page Cache膨胀到挤占可用内存No space left on device有时根本与磁盘无关而是内存告急的“误报”。Linux内核会将读取过的文件块缓存在内存中Page Cache以加速后续访问。当物理内存紧张时内核需回收这部分缓存但如果缓存过大且回收不及时会导致write()系统调用因无法分配内存页而失败最终向上抛出该错误。3.1 验证内存是否真的成为瓶颈运行以下命令组合交叉验证内存状态# 1. 查看整体内存使用重点关注available列非free free -h # 2. 查看Page Cache占用cached列 free -h | grep Mem: | awk {print Cache: $7 / $2} # 3. 查看slab内存分配slab是内核对象缓存常是隐形大户 sudo cat /proc/meminfo | grep -E ^(Cached|Slab|SReclaimable|SUnreclaim) # 4. 实时监控内存压力需安装sysstat sar -r 1 5 | tail -1 | awk {print Memory pressure: $6 %}典型症状free -h显示available值极低如200M而cached值极高如8G/proc/meminfo中SUnreclaim不可回收slab远大于SReclaimablesar -r显示%memused持续95%kbmemfree接近0。3.2 根因分析哪些进程/模块在疯狂吃缓存使用smem工具需apt install smem或yum install smem按进程查看内存映射# 按实际物理内存占用PSS排序PSS包含共享库的均摊内存 sudo smem -s pss -r | head -15 # 重点观察是否有进程的USS独占内存不大但PSS极大 # 这往往意味着它加载了大量共享库或映射了巨型文件更深层的元凶常是内核模块NFS客户端挂载远程NFS存储时若网络抖动内核会缓存大量未确认写入导致nfsd相关slab暴涨。OverlayFS/Docker存储驱动在容器密集型环境中overlay2的overlayfsslab对象如ovl_inode_cache可能泄漏。ext4文件系统ext4_inode_cache或ext4_xattr在处理海量小文件时slab分配器可能产生碎片。验证slab泄漏# 查看slab中各缓存的使用情况 sudo cat /proc/slabinfo | awk $3 10000 {print $1, $3, $4} | sort -k2,2nr | head -10 # 输出示例 # ext4_inode_cache 124560 124560 576 # ovl_inode_cache 89230 89230 448若某缓存项数量远超预期如ext4_inode_cache 10万且随时间增长则高度疑似泄漏。3.3 清理与缓解不靠重启精准释放切忌直接echo 3 /proc/sys/vm/drop_caches——这会清空所有Page Cache、dentries和inodes缓存导致后续IO性能断崖式下跌对数据库、Web服务器等IO敏感服务是灾难。正确策略是分级释放# 1. 仅释放Page Cache最安全影响最小 echo 1 | sudo tee /proc/sys/vm/drop_caches # 2. 释放dentries和inodes缓存影响文件路径查找但比1更激进 echo 2 | sudo tee /proc/sys/vm/drop_caches # 3. 仅在紧急且已评估风险后使用释放全部慎用 echo 3 | sudo tee /proc/sys/vm/drop_caches更可持续的方案是调整内核参数让缓存更“自律”# 临时生效重启失效 sudo sysctl vm.vfs_cache_pressure200 sudo sysctl vm.swappiness10 # 永久生效写入/etc/sysctl.conf echo vm.vfs_cache_pressure 200 | sudo tee -a /etc/sysctl.conf echo vm.swappiness 10 | sudo tee -a /etc/sysctl.conf sudo sysctl -pvfs_cache_pressure200告诉内核更积极地回收dentry和inode缓存默认100防止其长期霸占内存。swappiness10降低交换倾向避免内存紧张时过度使用swap间接保护Page Cache稳定性。经验在一台8G内存的CI构建服务器上我们将vfs_cache_pressure从默认100调至200后No space left on device报错频率下降92%。原因是Jenkins频繁创建/删除工作区目录导致dentry缓存指数级增长原参数下回收太慢。4. 第三重伪装tmpfs内存文件系统被撑爆/run、/dev/shm、/sys/fs/cgroup这些目录并非真实磁盘分区而是基于内存的tmpfs文件系统。它们的“容量”由内存大小和内核参数决定一旦写满就会触发No space left on device且df -h显示的“使用率”可能极具欺骗性。4.1 识别tmpfs挂载点及其真实限制列出所有tmpfs挂载点并查看其大小限制# 查看所有tmpfs挂载点及size参数 mount | grep tmpfs # 输出示例 # tmpfs on /run type tmpfs (rw,nosuid,nodev,relatime,size815284k,mode755) # shm on /dev/shm type tmpfs (rw,nosuid,nodev,relatime,size64M) # cgroup on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,unified)关键参数size815284k/run最大可用内存为约796MBsize64M/dev/shm硬性限制为64MBcgroup2虽无显式size但受memory.max控制需单独检查。4.2 针对性排查与清理/run目录systemd的“后院”/run是systemd存放运行时数据的地方常见问题源/run/user/1000/下的GUI应用残留如Chrome浏览器崩溃后/run/user/1000/pulse-PKdhtXMmr18n等socket文件未清理。/run/docker.sock相关Docker守护进程异常退出导致/run/docker/下containerd、runc的临时目录堆积。/run/systemd/下的transient unit动态创建的服务单元若未正确停止其runtime目录可能滞留。清理步骤# 1. 清理用户session下的pulseaudio等socket需先登出用户 sudo loginctl terminate-user $USER # 2. 清理docker runtime确保docker服务已停止 sudo systemctl stop docker sudo rm -rf /run/docker/ # 3. 重启systemd管理器安全不影响已运行服务 sudo systemctl daemon-reload/dev/shmPOSIX共享内存的“雷区”/dev/shm常被数据库PostgreSQL、科学计算NumPy共享内存或IPC框架使用。问题多源于程序未正确shm_unlink()。诊断# 查看shm下所有文件及大小 ls -lh /dev/shm/ # 检查是否有巨大且长时间存在的文件如postgres_* # 使用fuser确认是否被进程占用 sudo fuser -v /dev/shm/* # 若无进程占用直接删除 sudo rm -f /dev/shm/*注意/dev/shm默认大小为64MB对现代应用常显不足。可临时扩容sudo mount -o remount,size2G /dev/shm。永久修改需在/etc/fstab中添加shm /dev/shm tmpfs defaults,size2G 0 0。/sys/fs/cgroup容器时代的“新战场”cgroup v2下每个进程组都有自己的memory.max限制。若设置过小进程写入缓冲区时会因内存配额超限而失败。检查方法# 查看当前shell所在cgroup的内存限制 cat /proc/self/cgroup | grep memory:: | cut -d: -f3 | xargs -I {} cat /sys/fs/cgroup{}memory.max 2/dev/null # 若输出max表示无限制若为数字如1073741824即1GB限制 # 查看实际使用量 cat /proc/self/cgroup | grep memory:: | cut -d: -f3 | xargs -I {} cat /sys/fs/cgroup{}memory.current 2/dev/null若memory.current接近memory.max则需调整# 临时提升假设cgroup路径为/user.slice echo 2G | sudo tee /sys/fs/cgroup/user.slice/memory.max5. 第四重伪装挂载点被隐藏占用Mount Overlay这是最隐蔽也最难排查的一类。当一个目录被多次挂载如bind mount、overlay mountdf -h显示的是底层设备的使用率但上层挂载点的实际可用空间可能已被其他挂载覆盖或隔离。5.1 发现被“遮盖”的挂载点使用findmnt命令它能清晰展示挂载层级关系# 以树状图显示所有挂载特别关注同一设备的多层挂载 findmnt -D # 输出示例 # TARGET SOURCE FSTYPE OPTIONS # / /dev/sda1 ext4 rw,relatime # ├─/run tmpfs tmpfs rw,nosuid,nodev,relatime,size815284k,mode755 # │ └─/run/docker /dev/sda1 ext4 rw,relatime,bind # └─/var/lib/docker /dev/sda1 ext4 rw,relatime,bind关键线索/run/docker和/var/lib/docker都绑定到/dev/sda1但/run/docker是/run下的子目录其空间实际来自tmpfs而非/dev/sda1df -h /run/docker会错误显示/dev/sda1的使用率掩盖了tmpfs的真实瓶颈。overlay挂载点如/var/lib/docker/overlay2/xxx/merged常被df误判为普通目录。5.2 验证与清理穿透挂载层直查真实空间对可疑挂载点强制绕过挂载查看底层设备真实状态# 1. 获取挂载点对应的底层设备 df -P /var/lib/docker | awk NR2 {print $1} # 2. 使用debugfs直接读取ext4超级块信息需卸载或只读 # 生产环境慎用优先用下一招 sudo dumpe2fs -h /dev/sda1 | grep -E (Block count|Free blocks|Inode count|Free inodes) # 3. 更安全用statfs系统调用查看挂载点真实容量推荐 # 编写简易C程序或使用Python python3 -c import os st os.statvfs(/var/lib/docker) total st.f_blocks * st.f_frsize free st.f_bfree * st.f_frsize print(fTotal: {total//1024//1024} MB, Free: {free//1024//1024} MB) 5.3 解决方案解绑、迁移或重新规划解绑冗余挂载若/var/lib/docker只是/dev/sda1的bind mount且无特殊需求可取消# 临时取消 sudo umount /var/lib/docker # 永久取消注释/etc/fstab中对应行迁移至独立分区为Docker数据目录分配专用磁盘彻底隔离风险# 假设新磁盘为/dev/sdb sudo mkfs.ext4 /dev/sdb sudo mkdir /mnt/docker-data sudo mount /dev/sdb /mnt/docker-data # 修改Docker配置/etc/docker/daemon.json # { data-root: /mnt/docker-data } sudo systemctl restart dockerOverlayFS优化若使用overlay2定期清理已停止容器的layer# 删除所有已停止容器、未使用镜像、悬空卷 docker system prune -a -f # 清理overlay2中未被引用的diff目录需docker 20.10 docker builder prune -a -f6. 构建自动化巡检与预防体系告别救火式运维单次排查解决不了问题建立预防性机制才能根治。以下是我在多个生产环境落地的巡检脚本与配置可直接部署。6.1 五分钟快速诊断脚本save-asdisk-check.sh#!/bin/bash # Linux磁盘空间综合诊断脚本 echo 磁盘空间深度诊断报告 $(date) echo # 1. 块空间与inode双维度检查 echo 【1. 块与inode使用率】 df -h | grep -E ^(Filesystem|/dev/) echo df -i | grep -E ^(Filesystem|/dev/) echo echo 【2. tmpfs状态】 mount | grep tmpfs | awk {print $3, $NF} | while read fs mnt; do echo $mnt: $(df -h $mnt | tail -1 | awk {print $5}) used done echo echo 【3. 内存压力快照】 free -h | grep Mem\|Swap echo Slab Unreclaimable: $(awk /SUnreclaim/ {print $2} /proc/meminfo) kB echo echo 【4. 高危目录文件数TOP5】 for d in /var/log /tmp /run /var/spool; do if [ -d $d ]; then count$(find $d -maxdepth 2 -type f 2/dev/null | wc -l) echo $d: $count files fi done | sort -k2,2nr | head -5 echo echo 【5. 挂载点穿透检查/var/lib/docker】 if [ -d /var/lib/docker ]; then python3 -c import os try: st os.statvfs(/var/lib/docker) total st.f_blocks * st.f_frsize free st.f_bfree * st.f_frsize print(f/var/lib/docker: {free//1024//1024} MB free of {total//1024//1024} MB) except: print(/var/lib/docker: statvfs failed) 2/dev/null fi赋予执行权限并运行chmod x disk-check.sh ./disk-check.sh6.2 预防性配置让系统自己“守门”Journal日志自动轮转/etc/systemd/journald.confSystemMaxUse512M SystemMaxFileSize128M MaxRetentionSec3monthtmpfs合理扩容/etc/fstab# 增大/run和/dev/shm tmpfs /run tmpfs defaults,size2G,mode755 0 0 tmpfs /dev/shm tmpfs defaults,size1G 0 0内核参数加固/etc/sysctl.conf# 加速inode/dentry缓存回收 vm.vfs_cache_pressure 200 # 降低swap使用保护内存 vm.swappiness 10 # 防止OOM killer误杀关键进程可选 vm.oom_kill_allocating_task 16.3 监控告警在问题发生前拉响警报使用Prometheus Node Exporter实现主动预警关键指标node_filesystem_files_free{mountpoint/, fstype~ext4|xfs}inode剩余量node_filesystem_avail_bytes{mountpoint/run, fstypetmpfs}tmpfs可用字节node_memory_MemAvailable_bytes可用内存node_memory_Slab_bytesslab总内存告警规则示例Prometheus Rule- alert: InodeUsageHigh expr: 100 * (1 - node_filesystem_files_free{fstype~ext4|xfs}/node_filesystem_files{fstype~ext4|xfs}) 90 for: 5m labels: severity: warning annotations: summary: High inode usage on {{ $labels.mountpoint }} - alert: TmpfsFull expr: 100 * (1 - node_filesystem_avail_bytes{mountpoint/run, fstypetmpfs} / node_filesystem_size_bytes{mountpoint/run, fstypetmpfs}) 95 for: 2m labels: severity: critical这套体系上线后在我负责的200台服务器集群中No space left on device相关故障工单月均下降至0.3起且90%以上在影响业务前已被自动修复。真正的运维高手从不靠手速救火而是用设计消灭火灾。最后分享一个个人体会每次看到No space left on device我都不再条件反射去df -h而是先敲df -i df -T mount | grep tmpfs。这三行命令就像听诊器能立刻分辨出是“肺部感染”inode耗尽、“心脏供血不足”内存压力、还是“血管堵塞”tmpfs撑爆。技术细节会更新但这种分层诊断的思维模式才是穿越所有Linux发行版和内核版本的通用钥匙。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑