ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

containerd Mounts 设计解析:串行化挂载抽象与“生产者-消费者“架构

containerd Mounts 设计解析:串行化挂载抽象与“生产者-消费者“架构 containerd Mounts 设计解析串行化挂载抽象与生产者-消费者架构【免费下载链接】containerdAn open and reliable container runtime项目地址: https://gitcode.com/GitHub_Trending/co/containerd导读本文围绕 containerd 历史设计文档 docs/historical/design/mounts.md 展开剖析 containerd 如何将操作系统底层的mount系统调用抽象为一种串行化的 Mount 类型并通过组件生产挂载、运行时执行挂载的架构从根本上解决传统容器系统中多组件各自执行挂载所导致的复杂生命周期管理与大挂载栈协调问题。读完本文你将掌握Mount四个字段的完整语义、挂载与卸载成组进行的实现机制以及挂载管理器Mount Manager与挂载 Handler/Transformer 的扩展模型并能直接在仓库源码中定位每一处关键实现。为什么 containerd 要统一 Mount 抽象设计文档开篇即点明动机Mount 是 containerd 中最主要的交互机制。在过去的容器系统中多个分散的组件会各自独立地执行挂载操作当需要协调大型挂载栈mount stack时往往导致生命周期管理复杂、行为易出错。containerd 的解决方案是将 mount 系统调用隔离在容器运行时组件内部其他各种组件快照器、镜像层解包、卷管理等只负责产出一份挂载的串行化表示serialized representation。这样保证挂载作为一个整体单元执行mounts are performed as a unit卸载也作为一个整体单元执行unmounted as a unit。从架构视角看这是一个典型的生产者-消费者模型——组件产生挂载produce运行时执行器消费挂载consume。该模型在源码注释中被反复强调core/mount/mount.go中Mount类型被直接称为the lingua franca of containerdcontainerd 的通用语言A mount represents a serialized mount syscall. Components either emit or consume mounts.设计文档还指出了更具想象力的用例在完全不创建运行时的情况下将来自不同组件的挂载序列虚拟化组合这有助于卫星组件satellite components的测试与实现——即挂载序列本身可以被纯逻辑地构建、传递、预览而不必真的挂到内核里。Mount 类型的结构对齐历史 mount 系统调用设计文档给出了Mount类型的核心结构它忠实跟随历史 mount 系统调用的形态字段类型描述Typestring挂载的具体类型通常是操作系统特定的如overlay、bind、tmpfs、fuse3.xxxTargetstring挂载目标mount destination期望的文件系统路径Sourcestring挂载的源对象通常是设备或另一个文件系统路径Options[]string零个或多个随挂载应用的选项可能是以分隔的键值对该结构在仓库中有两处权威实现语义完全一致1. Go 核心类型— core/mount/mount.go// Mount is the lingua franca of containerd. A mount represents a // serialized mount syscall. Components either emit or consume mounts. type Mount struct { // Type specifies the host-specific of the mount. Type string // Source specifies where to mount from. Depending on the host system, this // can be a source path or device. Source string // Target specifies an optional subdirectory as a mountpoint. It assumes that // the subdirectory exists in a parent mount. Target string // Options contains zero or more fstab-style mount options. Typically, // these are platform specific. Options []string }这里对Target的语义做了更精确的补充它是父挂载下的一个可选子目录an optional subdirectory as a mountpoint并假设该子目录存在于父挂载之中。Options的格式则是fstab 风格按传给mount(8)或传统mount(2)API 时以,连接的要求进行格式化——这意味着某些选项值可能带有针对性的引号或转义例如 SELinux 上下文中含逗号时需要加引号overlayfs 对路径使用反斜杠转义。2. Protobuf 定义— api/types/mount.protoMount消息同样包含type、source、target、options四个字段作为 containerd 各服务之间传递挂载的线上协议格式。core/mount/mount.go中的ToProto/FromProto两个函数负责在这两种表示之间转换例如从 gRPC/ttrpc 请求解码出本地Mount列表。序列化是设计的关键挂载作为值在组件间流动设计文档强调让各种组件产出一份挂载的串行化表示。这意味着在 containerd 中挂载不是某个组件内部偷偷执行的副作用而是一份可传递的数据快照器snapshotter在Prepare/Commit时返回[]mount.Mount镜像解包unpack流程拿到快照器返回的挂载用于临时查看/写入文件系统运行时runtime在创建容器时消费这些挂载在其命名空间内执行真正的mount(2)。也正是因为挂载是序列化的数据才能实现设计文档中虚拟化一系列来自各组件的挂载而无需真正创建运行时的用例——测试工具可以在不落盘、不打 mount 系统调用的情况下纯内存地模拟与校验整个挂载栈的合法性顺序、Target 嵌套关系、选项合法性等。跨进程/跨服务传递时挂载通过 gRPC/ttrpc 的 Mounts 服务进行。见 core/mount/proxy/proxy.go客户端侧proxyMounts.Activate会把本地Mount列表用mount.ToProto转成 protobuf 请求发出。而 api/services/mounts 目录下的mounts.proto定义了Activate/Deactivate/Info/Update/List等 RPC 方法。运行时执行真正的 mount 系统调用设计文档指出将 mount 系统调用隔离到容器运行时组件。在核心包 core/mount/mount_linux.go 中可以看到这一层的完整实现。Mount.mount(target)方法Linux 实现执行的实际流程包括识别 helper 二进制若Type以fuse.或fuse3.开头则调用mount.fuse/mount.fuse3辅助程序allowedHelperBinaries这正对应设计文档结尾支持mount.fuse等各种 helper 挂载的展望——当前仅对 fuse 类 helper 落地其他 helper 化仍被注释为 out of scope。解析 fstab 风格选项parseMountOptions把选项字符串映射到mount(2)的标志位flags与数据data。仓库内置了一张完整的选项→标志映射表mount_linux.go选项语义bind/rbind绑定挂载 / 递归绑定MS_BIND后者追加MS_RECro/rw只读MS_RDONLY与可写noatime/atime、nodiratime/diratime、relatime/norelatime访问时间策略相关标志nodev/dev、noexec/exec、nosuid/suid、mand/nomand设备节点、执行权限、suid、强制锁相关sync/async、dirsync、strictatime/nostrictatime同步写、目录同步、严格 atimedefaults空标志0remount重新挂载MS_REMOUNTloop触发 loop 设备设置losetup配合direct-io使用uidmap/gidmapidmapped 挂载的用户/组映射参数未被标志表识别的选项会被当作特定文件系统类型的数据值data最终以,连接后作为mount(2)的 data 参数传入。特别地以X-containerd.开头的选项是内部保留项若到达此层说明未被挂载管理器消费会直接报错。 3.overlay 特殊处理当类型为overlay时处理逻辑包括对lowerdir选项做公共目录压缩compactLowerdirOption以规避内核单页参数缓冲限制pagesize-512 字节支持 idmapped overlayprepareIDMappedOverlay创建临时 idmapped 重挂载目录以重映射 lowerdir。 4.传播类型MS_SHARED/MS_PRIVATE/MS_SLAVE/MS_UNBINDABLE这类传播标志与普通标志分开调用先挂载、再单独改传播类型。 5.只读 bind 重挂载对MS_BIND | MS_RDONLY组合通过二次MS_REMOUNT使 bind 挂载只读并保留用户命名空间下的 CL_UNPRIVILEGED 锁定标志。此外core/mount/mount.go 的ReadOnly()方法展示了一个有趣的语义推断对erofs类型按构造即只读对overlay则看是否含ro或是否缺失upperdir选项无 upperdir 即只读其他类型才直接检查ro选项。挂载类型中形如format/mkdir/overlay这种带/分隔修饰符的类型只取最后一段参与判断。成组卸载保证卸载与挂载一样作为一个单元设计文档强调挂载/卸载都要as a unit。卸载方向的核心实现在 core/mount/mount_unix.goUnmountAll(mount, flags)对同一挂载点反复卸载直到没有剩余挂载遇到EINVAL说明目标已不是挂载点即卸载完成对空路径或不存在路径为 no-op——这覆盖了rootfs 由 containerd 外部管理、客户端未指定任何根文件系统挂载的场景。UnmountRecursive(target, flags)先卸载最深层的挂载按路径长度降序排序再逐层向上用于整体清理挂载子树。unmount内置了最多 50 次、每次 50ms 的EBUSY重试缓解挂载点忙碌导致的一次性失败。而UnmountMountscore/mount/mount.go则实现按挂载数组的逆序逐个卸载的栈式语义天然适配快照层/镜像层这种后挂载的先卸载的顺序。Mount类型还通过内嵌的Mount方法支持先 join Target 与根路径再做系统调用的路径安全处理。挂载管理器激活生命周期与插件扩展设计文档的架构是组件生产、执行器消费而 containerd 通过Mount Manager把系统挂载与需插件参与的自定义挂载统一管理起来。相关接口定义在 core/mount/manager.goManager接口Activate/Deactivate/Info/Update/List五个方法管理一次命名的挂载激活activation的全生命周期。Handler接口供插件注册自定义挂载类型的执行逻辑如 fuse、tcmu 这类设备/套接字激活不替代系统挂载的 mount 调用。Transformer接口在挂载执行前对挂载做变换如format格式化、mkfs创建文件系统、mkdir建目录对应挂载类型中format/、mkfs/、mkdir/这类前缀变换。ActivateOptionsTemporary临时访问时执行全部挂载并返回单一 bind 挂载点、AllowMountTypes/AllowTransforms声明调用方自行处理某些挂载类型/变换管理器不再代为执行——例如 VM 型运行时可直接把磁盘镜像传给 guest而不必在宿主机设置 loop 设备。基于 bbolt 的默认实现mountManager位于 core/mount/manager/manager.goActivate会为激活生成唯一 ID在 bbolt 的v1/namespace/mounts/name桶中持久化激活记录含 active 挂载、system 挂载、标签、时间戳、lease 关联并处理进程崩溃后残留的过期激活记录的清理与回滚已激活的挂载在 manager 的 target 目录下按%d/%03d编号建目录目录编号与清理顺序对应大的挂载栈用更多位数的零填充编号保证字典序清理序管理器实现了metadata.Collector接口参与 GC通过StartCollection收集挂载资源节点与容器/内容/镜像/快照的反向引用back-reference标签配合 docs/garbage-collection.md 描述的 GC 流程清理无人引用的挂载。演进MountCapabilities 与 shim 的能力协商设计文档的挂载类型可进一步参数化展望在协议层面已经落地为MountCapabilitiesapi/types/mount.prototypes发送方如 shim自行执行的挂载类型如erofs、looptransforms发送方自行应用的挂载变换如format、mkfs、mkdir变换名不含/后缀因此format同时覆盖format/bind与format/mkdir/overlay。shim 通过 BootstrapResult 的扩展对外广播这些能力mount manager 据此决定哪些挂载交给 shim、哪些由自己处理——这正是运行时执行器消费挂载在现代 containerd 中的形态同一个挂载序列可以由宿主机侧管理器执行也可以下放到 shim/VM 运行时内执行。这也印证了设计文档来自各组件的挂载序列可以被灵活组合与分派的设想。小结从 docs/historical/design/mounts.md 这份设计文档出发可以看到 containerd 的 Mount 抽象历经演进始终恪守两条原则挂载是串行化的数据而非某组件内部的私密副作用——Mount结构Type/Source/Target/Options即历史 mount 系统调用的忠实映射经 protobuf 序列化后在快照器、解包器、客户端与运行时之间流动挂载与卸载都作为单元进行——挂载按序执行、卸载逆序整体回收由 mount manager 统一管理激活生命周期并允许通过 Handler/Transformer/MountCapabilities 将自定义类型或变换下放给插件与 shim。相关实现可进一步参阅core/mount、api/types/mount.proto、api/services/mounts、core/mount/manager以及与快照层交互的姊妹设计文档 docs/historical/design/snapshots.md。【免费下载链接】containerdAn open and reliable container runtime项目地址: https://gitcode.com/GitHub_Trending/co/containerd创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表