这问题我太熟了。Ubuntu开机直接黑屏,屏幕上连个报错都没有,就剩一个光标在左上角闪,或者干脆卡死在品牌Logo界面。很多人第一反应是显卡驱动挂了、内核崩了,拆了一整晚驱动,最后才发现根分区已经100%满,系统根本没能力把桌面拉起来。
我遇到过不止一次这种局面,有物理机也有虚拟机,踩过的坑比想象中多。这篇文章就围绕“磁盘容量扩展”这个核心,把从诊断、紧急恢复、扩容操作到启动修复的完整流程捋一遍。内容覆盖LVM方案、传统分区方案、虚拟机磁盘扩容场景,以及各种黑屏变体问题的排查思路。有类似遭遇的直接按步骤抄作业,大部分情况能救回来。
1. 先搞清楚黑屏的根源:为什么空间满了会开不了机
1.1 空间不足导致黑屏的底层原因
很多人不理解,磁盘满跟开机黑屏有什么关系?核心在于现代Linux系统启动时是一个高度依赖写入的流程。从systemd初始化第一个服务开始,日志服务、用户会话管理器、显示管理器都在不停创建临时文件、写日志、更新缓存。
当根分区被写满,systemd-journald无法追加日志,关键服务初始化直接报错失败;图形界面需要创建.Xauthority、.cache目录、GNOME会话需要写临时数据,全部落不了地。最终表现就是显示器一片黑,但系统本身未必死机,只是到达不了登录界面。
再往深一层说,根分区满的时候连swap交换文件都没法创建,内核OOM处理也会变得异常。某些服务反复崩溃重启,又疯狂追加日志,进一步挤占本就不存在的空间,形成恶性循环。所以黑屏只是表象,真正的病灶是文件系统没有可用写入空间。
1.2 快速确认故障是不是空间问题
遇到黑屏先别急着重装,按下面的顺序排查,一般10分钟能定性。
如果屏幕还有光标闪烁或者能显示部分启动日志,尝试切换到文本终端。Ubuntu默认开启了多个TTY,按Ctrl+Alt+F3或F4切换,如果能看到登录提示,输入用户名密码,然后执行:
df -h df -i重点看/这一行的Use%,如果显示100%,基本就是空间问题实锤了。df -i也不能忽略,这是看inode使用率的,如果这里是100%,说明小文件数量爆炸,即使容量还有剩余,系统同样无法创建新文件。
如果连TTY都切不过去,或者屏幕完全没有任何反应,按住电源键强制关机,再开机时在BIOS界面后反复按Shift键(BIOS启动)或不断点按Esc键(UEFI启动),进入GRUB菜单。选择“Advanced options for Ubuntu”,再选“recovery mode”,回车。会进入一个蓝底菜单,先选“fsck”检查文件系统(会提示是否重新挂载为读写,选yes),再选“root”进入root shell。进去后同样执行df -h确认。
1.3 别忽略inode耗尽这种隐藏情况
遇到过一次很奇怪的现象:客户机df -h显示根分区只用了65%,但系统频繁报“No space left on device”,扩容之后问题依旧。最后df -i一看,inode使用率99.9%。
原因是一批并发的短连接服务在/tmp下创建了几十万个socket临时文件,每个文件极小,但都占用一个inode。这种情况下你用du按目录排查容量是看不出来的,必须用df -i看inode占用。
inode已满时,扩容分区本身不解决问题,因为新增空间的inode数量是文件系统格式化时定死的。正确做法是找出小文件集中的目录(通常是/tmp和/var/tmp),批量清理,或者重建文件系统时加大-i指定inode密度。我在实操中遇到这种情况,直接清掉/tmp下过期文件,当场释放了20多万个inode,系统立刻恢复正常。
2. 紧急自救:先进恢复模式腾出空间让系统活过来
2.1 recovery模式的手动挂载操作
恢复模式进入root shell后,根分区默认是只读挂载的,防止在文件系统有损坏时继续写入造成二次破坏。但我们要清理空间,必须先把它切成读写模式:
mount -o rw,remount /注意,如果此时根分区满到连remount都执行不了(理论上几乎不可能,但确实遇到过极端情况),可以用live USB启动系统,挂载后用resize2fs -M之类的方式先压缩文件系统腾出一点空间,再回来做清理。实操中这种情况比较少,一般remount没什么问题。
挂载成功后再确认其他关键目录状态:
mount | grep -E " / | /var | /tmp "如果有独立的/var或/tmp分区也满了,需要逐一检查,别只顾着根分区。
2.2 清理空间的标准命令序列
进入读写模式的root shell后,按优先级执行下面的清理操作。这个顺序就是我实际处理时经过多轮验证的,越靠前收益越大、风险越低。
第一步,清日志。这是空间杀手榜第一名,尤其跑过一段时间后的机器,journal日志几百MB甚至几个GB都很常见:
journalctl --disk-usage journalctl --vacuum-time=3d--vacuum-time=3d表示只保留最近3天的日志,也可以按大小清:
journalctl --vacuum-size=200M第二步,清包管理缓存。这条在长期运行的Ubuntu上效果立竿见影:
apt clean apt autoremove --purge/var/cache/apt/archives平时攒一堆安装包缓存,apt clean直接清空。autoremove会把不再需要的旧依赖包一起卸掉,顺带释放空间。
第三步,清旧内核。Ubuntu每次升级内核都会保留老版本,一个内核文件大约100-200MB,多了自然占用大:
dpkg --list | grep linux-image uname -r先看当前正在使用的内核版本(uname -r),用apt卸载掉其他老版本,至少要保留当前版本和最近一个旧版本作为回退。删除后记得:
update-grub第四步,用du定位剩余空间大头:
du -xhd1 / 2>/dev/null | sort -rh | head -20这条命令一跑,哪个目录在吃空间一目了然。常出现大占用的是/var/log、/var/lib/docker、/home下用户的.cache目录、/snap目录。对应清理策略:
rm -rf /home/用户名/.cache/*/var/log下的日志文件可以用truncate直接截断:
truncate -s 0 /var/log/syslog truncate -s 0 /var/log/kern.logDocker环境执行docker system prune -a,能把无用镜像、停止的容器、构建缓存全部清掉,经常一次释放几十GB。
2.3 清理时的禁忌
很多人空间一紧张就六神无主,到处乱删。这里要反复强调:/usr目录不要手动删任何东西,/lib/modules下当前内核的模块目录不能动,/etc下的配置要找专业工具来管理。删到让系统无法启动的“修复成本”,远高于一开始就谨慎操作的成本。
另外,清理完日志后,journald会在系统下次启动时重新创建日志文件,如果根分区剩余空间确实太小,建议顺手限制journald的容量上限(具体配置见后面第4节),防止一会儿又被写满。
2.4 腾出空间后的首个动作
清理完毕,确认df -h下根分区已经有了至少10%-20%的剩余空间,执行reboot -f正常重启。此时系统大概率能进入桌面或者登录界面。
但别高兴太早,这只是“续命”而不是“根治”。如果分区容量本身已经触及物理上限,不扩容的话下次再写满只是时间问题。我遇到过只清理不扩容的机器,用了两周再次黑屏,日志照常堆积,又得走一遍老流程。所以紧急恢复只是手段,扩容才是真正要做的事。
3. 磁盘扩容实战:按场景选择方案
扩容前先做一次盘点,搞清楚自己属于哪种布局。执行:
lsblk -f看输出里的FSTYPE列和挂载点。如果根分区是/dev/mapper/xxx-root或/dev/mapper/ubuntu--vg-root,说明走的是LVM逻辑卷;如果直接挂载的是/dev/sda1、/dev/nvme0n1p2这类物理分区,那就是传统分区布局。物理机、虚拟机、云主机三个场景的操作路径也不同,分开说。
3.1 虚拟机场景:先扩虚拟磁盘再进系统操作
虚拟机的扩容要分两步走:宿主机侧增加虚拟磁盘容量,客户机(Ubuntu)侧完成分区扩展。只做任意一侧都没用。
VMware Workstation用户在虚拟机关机状态下,右键虚拟机设置,找到硬盘,点击“扩展”,输入新的容量大小。或者用命令行:
vmware-vdiskmanager -x 80GB /path/to/disk.vmdkVirtualBox用户在GUI里选择虚拟硬盘,修改容量,或者用命令:
VBoxManage modifymedium disk /path/to/disk.vdi --resize 81920KVM/QEMU环境先改qcow2镜像大小:
qemu-img resize /path/to/disk.qcow2 80G宿主机侧扩展完成后,启动虚拟机,客户机里用lsblk查看磁盘设备,比如原来是/dev/sda有20GB,现在应该看到有未分配空间。需要说明的是,虚拟磁盘扩完以后如果是正在运行的虚拟机可能需要重启才能让虚拟控制器识别新大小,但如果是磁盘设备本身映射的变化,执行:
partprobe /dev/sda通常也能让内核重新读取分区表。
注意一个隐藏问题:如果虚拟磁盘存在快照链,部分虚拟化平台不允许直接扩展。VMware里必须先删除或合并快照,否则扩展按钮是灰色的。我在处理这个问题时就吃过亏,折腾半天最后把快照合并掉才顺利扩容。
3.2 LVM方案:扩展逻辑卷的完整操作链
LVM的优势在于弹性,物理卷、卷组、逻辑卷三层结构,可以随时调配。扩容前先看现状:
pvdisplay vgdisplay lvdisplay核心逻辑是:物理卷(PV)拿到新的空间 → 卷组(VG)可用空间变大 → 逻辑卷(LV)扩展 → 文件系统调整大小。
第一步,确认物理卷是否已经占满整个磁盘分区。例如物理卷在/dev/sda2,但磁盘扩出来的空间在sda2之后是未分配状态,需要先把分区扩展到新空间:
growpart /dev/sda 2growpart这个工具来自cloud-guest-utils包,没有就先安装:
apt install cloud-guest-utils如果物理卷对应的分区已经覆盖了整块磁盘,或者虚拟机在扩展磁盘时直接在设备末尾追加了空间,那就不需要growpart。
第二步,让物理卷识别新空间:
pvresize /dev/sda2执行完再pvdisplay,应该看到PV Size变大,Free空间非零。
第三步,扩展逻辑卷。比如根逻辑卷名字是/dev/mapper/ubuntu--vg-root,把它扩展到卷组全部剩余空间:
lvextend -l +100%FREE /dev/mapper/ubuntu--vg-root这一步只是把LV的边界扩大,文件系统还没感知到新增空间。
第四步,扩展文件系统。这里必须分文件系统类型,用lsblk -f看到的FSTYPE为ext4时:
resize2fs /dev/mapper/ubuntu--vg-root如果是xfs:
xfs_growfs /这两条命令本质不一样。resize2fs可以在线扩展ext4,也可以卸载后操作;而xfs文件系统的扩容只能在挂载状态下执行xfs_growfs且指定挂载点。把这两个命令搞混是新手踩坑高发区。
3.3 非LVM普通分区:growpart与resize2fs的顺序
没有LVM的普通分区扩容要更谨慎,因为分区表边界和文件系统边界是两套东西。操作前先用lsblk确认设备名和分区号,比如根分区是/dev/sda1。
第一步,扩大分区。使用growpart把分区扩展到磁盘末尾:
growpart /dev/sda 1这里的注意点是/dev/sda和分区号1是分开传参的。如果你用的是NVMe硬盘,设备名可能是/dev/nvme0n1,分区号同样是单独的参数。
growpart执行成功后会输出类似“CHANGED”的提示。随后让内核重新读取分区表:
partprobe /dev/sda如果partprobe执行失败提示设备忙,通常是因为根分区正在使用中,最稳妥的方法是重启后再继续。毕竟内核已经旧的分区表中运行,强制读取有一定风险。
第二步,扩展文件系统。确认是ext4:
resize2fs /dev/sda1如果是xfs:
xfs_growfs /第三步,验证:
df -h /看到容量已经涨上去了,说明成功。
一个常见的特殊场景:根分区在磁盘的起始位置,磁盘末尾的是其他分区比如/boot或swap。此时growpart的目标就不是根分区,而是根分区和新空间之间有没有阻隔。正确的做法是先确认整块磁盘的分区布局,如果根分区后面还有其他分区,而新空间又追加在末尾,根分区不能跨过其他分区去占用空间。这种情况下要么移动/删除中间分区,要么把中间分区先缩小,要么直接把新空间划给后面的分区。根据我处理过的案例,物理机上遇到这种布局,最安全的方案是使用live USB配合GParted图形化处理,可视化拖动分区边界,不容易出错。
3.4 云主机控制台扩容的处理方式
云主机和虚拟机思路类似,先在控制台侧扩容云硬盘容量,重启或热挂载后系统内执行扩容操作。很多主流云平台提供自动扩容脚本,原理还是growpart加resize2fs的组合。
云主机执行扩容要注意一点:部分云厂商使用LVM布局,部分使用GPT分区的普通分区,按照上文对应方案执行即可。如果系统盘扩容完成但lsblk看不到新增空间,先去控制台确认硬盘是否处于“已挂载且运行中可扩展”的状态,某些平台需要关机再开机才能识别新磁盘大小。
4. 扩展完成后:分区表与启动流程的收尾修复
4.1 验证扩容结果
扩容全程做下来,最后要养成验证的习惯。运行下面一组命令,逐项检查:
df -h / lsblk pvs vgs lvs正常的情况下,df -h应该显示扩展后的目标容量,lsblk里对应分区和逻辑卷的SIZE同步变大,LVM三层的PV/VG/LV容量逐级匹配。如果df显示的还是旧容量,大概率是文件系统扩展那一步没做或者做错了;如果lsblk显示分区变大但df没变,就是resize2fs或xfs_growfs还没执行成功。
4.2 重建GRUB与initramfs
分区表结构发生变化后,尤其如果涉及/boot分区或者根分区起始位置变更,GRUB引导需要同步更新。在正常启动的系统里执行:
update-grub顺带重建initramfs,防止启动时因缺少新内核模块或驱动导致黑屏:
update-initramfs -u这一步在普通扩容时未必严格必须,但如果前面经历了强制断电、文件系统fsck、内核更新,建议都做一遍。我在扩展完根分区后遇到过重启卡死,重做initramfs后立刻正常。
4.3 配置日志与缓存策略,防止二次爆盘
扩容只是解决了当下的物理容量,如果不改变使用习惯和日志策略,迟早还会再盘满。建议做下面三件事:
一是限制journald日志大小。编辑/etc/systemd/journald.conf,确保以下几行生效:
SystemMaxUse=500M MaxRetentionSec=7day RuntimeMaxUse=200M配置后重启服务:
systemctl restart systemd-journald二是设置apt自动清理缓存。新建/etc/apt/apt.conf.d/99clean:
APT::Periodic::Update-Package-Lists "1"; APT::Periodic::Download-Upgradeable-Packages "0"; APT::Periodic::AutocleanInterval "7";这样每周自动清理一次,避免缓存越积越大。
三是设定空间告警。最简单的方式是cron任务定期检查:
*/30 * * * * root df -h / | awk 'NR==2 && $5+0 > 85 {print "disk space alert" | mail -s "disk nearly full" admin@example.com}'不需要多复杂,能提前知道空间告急,就不会再次被动面临黑屏。
5. 黑屏扩容实战中的常见问题与排错清单
处理这类问题的次数多了,我总结出一张高频问题对照表,基本能覆盖90%的实战场景。
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 黑屏但能通过SSH连接 | 仅图形界面服务崩溃 | Ctrl+Alt+F1切tty,systemctl restart gdm3或lightdm |
| 扩容后df显示容量未变 | 文件系统未调整 | 按FSTYPE执行resize2fs或xfs_growfs |
| resize2fs报“Please run e2fsck” | 文件系统有异常标记 | 执行e2fsck -f /dev/xxx,只读修复后再重跑resize2fs |
| 恢复模式root shell提示只读文件系统 | 未重新挂载 | mount -o rw,remount / |
| 虚拟机硬盘已扩展但客户机看不到 | 虚拟磁盘有快照链或分区表未重读 | 删除/合并快照,执行partprobe或重启 |
| /boot独立分区且空间不足 | 内核更新失败导致启动异常 | apt autoremove --purge清理旧内核,再update-grub |
| 根文件系统是btrfs | 不支持常规resize2fs | 挂载状态下btrfs filesystem resize max / |
| journal日志占好几GB | journald无上限 | 配置SystemMaxUse并vacuum清理 |
| resize2fs全部完成后容量只增加一部分 | 分区中间有间隙或LVM PV未完全覆盖 | 用lsblk确认分区边界,必要时用GParted调整 |
| 扩容后直接Grub救援模式 | 分区起始位置变动未重建grub | live盘chroot后grub-install与update-grub |
再补充两个容易忽略的细节。
第一个是XFS文件系统只能扩容不能缩容,购买扩容前想清楚,别想着“试一下不行再缩回去”,XFS缩容没有正常通道。
第二个是扩容过程中尽量避免在根分区上进行读写操作。有些人一边跑着下载一边做resize2fs,结果中途系统卡死,文件系统处于不一致状态。我的做法是先停止跟根分区读写相关的服务,或者直接通过live环境操作,安全性最高。live环境操作前记得先e2fsck -f检查一遍文件系统。
写到最后的一点个人体会
经历过几次黑屏后,我的习惯是每台Ubuntu机器装好后立刻做三件事:启用journald容量限制、配置apt自动清理、给根分区留够冗余空间(至少15%)。这些前期功夫花不了多少时间,但能让我在做其他操作时不用时刻担心“某个服务突然写满磁盘”。
扩容本身没有想象中复杂,捋清LVM和普通分区的区别,认清ext4和xfs在扩容命令上的差异,剩下的就是按步骤执行。真正容易出事的反而是心态——黑屏时急着重装,反而把还有救的系统弄得更糟。先用恢复模式或者live环境把数据保住,再对照上面的方案一步步来,大多数情况下能平稳过关。