在刚接触 Linux 服务器时,很多从 Windows 转过来的同学经常会遇到这样一连串匪夷所思的困惑:
- “运维明明告诉我申请了一块 2TB 的大磁盘,为什么我一敲命令看可用空间只有 17GB?”
- “
df -hT输出里的Filesystem和Mounted on到底是什么意思?” - “我通过业务用户登录进去,敲
pwd显示在/data,我在这里存文件,消耗的究竟是哪个盘的空间?”
这些疑问不是个例,其背后本质上是 Windows 与 Linux 在存储设计哲学上的巨大鸿沟。本文用最直白的生活化比喻和实操图解,彻底为你理顺 Linux 磁盘挂载、空间分配与目录树的核心关系。
一、思维颠覆:Windows 盘符 vs Linux 目录树
要理解 Linux 的空间,首先必须把脑海里的“Windows 经验”清空。
1. Windows 的世界:“群岛并列模式”
在 Windows 中,存储是以“盘符(Drive Letter)”为核心的:
- 插入第一块盘,叫
C:; - 插入第二块盘,叫
D:; - 插入 U 盘,叫
E:。
每个盘符都是一棵完全独立的树,它们之间彼此平行,井水不犯河水。
2. Linux 的世界:“单根参天大树模式”
Linux 没有 C 盘、D 盘,一切皆文件。
整个 Linux 系统里有且仅有一棵倒挂的“目录树”,这棵树的树根就叫做 根目录(/)。所有的目录(如 /etc、/var、/home、/tmp)都长在这一棵树上。
问题来了:既然只有一棵树,那后续给服务器新加的硬盘,该放到哪里呢?
答案就是:挂载(Mount)。
二、什么是“挂载”(Mount)?——【主楼】与【后院大仓库】
在 Linux 中,物理硬件(如磁盘、分区)是不能直接拿来存文件的,必须把设备与目录树上的某个文件夹“绑定”在一起,这个绑定的动作就叫做“挂载”,被绑定的文件夹就叫“挂载点(Mount Point)”。
为了彻底搞懂,我们来看一个生动的比喻:
1. 系统盘(/dev/vda1,99GB)= 99 平方米的主楼
- 你刚买服务器时自带的那块盘(比如 99GB 的虚拟系统盘),就相当于盖了一栋 99 平方米的主楼,地基就是根目录
/。 - 主楼内部默认装修了很多房间:
/etc(配电房,放系统配置)/var(杂物间,放系统运行日志)/tmp(临时中转角)- 主楼一楼还有一个普通的空房间,门牌上写着
/data。
2. 数据盘(appvg-lvdata,2.0TB)= 2000 平方米的后院大仓库
- 随着业务扩展,99 平方米的主楼根本装不下海量的数据。于是公司又采购了一大批硬盘,拼成了一个 2000 平方米的独立大仓库(2.0TB 逻辑卷)。
- 这个大仓库在物理上是一块独立的存储设备。
3. “挂载”:把房间门做成穿梭通道
怎样才能把后院的大仓库用起来?
挂载操作,就相当于在主楼叫 /data 的房间门上,安装了一道“穿梭通道(传送门)”,直通后院的 2000 平方米大仓库!
- 当你推开
/data这扇门走进去时(cd /data),你表面上还在主楼的目录树里,但实际上你的双脚已经踏在后院 2000 平方米的大仓库中了! - 你在
/data里面堆放的货物(创建的大文件),全部落在后院大仓库里,分毫不挤占主楼本身的 99 平方米空间。
三、常识反直觉:/data 难道不是 / 的子集吗?
很多同学看到这里会提出一个极具深度的问题:
“按照一贯的常理理解,
/data明明在/路径下面,它就应该是/的子集,就像主楼里的一个房间。为什么说它们物理上根本没有关系?”
你的直觉非常敏锐,直接触及了 Linux 存储设计中最具欺骗性、却也最精妙的核心本质:“逻辑路径”与“物理存储”的彻底解耦。
1. 挂载前:它确实只是主楼的小空房
在服务器刚装好、尚未挂载 2.0TB 数据盘之前:
- 根目录
/是一栋 99GB 的主楼。 - 运维在主楼一楼通过
mkdir /data新建了一个空文件夹。 - 在这个时刻,你的理解完全正确:
/data就是主楼里的一个普通小房间。如果此时向里面写入文件,消耗的完全是主楼(系统盘)自己的 99GB 空间。
2. 挂载瞬间:内核的“移花接木”与空间遮蔽
一旦运维执行了挂载命令(mount /dev/mapper/appvg-lvdata /data),Linux 内核在底层做了一次“空间掉包”:
- 空间遮蔽(Masked):内核将主楼里原本属于
/data文件夹的物理空间完全遮盖隐藏起来; - 铺设传送门:内核在
/data这个路径入口处铺设了一条专用路由,直通后院那座独立的 2.0TB 大仓库。
【逻辑世界:用户和程序看到的】(依然表现为树形包含关系)
/
└── data
└── app.log <-- 路径看起来依然是根目录下的子目录
─────────────────────────────────────────────────────────────
【物理世界:内核实际路由的】(完全独立,移花接木)
主楼系统盘 (99GB) 后院大仓库 (2.0TB)
┌─────────────────┐ ┌─────────────────────────┐
│ /etc │ │ │
│ /var │ │ 真正物理写入这里: │
│ /data (被遮蔽) │ ──穿透──> │ └── app.log (占 2.0TB) │
└─────────────────┘ └─────────────────────────┘- 你看到的:
/data依然安安静静地待在/下面; - 真实发生的:一旦跨过
/data这个门牌号,所有的物理读写都被内核瞬间重定向到了后院独立的 2.0TB 硬盘上!
3. 颠覆认知的实验:如果卸载(umount)会发生什么?
要验证它们在物理上并非父子包含关系,最直接的证据就是执行卸载:
- 假设现在后院仓库里已经写入了 1.4TB 的核心数据;
- 此时管理员在后台执行卸载命令
umount /data; - 你再去执行
cd /data并ls:- 你会发现里面空空如也!那 1.4TB 数据凭空“消失”了!
- 数据被删除了吗?绝对没有! 只是连接通道被拆除了,你现在看到的是主楼原本那个空空如也的旧房间;
- 而那 1.4TB 的数据依然毫发无损地保存在底层的 2.0TB 独立硬盘中;
- 只要管理员重新执行
mount,通道复原,那 1.4TB 数据又瞬间完好如初地显现出来。
4. 为什么要设计得这么“反直觉”?
既然违背了日常生活中的“包含常识”,Linux 为什么一定要这么设计?
这正是 Linux 架构哲学的最高明之处:
- 对上层应用透明无感:业务开发人员在写代码时,只需要固定读写
/data/app.log路径,完全不需要关心底层到底是用本地 SSD、云厂商虚拟盘,还是远端的分布式网络存储(NFS/Ceph)。更换底层硬件无需修改业务系统一行代码。 - 对底层运维自由扩容:系统盘空间吃紧时,随时采购一块大容量新硬盘挂载到指定业务目录上,即可实现无缝扩容,而不需要重装系统或迁移整个根目录。
四、实战拆解:手把手读懂 df -hT
在终端输入 df -hT(Disk Free,-h 代表易读单位,-T 代表显示文件系统类型),通常会得到类似下面的回显:
[appuser@db-master-01 ~]$ df -hT
Filesystem Type Size Used Avail Use% Mounted on
devtmpfs devtmpfs 48G 0 48G 0% /dev
tmpfs tmpfs 48G 1.5M 48G 1% /dev/shm
tmpfs tmpfs 48G 4.0G 44G 9% /run
tmpfs tmpfs 48G 0 48G 0% /sys/fs/cgroup
/dev/vda1 ext4 99G 78G 17G 83% /
/dev/mapper/appvg-lvdata xfs 2.0T 1.4T 649G 69% /data
tmpfs tmpfs 9.5G 0 9.5G 0% /run/user/1000
tmpfs tmpfs 9.5G 0 9.5G 0% /run/user/0每一列的真实含义如下:
| 表头列名 | 中文含义 | 通俗解释 |
|---|---|---|
Filesystem | 文件系统来源 | 存储实体是哪块盘。比如 /dev/vda1(系统第一块虚拟盘)、/dev/mapper/...(LVM 逻辑卷)。 |
Type | 文件系统格式 | 数据的打包管理规则。如 ext4(Linux 经典格式)、xfs(擅长高并发与大文件存储)、tmpfs(内存虚拟临时盘)。 |
Size | 总容量 | 该设备格式化后的总空间。 |
Used | 已用空间 | 当前已经存放了多少数据。 |
Avail | 可用空间 | 还能再写入多少数据。 |
Use% | 使用率 | 已用 / 总容量 的百分比。达到 80%~90% 需要重点预警。 |
Mounted on | 挂载点 | 进入该空间的门牌路径。比如 / 表示挂载到根目录,/data 表示挂载到数据目录。 |
什么是 tmpfs?
回显里很多 tmpfs(如 /run、/dev/shm),它们并不是硬盘,而是 Linux 把 物理内存(RAM) 拿出来一部分模拟成的极速磁盘,关机后数据就消失了。看磁盘空间时主要盯紧实际的物理磁盘和逻辑卷即可。
五、真相大白:为什么“一共 2T,可用却只有 17G”?
产生“一共 2T 却只有 17G 可用”的错觉,是因为把两行完全独立的盘看串了!
让我们把关键的两行拎出来单独对比:
Filesystem Type Size Used Avail Use% Mounted on
/dev/vda1 ext4 99G 78G 17G 83% /
/dev/mapper/appvg-lvdata xfs 2.0T 1.4T 649G 69% /data- 第一块盘(系统盘):
/dev/vda1- 门牌号(挂载点):
/(根目录) - 总容量(Size)是 99G(不是可用 99G!)
- 可用剩余(Avail)仅剩 17G(已经用了 78G,使用率 83%)
- 门牌号(挂载点):
- 第二块盘(数据盘):
/dev/mapper/appvg-lvdata- 门牌号(挂载点):
/data - 总容量(Size)是 2.0T
- 可用剩余(Avail)还有 649G(已经用了 1.4T,使用率 69%)
- 门牌号(挂载点):
它们是两块不同的物理空间,各自独立记账,绝不能简单地把它们的容量混为一谈。
六、灵魂拷问:我在当前目录写文件,到底占用哪个空间?
你在终端执行:
[appuser@db-master-01 ~]$ pwd
/data此时如果新建一个 10GB 的文件,它吃的是哪里的空间?
1. 核心判定规则:路径“最长前缀就近匹配”
操作系统在决定文件写入哪块盘时,会顺着目录树从下往上寻找最近的“挂载点”:
情况 A:如果你在
/data或其子目录(如/data/app/logs/)下写文件:- 系统发现这个路径穿过了
/data挂载门; - 100% 消耗的是 2.0T 数据盘的空间;
- 数据盘剩余空间会从 649G 减少为 639G;
- 系统盘剩余的 17G 纹丝不动,分毫不受影响。
- 系统发现这个路径穿过了
情况 B:如果你退回到
/tmp、/var/log或普通用户家目录(/home/appuser)写文件:- 这些目录并没有单独挂载大硬盘,它们直属于根目录
/; - 消耗的是系统盘
/dev/vda1的空间; - 系统盘仅剩的 17G 会被进一步蚕食!
- 这些目录并没有单独挂载大硬盘,它们直属于根目录
2. 为什么运维要把业务用户的主目录设在 /data?
普通用户的家目录默认在 /home/用户名(属于系统盘)。
但在企业生产环境中,很多资深架构师或 DBA 会特意将业务账号的主目录直接配置到挂载的大数据盘(如 /data):
- 核心目的:防止业务研发人员或微服务程序上传大安装包、写大日志时,误把只有 99GB 的系统盘塞爆,从而保护操作系统核心稳定。
七、生产避坑:磁盘防爆三铁律
1. 铁律一:大文件严禁写入非挂载目录
部署业务应用、下载 Docker 镜像、存储日志或数据库数据时,必须确认路径位于独立的大容量挂载点下(如 /data/...),严禁随手丢在 /root、/tmp 或未挂载的根路径中。
2. 铁律二:密切监控系统盘(/)的 80% 警戒线
观察上方的数据,系统盘 / 的使用率已经达到了 83%(仅剩 17G):
- 当根目录达到 100% 时,系统将无法创建进程锁、无法写临时会话,甚至可能导致远程 SSH 无法登录。生产上应配置告警,阈值通常设为 80%~85%。
3. 铁律三:学会用 du 排查吃空间的“元凶”
当发现某个分区爆满时,可以使用以下命令在目标目录下揪出最大的子目录:
# 查看当前目录下各个文件夹占用的实际空间(深度为 1)
du -h --max-depth=1 /八、生产排障实战:用 docker system df 揪出根目录吃盘元凶
在前文的 df -hT 分析中,我们发现系统盘根目录(/)总共 99GB,已用 78GB(使用率高达 83%),只剩 17GB 可用。
这吃掉的 78GB 究竟是谁干的?在现代微服务服务器上,Docker 往往是头号“吞盘怪兽”(因为 Docker 默认的数据存储目录 /var/lib/docker 正是直接建在系统盘根目录下的)。
知识库专栏联动
本节聚焦于 Linux 磁盘空间排查视角。关于 Docker 各类容器的状态体检、性能监控与空间治理全套命令,详见 Docker 专栏文档:Docker 容器监测与状态体检命令汇总。
此时,我们需要祭出 Docker 专属的磁盘健康体检命令:
[appuser@db-master-01 ~]$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 520 17 50.59GB 49.08GB (97%)
Containers 25 11 3.279MB 2.742kB (0%)
Local Volumes 50 10 238B 190B (79%)
Build Cache 0 0 0B 0B1. 表格各项指标详细解析
docker system df 专门用来统计 Docker 各类资源所占用的磁盘空间:
| 列名 | 含义 | 对应现状深度剖析 |
|---|---|---|
TYPE | 资源类型 | 分别涵盖:Images(镜像)、Containers(容器读写层)、Local Volumes(数据卷)和 Build Cache(Dockerfile 构建缓存)。 |
TOTAL | 总数 | 机器上保留的实体总数量(当前有 520 个镜像)。 |
ACTIVE | 活跃数量 | 当前正在被运行中的容器使用的对象(当前仅有 17 个镜像处于活跃运行状态)。 |
SIZE | 当前占用空间 | 该类资源在硬盘上切切实实消耗的物理空间(镜像占据了 50.59GB)。 |
RECLAIMABLE | 可回收垃圾空间 | 核心指标! 指没有被任何运行中容器引用的闲置资源,可以直接清理释放的空间。当前高达 49.08GB(97%)! |
案情真相大白
根目录已用的 78GB 中,有整整 50.59GB 都是 Docker 镜像占用的!其中因为历史频繁构建发版、从未清理旧版本,堆积了 503 个闲置镜像,白白浪费了 49.08GB 的宝贵系统盘空间。
2. 命令行疑点:加不加 2>/dev/null 有何区别?
有时你会看到别人执行这个命令时带上了 2>/dev/null:
docker system df 2>/dev/null在 Linux 中:
1(stdout,标准输出):命令成功运行的正常结果(即上方表格);2(stderr,标准错误):命令运行时的报错信息与警告日志;/dev/null:系统的“黑洞垃圾桶”。
2>/dev/null 的作用就是**“屏蔽所有报错与黄色警告,只看纯净表格”**。
日常排障建议:**日常手动排查千万不要加!**
- 不加的好处:如果 Docker 服务没有启动、或者当前普通用户无权限访问 docker 套接字,不加能直接看到清晰的红色报错原因;
- 加了的坏处:一旦出错,错误被无声扔进黑洞,终端将一片空白、没有任何输出,极易让人误以为命令卡死。只有在编写无需人工交互的自动化监控脚本时,才适合添加以保证输出格式规整。
3. 运维实战:从治标到治本的三招清理方案
面对这 49.08GB 的闲置空间,运维工程师通常有以下三种处理策略:
招式一:温和清理(只清虚悬镜像,绝对安全)
清理那些没有标签名、显示为 <none>:<none> 的构建残留镜像(Dangling Images):
docker image prune招式二:强力瘦身(释放全部 49GB 闲置镜像)
删除所有当前未被任何容器使用的镜像(释放全部可回收空间):
docker image prune -a生产注意事项
执行 -a 会把那 503 个没在运行的旧版本镜像彻底清除。如果这些镜像都存放在企业自建的私有镜像仓库(Harbor/Nexus)中随时可以重新 pull,可放心执行;执行后系统盘立即可恢复近 50GB 可用空间。
招式三:治本之策(将 Docker 存储迁移到 2.0TB 大盘)
为了彻底避免 Docker 把只有 99GB 的系统盘撑爆,标准架构实践是将 Docker 的数据根目录迁移到挂载的独立大数据盘(如 /data/docker):
- 修改 Docker 核心配置文件
/etc/docker/daemon.json:json{ "data-root": "/data/docker" } - 停止 Docker、迁移旧数据后重启 Docker 服务。
此后无论微服务拉取多少镜像,全部自动消耗 2.0TB 的大池子,彻底解除系统盘爆满的隐患。