镜像与层
定义
镜像(image)是一份只读的打包结果。它至少包含两类信息:一组按顺序叠加的文件系统层(layer),以及一份配置(入口命令、环境变量、工作目录、默认用户等)。层以内容摘要寻址,因此同一层可以在多个镜像之间复用。容器启动时,运行时把这些层展开或叠加为 rootfs,再在其上执行入口进程。
alpine:3.20、nginx:alpine
是镜像引用(reference),不是镜像本身。引用指向仓库中的一份清单;清单再指向各层的摘要。真正不可变的身份是摘要(通常为
SHA-256),而不是标签(tag)。
问题背景
若每次部署都从源码现场编译并安装依赖,则两台机器上的「同一应用」很难保证一致。把已经安装完成的文件系统拍成快照,就可以把「环境」变成可分发、可校验的对象。
完整拍成一张大包也可以工作,但有两个缺点:更新一个配置文件也要重传整个根文件系统;不同应用若基于同一发行版,会重复存储相同的基础文件。分层把根文件系统拆成增量:基础系统一层,应用依赖一层,应用本身一层。拉取时只需传输本地缺失的层;存储时相同层只保留一份。
机制说明
标签、清单与摘要
一次 pull alpine:3.20 大致经历下列步骤:
- 将引用解析为仓库地址、仓库名与标签(或摘要);
- 向 registry 请求该标签对应的清单(manifest);
- 清单列出配置对象与各层的摘要、媒体类型与大小;
- 按摘要下载本地缓存中不存在的层;
- 校验每个对象的摘要,拒绝内容被替换的层。
标签是可变指针:今天的 app:latest 与明天的
app:latest 可以指向不同摘要。生产与取证应优先记录摘要。Box
的镜像子系统强调内容寻址缓存与签名策略,原因正在于此。
层如何叠加
层通常是一个 tar 档案,记录相对某一父层的文件增删改。叠加规则包括:
- 后一层覆盖前一层的同名文件;
- 白出版本(whiteout)表示删除前一层中的文件;
- 硬链接、符号链接、权限、所有者与扩展属性需要按目标文件系统可表示的方式保留。
运行时把只读层叠加后,通常再加一个可写的上层,供容器运行期产生的文件使用。容器删除后,这个可写上层默认丢弃;需要保留的数据应放到卷,而不是写在容器可写层里。
镜像与容器的关系
镜像是类,容器是实例。同一镜像可以同时启动多个容器,它们共享只读层,各自拥有独立的可写层、网络命名空间与进程树。修改一个正在运行的容器,不会改写原镜像;若要把修改固化下来,需要 commit 生成新镜像。
在 Box 中的位置
- 镜像子系统位于
src/runtime/src/oci/:pull.rs、layers.rs、image.rs、registry/、signing/、store/、build/。 - CLI 对应命令包括
a3s-box pull、images、rmi、tag、image-inspect、history、save、load、import。 - Box 在启动工作负载前完成层校验与 rootfs 准备;A3S OCI Runtime 消费已经准备好的 bundle,不负责从仓库拉取镜像。
验证命令
a3s-box pull alpine:3.20 |
image-inspect
输出中的摘要应视为该镜像的不可变身份;仅记录 alpine:3.20
不足以在事后证明运行的是哪一份内容。
相关与易混
- 相关:镜像仓库 Registry、Dockerfile 与构建、OCI 标准与 bundle、rootfs
- 易混:镜像不是正在运行的程序。镜像是只读输入;运行中的状态属于容器或 box。标签也不是身份,摘要才是。