很多朋友第一次买完云服务器,第一周用得美滋滋,后面突然发现磁盘满了,网站打不开,登录服务器一看/dev/root 100%,一时半会还不知道自己到底把文件装到哪个盘里了。这个场景我见过太多次了,尤其是新手,选了系统盘默认 40G,后面装环境、拉镜像、传备份,几下就把系统盘塞满了,而那块花钱买的数据盘还安安静静地空着。
这篇文章就把“系统盘和数据盘到底差在哪”和“怎么一眼认出文件存储位置”这两件事彻底讲清楚,全程基于 Linux 云服务器在阿里云、腾讯云这类主流环境下的实际表现来写。文里会带上排查命令、挂载实操、fstab 自动挂载的完整步骤,还有我踩过的一些坑,想少走弯路的可以直接照着抄作业。
1. 系统盘和数据盘到底哪里不一样
1.1 概念层面的本质区别
系统盘和数据盘,名字上就差了一个字,但本质区别很多人没仔细想过。系统盘,简单说就是云厂商帮你装好了操作系统的那块磁盘,也就是实例启动时用来加载内核、挂载根文件系统的那块盘。创建云服务器的时候,控制台里让你选“系统盘 40G/50G/80G”,选的就是它。系统盘在 Linux 里面通常被挂载到根目录/,承载着/etc、/usr、/var、/home这些操作系统运行必不可少的目录。
数据盘则是额外挂载的一块独立磁盘,云厂商不会在上面默认装系统,也不会帮你格式化。这块盘主要用来存放应用数据、网站文件、数据库文件、日志、备份之类的业务数据。数据盘在 Linux 里的设备名通常排在系统盘之后,比如系统盘是/dev/vda,数据盘大概率就是/dev/vdb、/dev/vdc这样往上排。
从生命周期的角度看,这两块盘也有一个非常容易踩坑的差异。系统盘默认是“随实例释放”的,也就是说你在控制台把服务器实例释放掉,系统盘跟着就没了。数据盘就灵活得多,控制台里可以选择“随实例释放”或“不随实例释放”,所以理论上你可以把数据盘单独解绑,再挂到另一台服务器上继续用。这也解释了为什么生产环境里大家总是强调“不要把数据写在系统盘上”——系统盘没了,你的业务数据也跟着一起没了。
1.2 云环境里的磁盘命名规则
很多新手第一次登录云服务器,在fdisk -l或lsblk里看到/dev/vda1、/dev/vdb这类名字会有点懵,因为它和自己电脑上熟悉的sda、sdb不一样。
这里的vd代表 virtio 块设备,是虚拟化环境里很常见的一种半虚拟化磁盘驱动模式。阿里云、腾讯云的标准实例基本上用的都是 virtio,所以在系统内看到的磁盘设备就是/dev/vda、/dev/vdb这种形式,分区则是/dev/vda1、/dev/vda2。如果你遇到的是/dev/sda、/dev/sdb,那说明这个实例可能用的是 SCSI 模拟方式,比如一些异构实例或特定虚拟化平台下的表现。搞清楚这一点对后文讲“怎么识别哪个盘是系统盘、哪个盘是数据盘”非常有帮助,因为设备名本身就是第一个线索。
另外再补充一点,云服务器里看磁盘容量,建议用lsblk来看块设备级别的真实大小,而不是只看df的输出。原因后续会详细说,但先记住一句话:lsblk管的是“云厂商给了几块盘、每块多大”,df管的是“操作系统里挂载好的文件系统用了多少”。
2. 文件到底藏在哪个盘上:四类核心命令识别法
2.1 lsblk:先看全局磁盘拓扑
lsblk的全称是 list block devices,它能以树形结构展示当前系统识别到的所有块设备、分区、挂载点。这是识别文件存储位置的第一步,也是最快的一步。
[root@cloud ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT vda 252:0 0 40G 0 disk ├─vda1 252:1 0 40G 0 part / vdb 252:16 0 100G 0 disk └─vdb1 252:17 0 100G 0 part /data看这个输出就很清楚了。vda这块盘大小 40G,它的第一个分区vda1挂载在/,说明 40G 的系统盘已经作为根文件系统在使用。vdb这块盘 100G,vdb1挂载在/data,这就是一块已挂载的数据盘。如果数据盘买了但没有分区,也没有格式化、挂载,那么lsblk里只会显示一个vdb,下面没有子分区,MOUNTPOINT 那栏也是空的,这时候你就知道这块 100G 的盘“还没启用”。
用lsblk去回答“系统里到底有几块盘”这个问题,比fdisk -l更直观,因为它直接带挂载点信息,一眼就能看出哪块盘对应哪个目录。
2.2 df:看文件系统维度的占用
df是 Linux 里查看文件系统磁盘使用情况的经典命令,全称 disk filesystem。日常用得最多的是df -h,-h表示 human-readable,把容量换算成 G、M 这种人类友好单位。
[root@cloud ~]# df -h Filesystem Size Used Avail Use% Mounted on /dev/vda1 40G 35G 3.6G 91% / devtmpfs 1.9G 0 1.9G 0% /dev tmpfs 1.9G 0 1.9G 0% /dev/shm tmpfs 1.9G 1.2M 1.9G 1% /run tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup /dev/vdb1 98G 25G 73G 26% /data这里要注意一个细节:df显示的/dev/vdb1 98G,而lsblk里显示的是 100G,少了 2G。不要慌,这 2G 是文件系统自身的元数据开销和保留块,ext4 默认会预留 5% 给 root 用户做紧急恢复用,所以 100G 的分区格式化完成后,df能看到的大约是 98G 多。这是正常现象,不是云厂商少给你容量。
df还有一个很实用的参数组合df -hT,-T会额外列出文件系统类型,能看到根目录是 ext4 还是 xfs,这对后面做扩容和备份时有参考意义。
2.3 mount 和 blkid:确认挂载关系与唯一标识
mount命令不带参数直接执行,会列出当前系统所有已挂载的文件系统以及挂载参数。想看数据盘是不是按预期挂载的,可以直接:
[root@cloud ~]# mount | grep -E "vda|vdb" /dev/vda1 on / type ext4 (rw,relatime) /dev/vdb1 on /data type ext4 (rw,relatime)blkid则是专门用来查看块设备 UUID 和文件系统类型的命令。在配置/etc/fstab自动挂载的时候,使用 UUID 而不是设备名来指定磁盘,是最稳妥的做法,原因后面第 4 节还会展开讲。
[root@cloud ~]# blkid /dev/vda1: UUID="8a1b2c3d-..." TYPE="ext4" /dev/vdb1: UUID="9f4e5d6c-..." TYPE="ext4"2.4 结合 pwd 和 du:确认当前文件到底在哪个挂载点
前面几个命令都是系统全局视角,但很多时候你只是想确认“我正要放的这堆文件,到底会落进哪个盘”。这时候最简单的方法就是pwd看当前目录,然后df -h .或df -h /具体路径,df后面可以直接跟一个目录路径,它会显示该目录所在文件系统的磁盘情况。
[root@cloud ~]# df -h /www/wwwroot Filesystem Size Used Avail Use% Mounted on /dev/vdb1 98G 25G 73G 26% /www/wwwroot这说明/www/wwwroot这个路径实际是位于数据盘/dev/vdb1上的。注意这里有一个 Linux 文件系统的核心概念——同一个路径、同一个目录,在不同挂载关系下可能对应完全不同的物理磁盘。比如你mkdir /data之前,/data目录在根文件系统(也就是系统盘)里;但当你把数据盘挂载到/data之后,/data底下所有文件就会写入数据盘,系统盘里原来的/data目录内容会被隐藏掉,看起来像是“文件凭空消失了”,其实它们还在原来的磁盘上,只是被挂载点遮挡了。
du命令则用来统计某个目录下所有文件的磁盘占用,排查到底是哪个目录吃满了磁盘时会反复用到:
[root@cloud ~]# du -sh /var/log 2.1G /var/log3. 磁盘满了别慌:定位高占用目录的标准排查流程
3.1 从根开始逐层 du,锁定大目录
系统盘 91% 的告警已经够吓人了,如果直接 100%,很多服务会开始报错,数据库可能直接挂掉。这时候第一要务不是急着删文件,而是先搞清楚到底什么吃了磁盘。
我的标准排查顺序是这样。先df -h确认是哪个挂载点紧张,再du -h --max-depth=1 /从根目录开始看一层,然后二级目录、三级目录一路往下追。--max-depth=1会限制 du 只统计当前目录下第一层子目录的大小。
[root@cloud ~]# du -h --max-depth=1 / | sort -hr | head -20 5.2G /usr 2.1G /var 1.8G /root 1.2G /opt 140M /etcsort -hr会让结果按人类可读数字从大到小排序,这样一眼就能看到最大的那个目录在哪。进入可疑目录之后,继续用同样的命令往下追,逐层缩小范围,直到找到具体是哪些文件占用的空间。
这里有一个很容易犯的低级错误:在/根目录下直接du -sh *统计,但因为某些目录里有挂载点,du默认会穿透挂载点去统计底层数据盘的内容,结果就是明明系统盘快满了,统计出来却显示某个挂载目录占用了几百 G,把真实情况盖住了。所以建议先用--max-depth=1看清楚整体轮廓,再针对具体的目录深入排查。
3.2 系统盘常见的“隐形占用”:日志、包缓存、Docker、内核
如果你统计完发现最大的目录在/var,那么恭喜,大概率是日志文件在作祟。/var/log底下有很多应用日志和系统日志,比如journald的 journal 文件、nginx 的 access log、MySQL 的 error log,日积月累相当可观。
journal 日志清理可以先用journalctl --disk-usage看当前占了多少,再配合journalctl --vacuum-time=7d只保留最近 7 天的日志,或者journalctl --vacuum-size=500M限制日志总大小。
包管理的缓存也是隐形大户型。CentOS/RHEL 系的yum clean all可以清掉已下载的 rpm 包缓存,Debian/Ubuntu 系的apt clean和apt autoremove同样能释放不少空间。/var/cache目录里堆的东西经常被人忽略。
Docker 环境则要看/var/lib/docker的体积。docker system df可以一次性查看镜像、容器、数据卷、构建缓存各占多少,然后docker system prune -a清理所有悬空镜像和缓存,但注意这会顺带删掉没有被容器引用的镜像,执行前要确认自己的操作不会导致需要重新拉取关键镜像。
还有一个非常隐蔽却经常爆雷的位置是/boot。内核更新之后,旧的内核文件并不会自动删除。uname -r查看当前内核版本,再用rpm -qa | grep kernel(RHEL 系)或dpkg --list | grep linux-image(Debian/Ubuntu 系)列出已安装的内核,然后手动移除旧内核。如果你完全不清理,几个内核叠在一起很容易把/boot分区占满,系统升级都没法进行。
3.3 被删除文件还在占用空间:lsof + deleted
这种情况属于资深运维都不一定第一时间想到的坑。你du和rm之后,df依然显示空间没释放。原因很简单——某个进程还握着这个已被删除文件的文件句柄,文件并没有真正从磁盘上消失。
排查方法是用lsof查看已删除但仍被进程占用的文件:
[root@cloud ~]# lsof +L1 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME nginx 1234 root txt REG 253,1 12345678 0 12345 /usr/local/nginx/logs/access.log (deleted)看到deleted标记,就说明文件被删了但进程还占着。解决办法通常是把对应服务重启一下,释放文件句柄,空间才能被真正回收。比如 nginx 的 access log 被rm之后,只要 nginx 不 reload,空间就一直被占着。这也是为什么我建议处理日志文件时不要直接rm,而是用> /path/to/log的方式清空内容,这样进程还在原文件上写日志,空间立即回收,不需要重启服务。
4. 新购数据盘的从零到自动挂载实操
4.1 分区、格式化、挂载三步走
很多人在云控制台买了一台带数据盘的服务器,或者后来单独加了一块数据盘,登录系统发现/dev/vdb是空的,于是开始困惑“为什么我明明买了数据盘但看不到任何容量”。原因前面已经说了,云厂商不会替你在数据盘上创建文件系统,这块盘对你来说是“一块能用的空磁盘”。
接下来就是完整的三步操作。注意:以下操作会清空目标磁盘原有数据,请先确认/dev/vdb上没有需要保留的文件。
第一步,分区。使用fdisk对/dev/vdb进行分区。如果数据盘小于 2T,建议直接用 MBR 分区表;如果大于 2T,则必须使用parted工具配合 GPT 分区表,否则无法识别超过 2T 的容量。
[root@cloud ~]# fdisk /dev/vdb Command (m for help): n Partition type: p primary (0 primary, 0 extended, 4 free) e extended Select (default p): p Partition number (1-4, default 1): 1 First sector (2048-209715199, default 2048): Using default value 2048 Last sector, +sectors or +size{K,M,G} (2048-209715199, default 209715199): Using default value 209715199 Partition 1 of type Linux and of size 100 GiB is set Command (m for help): w The partition table has been altered.整个交互过程不需要动脑筋,全部按回车选默认值即可,最终输入w保存退出。分区完成后lsblk应该能看到/dev/vdb1了。
第二步,格式化。这里需要根据你的 Linux 发行版选择文件系统。CentOS/RHEL 系的老版本默认用 ext4,新版本(如 Rocky Linux 9、AlmaLinux 9)虽然支持 xfs,但如果你没有特殊需求,ext4 的兼容性和稳定性最稳:
[root@cloud ~]# mkfs.ext4 /dev/vdb1如果你对文件系统形态有特殊倾向,比如想用 xfs,也可以用mkfs.xfs /dev/vdb1,但注意 xfs 和 ext4 的后期扩容方式不太一样,ext4 可以用resize2fs,xfs 要用xfs_growfs,这里不展开,按需选择即可。
第三步,临时挂载与验证。先创建一个目录作为挂载点,然后挂载并确认结果:
[root@cloud ~]# mkdir /data [root@cloud ~]# mount /dev/vdb1 /data [root@cloud ~]# df -h /data Filesystem Size Used Avail Use% Mounted on /dev/vdb1 99G 61M 94G 1% /data这时候数据盘已经可以正常使用了,往/data下写文件就是写进这块 100G 的数据盘。
4.2 用 UUID 配置 fstab,让重启后自动挂载
如果你只做到上一步就收工,重启服务器之后大概率会发现/data目录又空了,数据盘没挂上。原因很简单——手动mount只是临时生效,重启后系统不会自动挂载这块盘。
正确做法是把挂载关系写进/etc/fstab。这个文件是 Linux 开机时自动挂载文件系统的核心配置文件,格式固定为六列:设备、挂载点、文件系统类型、挂载参数、dump 备份标记、fsck 检查顺序。
最稳妥的设备标识是用 UUID,而不是/dev/vdb1。因为设备名在云环境里有可能因为实例重启、磁盘插拔顺序变化而改变,而 UUID 是文件系统创建时生成的唯一标识,不会变。
配置过程如下。先拿到数据盘的 UUID:
[root@cloud ~]# blkid /dev/vdb1 /dev/vdb1: UUID="d1e5e7c8-3f6b-4c4a-9f3d-2b5f2a9e8c40" TYPE="ext4"然后编辑/etc/fstab,追加一行:
UUID=d1e5e7c8-3f6b-4c4a-9f3d-2b5f2a9e8c40 /data ext4 defaults 0 2保存退出之后,用mount -a验证这一行配置是否完全正确。mount -a会重新读取/etc/fstab并挂载其中所有标记为自动挂载的分区,如果这条命令执行后没有任何报错,且/data能正常看到内容,说明 fstab 配置没毛病。
这里必须多说一句:fstab 写错是云服务器宕机重启后经常遇到的问题之一。如果 fstab 里指定的设备、UUID 或者挂载参数不正确,系统开机时在挂载环节会卡住,严重情况下直接进入 emergency mode 或者无法正常启动。所以每改完一次 fstab,都应该立即执行mount -a验证,而不是等重启之后才发现问题。
4.3 挂载点目录权限和开机自启动排查
挂载和 fstab 都配好之后,还有一个细节容易被忽略——挂载点目录的权限。比如你的运行用户是www,而/data的属主是 root,后面应用往/data下写文件就会遇到权限拒绝。
[root@cloud ~]# chown -R www:www /data如果目录里已经有很多子目录和文件,建议加-R递归处理。如果在挂载完成后才改属主,不会影响磁盘数据,只是把目录的元数据改了,可以放心操作。
还有一个常见场景:有些用户会把数据盘挂载到/home这种系统目录上,或者挂载到/var/lib/mysql这类数据库目录。如果你计划这样干,注意先确认原目录下没有重要数据,或者先把数据备份出来、挂载后再拷贝回去。因为挂载点目录原有的文件会在挂载后被隐藏,直接挂载过去会让旧文件看起来“全没了”,实际上它们还静静躺在原磁盘上,但你要是不拷贝,业务就会因为找不到数据而报错。
5. 高频问题速查与实战心得
5.1 八个最容易踩的坑
按我这些年给用户排查的经验,下面这几类问题出现频率最高,整理成表方便对照。
遇到“系统盘和数据盘傻傻分不清”的情况,按这个表定位基本不会错。第一列是你看到的现象,第二列是问题本质,第三列是处理动作。这里额外强调一下最后一行:系统盘和数据盘在控制台的释放选项不同,如果你的数据盘设置了“随实例释放”,那实例释放的时候数据盘也会被删,和位置识别无关但和数据安全强相关,提前确认一下没有坏处。
5.2 几个Linux命令组合技巧
单条命令好用,组合命令更高效。日常运维我经常用这几个组合,都是实战总结出来的。
# 查看所有磁盘及挂载点,一次性定位系统盘和数据盘 lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL # 查看根目录下一级子目录占用,排除挂载点干扰 du -h --max-depth=1 -x / 2>/dev/null | sort -hr | head # 统计某个目录下所有文件占用,找到体积排名前 10 的文件 find /data -type f -printf '%s %p\n' | sort -rn | head -10 # 清空日志而不是删除日志,避免进程持有已删除文件句柄 > /var/log/nginx/access.logdu -h --max-depth=1 -x /里的-x很关键,意思是“不要跨文件系统统计”。加上它之后,du就不会跑到挂载点里去统计数据盘的容量了,避免统计结果被挂载点污染。这个参数在排查系统盘满的时候堪称神器。
find配合-printf列出每个文件大小再排序,可以在海量小文件中迅速揪出几个巨型文件,胜过一个个du进入目录慢慢看了。
5.3 关于数据盘“不见了”的终极解释
很多人在社区里提问“麒麟系统数据盘不见了”“Ubuntu 数据盘不见了”“重启后数据盘不见了”,其实大概率就是这几种情况:
第一种,数据盘未格式化。在控制台看到有这块盘,但在系统内lsblk看不到任何 vdb 设备或者看到 vdb 没有子分区。处理方法就是把这块盘分区、格式化、挂载,也就是第 4 节讲的完整流程。
第二种,数据盘未挂载。盘已经分好区、格式化好了,但重启之后没有自动挂载,磁盘设备存在、分区存在、文件系统也存在,就是没挂到任何目录上。lsblk会看到/dev/vdb1,但 MOUNTPOINT 那栏是空的。处理方法就是mount /dev/vdb1 /data临时挂载,然后把挂载关系写入/etc/fstab。
第三种,fstab 写错导致开机没挂上。前面提到过,fstab 中路径或 UUID 写错,系统开机不会报错但不挂载,或者直接进入 emergency mode。这种情况先在控制台通过 VNC 登录进系统,修正 fstab 再重启。这里特别强调一下:如果你之前把数据盘挂在/data,后来因为某种原因重新格式化过数据盘,那么原来的 UUID 就变了,fstab 里必须同步更新成新 UUID。
第四种,数据盘确实丢了或云控制台侧有变化。控制台里看这块盘是不是还在实例详情中,如果被误操作卸载了,重新挂载即可;如果控制台显示盘还在,但系统内完全识别不到设备,那基本不是系统配置能解决的,需要提交工单给云厂商排查底层虚拟化状态。
5.4 我个人的几条实用建议
最后说几个我自己在平时运维中沉淀下来的习惯,供参考。
数据盘挂载路径一定不要乱。我见过有人把数据盘挂到/mnt、/media、/tmp这些路径,短期能干活,后期找文件非常痛苦。建议统一规划,比如全部放在/data下面,或者按业务区分/data/mysql、/data/www、/data/backup。路径规划越清晰,后面排查效率越高。
关键业务的数据目录尽量用ln -s软链接做一层转发。比如数据库数据目录原本在/var/lib/mysql,你想把它挪到数据盘上,但是程序配置和日志里到处都是绝对路径,改起来容易漏。这时候可以直接把数据迁到/data/mysql,然后删掉原目录、建立软链接/var/lib/mysql -> /data/mysql,程序无感切换。这个技巧在处理“大目录拆分到数据盘”时非常实用。
备份这件事别完全依赖云厂商的快照。快照确实方便,但它和你自己系统内的逻辑备份是两回事。数据库这种动态变化的内容,除了快照,还要定期做mysqldump或pg_dump的逻辑备份,并把备份文件放到数据盘上。这样即使系统盘完全损坏,数据盘解挂到新实例上,照样能恢复数据。
预算允许的话,系统盘尽量买大一点。40G 对于跑 Docker 的机器真的很紧张,镜像和容器日志涨起来非常快。现在很多云厂商系统盘最低 40G,加钱可以扩到 80G、100G,这个成本摊到每个月的账单里并不多,但能让你少经历几次“磁盘满导致服务挂掉”的深夜事故。