Skip to content

在刚接触 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 内核在底层做了一次“空间掉包”:

  1. 空间遮蔽(Masked):内核将主楼里原本属于 /data 文件夹的物理空间完全遮盖隐藏起来;
  2. 铺设传送门:内核在 /data 这个路径入口处铺设了一条专用路由,直通后院那座独立的 2.0TB 大仓库。
【逻辑世界:用户和程序看到的】(依然表现为树形包含关系)
   /
   └── data
       └── app.log   <-- 路径看起来依然是根目录下的子目录

─────────────────────────────────────────────────────────────

【物理世界:内核实际路由的】(完全独立,移花接木)
   主楼系统盘 (99GB)               后院大仓库 (2.0TB)
   ┌─────────────────┐            ┌─────────────────────────┐
   │ /etc            │            │                         │
   │ /var            │            │ 真正物理写入这里:       │
   │ /data (被遮蔽)  │ ──穿透──>  │ └── app.log (占 2.0TB)  │
   └─────────────────┘            └─────────────────────────┘
  • 你看到的:/data 依然安安静静地待在 / 下面;
  • 真实发生的:一旦跨过 /data 这个门牌号,所有的物理读写都被内核瞬间重定向到了后院独立的 2.0TB 硬盘上!

3. 颠覆认知的实验:如果卸载(umount)会发生什么? ​

要验证它们在物理上并非父子包含关系,最直接的证据就是执行卸载:

  1. 假设现在后院仓库里已经写入了 1.4TB 的核心数据;
  2. 此时管理员在后台执行卸载命令 umount /data;
  3. 你再去执行 cd /data 并 ls:
    • 你会发现里面空空如也!那 1.4TB 数据凭空“消失”了!
    • 数据被删除了吗?绝对没有! 只是连接通道被拆除了,你现在看到的是主楼原本那个空空如也的旧房间;
    • 而那 1.4TB 的数据依然毫发无损地保存在底层的 2.0TB 独立硬盘中;
  4. 只要管理员重新执行 mount,通道复原,那 1.4TB 数据又瞬间完好如初地显现出来。

4. 为什么要设计得这么“反直觉”? ​

既然违背了日常生活中的“包含常识”,Linux 为什么一定要这么设计?

这正是 Linux 架构哲学的最高明之处:

  • 对上层应用透明无感:业务开发人员在写代码时,只需要固定读写 /data/app.log 路径,完全不需要关心底层到底是用本地 SSD、云厂商虚拟盘,还是远端的分布式网络存储(NFS/Ceph)。更换底层硬件无需修改业务系统一行代码。
  • 对底层运维自由扩容:系统盘空间吃紧时,随时采购一块大容量新硬盘挂载到指定业务目录上,即可实现无缝扩容,而不需要重装系统或迁移整个根目录。

四、实战拆解:手把手读懂 df -hT ​

在终端输入 df -hT(Disk Free,-h 代表易读单位,-T 代表显示文件系统类型),通常会得到类似下面的回显:

text
[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 可用”的错觉,是因为把两行完全独立的盘看串了!

让我们把关键的两行拎出来单独对比:

text
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%)

它们是两块不同的物理空间,各自独立记账,绝不能简单地把它们的容量混为一谈。

六、灵魂拷问:我在当前目录写文件,到底占用哪个空间? ​

你在终端执行:

bash
[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 排查吃空间的“元凶” ​

当发现某个分区爆满时,可以使用以下命令在目标目录下揪出最大的子目录:

bash
# 查看当前目录下各个文件夹占用的实际空间(深度为 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 专属的磁盘健康体检命令:

bash
[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        0B

1. 表格各项指标详细解析 ​

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:

bash
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):

bash
docker image prune

招式二:强力瘦身(释放全部 49GB 闲置镜像) ​

删除所有当前未被任何容器使用的镜像(释放全部可回收空间):

bash
docker image prune -a

生产注意事项

执行 -a 会把那 503 个没在运行的旧版本镜像彻底清除。如果这些镜像都存放在企业自建的私有镜像仓库(Harbor/Nexus)中随时可以重新 pull,可放心执行;执行后系统盘立即可恢复近 50GB 可用空间。

招式三:治本之策(将 Docker 存储迁移到 2.0TB 大盘) ​

为了彻底避免 Docker 把只有 99GB 的系统盘撑爆,标准架构实践是将 Docker 的数据根目录迁移到挂载的独立大数据盘(如 /data/docker):

  1. 修改 Docker 核心配置文件 /etc/docker/daemon.json:
    json
    {
      "data-root": "/data/docker"
    }
  2. 停止 Docker、迁移旧数据后重启 Docker 服务。
    此后无论微服务拉取多少镜像,全部自动消耗 2.0TB 的大池子,彻底解除系统盘爆满的隐患。

基于 VitePress 构建 · 遵循 CC BY-NC-SA 4.0 许可协议