前两天凌晨,一台跑了快两年的服务器突然抽风了。
服务起不来,日志写不进去,新文件一个都建不了。 整台机器看起来像"废了"。
第一反应肯定是——磁盘满了呗。
df -h 一看:
还剩 40G。
空间充裕得很。
但服务就是起不来。
说实话,那一刻我是有点慌的。空间够,又不是内存爆了,到底是什么在作怪?
排查了半小时后,终于找到了真凶:
inode 用完了。
inode 是个什么东西?
你可以把磁盘想成一个小区:
不管文件多大,哪怕只有 1 字节,只要它是一个文件,就占 1 个 inode。
所以会出现一种特别反直觉的情况:
房子还能盖(空间够), 但房产证发完了(inode 用光)。
这时候系统就进入了一种"假死"——看起来什么都正常,但什么都干不了。
这就是很多人服务器"诡异挂掉"的真正原因。
谁把 inode 吃光的?
最终定位到一个东西:Gitea——自建的代码仓库服务。
很多人觉得一个 Git 仓库就是一个文件夹,里面放几个文件。
真实情况是这样的:
一个 Git 仓库 =
objects/ → 成千上万个小文件
refs/
logs/
hooks/
config
...
一个中等大小的仓库,轻轻松松消耗几百到几千个 inode。
这台服务器上仓库数量不少,很多还是脚本自动建的,全堆在根分区。
于是 inode 就被悄无声息地吃光了。
你可以跑一下这个命令感受一下:
find /data/gitea/repositories -type f | wc -l
可能结果会吓你一跳。
为什么 inode 满了,服务器表现得像"挂了"?
因为 Linux 里到处都要创建文件:
当 inode 耗尽,以上所有操作全部失败。
但坑爹的是,错误信息不会告诉你"inode 满了"。
它只会报各种莫名其妙的错:
No space left on device ← 明明还有空间
Cannot create temp file ← 看起来是权限问题
Failed to start service ← 像是配置错了
如果你只会看 df -h,大概率会在这里绕很久。
怎么解决的?
说出来你可能不信,解决过程特别简单:
没重启,没重装,就删了一堆小文件。
整个过程不到 10 分钟。但找到原因花了半小时——因为一开始根本不会往 inode 这个方向想。
这个坑,太多人都会踩
坦白讲,Git 服务 + 根分区 = 高危组合。
三个原因:
- Git 天生是"小文件制造机"。
objects/ 目录下动辄几万个松散对象文件,每个只有几 KB,但每个都消耗一个 inode。 - inode 数量是建文件系统时就定死的。 不像磁盘空间可以扩容,inode 后期极难调整。
- 根分区的 inode 一旦用完,整台机器就瘫了。 不只是 Git 挂了,所有服务都别想跑。
三条保命经验
第一条:Git 数据目录,永远不要放根分区。
单独挂一块盘,哪怕是个小盘,也比混在根分区强一百倍。Git 把那块盘的 inode 吃光了,最多那个盘废了,系统还活着。
第二条:限制仓库创建来源。
自动脚本、测试程序、CI 流水线,这些东西建仓库从不手软。不加限制的话,仓库数量可以指数级增长,inode 根本扛不住。
第三条:给巡检脚本加一条命令。
df -i
就这四个字符,值得和 df -h 一样刻进你的肌肉记忆。
很多运维只看空间、看 CPU、看内存,唯独没人看 inode。直到它把你的服务器干趴下。
最后说一句
很多服务器不是空间不够死的,是文件名额先用完了。
这次事故说白了不是操作失误,而是对 inode 这个东西认知不足导致的结构性翻车。
如果你在跑 Git、Jenkins、日志采集、缓存服务这类"小文件炸弹",inode 一定要当成和磁盘空间同等重要的资源来看。
今天看完这篇,打开终端,跑一次 df -i。
如果使用率超过 50%——恭喜你,你提前拆了一颗定时炸弹💣