1. 磁盘告警背后的真凶定位
1.1 从一次真实的磁盘爆满说起
某天下午,一台跑了两年多的 Ubuntu 18.04 开发机突然开始报警,根分区使用率直接飙到 100%。SSH 能连上,但任何写操作都失败,日志写不进去,服务一个接一个挂掉。第一反应是日志文件爆了,du -sh /*一跑,发现/var/lib/snapd这个目录占了大头,再往下钻,/var/lib/snapd/snaps里堆了几十个.snap文件,每个动辄几百 MB,加起来轻松吃掉十几 GB。
这就是 snap 在 Ubuntu 18.04 上的经典问题。Ubuntu 从 16.04 开始把 snap 作为默认的包管理方式之一,18.04 更是把不少系统组件都做成了 snap 包,比如core、gnome-3-26-1604、gtk-common-themes这些。问题在于,snap 的更新机制是保留旧版本 revision 的,默认保留最近 3 个版本(有些资料说 2 个,实测不同版本行为有差异),每次更新都会新增一个 revision 目录,旧的不会自动删。时间一长,磁盘就被这些"历史遗留"撑爆了。
1.2 snap 到底是个什么东西
在动手卸载之前,得先搞清楚 snap 是什么,不然容易误伤系统。snap 是 Canonical 主推的一种软件打包和分发格式,核心理念是"一个包搞定所有依赖",把应用和它需要的运行时库全部打包在一起,运行在沙箱环境里。好处是跨发行版、依赖隔离、自动更新;坏处也很明显——包体积大、启动慢、磁盘占用高、更新策略不透明。
你可以把 snap 理解成"自带干粮的旅行团":每个 snap 应用都自己带了一套锅碗瓢盆(依赖库),走到哪儿都能开火做饭,不用管当地厨房(系统库)长什么样。代价就是行李特别重,而且每来一批新游客(新版本),旧游客的行李还堆在仓库里不扔。
Ubuntu 18.04 上默认预装的 snap 相关组件包括:
| 组件 | 作用 | 是否可卸载 |
|---|---|---|
| snapd | snap 后台守护进程 | 可卸载,但需谨慎 |
| core | snap 的基础运行时 | 卸载 snapd 后自动清理 |
| gnome-3-26-1604 | GNOME 运行时 | 随 snapd 一起处理 |
| gtk-common-themes | GTK 主题资源 | 随 snapd 一起处理 |
| snap-store | 软件商店 | 可单独卸载 |
1.3 为什么直接删文件不管用
很多人第一反应是rm -rf /var/lib/snapd/snaps/*,删完发现磁盘是空出来了,但过一会儿又满了,而且 snap 命令开始报错。原因在于 snapd 是个常驻服务,它维护着自己的状态数据库,你手动删文件,它不知道,下次更新或者重启时又会把东西拉回来,甚至因为状态不一致导致服务崩溃。
正确的思路是:要么用 snap 自己的命令清理旧版本,要么彻底把 snapd 卸掉。前者是"节流",后者是"断根"。下面两条路我都会讲清楚,你可以根据自己的需求选。
注意:卸载 snapd 之前,务必确认系统里没有正在依赖 snap 包的关键组件。Ubuntu 18.04 的桌面版有些应用(比如某些版本的 Chromium、Firefox)可能是 snap 装的,卸之前先
snap list看一眼。
2. 先礼后兵:用 snap 命令清理旧版本
2.1 查看当前 snap 占用情况
动手之前先摸清家底。三条命令搞定:
# 查看所有已安装的 snap 包 snap list # 查看 snap 目录总占用 du -sh /var/lib/snapd/snaps # 查看每个 snap 的详细信息和 revision snap list --allsnap list --all会列出所有 revision,包括被禁用的旧版本。输出里Notes列显示disabled的就是旧版本,这些就是占空间的元凶。实测一台跑了半年的机器,gnome-3-26-1604能攒下 4 个 revision,每个 200MB 左右,光这一个就吃掉 800MB。
2.2 设置自动清理旧版本
snap 提供了一个配置项控制保留多少个旧 revision:
# 只保留最近 2 个 revision(默认通常是 3) sudo snap set system refresh.retain=2这个值最小可以设到 2,不能再低了,设 1 会报错。设完之后,snapd 在下次自动更新时会按这个策略清理。但注意,这个设置只对之后的更新生效,已经堆积的旧版本不会自动删,得手动清一次。
2.3 手动批量清理 disabled 的旧版本
这是最实用的操作,一条命令搞定:
# 找出所有 disabled 的 snap 并逐个移除 snap list --all | awk '/disabled/{print $1, $3}' | \ while read snapname revision; do sudo snap remove "$snapname" --revision="$revision" done拆解一下这条命令的逻辑:snap list --all输出所有包,awk过滤出含disabled的行,提取第一列(包名)和第三列(revision 号),然后循环调用snap remove指定 revision 删除。实测下来,这一条命令能从一台积攒了半年的机器上清出 5-8GB 空间。
实操心得:执行前先
snap list --all | grep disabled | wc -l数一下有多少个待清理项,心里有数。清理过程中如果某个包正在被使用,会提示失败,跳过即可,不影响其他包。
2.4 清理后的验证
清完再跑一次du -sh /var/lib/snapd/snaps,对比一下前后的数字。如果空间释放不明显,可能是/var/lib/snapd/cache里还有缓存的下载包,这个目录也可以清:
sudo rm -rf /var/lib/snapd/cache/*这个 cache 目录存的是下载中的 snap 包临时文件,正常情况下会自动清理,但异常中断时可能残留。删它没有风险,snapd 需要时会重新下载。
3. 彻底卸载 snapd 的完整流程
3.1 卸载前的依赖排查
如果你决定彻底跟 snap 说再见,第一步不是直接卸,而是排查依赖。跑这两条命令:
# 列出所有 snap 包 snap list # 检查是否有系统关键组件依赖 snap apt list --installed 2>/dev/null | grep -i snap重点关注snapd、snap-confine、ubuntu-core-launcher这几个。如果snap list里有你正在用的应用(比如某些编辑器、浏览器),先想好替代方案——用 apt 装 deb 版本,或者换其他分发方式。
3.2 分步卸载 snapd
卸载顺序很重要,搞错了会留下残留。推荐流程:
# 第一步:先移除所有已安装的 snap 应用 # 逐个移除,或者用循环 for pkg in $(snap list | awk 'NR>1{print $1}'); do sudo snap remove "$pkg" done # 第二步:停止 snapd 服务 sudo systemctl stop snapd sudo systemctl disable snapd # 第三步:卸载 snapd 包 sudo apt purge snapd -y # 第四步:清理残留目录 sudo rm -rf /var/lib/snapd sudo rm -rf /var/cache/snapd sudo rm -rf ~/snapapt purge比apt remove更彻底,会连配置文件一起删。~/snap是当前用户的 snap 数据目录,也要清掉,不然每个用户目录下都留一份。
3.3 阻止 snapd 被重新安装
Ubuntu 18.04 有个坑:卸载 snapd 之后,某些 apt 操作可能会把它作为依赖又装回来。比如你装某个包,它的推荐依赖里带了 snapd。解决办法是创建一个 apt 的偏好文件,把 snapd 钉死:
# 创建偏好文件 sudo tee /etc/apt/preferences.d/nosnap.pref << 'EOF' Package: snapd Pin: release a=* Pin-Priority: -10 EOFPin-Priority: -10表示负优先级,apt 永远不会主动安装这个包。这个技巧在 Debian 系里很通用,用来屏蔽任何你不想装的包。
3.4 卸载后的系统检查
卸完重启一次,然后检查:
# 确认 snapd 服务不存在 systemctl status snapd # 确认 snap 命令不存在 which snap # 确认目录已清理 ls /var/lib/snapd 2>/dev/null如果systemctl status snapd报 "Unit snapd.service could not be found",说明卸干净了。which snap应该没有任何输出。这时候再df -h看一眼根分区,通常能释放出 10GB 以上的空间。
注意:卸载 snapd 后,Ubuntu 的软件商店(如果之前是 snap 版)会失效。桌面用户如果还需要图形化装软件,可以改用
gnome-software的 apt 后端,或者直接用命令行 apt。
4. 卸载 snap 之后的替代方案
4.1 用 apt 装回必要的应用
snap 卸了,原来用 snap 装的应用得找替代。常见对应关系:
| 原 snap 应用 | apt 替代方案 | 备注 |
|---|---|---|
| chromium | apt install chromium-browser | 注意 18.04 上这个包可能又指向 snap,需加 PPA |
| firefox | apt install firefox | 官方 deb 版 |
| code | 官方 deb 包手动安装 | 从官网下 .deb |
| vlc | apt install vlc | 直接可用 |
| htop | apt install htop | 直接可用 |
Chromium 在 Ubuntu 18.04 上是个特例,官方源里的chromium-browser后来被改成了 snap 的过渡包,装完实际还是 snap。要装真正的 deb 版,得加第三方 PPA 或者用其他方式。这也是很多人下决心卸载 snap 的导火索之一。
4.2 用 Flatpak 或 AppImage 作为补充
如果你确实需要 snap 那种"跨发行版、依赖隔离"的特性,可以考虑 Flatpak 或 AppImage。Flatpak 的磁盘管理比 snap 友好一些,flatpak uninstall --unused能清理不再需要的运行时。AppImage 更简单,单个文件,用完删掉就行,不留残留。
不过说实话,对于服务器场景,apt 足够了。服务器上跑的东西大多是命令行工具和服务,deb 包管理成熟稳定,没必要引入 snap 这层额外的复杂度。
4.3 服务器场景的极简建议
如果是服务器,我的建议是:装系统时就选 minimal 安装,或者装完立刻卸 snap。服务器不需要图形界面,snap 带来的那点"便利"完全抵不过它带来的磁盘和维护负担。一台干净的 Ubuntu 18.04 服务器,卸掉 snap 后根分区占用能控制在 2GB 以内,跑几年都不用担心磁盘问题。
5. 常见问题与排查实录
5.1 卸载 snapd 后 apt 报错
现象:apt update或apt install时报 "snapd 依赖问题"。
原因:某些包的依赖链里还有 snapd,purge 时没处理干净。
解决:
sudo apt --fix-broken install sudo apt autoremove如果还不行,检查/etc/apt/preferences.d/nosnap.pref是否生效,然后sudo apt update刷新缓存。
5.2 snap 命令删不掉某个包
现象:snap remove xxx卡住或报 "snap is in use"。
原因:该 snap 的进程还在运行,或者有 mount 挂载点没释放。
解决:
# 查看相关进程 ps aux | grep snap # 强制终止 sudo pkill -f snap # 检查挂载点 mount | grep snap # 卸载挂载 sudo umount /snap/xxx/... # 再试删除 sudo snap remove xxx实在删不掉,直接走彻底卸载流程,apt purge snapd会强制清理。
5.3 磁盘空间释放了但很快又满
现象:清理后空间回来,过几天又满了。
原因:snapd 自动更新又拉了一堆新 revision。
解决:要么设置refresh.retain=2并定期手动清,要么彻底卸载。治标不治本的话,可以写个 cron 任务每周跑一次清理脚本:
# 加到 crontab,每周日凌晨 3 点清理 0 3 * * 0 snap list --all | awk '/disabled/{print $1, $3}' | while read n r; do snap remove "$n" --revision="$r"; done5.4 卸载后系统启动变慢或异常
现象:卸完 snapd 重启,启动时间变长或有报错。
原因:某些 systemd 单元还引用 snap 路径。
解决:
# 检查失败的单元 systemctl --failed # 检查 snap 相关的残留单元 ls /etc/systemd/system/ | grep snap ls /lib/systemd/system/ | grep snap # 手动禁用或删除残留单元 sudo systemctl disable <unit-name>Ubuntu 18.04 上常见的是snapd.socket和snapd.seeded.service,purge 时一般会处理,但偶尔有残留。
5.5 常见问题速查表
| 问题 | 排查命令 | 解决方向 |
|---|---|---|
| 磁盘满 | du -sh /var/lib/snapd/* | 清旧 revision 或卸 snapd |
| snap 命令卡住 | ps aux | grep snap | 杀进程后重试 |
| apt 依赖报错 | apt --fix-broken install | 修复依赖链 |
| 空间反复满 | snap list --all | 设 retain 或写 cron |
| 启动异常 | systemctl --failed | 清理残留单元 |
| 包删不掉 | mount | grep snap | 先 umount 再删 |
6. 我踩过的坑和几条实在建议
第一次处理这个问题时,我图省事直接rm -rf /var/lib/snapd,结果 snapd 服务崩溃,系统日志刷屏,最后不得不重装。后来才明白,snapd 的状态管理是"数据库+文件"双轨的,手动删文件等于把数据库和实际文件搞不一致了,服务自然崩。
第二次我学乖了,先snap remove再apt purge,但忘了清~/snap,结果用户目录下留了一堆缓存,虽然不影响系统,但看着膈应。所以清理要彻底,系统目录和用户目录都得管。
还有一次是在一台生产服务器上操作,卸完 snapd 后没检查 systemd 单元,重启后发现snapd.socket还在尝试启动,虽然不影响业务,但日志里一直报错。后来加了systemctl mask snapd.socket才彻底安静。
几条实在建议:
- 服务器装系统时直接选 minimal,从源头避免 snap 进来。
- 桌面用户如果要用 snap,第一时间设
refresh.retain=2,并养成每月清一次的习惯。 - 卸载前一定先
snap list,确认没有关键应用依赖。 - 卸载后重启验证,别嫌麻烦,很多残留问题重启才暴露。
- 保留一个 apt 偏好文件,防止 snapd 被依赖链偷偷装回来。
这套流程我在好几台机器上跑过,从 Ubuntu 18.04 到 20.04 都适用,20.04 之后 snap 的清理机制略有改进,但核心逻辑一样。磁盘告警这种事,预防比救火重要,与其等满了再手忙脚乱,不如装完系统就花五分钟把 snap 处理掉。