lsblk / df / du —— 三个被混淆的"看磁盘"命令
一句话定义
这三个命令经常被一起提,但看的是完全不同的东西:
lsblk—— 看块设备(物理盘、分区、LVM、加密层),无关是否挂载df—— 看已挂载的文件系统用了多少du—— 看某个目录树实际占多少
理解清楚这三个的分工,是装机和排查"磁盘满了"问题的基本功。
三者对比
| 维度 | lsblk | df | du |
|---|---|---|---|
| 看什么 | 块设备拓扑(盘 → 分区 → 文件系统) | 文件系统级用量 | 目录树级用量 |
| 单位 | 字节 / 人类可读 | 文件系统块 | 目录/文件大小 |
| 不挂载也能看 | ✅ | ❌(只列已挂载) | ❌(要能进入目录) |
| 找"哪个目录占空间" | ❌ | ❌ | ✅ |
| 找"磁盘没挂上" | ✅ | ❌ | ❌ |
| 找"文件系统满了" | ❌ | ✅ | ❌ |
| 速度 | 瞬时 | 瞬时 | 慢(要遍历目录) |
装机第一个该跑的是 lsblk,不是 df。
df 只显示已挂载的文件系统,可能有大盘没挂载、或者挂在莫名其妙的 /www 路径上等着 Day1 处理 —— df 看不见,lsblk 才看得见。
lsblk —— 块设备拓扑
默认输出
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 100G 0 disk
├─sda1 8:1 0 512M 0 part /boot/efi
├─sda2 8:2 0 1G 0 part /boot
└─sda3 8:3 0 98.5G 0 part /
sdb 8:16 0 500G 0 disk ← 注意这块盘没挂载
nvme0n1 259:0 0 1.8T 0 disk
└─nvme0n1p1 ... part
读法:
- 树形结构:
sda是磁盘,sda1/2/3是它的分区 MOUNTPOINTS空 → 没挂载(装机时这是最该关心的信号)SIZE列:人类可读RM:是否可移动设备RO:只读
上面例子里 sdb 是 500G 的盘但没挂,要么你忘了挂、要么 IDC 给你算了但没初始化。
最常用的几个 flag
lsblk -f # 加上文件系统类型和 UUID
lsblk -nbo NAME,SIZE,TYPE,MOUNTPOINT # 字节单位 + 自定义列 + 无表头(脚本用)
lsblk -d # 只显示磁盘,不显示分区
lsblk -p # 显示完整路径(/dev/sda 而不是 sda)
lsblk -J # JSON 输出(脚本里配 jq 用)
lsblk -f 看文件系统
$ lsblk -f
NAME FSTYPE FSVER LABEL UUID MOUNTPOINTS
sda
├─sda1 vfat FAT32 7E3D-AB42 /boot/efi
├─sda2 ext4 1.0 a1b2c3d4-... /boot
└─sda3 ext4 1.0 e5f6g7h8-... /
sdb ext4 1.0 <empty> ← 已格式化但没挂
FSTYPE 空 → 没格式化,要 mkfs.ext4 /dev/sdX。 有 FSTYPE 但 MOUNTPOINTS 空 → 已格式化但没挂,要写 /etc/fstab 或临时 mount。
装机典型用法
# 字节级、无表头、自定义列 —— 脚本最爱
lsblk -nbo NAME,SIZE,TYPE,MOUNTPOINT
# 看出哪块盘最大、是否挂载
lsblk -d -o NAME,SIZE,MODEL,SERIAL
# 用厂商/序列号区分多盘
df —— 文件系统用量
默认输出
$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda3 98G 45G 49G 48% /
/dev/sda2 974M 120M 786M 14% /boot
tmpfs 3.9G 8.0K 3.9G 1% /run
overlay 98G 45G 49G 48% /var/lib/docker/...
-h人类可读(K/M/G/T)-T显示文件系统类型(很有用,能看出哪个是 tmpfs / overlay)-i看 inode 用量(重要,下文专门讲)
df -h 看不全的东西
$ df -h
Filesystem Size Used Avail Use% Mounted on
tmpfs 3.9G 8.0K 3.9G 1% /run
tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup
tmpfs 791M 1.2M 790M 1% /run/user/1000
很多 tmpfs / overlay 看着像独立"盘"实际是内存或映射,不消耗磁盘。看真实磁盘用量加 -T:
df -hT | grep -E 'ext4|xfs|btrfs|zfs'
df -i 看 inode 用量(坑王)
$ df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda3 6291456 6291300 156 100% / ← inode 满了!
文件系统不仅有"字节空间",还有"inode 空间"。每个文件占一个 inode。如果有几百万个小文件(比如 K8s 的 hostPath logs 没清理),字节空间没满但 inode 满了,新文件创建失败。
df -h 看到 Use% 才 50%,但实际写不进去 —— 一定要看 df -i。
排查 docker / kubelet 撑满的 overlay
df -h
# overlay 98G 95G 3G 97% /var/lib/docker/overlay2/...
K8s 节点上 overlay 暴涨,常见原因:
- 容器日志没轮转
- 镜像太多没清(
crictl rmi --prune) emptyDir写爆
排查接下来用 du。
du —— 目录树用量
默认输出(小心爆量)
du /var/log
# 每个子目录一行,能滚屏一万行
du 不加参数就递归显示每一个子目录。基本没人这么用。
常用 flag
du -sh /var/log # -s summary 总和,-h 人类可读
# 1.2G /var/log
du -sh /var/* # 看 /var 下每个一级子目录占多少
du -sh /var/* 2>/dev/null | sort -h # 按大小排序
du -h --max-depth=1 /var # 同上,新写法
-h --max-depth=N 是排查"哪个目录占空间"的标配。
找最大的 N 个文件 / 目录
# 最大的 10 个文件
du -ah /var | sort -rh | head
# 最大的 10 个一级子目录
du -sh /var/* 2>/dev/null | sort -rh | head
du vs ls -l 大小不一致?
ls -l file.log
# -rw-r--r-- 1 root root 10737418240 ... file.log ← 10 GB
du -sh file.log
# 4.0K file.log ← 才 4K?
稀疏文件(sparse file)—— 文件逻辑大小 10G,但实际只占 4K(中间全是空洞,文件系统不分配实际块)。ls -l 看逻辑大小,du 看物理占用。
K8s 里某些 PV 文件、数据库初始化文件经常是稀疏的。
实战:节点根分区满了,怎么排查
经典故障链:df -h(发现哪个 mount 满)→ du(找哪个子目录占)→ 处理。
# 第 1 步:哪个分区满了
df -h
# /dev/sda3 98G 97G 1.0G 99% /
# 第 2 步:根下哪个目录最大
du -sh /* 2>/dev/null | sort -rh | head
# 50G /var
# 30G /home
# 10G /opt
# ...
# 第 3 步:钻进 /var 继续
du -sh /var/* 2>/dev/null | sort -rh | head
# 40G /var/lib/docker
# 8G /var/log
# ...
# 第 4 步:继续钻
du -sh /var/lib/docker/* 2>/dev/null | sort -rh | head
# 35G /var/lib/docker/overlay2
# ...
# 第 5 步:清掉
docker system prune -af # 或 crictl rmi --prune
journalctl --vacuum-size=500M # journald 日志限大小
inode 满了怎么排查
df -i
# /dev/sda3 ... 100% /
# 找谁文件最多
for d in /*/; do
echo "$(find "$d" -xdev | wc -l) $d"
done | sort -rn | head
-xdev 让 find 不跨文件系统(不进入 tmpfs / 别的挂载点)。
常见元凶:
- K8s 的
/var/lib/kubelet/pods/*/volumes/...几万个小 socket / configmap /tmp没清理- 日志切片太碎(logrotate 没启用、或 retention 太长)
常见踩坑
坑 1:装机只看 df,错过未挂载的盘
df 看不到没挂载的盘。Day0 必须先 lsblk 看完整拓扑,确认所有盘都被识别、再决定怎么挂。
坑 2:删完文件 df 没变
rm /var/log/huge.log # 删了
df -h # 用量没降!
某进程还持有这个文件的句柄(典型:journald、应用日志没轮转)。Linux 文件系统的语义:句柄没关、文件实际不会释放。
排查:
lsof | grep deleted
# 或
lsof | grep huge.log
解决:重启那个进程(或 systemctl restart 该服务),文件句柄关了 → 空间释放。
坑 3:df -h Used + Avail < Size
# /dev/sda3 98G 80G 13G 87% /
# 80 + 13 = 93G,不是 98G?
不是 bug。ext4 默认保留 5% 给 root 用户用(tune2fs -m 0 可以改),普通用户用不到这部分,所以"Avail"不等于"Size - Used"。
坑 4:du 跟着 symlink 跑
du -sh /var
# 慢得要死、数字也不对
如果 /var/something 是个 symlink 指向别的 mount,du 默认不跨 mount 但会跟 symlink。加 -x(同 -xdev)只在当前文件系统内:
du -sh -x /var
坑 5:lsblk 看不到刚插的盘
$ lsblk
# 还是旧的拓扑
云厂商热添加盘之后,内核可能没刷。强制扫描:
echo "- - -" > /sys/class/scsi_host/host0/scan
# 或
partprobe
lsblk