卷与挂载

定义

挂载(mount)是把一棵外部目录树接到工作负载 rootfs 的某个路径上。卷(volume)是由产品运行时托管的持久存储对象;使用时同样表现为一次挂载。容器可写层在实例删除后默认丢弃,因此需要跨实例保留的数据必须通过挂载进入,而不是写在 / 的可写层里。

问题背景

镜像应当保持可替换:升级应用等于换一个镜像,而不是在旧容器的可写层里打补丁。若数据库文件、用户上传或构建缓存也写在可写层,则换镜像等于丢数据。把「代码与根文件系统」和「持久数据」分开,是容器工作流能够滚动更新的前提。

机制说明

三种最常见的形式如下。

形式 数据位于何处 生命周期 典型用途
bind mount 宿主上已存在的目录或文件 由宿主路径的所有者决定 开发时把源码挂进容器、注入配置文件
命名卷 运行时管理的目录(通常在数据根下) 显式 volume rm 或 prune 才删除 数据库、需要备份的状态
tmpfs 内存 随容器停止而消失 短寿命密钥、不希望落盘的临时文件

bind mount

-v /home/user/app:/src 把宿主路径直接映射到容器内 /src。优点是立即可见、便于编辑;缺点是把宿主文件系统的布局与权限暴露给工作负载。路径写错或把 / 挂进去,等于取消文件系统隔离。Box 的威胁模型因此强调:通过 bind mount 故意暴露的数据,共享内核 Sandbox 无法再保护。

命名卷

a3s-box volume create data 之后,-v data:/data 把名为 data 的卷挂到 /data。调用方不必知道宿主上的真实路径。删除 box 默认不删除卷,因此同一卷可以挂到后继实例。备份与迁移应以卷为单元,而不是以容器可写层为单元。

tmpfs

tmpfs 挂载把文件放在内存中。适合存放启动期才注入、退出后必须消失的材料。Box 在 Linux 上对 Compose 的 secret_environment 使用私有 tmpfs:把进程环境中的密钥物化到 <A3S_HOME>/runtime-secrets,只读挂载,并随 box 移除。Box 不会把该支撑降级到磁盘;宿主无法提供符合要求的 tmpfs 时,启动在资源变更前失败。

权限与所有权

挂载成功不等于进程能读写。容器内用户 ID 若与宿主文件所有者不一致,会出现「目录看见了但打不开」。User namespace 会进一步改变 ID 映射。排查时应同时检查:挂载是否存在、进程 UID、文件 mode,以及是否被标为只读。

在 Box 中的位置

  • 卷子系统位于 src/runtime/src/volume/,核心类型见 src/core/src/volume.rs
  • CLI 为 a3s-box volumerun / compose 通过 -v 或 ACL / YAML 中的卷字段引用。
  • Sandbox 路径上,Box 在 bundle 中声明挂载,由 OCI Runtime 完成实际 attach。MicroVM 路径上,客户机 init(src/guest/init/)挂载 virtio-fs 与 tmpfs。
  • 镜像声明的匿名卷计划在能力预检后写入初始预留,并由确切执行在 bundle 准备期间原子认领,避免靠扫描宿主目录恢复所有权。

验证命令

a3s-box volume create data
a3s-box run -d --name data-app -v data:/data alpine:3.20 -- sleep 3600
a3s-box exec data-app -- sh -lc 'echo hello > /data/note.txt'
a3s-box stop data-app
a3s-box rm data-app
a3s-box run --rm -v data:/data alpine:3.20 -- cat /data/note.txt

最后一条应输出 hello。这说明数据属于卷,不属于已删除的 box。

相关与易混

  • 相关:rootfs快照、commit、diff 与 exportCompose
  • 易混:-v 的左端是宿主路径时为 bind mount,是卷名时为命名卷。二者写法相近,安全含义不同。commit 把可写层固化进新镜像,不能代替卷来保存数据库。