白皮书入口 (opens new window) | 下载链接 (opens new window)

# OTA 的目标

OTA 的场景,和边缘计算有一定重合。Ubuntu 的这篇文章讲的就是 IoT 设备的 OTA。现实生活中的已经比较常见的 OTA 例子有:Windows 更新、手机系统 OTA、汽车智能系统 OTA。这些场景的共同特点是:

  • 设备网络不稳定;
  • 设备电源不稳定:又不是在机房里能保证 24 小时持续供电,OTA 更新到一半没电了是必须考虑到的场景;
  • 工程师几乎无法远程调试和运维:机房里机器更新挂了还可以 SSH 连上去运维,无网环境根本没有办法运维;
  • 设备基数大。而且设备基数越大,做好 OTA 能带来的收益就越可观。反过来说,前期设备基数小的时候,其实不一定需要投入特别多精力来实现一个特别完备的 OTA

实现一个 OTA 需要做到以下几点:

  • 更新是自动的
  • 提供恢复机制:如果更新失败,能够自动恢复;就算系统启动不了,也要能够恢复
  • 更新有优先级:高优先级的更新(如关键更新)能够在其它更新之前完成更新
  • 提供冗余机制:如果更新失败,能把宕机时间降到最低

# Snap

Snap 是 Ubuntu 主推的包安装器。其特点有:

  • 无依赖。Linux 现在主流的包管理器(apt、yum、pacman 等)安装包的时候会有很多依赖。共用依赖的好处是节省网络流量和存储成本。虽然大部分时候依赖不会出问题,但是依赖一出问题就是大问题。显然 OTA 失败后不可能让工程师连到设备上一个一个解依赖问题,零依赖相当于是用空间换取系统的稳定性。
  • 容器化,保证系统和应用、应用和应用之间、应用和其数据的隔离性。但如果应用申请权限,又可以做到精细粒度的授权和控制。这一步是在 snapd 使用 cgroups、Sccomp、AppArmor 等技术实现的。
  • 安装过程是事务的。如果没成功,系统会自动回滚,除了错误日志以外,不留下任何错误的痕迹。

Ubuntu Core 架构图

Ubuntu Core 将大部分系统组件都拆成了 Snap,包括内核和 Ubuntu Core 本身。也就是说,如果我想要更新内核或者 Ubuntu Core 系统,我只需要更新其 Snap 包就可以了,这个更新过程能享受到 Snap 里的所有优点。不过有一个问题我不太理解,整个系统是否存在不是 Snap 的部分?如果有,这部分怎么更新呢?

Ubuntu Core 的系统底层只有内核、init 进程、必要的工具和 snapd 进程(Snap 的守护进程)。

在没有显式授权的情况下,Snap 包不能修改其它用户、新增用户、访问硬件、更改安全策略、修改系统、修改内核运行变量,以及别的能够让系统挂掉的操作。

除了预装的 Snap 包以外,所有包都是从具有签名的可信源获取。

Ubuntu 提供了一个 Snapcraft build tool,用于将其它 app 修改为 Snap 包。大部分 app 只需要添加一个配置文件声明其需要的东西,就可以了。

# 更新策略

Snap app 的升级是事务的。为了做到这一点,Snap 在更新 app 时,会复制 app 需要的用户数据,以供回滚。这点利用了 Snap app 的设计:app 被限制为只能访问某些目录的数据。另外,我理解升级只在 app 不工作时进行,否则数据可能会有不一致性。

对于内核级 snap app,除了会复制用户数据以外,还会在系统启动失败后自动回滚。

Transactional updates in Ubuntu Core

默认情况下,Ubuntu Core 会保留两个旧版本的 app(以及其数据)。


如果 app 出现了大版本升级(大幅度的配置变化和数据格式变化等),Ubuntu Core 引入了 Epochs 的概念。

  • 每个大版本对应一个 Epoch。
  • 厂商需要提供 Epoch 升级的方法(如更新数据格式)。
  • 厂商可以控制每个 app 上 Epoch 升级的时机。
  • 跨 Epoch 的 app 升级同样是事务的、可以回滚的。

Epochs in Ubuntu Core

# 恢复系统

Recovery 部分,Ubuntu 有 Recovery system,可以在启动时进入,或者通过 API 远程启动。Recovery system 中,可以创建 snapshot 和回滚 snapshot。snapshot 的内容是设置和 apps 的集合。(看来 Ubuntu Core 需要足够简单,才能在有限的设置和 apps 上跑通)

# Snap Delta 增量更新

OTA 的这篇文章里放了 Snap Delta 增量更新的链接,不过文章内容也更多是讲 Snap Delta 的操作无感,以及用实例来表明 Snap Delta 能解决 30%-40% 的流量,并没有对技术细节进行展开。Snap Delta 文章入口在 Optimising IoT bandwidth with delta updates | Ubuntu (opens new window),也有下载直链 (opens new window)

# 增量更新生成流程

和 Android 厂商需要跑工具生成 OTA 包 (opens new window) 不同的是,Snap 开发者只需要把包提交到 Snap Store 后,Snap Store 会负责生成增量更新包,开发者不需要改变现有工作流就能支持增量 OTA。这也合理,毕竟 Android 厂商是自己负责分发 OTA,而 Snap 全量包/增量包的生成和分发工作交给 Snap Store 就好,开发者不用操心。

Snap 也支持上传增量包,用以节省上传流量。Snap 把生成增量包和上传的步骤封装到了 Snapcraft 的 push 里,所以对于开发者也是无感的。

# Snap Delta 性能

在下面的例子中,Snap Delta 的压缩率在 1~56% 不等。文档里表明是在 30% 左右,也有 Delta 包甚至大于全量包的,这种情况下 Snap Store 会直接分发全量包。

Snap name Full download size Delta download size Delta size Time to apply delta
(Broadcom BCM2837 Quad Core ARM Cortex-A53 USB storage)
Time to apply delta
(Intel Core i7-4500U CPU @ 1.80GHz SSD storage)
alsa-utils 6.5MiB 62.8KiB 0.94% .45s 0.045s
nextcloud 197MiB 112MiB 56.85% 24.0s 1.16s
core 87.9MiB 27.55MiB 31.34% 10.75s 0.42s
pc-kernel 154.77MiB 54.20MiB 35.01% 18.9s .788s

文章后面举了很多嵌入式和 PC 的实例,数据表明整体更新的压缩率在 30-40% 左右。

# 总结

Canonical 先是推出了 Snap,对 app 的权限和文件目录等加以限制。然后基于 Snap app 的这些限制,实现了软件包安装的事务性。管理软件包版本、升级和回滚、保证系统稳定从而变得容易。

跑个题,这个思想其实和 bazel 很相似。

  • Snap 限制了 app 的权限和能读写的数据目录等。在这些限制下,实现一个稳定的系统,变得容易得多;
  • bazel 限制了编译指令的输入和输出等。在这些限制下,实现一个幂等的、可并行的、缓存命中率高的编译系统,也会容易很多。

在做一个管理资源的框架时,资源的权限并不是越多越好。限制被管理的资源的权限后,性能和稳定性优化的空间就更大,所以在设计之初就需要对二者做权衡。