镜像与层

定义

镜像(image)是一份只读的打包结果。它至少包含两类信息:一组按顺序叠加的文件系统(layer),以及一份配置(入口命令、环境变量、工作目录、默认用户等)。层以内容摘要寻址,因此同一层可以在多个镜像之间复用。容器启动时,运行时把这些层展开或叠加为 rootfs,再在其上执行入口进程。

alpine:3.20nginx:alpine 是镜像引用(reference),不是镜像本身。引用指向仓库中的一份清单;清单再指向各层的摘要。真正不可变的身份是摘要(通常为 SHA-256),而不是标签(tag)。

问题背景

若每次部署都从源码现场编译并安装依赖,则两台机器上的「同一应用」很难保证一致。把已经安装完成的文件系统拍成快照,就可以把「环境」变成可分发、可校验的对象。

完整拍成一张大包也可以工作,但有两个缺点:更新一个配置文件也要重传整个根文件系统;不同应用若基于同一发行版,会重复存储相同的基础文件。分层把根文件系统拆成增量:基础系统一层,应用依赖一层,应用本身一层。拉取时只需传输本地缺失的层;存储时相同层只保留一份。

机制说明

标签、清单与摘要

一次 pull alpine:3.20 大致经历下列步骤:

  1. 将引用解析为仓库地址、仓库名与标签(或摘要);
  2. registry 请求该标签对应的清单(manifest);
  3. 清单列出配置对象与各层的摘要、媒体类型与大小;
  4. 按摘要下载本地缓存中不存在的层;
  5. 校验每个对象的摘要,拒绝内容被替换的层。

标签是可变指针:今天的 app:latest 与明天的 app:latest 可以指向不同摘要。生产与取证应优先记录摘要。Box 的镜像子系统强调内容寻址缓存与签名策略,原因正在于此。

层如何叠加

层通常是一个 tar 档案,记录相对某一父层的文件增删改。叠加规则包括:

  • 后一层覆盖前一层的同名文件;
  • 白出版本(whiteout)表示删除前一层中的文件;
  • 硬链接、符号链接、权限、所有者与扩展属性需要按目标文件系统可表示的方式保留。

运行时把只读层叠加后,通常再加一个可写的上层,供容器运行期产生的文件使用。容器删除后,这个可写上层默认丢弃;需要保留的数据应放到,而不是写在容器可写层里。

镜像与容器的关系

镜像是类,容器是实例。同一镜像可以同时启动多个容器,它们共享只读层,各自拥有独立的可写层、网络命名空间与进程树。修改一个正在运行的容器,不会改写原镜像;若要把修改固化下来,需要 commit 生成新镜像。

在 Box 中的位置

  • 镜像子系统位于 src/runtime/src/oci/pull.rslayers.rsimage.rsregistry/signing/store/build/
  • CLI 对应命令包括 a3s-box pullimagesrmitagimage-inspecthistorysaveloadimport
  • Box 在启动工作负载前完成层校验与 rootfs 准备;A3S OCI Runtime 消费已经准备好的 bundle,不负责从仓库拉取镜像。

验证命令

a3s-box pull alpine:3.20
a3s-box images
a3s-box image-inspect alpine:3.20

image-inspect 输出中的摘要应视为该镜像的不可变身份;仅记录 alpine:3.20 不足以在事后证明运行的是哪一份内容。

相关与易混