Snap简介
他山之石
在2011年,Cananical创建Ubuntu Touch项目,要把Ubuntu操作系统扩展
到手机和平板的时候,试图解决如下问题:
应用商店连接应用用户和应用开发者,
应用有限使用操作系统资源,应用之间隔离,
应用安全安装卸载升级回退。
这几个问题用已有的Advanced Package Tool方案(Apt)不能很好解决。比如
应用用户和应用开发者之间,隔着一个发行版开发商。
发行版
Linux最初并无发行版,Linux用户从开发者那里得到源码,自己编译,自己安装,
用户和开发者的界限模糊。后来发行版进来,承担了编译打包的工作,用户只需
安装就好了。
如今Linux有300+发行版。比如深度发行版,这是它的谱系:
debian └── knoppix └── morphix └── hiwix hiweed └── deepin
略去深度血缘更近的不说,deepin
祖上是debian
。
欧拉发行版的谱系:
redhat └── redhat enterprise └── centos └── euleros
这些大小不一的发行版,犹如丛林一般,让人迷茫。应用从开发出来到用户使用,
就不得不穿越这丛林。
这事实上也造成了Linux应用生态的分裂。
应用打包格式
Linux应用打包有两大派系,rpm和deb。
围绕rpm包格式,有yum包管理,dnf包管理。deb包格式有apt包管理,aptitude
包管理。
发行版决定使用使用什么包格式,什么包管理。
包管理
包有版本,包和包之间有版本依赖,版本排斥,包安装可能失败,包卸载可能有
残留,包升级可能有种种不测。这像极了一片沼泽地,稍不留意就会陷进去。
举个例子。Python为规避发行版丛林,提出了自己的包管理pip,不想陷入了包
版本沼泽地。为走出沼泽地,提出virtualenv思想,把所有依赖的pip包打到一
处。一些开发者就用virtualenv方案打出python大包,做成rpm或deb包,重归丛
林。
灵魂宝石
于是Ubuntu Touch团队专门创建了Click项目来管理手机应用。
2017年,Ubuntu Touch不敌苹果和安卓的竞争,宣告失败。但Click项目存活
下来,改名Snappy,转身管理更广泛意义上的应用。也许Snappy这个名字听
起来太卡哇伊了,Snappy进一步改名Snap。
要知道灭霸打一个响指,可是灭掉了半个宇宙的人口。
还不是因为灭霸集齐了宇宙原石?
钢铁侠拼命打一个响指,灭掉的人们才能回来。
可娜塔莎永远也回不来了。
因为灵魂宝石。
Snap得到灵魂宝石:
+-----------+ +------------+ +------------+ | Developer +------>| Snapcraft +----->| Snap Store | +-----------+ +------------+ +-----+------+ | update v +-----------+ +------------+ +------------+ | End User +------>| Snap +----->| Snapd | +-----------+ +-----+------+ +-----+------+ | containerize | v | +------------+ | | Snaps |<-----------+ manage +------------+ manage: snap install lxd snap list lxd snap refresh lxd snap revert lxd snap remove lxd containerize: lxd --> /usr/bin/snap /usr/bin/snap --> lxd snap
Ubuntu Touch Click永远也回不来了。
Snap Store
Snap Store,应用商店,允许应用开发者把自己开发的应用发布给用户。
去发行版
注意这和传统的应用仓库如APT或YUM有重要区别。传统的应用仓库,应用开发者
开发应用,发行版维护者打包上传应用到应用仓库之后,用户才能从应用仓库获
取应用。而Snap Sotre去掉了发行版维护者这个角色,直接让应用开发者发布应
用到应用商店供用户使用。
持续集成
那谁来打包呢?Snap Store引入自动持续集成,应用开发者完成Snap应用开发,
后面测试、打包、病毒扫描、发布都是自动的。Snap应用测试通过后,完成打包,
通过病毒扫描,就进入了发布流程。
发布管道
发布按如下管道依次进行:
edge,
beta,
candidate,
stable
一般用户可以订阅Snap应用的稳定版。喜欢尝鲜的用户可以订阅Snap应用的beta
版。edge
和beta
可供开发人员自己测试使用。
以下是snapd
应用的发布管道:
# snap info snapd type: snapd snap-id: PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4 tracking: latest/stable refresh-date: 2 days ago, at 15:03 CST channels: latest/stable: 2.46.1 2020-09-16 (9287) 27MB - latest/candidate: 2.46.1 2020-09-08 (9287) 27MB - latest/beta: 2.47~pre1+git733.g799ae88 2020-09-21 (9499) 28MB - latest/edge: 2.47~pre1+git802.g8d37a7a 2020-09-22 (9517) 28MB - installed: 2.46.1 (9287) 27MB snapd
可以看到edge 2.47~pre1+git802.g8d37a7a
发布于2020-09-22,最新稳定版2.46.1
发布于2020-09-16。实际上早在2020-09-08你就可以从candidate
得
到版本2.46.1
进行尝试。
一次打包
开发者把应用打包为Snap应用有巨大的好处:他不需要为每个Linux发行版打包
应用了。Snap应用可以运行在几乎所有的Linux发行版。Snap应用做到了与发行
版无关。
可信开发者
Snap应用的开发者和Snap应用的发布者现在合二为一了。那Snap应用的用户可以
完全相信Snap应用的开发者吗?2018年5月,用户发现有两个Snap应用(同一个
作者)包含可后台挖矿的逻辑。[Canonical]从Snap Store删除了这两个应用,
并把这两个应用的所有权转给了可信第三方。这两个应用去掉挖矿逻辑后得以重
新发布。之后[Canonical]建议用户只信任可信开发者(发布者)。
分布式发布
由于Snap应用由开发者自己维护,应用的质量和及时更新并不能得到保证。以微
软的Skype为例,在Snap Store有超过一年的时间没有更新。在被人点名后一年
后,微软抓紧更新了几版。可见分布式发布的管理要比集中式发布管理更难。
Snap Store当前只有一个,由Cananical所有。Snap Store应用商店只是
门面,背后有一个完善的持续集成,发布系统。Snap Store的唯一性,也使
Snap应用的集中化管理成为可能。但要注意,Snap应用的发布,是分布式的。
Snap可以脱离Snap Store使用。比如可以从Snap Store下载[LXD]:
# snap download lxd Fetching snap "lxd" Fetching assertions for "lxd" Install the snap with: snap ack lxd_17329.assert snap install lxd_17329.snap
得到lxd_17329.assert
和lxd_17329.snap
后,就可以脱离Snap Store使用
了。
闭源
Snap Store的是闭源的。这一点饱受批评。Snap Store的闭源与
Cananical的开源做派格格不入。我想一个原因,是Snap Store开源不易,
撇开Snap Store的API不说(其实Snap Store的API已经事实上开源),
Snap Store背后有庞大的持续集成和发布系统,代码庞杂,很难开源。
另外Cananical也不太想让Snap Store开源,想对Snap应用施加更多管控。
Bret Barker曾经发布过一个Noise Snap Store,在README里他写,为避免
混淆删除了Noise Snap Store的代码。
代码其实没删:
# git clone https://github.com/noise/snapstore # cd snapstore # git checkout HEAD^^^ # ls #=> LICENSE README.md files requirements.txt snapcraft.yaml store-wrapper.sh store.py
往上跳3个Commit,基本功能是可以工作的。
通用包格式
和传统Linux包不同,Snap应用运行不依赖具体Linux发行版。同一个Snap应用可
以运行在Ubuntu 18.04,可以运行在CentOS 7.9, 可以运行在Fedora,可以运行
在欧拉2.09,可以运行在UOS 20.0,甚至可以运行在Windows WSL2。
SquashFS压缩
Snap包文件是压缩SuqashFS系统格式。以上节下载的lxd_17329.snap
为例,可
以这样解包:
# unsquashfs lxd_17329.snap #=> Parallel unsquashfs: Using 8 processors 1418 inodes (4425 blocks) to write [================================================\] 4425/4425 100% created 1272 files created 122 directories created 146 symlinks created 0 devices created 0 fifos
文件解在当前squashfs-root
目录:
# tree squashfs-root #=> squashfs-root ├── bin ├── etc ├── lib ├── lxc ├── lxcfs ├── meta │ ├── hooks │ │ ├── configure │ │ ├── install │ │ └── remove │ └── snap.yaml ├── share ├── snap ├── usr ├── wrappers ├── zfs-0.6 ├── zfs-0.7 └── zfs-0.8
基础镜像
其中squashfs-root/meta/snap.yaml
是此snap的描述文件:
name: lxd version: '4.6' summary: LXD - the container lightervisor description: |- LXD is a container manager for system containers... architectures: - arm64 assumes: - command-chain - snapd2.39 base: core18 confinement: strict grade: stable
这行base: core18
,表明snap lxd基于snap core18
。那core18
是啥呢?
可以download下来看看:
# snap download core18 # unsquashfs core18_1888.snap #=> Parallel unsquashfs: Using 8 processors 9741 inodes (10104 blocks) to write [===========================================\] 10104/10104 100% created 7517 files created 1238 directories created 2011 symlinks created 4 devices created 0 fifos # cat squashfs-root/etc/os-release #=> NAME="Ubuntu Core" VERSION="18" ID=ubuntu-core PRETTY_NAME="Ubuntu Core 18" VERSION_ID="18"
可以看到core18
其实就是Ubuntu Core 18。除了core18
,还有core
和core20
。其中core
是Ubuntu Core 16, core20
是Ubuntu Core 20。因此
你知道Snap应用的跨Linux发行版是如何实现的了吧?Snap应用运行在以Ubuntu
为核心的容器里,与各个发行版做到了解耦。因此Snap应用的开发者很开心了,
只需保证自己应用在[Ubuntu Core]欢快运行即可,比如考虑其他发行版的适配。
Snap应用的用户也开心,装Snap应用的时候不必考虑自己的Linux发行版是否支
持。Ubuntu发行版也很开心,自己用Snap应用统一了世界。而Linux其他发行版
就很悲催了。Snap应用统一Linux应用的时候,除Ubuntu之外的发行版存在还有
什么意义?
Snap打包
回到Snap包格式。Snap包格式是SquashFS的xz压缩格式,可以这样打包:
# mksquashfs squashfs-root core.snap -comp xz #=> Parallel mksquashfs: Using 8 processors Creating 4.0 filesystem on core.snap, block size 131072. [===============================================/] 8089/8089 100% Exportable Squashfs 4.0 filesystem, xz compressed, data block size 131072 Filesystem size 44670.64 Kbytes (43.62 Mbytes) 28.27% of uncompressed filesystem size (158015.07 Kbytes) Inode table size 90980 bytes (88.85 Kbytes) 24.35% of uncompressed inode table size (373578 bytes) Directory table size 98378 bytes (96.07 Kbytes) 41.36% of uncompressed directory table size (237842 bytes) Number of duplicate files found 166 Number of inodes 10770 Number of files 7517 Number of fragments 631 Number of symbolic links 2011 Number of device nodes 4 Number of fifo nodes 0 Number of socket nodes 0 Number of directories 1238 Number of ids (unique uids + gids) 6 Number of uids 1 root (0) Number of gids 6 root (0) shadow (42) crontab (105) tty (5) systemd-network (103) mail (8)
广泛应用支持
和其他被架空发行版推出的应对包管理(如Flatpak)不同,Snap应用支持桌面应
用、服务端应用、IoT应用甚至诸如打印机驱动等的系统应用。代价是Snap只能
运行在以Systemd为初始化系统的发行版。还好Systemd已成为初始化系统的事实
标准。比如Snap在Windows WSL上运行不好,就是与WSL对Systemd的支持不好所
致。Snap在Windows WSL2上运行是可以的。
Snap运行时
Snap应用运行在以Ubuntu Core为基础的可配置容器里。容器执行安全策略,用
户可以通过容器这一层给应用权限,访问特定资源。
下面以lxd
为例分析snap如何实现。
我们知道lxd
基于core18
,lxd
和core18
的文件系统挂载如下:
# snap list #=> Name Version Rev Tracking Publisher Notes core 16-2.46.1 9994 latest/stable canonical✓ core core18 20200724 1888 latest/stable canonical✓ base lxd 4.6 17329 latest/stable canonical✓ - # mount | grep .snap #=> /var/lib/snapd/snaps/core18_1888.snap on /snap/core18/1888 type squashfs (ro,nodev,relatime,x-gdu.hide) /var/lib/snapd/snaps/lxd_17329.snap on /snap/lxd/17329 type squashfs (ro,nodev,relatime,x-gdu.hide)
lxd
对外提供两个命令lxd和lxc:
# ls -l /snap/bin/{lxd,lxc,lxd.lxc} #=> lrwxrwxrwx 1 root root 13 9月 22 02:09 lxd -> /usr/bin/snap lrwxrwxrwx 1 root root 7 9月 22 02:09 lxc -> lxd.lxc lrwxrwxrwx 1 root root 13 9月 22 02:09 lxd.lxc -> /usr/bin/snap
其中lxd
与包同名,可以直接指向/usr/bin/snap
,lxc
先指向lxd.lxc
,
然后lxd.lxc
再指向/usr/bin/snap
。
当我们运行lxd
命令的时候,/usr/bin/snap
被调用。/usr/bin/snap
会先
看看自己叫什么名字,名字里有没有点。以lxd
为例,里面没点,因此/usr/bin/snap
知道要去调用snaplxd
里的lxd
命令。以lxc
为例,lxd.lxc
里有点,因此/usr/bin/snap
知道调用snap lxd
里的lxc
命令。
无论那种情况,/usr/bin/snap
都知道了snap包名。之后它就可以去读snap包
里的meta/snap.yaml
文件了。meta/snap.yaml
里有对这个snap的描述,包括
基于core
¸ core18
还是core20
。于是/usr/bin/snap
可以确定snap的基
础镜像。基础镜像确定后,/usr/bin/snap
准备容器的文件系统,然后继续根
据meta/snap.yaml
描述,应用资源使用策略,这时候AppArmor
会派上用场。
一切准备完毕,最终在容器里调用用户所要运行的命令。
Snap运行时对容器技术使用,局限在文件系统命名空间,资源隔离,并不涉及网
络命名空间,用户命名空间等。因此习惯上被叫做Snap沙箱。
Snap沙箱依赖[AppArmor]安全模块。而一些发行版诸如Fedora以SELinux为默认
安全模块。因此Snap对SELinux也有支持。
应用更新
Snap应用更新有两个特色:
自动化,
可回退。
Snapd的坚守
Snap应用由snapd
守护进程管理。snapd
每天会去Snap Store查本地Snap应
用有没有可用更新,如果有,就把更新拿下来,应用到当前Snap应用上。
自动更新不可关闭
这看起来是个好用的功能,但常被系统管理员诟病。应用是否升级,何时升级应
该是系统管理员的事情,怎么snapd
就私自定了?因此系统管理员们对snapd
大吐口水,要求禁止自动更新这个功能,但snapd
不为所动,固执地认
为自己可做。长期的吐槽最终得到了两个改进:
自动更新不可关闭,但用户可以推迟更新,最长可推迟到60天后,
自动更新不可关闭,但开发者可以不更新一个发布管道,把更新发布到新的
发布管道。
以lxd
为例,对自动更新的第2点做个说明:
# snap info lxd #=> ... channels: latest/stable: 4.6 2020-09-21 (17329) 63MB - latest/candidate: 4.6 2020-09-22 (17382) 63MB - latest/beta: ↑ latest/edge: git-f1a7f8a 2020-09-23 (17390) 63MB - 4.6/stable: 4.6 2020-09-21 (17329) 63MB - 4.6/candidate: 4.6 2020-09-22 (17382) 63MB - 4.6/beta: ↑ 4.6/edge: ↑ 4.5/stable: 4.5 2020-09-18 (17303) 63MB - 4.5/candidate: 4.5 2020-09-17 (17283) 63MB - 4.5/beta: ↑ 4.5/edge: ↑ 4.4/stable: 4.4 2020-08-19 (16946) 66MB - 4.4/candidate: 4.4 2020-08-19 (16946) 66MB - ...
比如用户订阅4.5/stable
发布管道,开发者把最新的更新发布到4.6/stable
(其实也就是latest/stable
管道),那4.5/stable
就基本停
止更新了。当然开发者也可以把一个安全补丁回溯到4.5/stable
,这时候订阅4.5/stable
的用户又被迫更新了。
总之snap
认为自动更新非有不可,是原则问题,绝不改变。
可回退
下面说说更新可回退。snap
应用的安装非常简单,其实就是把squashfs文件挂
载一下,卸载是就是文件系统去载。以lxd
为例:
snap list lxd #=> Name Version Rev Tracking Publisher Notes lxd 4.6 17329 latest/stable canonical✓ - # ls -l /var/snap/lxd/ 总用量 12 drwxr-xr-x 2 root root 4096 8月 29 18:30 17303 drwxr-xr-x 2 root root 4096 8月 29 18:30 17329 drwxr-xr-x 7 root root 4096 9月 22 02:09 common lrwxrwxrwx 1 root root 5 9月 22 02:09 current -> 17329
可看到current
指向17329
。现在回退:
# snap revert lxd lxd reverted to 4.5 # ls -l /var/snap/lxd drwxr-xr-x 2 root root 4096 8月 29 18:30 17303 drwxr-xr-x 2 root root 4096 8月 29 18:30 17329 drwxr-xr-x 7 root root 4096 9月 23 18:25 common lrwxrwxrwx 1 root root 5 9月 23 18:25 current -> 17303
可看到current
指向了17303
。snapd
所做的就是改下指向,重启一下而已。
再更新回来:
# snap refresh lxd #=> lxd 4.6 from Canonical✓ refreshed # ls -l /var/snap/lxd 总用量 16 drwxr-xr-x 2 root root 4096 8月 29 18:30 17303 drwxr-xr-x 2 root root 4096 8月 29 18:30 17329 drwxr-xr-x 2 root root 4096 8月 29 18:30 17329.old drwxr-xr-x 7 root root 4096 9月 23 18:29 common lrwxrwxrwx 1 root root 5 9月 23 18:29 current -> 17329
变更与任务
这也是snap
坚持自动更新的原因,因为更新过程简单,几乎不会出错。我们看
看每一步都做了什么:
# snap changes #=> ID Status Spawn Ready Summary 19 Done 18:25 CST 18:25 CST Revert "lxd" snap 20 Done 18:29 CST 18:29 CST Refresh "lxd" snap # snap tasks 19 Status Spawn Ready Summary Done 18:25 CST 18:25 CST Ensure prerequisites for "lxd" are available Done 18:25 CST 18:25 CST Prepare snap "" (17303) Done 18:25 CST 18:25 CST Stop snap "lxd" services Done 18:25 CST 18:25 CST Remove aliases for snap "lxd" Done 18:25 CST 18:25 CST Make current revision for snap "lxd" unavailable Done 18:25 CST 18:25 CST Setup snap "lxd" (17303) security profiles Done 18:25 CST 18:25 CST Make snap "lxd" (17303) available to the system Done 18:25 CST 18:25 CST Automatically connect eligible plugs and slots of snap "lxd" Done 18:25 CST 18:25 CST Set automatic aliases for snap "lxd" Done 18:25 CST 18:25 CST Setup snap "lxd" aliases Done 18:25 CST 18:25 CST Start snap "lxd" (17303) services Done 18:25 CST 18:25 CST Run configure hook of "lxd" snap if present Done 18:25 CST 18:25 CST Run health check of "lxd" snap # snap tasks 20 Status Spawn Ready Summary Done 18:29 CST 18:29 CST Ensure prerequisites for "lxd" are available Done 18:29 CST 18:29 CST Prepare snap "" (17329) Done 18:29 CST 18:29 CST Run pre-refresh hook of "lxd" snap if present Done 18:29 CST 18:29 CST Stop snap "lxd" services Done 18:29 CST 18:29 CST Remove aliases for snap "lxd" Done 18:29 CST 18:29 CST Make current revision for snap "lxd" unavailable Done 18:29 CST 18:29 CST Copy snap "lxd" data Done 18:29 CST 18:29 CST Setup snap "lxd" (17329) security profiles Done 18:29 CST 18:29 CST Make snap "lxd" (17329) available to the system Done 18:29 CST 18:29 CST Automatically connect eligible plugs and slots of snap "lxd" Done 18:29 CST 18:29 CST Set automatic aliases for snap "lxd" Done 18:29 CST 18:29 CST Setup snap "lxd" aliases Done 18:29 CST 18:29 CST Run post-refresh hook of "lxd" snap if present Done 18:29 CST 18:29 CST Start snap "lxd" (17329) services Done 18:29 CST 18:29 CST Clean up "lxd" (17329) install Done 18:29 CST 18:29 CST Run configure hook of "lxd" snap if present Done 18:29 CST 18:29 CST Run health check of "lxd" snap Done 18:29 CST 18:29 CST Consider re-refresh of "lxd"
注意每次更新都被定义为一个变更(change),一个变更由多个任务(task)组
成。变更有原子性,是说如果一个变更里的某个任务失败,那整个变更会回退。
这是理想的情况。理想很丰满,现实很骨感,世界上还没有系统可以真正做到。
Snap开发
Snap的开发有一套工具,Snapcraft。Snapcraft支持大量构建工具以及编程语言,比如Go, Java, JavaScript,
Python, C/C++和Rust等。它也支持从多种源导入源码,比如git等。
Multipass
这套工具在各个Linux发行版,macOS,甚至Windows都可以正常工作。这很神奇
不是?背后的实现在Ubuntu的一个神奇的工具:Multipass。Multipass是一
个虚拟化工具,可以高效运行在Linux,MacOS以及Windows上。Snapcraft实际运
行在Multipass虚拟出来的Ubuntu虚机上。
开发
在Linux上,Snapcraft也可运行在LXD系统容器上。
Snapcraft在Snap Store里也有:
# snap install snapcraft --classic
之后可以按Snapcraft文档开发。
发布
把带上上传到Github,要在
snapcraft.io注册开发者账号,添加应用到Snap
Store。
这样发布流程就打通了。
- 点赞
- 收藏
- 关注作者
评论(0)