资讯动态

snap 包管理实战:沙箱原理、snapcraft 打包与跨发行版环境复现

发布时间:2026/9/16 21:44:00 来源:尧图企业网站定制
1. 为什么我又把 snap 包管理器捡回来了snap 包管理器这个工具我在 Ubuntu 上用了快八年从 16.04 一直用到现在。说实话这几年我对它的态度是来回摇摆的早年间嫌它启动慢、磁盘占得多一度把它从系统里卸得干干净净所有软件都走 apt 和源码编译后来又因为几个工具只在 snap 渠道发布了新版本被迫装回来再到现在我做跨发行版的开发环境复现时snap 反而成了我优先考虑的方案之一。这个转变不是因为我突然变成了 snap 的拥趸而是因为踩的坑足够多之后我慢慢想明白了一件事包管理器从来不是哪个更好的问题而是哪个更适合当前这段活的问题。snap 解决的核心问题是把应用和它依赖的运行时一起打包成自包含的只读镜像让同一个应用能在 Ubuntu、Debian、Fedora、Arch 甚至一些嵌入式发行版上跑出一模一样的行为。它不解决所有问题但它把环境不一致这一类问题处理得相当干脆。这篇内容我想聊的东西比较杂snap 的沙箱和挂载机制到底是怎么跑的、通道和修订号这套版本体系怎么用、自动刷新怎么管、在 ROS 2 加 VSCode 加 PlatformIO 的 ESP32 开发链路里 snap 该站在哪个位置、Python 那边为什么我又换成了 uv、以及自己用 snapcraft 打一个包时会遇到哪些真实问题。内容偏实操适合已经用过 Linux、被依赖冲突折磨过、想找一套更稳的软件分发方式的人看。如果你完全没接触过 Linux 包管理也能看懂我会尽量把底层的东西用生活化的方式讲清楚。2. snap 的本质它到底和 apt 差在哪里2.1 squashfs 只读镜像与挂载点理解 snap 最快的方式是去看/snap这个目录。你随便找台装了 snapd 的机器执行ls /snap会看到一堆软链接或者目录每个目录名就是一个 snap 包名里面按修订号revision分成子目录。比如/snap/code/175和/snap/code/172可能同时存在。这些目录不是普通的文件系统目录而是squashfs 只读镜像的挂载点。snapd 启动时会把这些镜像 loop mount 到对应路径应用运行时读到的就是镜像里的内容写不进去也不会有任何进程能篡改它。这件事的意义比看起来大得多。传统 deb 包在安装时把文件散落到/usr/bin、/usr/lib、/etc各个角落任何一个包升级都可能动到共享库进而把另一个毫不相干的程序搞崩。squashfs 镜像则是一整块文件之间的相对关系在打包那一刻就冻结了。我做过一个对比测试同一个 Python 脚本在 apt 装的 Python 上用系统 site-packages和在一个 snap 自带的 Python 运行时上跑前者在我升级了一次libssl之后就报了符号找不到后者纹丝不动。代价也很明确每个 snap 都自带一份运行时。一个 Electron 应用光本体就能到 200MB 上下因为里面塞了 Chromium。同一台机器上装三个基于 Electron 的 snap就有三份 Chromium。这是我的 SSD 上真实发生过的事情也是很多人第一次用 snap 就掉头走的原因。所以我的实践原则是桌面重应用适可而止工具链和 CLI 类的东西可以放心用。注意不要手动去删/snap/name/revision下面的东西也不要对/snap做chmod或者写操作。那层是只读挂载破坏了挂载点会导致 snapd 状态机混乱严重时整个 snapd 卡住只能靠systemctl restart snapd甚至重装 snapd 来恢复。2.2 沙箱、权限接口与 AppArmorsnap 的第二个关键设计是限制模式confinement。绝大多数 snap 跑在strict模式下进程被 AppArmor 规则限制住只能访问自己的家目录$HOME/snap/name/、自己的数据目录/var/snap/name/以及少数几个明确声明的系统路径。想访问摄像头、串口设备、系统时间、网络、音频都得在 snap 的元数据里声明一个 plug然后由用户或者系统把它连到对应的 slot 上。这套机制我用两个词概括声明式和可审计。声明式体现在应用作者在snapcraft.yaml里写清楚需要什么权限安装时不弹一堆看不懂的选项可审计体现在你随时可以snap connections name看到这个包到底连了什么有没有连摄像头、有没有读写家目录。这一点对企业环境特别有价值——审计一个 snap 需要什么权限比审计一个 deb 包在 postinst 脚本里偷偷干了什么要容易太多。但沙箱也带来一批玄学问题。最典型的是你装了个工具以普通用户身份跑正常工作一加sudo就报权限错误。原因是sudo会切换成 root 身份AppArmor 的规则按用户维度匹配的家目录路径变成了/root/snap/...而那个目录根本不存在于是应用要么报错要么静默降级。还有个高频坑是串口设备ESP32 烧写走/dev/ttyUSB0strict 模式下 snap 默认看不到这个设备要么用--devmode临时放开只用于调试要么在打包时声明serial-port接口。实操心得遇到 snap 应用莫名其妙没有权限第一件事不是去改文件权限而是执行snap connections name看 plug 是否处于已连接状态再看dmesg | grep -i apparmor有没有拒绝记录。90% 的权限问题在这两步里就能定位。2.3 通道、修订号与自动刷新snap 的版本体系是理解它的第三块拼图。每个 snap 有一条或多条轨道track每条轨道下有四条风险通道riskstable、candidate、beta、edge。写成完整形式就是track/risk比如1.0/stable或者latest/edge省略 track 时默认走latest/stable。修订号是每个构建产物的唯一编号安装时你拿到的是某个具体修订号但对外可见的是通道。这套设计的好处是你可以精确控制升级的激进程度而不用改命令行。生产机上装stable测试机上装candidate自己想尝鲜的那台装edge三台机器上跑的是同一个应用的不同构建切换只需要一条snap refresh name --channel...。我在给团队做灰度发布时就是这么干的先让两台工作站切到candidate跑一周没问题再把通道推给其余机器出问题就snap revert回到上一个修订号整个过程五秒钟。自动刷新是 snap 的默认行为每天检查几次、有新版本就后台升级。很多人反感这一点觉得系统不受自己控制了。我的看法是默认开自动刷新是对的选择但你必须知道怎么关。snap refresh --hold可以全局暂停刷新snap refresh --holdname针对单个包snap refresh --list能看到哪些包有新版本但还没升。反过来如果你希望刷新只在你指定的时间窗口内发生可以设置# 只允许周一至周五凌晨 3 点到 5 点之间刷新 sudo snap set system refresh.timermon-fri,3:00-5:00 # 保留最近 3 个修订版本方便随时回滚 sudo snap set system refresh.retain3refresh.retain这个值默认是 2取值范围 2 到 20。我一般设成 3因为遇到过一次回滚到上一个版本仍然有 bug、只能退到上上个版本的情况。多留一个版本成本是几百兆磁盘换来的是凌晨两点不用再想办法降级。2.4 依赖关系图connections 与 content interface这一节我想专门讲 snap 的图因为这是我见过最多人搞不清的部分。snap 的依赖关系和 deb 完全不同snap 包之间不通过共享库互相依赖每个包自带运行时所以不存在 deb 那种A 依赖 B 的 1.2 版本、C 又依赖 B 的 1.3 版本的死锁。snap 之间的关系体现在两个层面。第一个层面是接口连接图。每个 snap 声明自己提供什么 slot、需要什么 plug安装之后 snapd 尝试自动连接能连的剩下的靠用户手动snap connect。snap connections输出的就是这张图的当前状态。典型的例子是桌面应用依赖gnome-3-42-2004或者gtk-common-themes这类内容包——它们不是传统意义的依赖库而是以 content interface 的形式提供一个运行时环境应用通过 plug 连上去读里面的主题和库。第二个层面是构建期的依赖图也就是你在 snapcraft 里写 parts 时形成的 DAG。每个 part 可以用after:声明它依赖哪些 part 先构建完成snapcraft 会据此算出一个拓扑排序并行执行没有依赖关系的 part。我打包一个带命令行工具加图形前端的项目时直接把after: [build-tool]写进去构建时间从串行的四分半降到了并行的一分四十秒省下来的时间全在等待上。提示想知道某个 snap 到底连了哪些接口别只看文档直接跑snap connections name想回溯历史上连接关系的变更可以配合snap changes看任务记录。这两条命令我基本上每周都要用几次。3. 从零上手安装配置与高频命令实操3.1 环境确认与 snapd 安装Ubuntu 16.04 之后的桌面版和服务器版默认带 snapdsnap version能输出版本号就说明环境没问题。其他发行版需要手动装Debian 系用sudo apt install snapdRHEL 系用sudo dnf install snapdArch 系在 community 仓库里有。装完必须做的一步是建立/snap到/var/lib/snapd/snap的符号链接并重启 snapd 的 socket否则经典的--classic类应用会因为路径找不到而启动失败。# 各发行版通用把 /snap 指到实际存储位置 sudo ln -s /var/lib/snapd/snap /snap sudo systemctl enable --now snapd.socket # 让 /snap/bin 进入 PATH多数发行版装完自动处理 echo export PATH$PATH:/snap/bin ~/.bashrc source ~/.bashrc验证装好了没有别只看snap version还要看守护进程的状态systemctl status snapd.service snapd.socket snap debug connectivity # 检查与 snap store 的连通性 snap list # 列出现有包snap debug connectivity这条命令值得单独说。它不常被提到但在我处理snap 装不上、卡在下载 0%这类问题时它是最快的分诊工具如果这条命令直接失败说明是网络层问题跟 snap 本身无关如果它成功但安装仍然卡住问题就在本地状态或者磁盘空间上。我见过好几次是/var/lib/snapd/snaps所在分区满了清理旧修订号就好了。3.2 高频命令速查与实测记录下面这张表是我自己整理的高频命令清单按使用频率排序实测在 snapd 2.x 各版本上都通用。我把容易记混的几条放在了备注列里。命令作用实操备注snap find 关键词搜索商店里的包加--sectiondevelopment可限定分类snap info 包名查看通道、修订号、描述输出里的channels段落信息量最大snap install 包名安装需要系统级访问加--classicsnap list --all列出含禁用状态的包能看到残留的旧修订号snap remove --purge 包名卸载并清数据不加--purge会保留$HOME/snap里的数据snap revert 包名回滚到上一修订号回滚后该包会被锁定需手动放行刷新snap refresh --list查看待更新列表排查为什么版本没变的第一步snap changes查看任务历史卡在Doing状态的记录是故障线索snap services 包名查看包内服务状态后台服务类 snap 必用snap set/get 包名读写包的配置项各包的配置键在snap info里查不到得看文档snap info的输出值得单独展开。它会列出所有 track 和 risk以及每个通道当前指向的修订号、发布时间、下载大小。我判断一个包是否还活跃维护就是看stable通道的最近发布时间如果超过一年没动过我会倾向于找替代方案因为这类包往往在底层运行时更新后出问题。另外要看的是publisher字段verified标记说明发布者通过了商店的身份验证对于生产环境我基本只装 verified 的包。3.3 手动刷新、离线安装与自定义源自动刷新虽然方便但在两种场景下必须手动接管内网环境和批量部署。内网机器根本连不上商店批量部署则要求所有机器版本一致不能让某台机器半夜自己升了。这两种情况我都处理过方案不复杂。离线安装的流程是在一台能联网的机器上把包和它的元数据一起下载下来拷到目标机器上装。# 联网机器下载包本体、断言文件元数据和校验清单 snap download code # 产物code_175.snap / code_175.assert / code_175.assert.sha3-384 # 目标机器先导入断言再装本体 sudo snap ack code_175.assert sudo snap install code_175.snap这里有个容易踩的坑断言文件必须先导入。直接snap install ./code_175.snap会报assertion not found因为 snapd 不认识这个包的来源。另外snap ack导入的断言会永久留在本地信任库里如果后续要装同一个包的更新版本得再导入新的断言。我在一台完全隔离的编译机上做过这套流程二十多个包全部离线装完没出过问题。对于内网有自建镜像的场景可以改 snapd 的商店地址# 指向企业内部镜像需镜像服务端支持 snap store 协议 sudo snap set core proxy.store镜像地址 sudo systemctl restart snapd注意改商店地址前先在测试机上验证镜像的完整性和签名策略不要在生产机上直接改。我见过一次因为镜像同步滞后导致一批机器装到了旧版本的运行时。3.4 关掉自动刷新与保留历史版本关于要不要关自动刷新我的立场比较明确除非你有明确的版本控制需求否则不要全关。安全更新是靠刷新进来的关掉等于自己放弃了这条通道。真正合理的做法是缩窄刷新窗口加保留多个修订号前面 2.3 节里已经给了配置。如果你确实需要临时全停用# 临时暂停所有刷新最长可以按小时/天指定 sudo snap refresh --hold24h # 恢复刷新 sudo snap refresh --unhold # 只暂停某一个包 sudo snap refresh --holdforever code还有一个容易忽略的点卸载 snap 之后用户数据是留在$HOME/snap/name/里的。这属于设计上的保守选择避免误删数据但如果你在反复装同一个包做测试残留的配置文件会一直生效导致我明明重装了怎么配置还是老的这类问题。我在调试一个应用的配置解析逻辑时被这个坑耽误了大半天。# 完全清理包、系统数据、用户数据一起删 sudo snap remove --purge 包名 rm -rf ~/snap/包名3.5 用 snap 搭建 ROS 2 VSCode PlatformIO 的 ESP32 工作流接下来聊一个更具体的场景也是我最近半年花时间最多的方向给嵌入式团队做一套可复现的开发环境。链路是 ESP32 硬件、micro-ROS 作为通信中间件、ROS 2 作为上位机、VSCode 加 PlatformIO 作为编辑器。这套环境要在五台不同配置的机器上装出完全一致的行为snap 在其中承担了系统级工具分发的角色。分工是这样的VSCode 走 snap因为它是桌面应用且依赖一大堆图形库用 snap 装能避免污染系统库snap install code --classic一条命令搞定升级也不会去动系统里的libgtk。ROS 2 和 micro-ROS 相关的命令行工具走源码或者 apt 源因为它们需要与硬件和内核模块打交道沙箱在这里只会添乱。PlatformIO 的 CLI 我走 uv 的 pip 生态装理由是它更新极快snap 通道经常滞后两三个版本而嵌入式工具链对版本敏感。串口权限是这条链路上最容易卡住的地方。ESP32 通过 USB 转串口暴露为/dev/ttyUSB0或/dev/ttyACM0VSCode 里用 PlatformIO 插件烧写时、以及上位机用 micro-ROS 通信时都需要访问这个设备。strict 限制下的 snap 默认看不到设备节点直接烧写会报Permission denied。# 方案一把当前用户加入 dialout 组让设备节点对用户可读写 sudo usermod -aG dialout $USER # 需要重新登录生效 # 方案二临时放开 VSCode 的沙箱限制仅用于调试 sudo snap set code confinementclassic # 不推荐长期使用 # 方案三用 snapcraft 打包自己的工具时声明串口接口 # 在 snapcraft.yaml 的 apps 段落里写 plugs: [serial-port]我在实际项目里用的是方案一加方案三的组合日常开发把用户加进dialout团队自研的烧写工具则用 snapcraft 打成一个声明了serial-port的包安装后自动连接接口方便分发给不熟悉 Linux 权限体系的同事。这里必须强调一句--devmode和放宽限制只适合本地调试一旦进入交付流程就应该回到 strict 加显式声明接口的方式否则审计的时候会很难看。micro-ROS 的工作流还有一点值得记录它的固件构建产物体积和版本高度耦合同一个工程换个 ROS 2 发行版就可能编译不过。我的做法是把每个硬件项目的工具链版本写进仓库的说明文件用rosdep或者一个固定的依赖清单锁定而不是依赖某个环境恰好装对了版本。这套思路和后面要说的 uv 是同一个逻辑锁文件比口头约定可靠。3.6 Python 侧uv 与 snap 各管一段说到锁文件就不得不提我最近把 Python 环境从 venv 加 pip 换成 uv 这件事。uv 和 snap 在功能上完全不重叠但放在一起讲很有意思因为它们代表了两种不同的思路。uv 是 Rust 写的 Python 包管理和环境工具一条命令同时管 Python 解释器版本、虚拟环境和依赖解析速度比 pip 快一个数量级。它的工作方式是为项目生成pyproject.toml和uv.lock锁文件里记录每个包的精确版本和哈希。换一台机器uv sync就能还原出字节级一致的环境。# 初始化项目并添加依赖 uv init demo-api cd demo-api uv add fastapi uvicorn[standard] # 创建虚拟环境uv 会自动管理解释器版本 uv venv --python 3.12 # 运行uv 会自动同步锁文件里的依赖 uv run uvicorn main:app --reload一个最小的 FastAPI 入口长这样from fastapi import FastAPI app FastAPI() app.get(/health) def health(): return {status: ok}关键在于这套流程里的依赖全部由 uv 管snap 一点都不参与。我不建议用 snap 来分发 Python 应用的依赖因为 snap 的沙箱会让 Python 的 C 扩展和文件系统访问变得复杂而 venv 本身就是一层足够干净的隔离。两者的边界我总结成一句话snap 管系统级应用和运行时uv 管语言级依赖和环境谁也别越界。分清楚这条线之后我的开发机就再没出现过升级了系统某个库结果 Python 项目起不来这种事。4. 用 snapcraft 给自己的项目打包4.1 snapcraft.yaml 的关键字段理解了 snap 的运行方式之后自己打包其实是水到渠成的事。snapcraft 的配置文件是一份 YAML核心结构分成三块元信息、应用入口、构建部件parts。下面是我常用的一个模板注释里标了几个容易写错的点。name: my-tool base: core22 # 基础运行时决定内置哪些库 version: 1.2.0 summary: 一句话说明不超过 79 字符 description: | 多行详细描述会显示在商店页面。 grade: stable # stable 表示可发布到 stable 通道 confinement: strict # strict 最安全devmode 仅调试 apps: my-tool: command: bin/my-tool plugs: - home # 访问用户家目录 - network # 网络访问 - serial-port # 串口设备嵌入式工具常用 parts: my-tool: plugin: cmake source: . build-packages: - libssl-dev # 只在构建期需要 stage-packages: - libssl3 # 运行期需要会被打进 snapbase这个字段要重点说。它决定了 snap 内置哪个版本的运行时环境core22对应 Ubuntu 22.04 的基础库core24对应 24.04。base 一旦发布就不能改想换只能发新版本。我一开始选 base 的时候纠结了很久最后的判断标准是看目标机器的内核版本和 glibc 版本选一个比最老的机器还要老一档的 base牺牲一点新特性换取兼容性。build-packages和stage-packages的区别也是新手最容易搞混的地方。前者只在构建容器里安装不进最终产物后者会被复制进 snap成为运行环境的一部分。我打过一个依赖 openssl 的命令行工具一开始把libssl-dev写进了 stage-packages结果 snap 体积多出来 40 多兆因为开发包带了一堆头文件和静态库。改成只 stage 运行时库之后体积掉回正常水平。4.2 构建、试运行与发布本地构建最省事的路径是让 snapcraft 自己起一个隔离环境# 用 LXD 起一个干净的构建容器避免污染主机 snapcraft --use-lxd # 产物是一个 .snap 文件本地试装 sudo snap install --dangerous ./my-tool_1.2.0_amd64.snap # 试运行没问题后登录商店准备发布 snapcraft login snapcraft upload --releasestable ./my-tool_1.2.0_amd64.snap--use-lxd这个参数我强烈建议一直加上。不加的话 snapcraft 会尝试在主机上直接构建如果主机环境不干净很容易出现本地能编过、别人编不过的情况。我第一次打包时没加这个参数本地一切正常换到 CI 上构建时因为主机装了不同版本的 cmake 而失败排查了两个小时才发现问题出在构建环境而不是代码。--dangerous这个参数名听起来吓人其实是说这个包没有经过商店签名snapd 无法验证来源。本地开发阶段用它完全没问题但交付给别人的包必须走正式上传流程不要让人家去装一个--dangerous的文件。发布之后还有一步容易被忘记通道的释放策略。--releasestable会把包直接推到 stable 通道这对已经有用户的包来说是危险的。我的习惯是先推到candidate通道观察几天确认没有回归再前进snapcraft upload --releasecandidate ./my-tool_1.2.0_amd64.snap # 观察无误后把 candidate 的修订号提升到 stable snapcraft release my-tool 12 stable4.3 依赖图与构建顺序优化回到前面提到的构建期依赖图。当一个 snapcraft.yaml 里有多个 part 时snapcraft 默认会尽量并行构建但有些 part 之间必须讲先后顺序比如先用一个 part 生成代码或者下载资源另一个 part 才能编译它。这时用after和before声明。parts: fetch-assets: plugin: dump source: https://example.invalid/assets.tar.gz build-app: plugin: cmake source: . after: [fetch-assets] # 等资源就位后再编译 override-build: | cmake -S . -B build -DCMAKE_BUILD_TYPERelease cmake --build build -j$(nproc) install -Dm755 build/my-tool $SNAPCRAFT_PART_INSTALL/bin/my-tooloverride-build给了你完全的构建控制权代价是要自己处理安装路径。这里$SNAPCRAFT_PART_INSTALL是必须用对的变量它指向 stage 目录。我见过最常见的错误是把文件install到/usr/bin结果构建成功但启动时提示找不到命令因为那个路径在主机上而不是在 snap 里。想知道 snapcraft 实际算出来的构建顺序可以用snapcraft expand-extensions看展开后的完整配置它会显示 part 的最终形态和执行顺序。打包时的另一个省时技巧是把变化频率低的 part 放在前面snapcraft 会缓存已完成的 part 的构建结果改一次代码只重新构建受影响的那一个全量构建十分钟的项目能压到一分多钟。5. 常见问题与排查技巧实录5.1 启动失败与权限拒绝这是最高频的一类问题。排查顺序我固定成四步先看snap connections name确认接口连上了没有再看journalctl -u snap.name.*是否有服务启动错误然后用snap run --shell name进到沙箱里手动执行命令看真实报错最后才去查 AppArmor 的拒绝日志。snap run --shell这条命令价值很高它给你一个在目标 snap 相同限制环境下运行的 shell能把是权限问题还是程序自身 bug彻底分清楚。一个具体的例子我在调试一个自研的串口工具时命令行直接跑报open /dev/ttyUSB0: Permission denied但主机上ls -l显示设备是crw-rw---- root dialout当前用户确实在dialout组里。用snap run --shell进去一看id命令显示的用户组列表是裁剪过的dialout组不在里面——这是沙箱对用户信息的处理方式导致的。最终方案是在 snapcraft.yaml 里显式声明serial-port接口并连接问题就解决了。5.2 磁盘占用与版本回退磁盘问题基本都来自保留的旧修订号。snap list --all会把禁用的旧版本也列出来du -sh /var/lib/snapd/snaps能看到实际占用。清理方式很简单# 列出所有包的所有修订号Disabled 的那些就是可以清的 snap list --all | awk /disabled/{print $1, $3} | \ while read name rev; do sudo snap remove $name --revision$rev; done回退则用snap revert name注意回退之后这个包会被锁定在当前修订号不会再自动刷新需要手动snap refresh name解锁。我处理过一次线上事故某个包的新版本在处理大文件时内存暴涨snap revert之后瞬间恢复然后我把这个包--hold住等上游修复。整个处置过程不到三分钟这是不可变镜像加多修订号设计带来的最直接收益。5.3 刷新失败与网络受限snap changes里看到任务卡在Doing状态超过十分钟基本可以判断是网络问题。分诊顺序是snap debug connectivity判断商店可达性systemctl status snapd看守护进程有没有报错journalctl -u snapd --since 1 hour ago看具体报错信息。如果确认是网络不可达就先snap refresh --hold停掉重试避免它反复失败消耗资源等网络恢复再手动触发。还有一类比较隐蔽的问题是磁盘 inode 耗尽。snap 的挂载点和平凡文件混在一起inode 消耗比看起来快。df -i能看到 inode 使用率如果某个分区到了 90% 以上snapd 会出现各种奇怪的失败报错信息完全指不到这个方向。我在这上面栽过一次最后是靠df -i才找到根因。5.4 与 apt 混用时的坑最后说说混用。同名的应用如果既用 apt 装了又用 snap 装了PATH 里谁先出现就用谁而/snap/bin通常排在/usr/bin后面所以你会启动 apt 那个版本却发现配置文件读的是 snap 那份整个人一头雾水。我的做法是同类工具只保留一种安装方式并且定期用which -a 命令检查是否存在多份。现象最可能的原因处理方式命令行版本和snap list里的不一致apt 与 snap 双份安装which -a确认卸掉不需要的那份应用启动后配置不生效残留的$HOME/snap/name旧配置清理用户数据目录后重装升级后行为突变通道被切到了 edge 或者 track 变更snap info核对通道必要时 revert服务类 snap 起不来服务未启用或者端口被占snap services查看journalctl看日志安装进度长期卡住分区磁盘或 inode 不足df -h与df -i双查这些坑的共同点是它们都不在文档的第一页但每一个都能让你在半夜多耗掉一个小时。我把它们整理成表放在这里希望能帮你省下那几个小时。我在实际使用中最大的体会是snap 的价值不在单机使用而在环境复现。当你的项目需要在十台配置各异的机器上跑出相同行为时不可变镜像加锁定的通道这套组合比写一份安装文档要可靠得多。至于它占的那点磁盘和略慢的首次启动在我这里换来的确定性是值得的。如果你正在纠结要不要用它我的建议是先挑一两个 CLI 工具试试把snap connections和snap changes这两条命令用熟再决定要不要往更深的场景推。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价