
星期一早上刚进办公室告警平台就刷出一条消息/data 分区使用率已经到 94%。我第一反应是有人传了什么大文件进来跑过去df -h一看确实快满了但接下来用du -sh /data/*排了半天愣是没找到哪个目录在“疯狂吃”空间。折腾到中午我决定换一个思路装了一个叫 agedu 的磁盘分析工具才算把整个空间占用彻底看明白。这篇文章就把我在 KeyarchOS 环境里从安装到实战排查的完整过程写出来给同样被磁盘告警折磨的运维朋友一个可以照着操作的参考。agedu这个名字可以拆成age du意思是“带年龄统计的 du”。它由 Simon Tatham也是 PuTTY 的作者开发核心设计很有意思在统计磁盘空间大小的同时记录每个目录和文件的最后访问时间生成一份独立的索引数据。之后你可以基于这份索引随时启动 Web 界面或终端界面反复查看不需要对磁盘做第二次全量扫描。在“盘满了但不知道删什么”的场景里这东西比传统du顺手太多。这篇文章适合两类人看一类是 Linux 服务器运维和 SRE经常处理磁盘空间告警另一类是玩 NAS、自建服务器、喜欢折腾 Linux 系统的发烧友。就算你之前完全没用过 KeyarchOS只要会基础 Linux 命令跟着下面的操作走也能把 agedu 跑起来并真正用上。1. 磁盘分析工具的现状与选型思考1.1 传统工具为什么不够用在聊 agedu 之前我得先吐槽一下传统组合拳的痛点。df -h只能看到文件系统层面的占用率告诉你/data满了但回答不了“里面到底是什么满了”。du -sh /data/*可以按一级目录把大小排出来基本能定位到嫌疑范围可它有几个硬伤。第一是慢它要递归累加所有文件的块大小遇到上百万小文件或海量日志目录时一跑就是几十分钟磁盘 IO 被打得喘不过气生产环境里很容易火上浇油。第二是它只告诉你大小不告诉你时间一个目录占 200G但里面全是三年前的历史归档和另一个占 200G 但每天都在高速增长的日志目录在du的输出里长得一模一样后续处理策略完全不一样靠一个数字根本没法判断。ncdu是个很好的交互式工具界面友好可以一层层往下点但它本质上还是解决“哪里大”的问题仍然没有把“最后一次访问时间”这个维度带进来。baobab是图形化工具在带桌面的系统上很好看可服务器一般没有显示器我也懒得为看个磁盘占用专门开 X11 转发。lsof grep deleted是另一个经典套路专门查“文件被删了但进程还占着句柄”的情况这招在排查空间没释放时非常有用但它只能覆盖这一种特定场景对整体空间分布仍然无能为力。所以我需要的工具最好满足三个条件第一能一次扫描生成可反复使用的数据文件第二能同时给出空间大小和“最后访问时间”两类信息第三在没有图形界面的服务器上也能舒服地使用。agedu 恰好把这三点都占了。1.2 agedu 的核心特性与适用场景agedu 在扫描阶段会构造一棵目录树每个节点记录两部分信息累计占用空间大小以及该节点内文件的最新访问时间。扫描完成后所有数据会写入一个紧凑的索引文件这个文件远比原始文件总量小得多后续无论是开 Web 界面还是终端界面都只需要读这个索引不会再碰磁盘上的真实文件。它默认使用 atime最后访问时间作为“年龄”依据这是它和普通磁盘分析工具最大的区别。为什么关注 atime 这么重要因为在真实服务器上大量占用空间的罪魁往往是那些“活着但没有被访问”的数据历史备份包、几年前的日志、旧版本软件安装包、已经离职同事留下的项目目录。它们占着空间却可能半年、一年都没有任何进程读过。如果把“块头大”和“年龄老”两个条件结合起来看清理优先级一下就出来了。此外agedu 还支持按 ctime 或 mtime 分析这在后面我讲 noatime 挂载坑的时候会详细说。运行模式上它提供了-s扫描、-wWeb 服务、-T终端交互界面三种主要用法覆盖了远程服务器和日常运维的大多数场景。2. KeyarchOS 环境准备与 agedu 安装的两种方式2.1 先确认系统基本信息我手头这台机器装的是 KeyarchOS一套基于 Linux 内核构建的企业级服务器操作系统。在开始装任何东西之前建议先确认系统版本、架构和包管理器类型避免装错或漏掉依赖。一般我会先执行这几个命令cat /etc/os-release uname -a which dnf || which yumKeyarchOS 的软件生态跟主流 Linux 发行版兼容度比较高我的这台机器用dnf作为包管理器命令输出里的系统标识也能正常识别。架构是 x86_64这决定了后续源码包要选对应平台的内容。做这一步不是为了走形式而是很多安装问题都出在“系统其实没有网络源”“架构不匹配”“默认包管理器是 yum 而不是 dnf”这些细节上。另外提醒一句安装前最好确认当前用户有 sudo 权限。虽然 agedu 可以装在用户目录下但在服务器上通常还是/usr/local/bin这种系统目录更合适免得到时候还得折腾 PATH。2.2 方式一用 dnf 直接安装如果你的系统软件源里已经有 agedu那这是最省事的方式。先在源里搜一下dnf search agedu有结果的话直接安装dnf install -y agedu装完验证一下agedu --help--help能正常输出就说明命令已经可用了。我在这台 KeyarchOS 上最初也想走这种方式但现实很骨感默认源里并没有 agedu 的直接安装包。如果你也遇到同样的情况别急着放弃还有两个思路。一是看看系统有没有开放 EPEL 这类第三方软件源配置好之后再用dnf search agedu查一次二是直接走源码编译这也是我本文要重点讲的方式因为源码编译不受仓库维护进度影响只要系统能装编译工具什么发行版都能用。2.3 方式二源码编译安装 agedu源码编译听上去比dnf install重但 agedu 是个轻量工具依赖很少编译过程很快。我完整走了一遍整个过程大概只需要几分钟主要步骤是下载源码、解压、配置、编译、安装。先装编译需要的基础环境。KeyarchOS 上可以用dnf install -y gcc make如果你愿意也可以一次性装完开发工具组里面有 gcc、make、autoconf 等一整套避免后面再补依赖。命令是dnf groupinstall -y Development Tools接着去 agedu 的官网下载最新源码包。地址是https://www.chiark.greenend.org.uk/~sgtatham/agedu/这个页面会列出当前最新的版本号复制 tar.gz 包的下载链接后在服务器上下载并解压。下面命令里的版本号你换成当时最新的即可cd /usr/local/src wget https://www.chiark.greenend.org.uk/~sgtatham/agedu/agedu-xxxx.tar.gz tar -zxvf agedu-xxxx.tar.gz cd agedu-xxxx接下来是标准三连./configure make make install默认情况下make install会把可执行文件装到/usr/local/bin/agedu。如果/usr/local/bin不在 PATH 里可以用完整路径执行或者做个软链接比如ln -s /usr/local/bin/agedu /usr/bin/agedu装完之后执行agedu --version或者agedu --help确认安装成功了。我编译的过程很顺利一次通过中间唯一需要注意的就是系统必须装了 gcc 和 make否则会在 configure 阶段或者 make 阶段直接报错。2.4 安装后的自检与测试环境搭建工具装好之后我习惯先建一个小型测试目录确认扫描和展示功能都正常再上生产目录。千万别一上来就对着/整盘扫描万一工具用得不对白跑几十分钟不说还拖累生产 IO。测试目录可以这样准备mkdir -p /tmp/agedu_test cd /tmp/agedu_test dd if/dev/zero ofbigfile.bin bs1M count200 dd if/dev/zero ofoldfile.bin bs1M count50 touch -a -d 2024-01-01 00:00:00 oldfile.bin mkdir logs dd if/dev/zero oflogs/app.log bs1M count100这里我用dd生成了几个不同大小文件再用touch -a -d把其中一个文件的 atime 改成很久以前的时间。这样在 agedu 的界面里就能明显看到“大文件”和“老文件”的区分验证功能非常直观。测试扫描命令cd /tmp/agedu_test agedu -s .如果没有报错并且当前目录下生成了.agedu索引文件说明扫描功能正常。再启动 Web 界面看一眼如果页面能打开安装就算完全通过了。3. agedu 核心参数详解与操作实战3.1 扫描目录的基本姿势agedu 最基本的使用流程是“先扫描后查看”。扫描命令的格式是agedu -s 路径它会递归统计该路径下所有目录和文件并把结果保存到索引文件中。默认情况下索引文件会写到当前目录的.agedu文件里。要注意的是扫描命令和查看命令的工作目录要一致否则 agedu 找不到索引文件。为了避免这种混乱我更推荐用-f参数显式指定索引文件位置这样不管在哪个目录执行命令都能准确找到数据mkdir -p /var/lib/agedu agedu -s -f /var/lib/agedu/data.agedu /data这个做法的另一个好处是索引文件和数据源可以分开。比如你在一台大存储服务器上扫描了/data索引文件生成后可以拷贝到自己的笔记本上用agedu -w -f data.agedu直接在本地打开完全不影响生产服务器。扫描阶段还有几个值得注意的点。第一大目录首次扫描会比较耗时因为要 stat 每个文件第二如果目录里混有无法访问的文件或子目录会有 Permission denied 之类的提示这种时候直接用 root 或者 sudo 执行扫描最省心第三扫描过程中尽量不要对目标目录做大量写入操作否则结果会有偏差而且 IO 叠加会让服务器变慢。3.2 Web 界面查看空间占用扫描完成之后最直观的查看方式是启动 Web 界面agedu -w -f /var/lib/agedu/data.agedu --auth none --address 0.0.0.0 --port 8080然后浏览器访问http://服务器IP:8080。页面上会展示目录树每个节点都有两个核心数据空间大小和最后访问时间。节点颜色通常由访问时间决定越是长时间没被访问的内容颜色越偏暖色看起来就像一张“磁盘热力图”一眼就能瞄出哪些目录又大又老。这里说说--auth none。agedu 的 Web 服务默认可能要求认证因为目录和文件信息属于敏感数据不认证就暴露在网络上不太安全。我一般在两种情况下才会关掉认证一是本机临时查看浏览器和服务器在同一台机器二是内网测试环境且确认没有外网访问路径。生产环境如果要开远程查看建议保留认证或者配合防火墙限制来源 IP。即使关掉了认证也最好把监听地址限制在127.0.0.1然后用 SSH 隧道到本地访问这样最稳妥。如果服务器没有图形浏览器也可以先用curl -I http://127.0.0.1:8080验证 Web 服务是否正常监听返回 200 或 302 都说明服务起来了。3.3 用时间维度筛选“陈年文件”agedu 区别于普通 du 的核心能力就是能用时间维度来做筛选。扫描时可以通过-t参数设定一个时间阈值让结果重点体现“超过这个时间没有活动”的内容。比如我想找/backup下面一年以上没被访问过的数据可以这样扫描agedu -s -f /var/lib/agedu/backup.agedu -t 365d /backup这里的-t 365d表示 365 天。agedu 会把超过阈值未访问的节点突出显示而那些近期还在活跃的数据会被弱化。这个思路特别适合处理“备份目录越来越满”的经典问题备份是给未来恢复用的但绝大多数情况下历史版本根本不会有人去碰一年前的备份和五年前的备份占用同样的空间却有着完全不同的保留价值。在 Web 界面上这种“又大又老”的目录会非常显眼。我通常先按大小点开最大的分支再看颜色是否偏暖两者都满足的节点就是重点清理对象。如果只看大小很容易误删当前仍在高频读写的活跃数据如果只看时间又可能忽略那些体量巨大的压缩包。只有把大小和年龄放在一起看才谈得上“精准清理”。3.4 终端界面与离线分析Web 界面在远程桌面上很舒服但有时候我只想 SSH 上去快速看一眼这时候终端界面更合适。执行agedu -T -f /var/lib/agedu/data.agedu会进入一个类似 ncdu 的交互界面可以用方向键浏览目录回车进入子目录按 q 退出。对于纯命令行环境的人来说这种体验比 Web 页面还要顺手因为它不需要额外监听端口也不用在浏览器里翻找路径。再说说离线分析。索引文件生成后你就可以把它拷走在任何一台装有 agedu 的机器上打开不需要目标目录连在机器上。我偶尔会在周末值班时把生产环境的索引文件 scp 到自己的机器上慢慢分析不占用生产资源。这条经验在很多场景下非常实用尤其适合那种只有维护窗口才能登录服务器、但磁盘分析又特别耗时的环境。4. 实战记录两个真实场景的排查过程4.1 场景一/data 分区告警定位大文件回到开头提到的那个告警。当时/data分区使用率 94%容量 1.8T可用空间只剩几十 G。我用du -sh /data/*跑了一次发现所有一级目录加起来跟df显示的占用对不上差了将近 200G。这是磁盘排查里很常见的“空间去哪儿了”现象普通工具很难解释。我先用 agedu 对整个 /data 做了一次扫描cd /root mkdir -p /var/lib/agedu agedu -s -f /var/lib/agedu/data.agedu -m /data这里的-m表示按 mtime 统计因为这台机器的挂载选项里带了noatimeatime 已经不可靠具体原因我在后面章节会展开。扫描大概跑了二十多分钟中间没有出现超时或报错生成的数据文件大约 50M相比 1.8T 的数据量压缩率已经很可观了。扫描结束后启动 Web 界面agedu -w -f /var/lib/agedu/data.agedu --auth none --address 127.0.0.1 --port 8080用 SSH 隧道把 8080 端口映射到本地后浏览器打开页面我顺着最大节点一层层点进去。不到五分钟问题就暴露了/data/apps/service_A/tmp下面有一堆 4G 大小的临时文件总共三百多个全部是 7 天前由某个后台任务生成的处理完忘了清理。这些文件散落在多个子目录里单看任何一级目录都不算特别大但加起来正好是那消失的 200G。定位到具体文件后我用find做了二次确认find /data/apps/service_A/tmp -type f -size 1G -exec ls -lh {} \;确认无误后联系业务方同意清理这批临时文件。删除后分区使用率从 94% 降到了 76%。整个过程如果没有 agedu我可能还得一个个目录去 du耗时至少长一倍。4.2 场景二清理长期未访问的历史备份第二个案例是 /backup 分区的历史归档问题。这个分区有一批全量数据库备份文件每天一个 tar.gz保留了整整一年。单个文件平均 30G整年下来就是 10T 以上的占用而磁盘总容量也才 12T日子一久肯定装不下。这种场景用时间维度分析最合适。我先按 mtime 做一次带时间阈值的扫描agedu -s -f /var/lib/agedu/backup.agedu -t 90d -m /backup-t 90d的目的是把 90 天以内还在更新的备份视为“活跃区”重点观察超过 90 天没变化的旧备份。扫描完成后我用-T终端模式打开agedu -T -f /var/lib/agedu/backup.agedu结果非常清晰8 月之前的备份全部显示为偏暖色调大小完全一样但没有任何进程访问过。我再结合find验证一下具体文件列表find /backup -type f -name *.tar.gz -mtime 90 -size 20G | wc -l数量确认后我按保留策略删除了 6 个月之前的备份包只保留最近半年。释放出将近 5T 空间并且不影响任何恢复演练计划。这里有个重要经验删除备份不是简单的rm -rf一定要先在本地做一次抽样校验确认归档文件的完整性再批量删除。我习惯用tar -tzf 文件名 | head -5抽查几个文件能正常列出包内清单才继续。4.3 解读报表的实操心得用多了 agedu我总结了一套看报表的顺序先看最大的分支再看最老的分支最后找两个条件的交集。具体来说打开 Web 界面或 TUI 后我第一步永远是点开占用空间最大的几个节点因为清理空间目标明确优先处理大块头见效最快。第二步观察最大几个节点里有没有颜色明显偏暖的这类数据就是清理性价比最高的对象删掉它们既释放空间又基本不影响业务。第三步如果发现某个目录占用巨大但颜色很冷说明它被频繁访问是活跃数据这种情况下通常不能直接清理只能考虑扩容、归档到独立存储或者做压缩。还要提醒一点很多表面上“单个文件不大”的目录累积空间也可能非常惊人。比如每个用户目录下都有几百个几 KB 的临时配置文件单个可以忽略但几千个用户叠加起来就是几十 G 甚至上百 G。agedu 的目录聚合视图对这种“小文件堆积型”占用特别友好你不需要猜顺着节点看大小就能定位到具体目录层级。5. 常见问题与排查笔记5.1 安装编译阶段的问题我这次在 KeyarchOS 上安装整体很顺利但下面这些坑在其他机器上大概率会遇到提前列出来省得你踩。最常见的是缺编译工具。./configure执行时报错说找不到 C 编译器或者make的时候报cc: command not found基本都是系统没有 gcc。解决办法很简单dnf install -y gcc make如果源没配好直接提示找不到软件包那就先检查/etc/yum.repos.d/下的仓库配置确认 dnf search 能搜到 gcc再继续装。还有一个情况是系统里已经有 gcc但默认编译环境缺少 make也会导致make命令不可用。所以第一步统一把gcc make都装上能避免大半问题。另外如果你下载的源码包不完整解压时会报gzip: stdin: not in gzip format这通常是 wget 把下载失败的错误页存成了 .tar.gz 文件。解决方式是重新下载下载后先用file agedu-xxx.tar.gz查看文件类型确认是 gzip compressed data 再解压。5.2 扫描与 Web 界面阶段的问题扫描阶段最常见的现象是“卡住不动”。排除目录本身超大的因素很可能是扫描到了特殊目录比如/proc、/sys、/dev这类虚拟文件系统或者网络挂载目录。这些目录的数据不具备常规磁盘文件的含义统计进去也没参考价值。我的做法是扫描时精确指定业务路径比如/data、/home、/var/log不要图省事直接扫/。如果非要扫根目录一定要用工具自带的排除机制跳过挂载点和虚拟文件系统不同版本参数不一样扫之前先看agedu --help确认。Web 界面打不开优先排查三件事端口有没有被防火墙挡掉、进程有没有正常监听、监听地址是不是 127.0.0.1 导致外部访问不通。我常用命令是ss -lntp | grep 8080 curl -I http://127.0.0.1:8080ss看监听状态curl看本地访问是否正常。如果本机 curl 正常但外部访问不了大概率是防火墙的问题可以用firewall-cmd --list-ports检查端口是否放行。在测试环境我偶尔为了省事直接停掉防火墙但生产环境强烈不建议这么干正确做法是只放行指定来源 IP 访问这个端口。5.3 容易被忽视的几个坑最值得单独拎出来说的坑是noatime挂载导致的 atime 失效。现在很多服务器为了降低磁盘写放大会在挂载文件系统时加上noatime选项访问文件不更新 atime。这种情况下agedu 默认按 atime 统计的“最后访问时间”就失真了——所有文件看起来都像很久没被访问过筛选结果完全失去参考价值。解决办法是改用 mtime 或 ctime 维度分析。mtime 表示文件内容最后修改的时间对备份文件、日志文件、数据文件这些“最后一次写入”更有意义ctime 表示 inode 变更时间比如权限变更、链接数变化都会更新。我在前面实战案例里用了-m参数就是因为在排查之前已经确认了机器挂载参数里有 noatime按 mtime 来分析才靠谱。你可以在自己的机器上执行mount | grep noatime检查哪些分区带 noatime 标志再决定用哪种时间维度。另一个容易被忽视的点是索引文件本身会占用空间。agedu 的数据文件虽然小但如果你反复扫描不同大分区且都堆在根目录攒多了也会有几百 MB。建议专门建一个目录管理这些索引文件并给它们设置定时清理策略。最后老生常谈但非常重要删除文件之前一定确认业务影响。agedu 是帮你定位空间的不能代替完整的变更流程。特别是遇到生产环境的大目录我建议先记录当前的文件列表保留至少一周的变更痕迹再执行清理操作。6. 写在最后的经验补充用了一段时间 agedu 之后我最大的感触是磁盘分析工具不是越多越好关键是在合适的时候选出合适的那一个。常规告警我会先用df -h确认盘位用du摸个大概再配合lsof | grep deleted查句柄占用。但当问题上升到“空间分布不明、迟迟定位不到大文件”时agedu 的效率优势就非常明显了。我通常的做法是给定期需要监控的分区建立每两周一次的定时扫描用 crontab 把agedu -s命令跑一遍索引文件统一保存到/var/lib/agedu/。这样每次接到告警我只需要打开最新的索引文件几乎零成本地看清空间分布不用在服务器上长时间跑 du 干扰生产 IO。还有个小技巧如果目录太大、首次扫描要花很久可以在业务低谷期执行并把日志输出重定向到文件里比如agedu -s -f /var/lib/agedu/data.agedu /data /var/log/agedu_scan.log 21。扫完之后再慢慢分析完全不占用白天的宝贵时间。最后再分享一点个人体会磁盘空间问题的本质往往不是“没空间了”而是“不知道空间被谁占了”。agedu 这个组合拳正好把“谁占的”和“占了多久”一起回答清楚。希望这篇文章能帮你把 agedu 用起来下次再遇到磁盘告警少走点弯路。