对 Ubuntu 的 Snap 打包格式最常见的误解之一是它是专有的 —— 但是深入研究其文档后,会发现这个说法并不对。
在上周末拉脱维亚的里加举行的 Ubuntu 峰会上,笔者有幸采访到 Ubuntu 的开发者大使developer advocate,Igor Ljubuncic。期间,他们详细探讨了关于 Snap 的各种误区,包括它被视为完全闭源的、受 Canonical 控制、必须使用 Canonical 的 Snap 商店等众多谬论。
如果说有什么比糟糕的软件更加厌恶的,那一定是谎言。正如我们在 点评 Fedora 39 时所注意到的,即使在 Linux 诞生之前,各种软件的拥趸们就经常爆发各种 圣战。但我们至少希望能坚守事实的公道。毫无根据的恶意指责是没有必要的:生活本身已经足够糟糕。
笔者的立场很明确,我们并不特别偏爱任何 Linux 发行版或其打包工具。像许多资深电脑技术人员一样,在长期和各种软件打交道后,笔者已经对所有的软件厌烦至极。一句广为接受的说法就是:没有一个软件不让人头疼。
Linux 就是一个软件,因而它难免让人头疼。承此,所有的 Linux
发行版也都不尽如人意。包管理器也是一个软件,同样也不尽人意。但幸运的是,至少大多数 Linux
发行版都有一个包管理器。这比没有软件包管理器要好,或者更糟糕的是,有不止一个以上的包管理器,这一点 XKCD 927 漫画体现的淋漓尽致。
我们并不特别青睐 Snap,也不特别反对 Flatpak。笔者个人更偏好 AppImage 格式,它不需要其他额外的框架。但虽然有个 AppImageHub,但该格式却并没有提供软件更新的工具,这个问题就留给了应用本身来解决。
鉴于所有的软件都不完美,那唯一重要的区别就在于其问题严重的程度。一段时间以后,你最关注的就是它是否可运行,能否满足你的需要,以及它的可靠性。
我在早年的职业生涯中花了很多时间在技术支持上,修复其他人的软件。因此,我学到了一个经验,那就是降低软件让人厌烦程度的一个重要因素就是它工作的方式是否容易理解。
Btrfs 是复杂的,而修复它则更是如此。Git 属于本质复杂,其 名称 就体现出这一点。(没错,“git” 是一个名词,而非缩写或代号,有实际的意思 —— “饭桶”。)OStree 可以说是针对二进制文件的 Git,这使得它比普通 Git 至少复杂两倍。而 Flatpak 则是 OStree 的封装。
这意味着增加了两层额外的复杂度:首先,对复杂事物的封装只能隐藏其复杂性,而不能消除其复杂性。其次,你不能使用 Flatpak 构建一个操作系统,因此你还需要 OStree。
因此,我们将来逐一揭穿关于 Snap 格式和工具的一些误解。这不是一篇入门指南,而是对那些不那么显而易见,并且对 Snap 有所误解的人的一份快速概览。
无需商店进行分发
Snap 包其实就是一个 Squashfs,类似于大多数 Linux 安装介质上的系统镜像。Snap 包以两个文件传递:其中一个是命名为 _.snap
,该文件包含了软件本身;另一个则是一个伴随的 声明文件,它为 Snap 提供了数字签名。然后,Canonical 还进一步 详细阐明 了版本修订的工作原则。
使用 snap download
的指令(而非 snap install
)可以容易获取这些基本文件:
# snap download firefox
Fetching snap "firefox"
Fetching assertions for "firefox"
Install the snap with:
snap ack firefox_3252.assert
snap install firefox_3252.snap
然后,这些文件便可以被复制到另一台设备上进行安装,这种操作不需要访问 Snap 商店,仅需使用输出中的指令即可。
如 Igor 所说:
“这样,从 Snap 商店中,你可以选择你想要的 Snap 包(如
Firefox),将其放入你的内部仓库中,或是 FTP,或是 NFS 上。接着你可以使用它作为在内部安装 Snap
的来源,而这不需要去访问商店。此外,你还可以将这个操作与你所使用的任何调度或部署机制结合起来,就如配置管理那样。”
安装无需声明文件的 Snap 包
通常来说,snap ack
命令会首先读取并验证签名,但是你可以选择跳过这个步骤。
snap install "downloaded snap" --dangerous
上述指令会安装该 Snap 包,并不会验证其签名。请注意,这样做虽然操作简单,但也有一个重要的限制:使用 --dangerous
选项安装的 Snap 包不会自动从商店中更新。
所以,实际上,你可以在你的网络内部分发 Snap 包,避免它们试图连接到 Snap 商店,并自主管理更新。
管控 snapd 内置的更新机制
另一方面,你可以在不忽略验证机制的前提下,管理和控制操作系统何时以及如何更新 Snap 包。Igor 则曾撰写过关于如何使 Snap 更新暂停 的文章。
你可以设置暂停 Snap 的更新一段时间,或永久暂停,甚至只选择暂停特定的 Snap 包,同时也能简单取消此设置。例如:
snap refresh --hold
Auto-refresh of all snaps held indefinitely.
另外,你也可以通过以下方式设置防火墙拦截 Snap API:
sudo iptables -A OUTPUT -d api.snapcraft.io -j DROP
在无 snapd 环境下运行 snaps
.snap
文件实际上就是一个压缩的文件系统,它包含着程序文件(以及各种库等),这些都被存放在一个传统的目录结构中,而该目录结构对于打包在 Snap 应用程序内的应用来说,就是它的根目录。Snapd 负责为此设置挂载名空间,并通过 Apparmor 和 seccomp 实现安全隔离。
你可以将其内容解压并直接运行:
unsquashfs firefox_3252.snap
Parallel unsquashfs: Using 20 processors
565 inodes (5428 blocks) to write
[=====================/] 5428/5428 100%
created 399 files
created 149 directories
created 166 symlinks
created 0 devices
created 0 fifos
created 0 sockets
ll squashfs-root/
total 80
drwxr-xr-x 7 igor igor 4096 lis 10 02:33 ./
drwxr-xr-x 10 igor igor 4096 lis 19 15:32 ../
drwxr-xr-x 5 igor igor 4096 lis 10 02:33 data-dir/
-rw-r--r-- 1 igor igor 32441 lis 10 02:33 default256.png
-rw-r--r-- 1 igor igor 9146 lis 10 02:33 firefox.desktop
-rwxr-xr-x 1 igor igor 2680 lis 10 02:33 firefox.launcher*
drwxr-xr-x 2 igor igor 4096 lis 10 02:33 gnome-platform/
drwxr-xr-x 4 igor igor 4096 lis 10 02:33 meta/
-rwxr-xr-x 1 igor igor 3716 lis 10 02:33 patch-default-profile.py*
drwxr-xr-x 4 igor igor 4096 lis 10 02:33 snap/
drwxr-xr-x 4 igor igor 4096 sij 19 2022 usr/
如果你查看 Snap 内 Firefox 二进制文件的动态依赖,你会注意到它希望从根文件系统中获取文件:
ldd usr/lib/firefox/firefox-bin
linux-vdso.so.1 (0x00007fff33cc5000)
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f6cf2c00000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f6cf2e40000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f6cf2be0000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6cf2800000)
/lib64/ld-linux-x86-64.so.2 (0x00007f6cf300e000)
在 Snap 内部,这个“根”就是你的基础系统(比如 core18 或 core20
等)。但是一旦你解压了这个 Snap,没有 snapd 在安装和运行 Snap 时提供的安全隔离,Firefox
将会尝试直接访问你的根目录的库。这可能会导致执行时的不一致性。
举例来说,你的 Snap 内可能包含的是 GNOME 3.38 版的库,但是你的主机上运行的可能是 GNOME 3.32。如果你尝试解压并运行这个应用,它可能会试图从主机中加载库,这可能引起不一致 —— 更甚者,可能会让程序崩溃。
为了避免这种情况发生,你需要做的唯一事情就是设置 LD_LIBRARY_PATH
环境变量,以让程序知道其库在何处,确保它首选这些库,而不是使用可能导致其运行失败的操作系统中的库副本。
LD_LIBRARY_PATH: ${SNAP_LIBRARY_PATH}${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}:$SNAP/usr/lib:$SNAP/usr/lib/x86_64-linux-gnu
通常,你会希望 LD_LIBRARY_PATH
开始于 /snap//
,然后是 /lib
、/usr/lib
和其他常用路径。至于其他内容,firefox.launcher
文件负责准备运行环境,剩余的,比如 firefox.desktop
,都用于桌面集成:如图标、全名、文件关联等。这些内容虽然使应用看起来效果更好,但它们并非严格的必需品。
其实,你甚至不需要解压 Snap 的内容,你可以直接将 Snap 文件本身作为一个 回环设备 挂载 —— 你甚至可以设置为只读 —— 但没有挂载命名空间隔离。并且,如果没有设置环境让 Snap 内部的应用在寻找它的库时首先从 Snap 内部开始,你仍然需要正确地设置库路径。
代理和缓存 Snap 包
正如 Igor 所说,如果客户并不打算自行运营一家具备完整品牌属性的 Snap 商店,他们可以选择手动设置一个 Snap 代理。对此,Canonical 也提供了相应的 文档,并描述了所需的 网络访问 权限。
同时,你也可以 配置 一个缓存 Snap 代理 —— 这项任务稍微简单一些,对于希望降低下载带宽的家庭网络来说,可能是个不错的选择。
搭建自己的 Snap 商店
就如我们之前所述,你完全可以忽略所有来自 Canonical 的基础设施,直接运行自己的 Snap 商店。去年,我们写过一篇关于 Ubuntu Unity 维护者 Rudra Saraswat 的文章,他就 做到了这一点,这只是他的众多项目中之一。据悉,好几个在生产环境中使用 Ubuntu Core 的组织都采取了此种做法,而所有所需的工具都存放在 Ubuntu 仓库中。
Canonical 在这方面发布了大量的文档,包括怎样构建你的 第一个 Snap 包,以及如何用 不同的编程语言 构建。今年的峰会上有多场关于如何构建 Snap 的演讲 - 包括 在平板电脑上构建 Snap 包,以及如何 自动化构建更新的 Snap 包,虽然这对笔者来说有点过于复杂。
学习一些新的术语是有必要的,同时也有 官方文档 提供帮助。这段解释我们特别喜欢:
- 插槽slots
- 插口plugs
- 接口interfaces
从我们与 Canonical 代表的对话中,他们似乎对 Snap 商店被误解,以及 Snap 被视为封闭、专有系统的争论显得尤为不满。
大约十五年前,有人曾声称 Canonical 的代码托管和项目管理平台 Launchpad 是专有的,所以 Canonical 在整理代码后在 2009 年 公开发布 了代码库。但如我们交谈的人所言:“没人在意。” 它是 Canonical 的内部工具,对其他人来说并没有太大的用处。他们表示,他们不希望再经历一次这样的情况。
我们还注意到,红帽正在朝反方向前进,即从开源的 Bugzilla 迁移 到封闭的、基于云的 Jira —— 这并未引起太大的争议。
snapd 自身的代码已经托管在 GitHub 上,作为 Canonical 的 snapcore 仓库的一部分。这个被大多数发行版使用的打包格式是一个已经存在、有文档记录的格式。用于进行隔离的工具,是已经存在并在其他发行版中使用的第三方工具,比如,Debian 和 SUSE 家族也使用了 AppArmor,这与 Arch 维基中的 描述 相符,而它的主要竞品,SELinux,则更复杂,主要在红帽及其衍生产品中使用。
尽管 Canonical 自家定制的 Snap 商店 的后端仍然 闭源,但 Snap 格式、snapcore 软件、snapcraft.io 前端,以及更多组件都是开放的。我们再次强调,你完全可以自行搭建 自己的 Snap 商店。
请不要受到愤怒的论坛喷子们的误导。
最后再说一点...
实际上,撰写这篇文章的作者曾经就职于红帽和 SUSE,但他主要还是使用 Ubuntu,从
2004 年 Ubuntu 刚刚发布起就开始一直使用。Ubuntu
不但运行顺畅,使用起来也十分便捷。然而,早在多年前他就已经从他的主要工作电脑上删除了 snapd 和相关的一切工具,取而代之的是 deb-get —— 最初这是 Ubuntu MATE 的创造者 Martin Wimpress 编写的。为了更加迅速,他还选择使用 Nala 包管理器 而不是 Apt。
如果可以的话,笔者很希望可以放弃各种形式的 Unix,除了服务器,其他情况下更倾向于使用 RISC OS 或是经典的 MacOS。但是遗憾的是,这两个操作系统在网络浏览器、网络连接,还有多核支持和整体稳定性上有待改进。
笔者今年参加 Ubuntu 峰会的费用是由 Canonical 承担的,这一点他愿意公开。类似的,Linux 基金会曾资助他参加 今年 在 Bilbao 的开源峰会,而红帽则资助了他在 2016 年在 Kraków 参加 Flock to Fedora 峰会。这类赞助可以让我们将广告预算分配到其他地方,但并不会对我们的报道产生影响:我们总会积极追踪那些 IT 新闻。