Snap简介

举报
nanjunjie 发表于 2020/09/24 14:14:14 2020/09/24
【摘要】 他山之石,可以攻玉

他山之石

在2011年,Cananical创建Ubuntu Touch项目,要把Ubuntu操作系统扩展
到手机和平板的时候,试图解决如下问题:

  1. 应用商店连接应用用户和应用开发者,

  2. 应用有限使用操作系统资源,应用之间隔离,

  3. 应用安全安装卸载升级回退。

这几个问题用已有的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应用测试通过后,完成打包,
通过病毒扫描,就进入了发布流程。

发布管道

发布按如下管道依次进行:

  1. edge,

  2. beta,

  3. candidate,

  4. stable

一般用户可以订阅Snap应用的稳定版。喜欢尝鲜的用户可以订阅Snap应用的beta
版。edgebeta可供开发人员自己测试使用。

以下是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.assertlxd_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基于core18lxdcore18的文件系统挂载如下:

# 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/snaplxc先指向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应用更新有两个特色:

  1. 自动化,

  2. 可回退。

Snapd的坚守

Snap应用由snapd守护进程管理。snapd每天会去Snap Store查本地Snap应
用有没有可用更新,如果有,就把更新拿下来,应用到当前Snap应用上。

自动更新不可关闭

这看起来是个好用的功能,但常被系统管理员诟病。应用是否升级,何时升级应
该是系统管理员的事情,怎么snapd就私自定了?因此系统管理员们对
snapd大吐口水,要求禁止自动更新这个功能,但snapd不为所动,固执地认
为自己可做。长期的吐槽最终得到了两个改进:

  1. 自动更新不可关闭,但用户可以推迟更新,最长可推迟到60天后,

  2. 自动更新不可关闭,但开发者可以不更新一个发布管道,把更新发布到新的
    发布管道。

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指向了17303snapd所做的就是改下指向,重启一下而已。
再更新回来:

# 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的开发有一套工具,SnapcraftSnapcraft支持大量构建工具以及编程语言,比如Go, Java, JavaScript,
Python, C/C++和Rust等。它也支持从多种源导入源码,比如git等。

Multipass

这套工具在各个Linux发行版,macOS,甚至Windows都可以正常工作。这很神奇
不是?背后的实现在Ubuntu的一个神奇的工具:MultipassMultipass是一
个虚拟化工具,可以高效运行在Linux,MacOS以及Windows上。Snapcraft实际运
行在Multipass虚拟出来的Ubuntu虚机上。

开发

在Linux上,Snapcraft也可运行在LXD系统容器上。

SnapcraftSnap Store里也有:

# snap install snapcraft --classic

之后可以按Snapcraft文档开发。

发布

把带上上传到Github,要在
snapcraft.io注册开发者账号,添加应用到Snap
Store

这样发布流程就打通了。


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。