尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Kata Containers 集成 Nydus 实现镜像懒加载(Lazyload):设计与源码级实现剖析

Kata Containers 集成 Nydus 实现镜像懒加载(Lazyload):设计与源码级实现剖析 云原生容器运行时【免费下载链接】kata-containersKata Containers is an open source project and community working to build a standard implementation of lightweight Virtual Machines (VMs) that feel and perform like containers, but provide the workload isolation and security advantages of VMs. https://katacontainers.io/项目地址https://gitcode.com/gh_mirrors/ka/kata-containers点击查看免费下载Kata Containers 通过在轻量虚拟机guest中运行容器获得了虚拟机级隔离但镜像的拉取与解压过程会显著拖慢容器冷启动。本篇文章基于仓库中的 kata-nydus-design.md 设计文档系统讲解如何用 Nydus 的nydusd守护进程替换virtiofsd让 Kata Containers 以“按需加载”方式使用rafsRegistry Acceleration File System镜像从而大幅缩短容器启动时间。读完本文你将掌握 Nydus 与 Kata 集成后的完整工作流程、shim 如何通过extraoption机制把镜像元数据从nydus-snapshotter传递到 guest、以及 nydusd 在宿主机侧的启动、挂载与清理实现细节。背景容器启动的时延瓶颈与按需加载业界研究FAST16 论文表明镜像 pull 操作占容器启动时间的76%但其中只有6.4%的数据会被实际读取。这意味着绝大多数镜像数据在启动阶段被白白传输、解压、落盘却没有被使用。如果能在容器启动时只获取所需数据按需加载 / lazy load启动速度将得到质的提升。Nydusdragonflyoss/image-service 项目正是为解决该问题而生它以新格式构建镜像支持在容器启动时按需获取数据。从性能对比图可以看到随着 OCI 镜像体积增大使用 OCI 镜像的容器冷启动耗时快速上升而使用 Nydus 镜像的冷启动耗时始终保持在很低的水平。Kata Containers 的价值在于为容器提供虚拟机级隔离将 Nydus 的按需加载能力引入 Kata可以让“安全隔离”与“快速启动”兼得。本文对应的设计文档位于 docs/design/kata-nydus-design.md下文所有实现细节均可在仓库源码中找到对应证据。核心思路用 nydusd 替换 virtiofsdnydusd是 Nydus 项目提供的 FUSE / virtiofs 守护进程它原生支持两类文件系统文件系统类型常量源码用途passthrough_fsnydusPassthroughfs将宿主机目录直通共享给 guest用于目录共享rafsnydusRafsRegistry Acceleration File System按需加载的镜像文件系统这两个常量定义在 nydusd.go// Registry Acceleration File System which is nydus provide to accelerate image load nydusRafs rafs // used to shared directories between host and guest nydusPassthroughfs passthrough_fs因此在 Kata Containers 中可以用 nydusd 替代 virtiofsd它既承担 virtiofsd 的目录共享职责又能在 guest 内挂载 Nydus 镜像一举两得。传统 virtiofsd 模式的创建流程在设计文档中使用 virtiofsd 的 Kata 容器创建流程为创建 sandbox 时Kata Containers 的 containerd v2 shim 在虚拟机启动前拉起virtiofsd与 VM 共享目录。创建容器时shim 将 rootfs 挂载到宿主机的kataShared路径/run/kata-containers/shared/sandboxes/SANDBOX/mounts/CONTAINER/rootfs使其在 guest 内可见于/run/kata-containers/shared/containers/shared/CONTAINER/rootfs作为容器的 rootfs。nydusd 模式的创建流程使用 nydusd 时流程变为创建 sandbox 时shim 在 VM 启动前拉起nydusd守护进程以 virtiofs 模式运行VM 启动后kata-agent在 guest 内将virtiofs挂载到/run/kata-containers/sharedshim 通过 nydusd 的 API 将passthroughfs文件系统挂载到/run/kata-containers/shared/containers。创建普通容器时shim 通过 nydusd 的 API 将rafs挂载到 guest 的/run/kata-containers/shared/rafs/container_id/lowerdir。手工演示nydusd 的启动与挂载 API设计文档给出了完整的可复现命令。以下命令在宿主机上执行用于模拟 shim 的实际行为。启动 nydusd 守护进程# start nydusd $ sandbox_idmy-test-sandbox $ sudo /usr/local/bin/nydusd --log-level info --sock /run/vc/vm/${sandbox_id}/vhost-user-fs.sock --apisock /run/vc/vm/${sandbox_id}/api.sock关键参数说明--sockvirtiofs 的 vhost-user socket 路径供 QEMU/CLH 通过 vhost-user 协议与 nydusd 通信--apisocknydusd 的 REST API 监听 socketshim 通过 Unix socket 向其下发挂载指令--log-level日志级别info/debug源码中 debug 模式下会切换为debug级别见 nydusd.go 的args()方法——它实际上还会在参数前追加virtiofs子命令args : []string{ virtiofs, --log-level, logLevel, --apisock, nd.apiSockPath, --sock, nd.sockPath, }挂载 passthrough_fs目录共享# source: the host sharedir which will pass through to guest $ sudo curl -v --unix-socket /run/vc/vm/${sandbox_id}/api.sock \ -X POST http://localhost/api/v1/mount?mountpoint/containers -H accept: */* \ -H Content-Type: application/json \ -d { source:/path/to/sharedir, fs_type:passthrough_fs, config: }这条请求将宿主机上的共享目录/path/to/sharedir以passthrough_fs方式挂载到 guest 内的/containers路径即/run/kata-containers/shared/containers的挂载点。挂载 rafs按需加载的镜像文件系统# source: the metafile of nydus image # config: the config of this image $ sudo curl --unix-socket /run/vc/vm/${sandbox_id}/api.sock \ -X POST http://localhost/api/v1/mount?mountpoint/rafs/container_id/lowerdir -H accept: */* \ -H Content-Type: application/json \ -d { source:/path/to/bootstrap, fs_type:rafs, config:config:{\device\:{\backend\:{\type\:\localfs\,\config\:{\dir\:\blobs\}},\cache\:{\type\:\blobcache\,\config\:{\work_dir\:\cache\}}},\mode\:\direct\,\digest_validate\:true}, }rafs挂载请求中的config是一段 JSON 字符串它决定了镜像数据的后端来源与缓存策略核心字段如下配置字段含义示例值device.backend.type镜像数据后端类型localfs本地文件系统上的 blob 数据device.backend.config.dirblob 数据存放目录blobscache.type缓存类型blobcache本地 blob 缓存cache.config.work_dir缓存工作目录cachemode运行模式directdigest_validate是否校验 digesttruesource指向 Nydus 镜像的 bootstrap元数据文件guest 中的 nydusd 据此按需从后端读取 blob 数据。关键实现细节API 端点shim 实际使用的端点与文档中的 curl 完全一致定义在 nydusd.goinfoEndpoint http://unix/api/v1/daemon mountEndpoint http://unix/api/v1/mount挂载点校验shim 在发起 rafs 挂载前会校验挂载点格式必须匹配正则/rafs/[\w-\.]/lowerdir防止恶意路径见 nydusd.go 的checkRafsMountPointValid。HTTP 传输层NydusClient通过DialContext连接unixsocket 与 nydusd 通信并设置了超时连接 5s、keep-alive 5s、整体 30s见 nydusd.go。网络命名空间nydusd 需要在shim 的网络命名空间中启动因为它要访问宿主机网络见 nydusd_linux.go 的startInShimNS。guest 内 rootfs 的 Overlay 组装shim 除了挂载 rafs还会把nydus-snapshotter分配的snapshotdirbind mount到共享目录sharedir上。于是 guest 中容器的 rootfs 由 overlay 文件系统组装container rootfs overlay(lowerdirrafs, upperdirsnapshotdir/fs, workdirsnapshotdir/work)lowerdirrafs只读的 Nydus 镜像层按需加载仅加载被读取的数据upperdirsnapshotdir/fs可写层存放容器运行时的写入workdirsnapshotdir/workoverlay 内部工作目录。这样容器读文件时命中 Nydus 镜像按需从宿主机拉取数据写文件时落在宿主机共享目录既实现了懒加载又保持了可写语义。关键机制rafs 元数据如何从 snapshotter 传递到 shim一个核心问题是shim 如何得知容器对应哪份 Nydus 镜像bootstrap、config、snapshotdir默认行为snapshotter 返回 overlay Mount 切片创建 OCI 镜像容器时nydus-snapshotter默认向 containerd 返回如下Mount切片containerd 据此挂载 rootfs[ { Type: overlay, Source: overlay, Options: [lowerdir/var/lib/containerd/io.containerd.snapshotter.v1.nydus/snapshots/snapshot_A/mnt,upperdir/var/lib/containerd/io.containerd.snapshotter.v1.nydus/snapshots/snapshot_B/fs,workdir/var/lib/containerd/io.containerd.snapshotter.v1.nydus/snapshots/snapshot_B/work], } ]为什么不能直接附加 rafs 信息直观想法是在Options中追加 rafs 信息。但 containerd 无法识别这些额外字段直接这样做会导致 containerd 挂载失败。解决方案nydus-overlayfs 挂载助手参照 containerd 的 mount helper 机制项目提供了名为nydus-overlayfs的二进制程序。改造后的Mount切片变为[ { Type: fuse.nydus-overlayfs, Source: overlay, Options: [lowerdir/var/lib/containerd/io.containerd.snapshotter.v1.nydus/snapshots/snapshot_A/mnt,upperdir/var/lib/containerd/io.containerd.snapshotter.v1.nydus/snapshots/snapshot_B/fs,workdir/var/lib/containerd/io.containerd.snapshotter.v1.nydus/snapshots/snapshot_B/work,extraoptionbase64({source:xxx,config:xxx,snapshotdir:xxx})], } ]当 containerd 发现挂载类型为fuse.nydus-overlayfs时挂载链为containerd 调用mount.fuse命令mount.fuse内部调用nydus-overlayfsnydus-overlayfs忽略extraoption只执行常规 overlay 挂载保证宿主机侧文件系统可用。而 Kata Containers 的 containerd v2 shim 会解析extraoption取出其中的source、config、snapshotdir进而在 guest 内完成 rafs 挂载与 overlay 组装。源码中的 extraoption 解析shim 侧的解析逻辑在 nydusd.goextraoption的值是{source, config, snapshotdir}三元组的base64 编码 JSON解析时先按extraoption前缀取出再 base64 解码、JSON 反序列化并校验三个字段均非空type extraOption struct { Source string json:source Config string json:config Snapshotdir string json:snapshotdir } const extraOptionKey extraoption func parseExtraOption(options []string) (*extraOption, error) { // 1. 从 options 中取出 extraoption 前缀的项 // 2. base64.StdEncoding.DecodeString(extraOpt) // 3. json.Unmarshal(opt, no) // 4. 校验 no.Config / no.Snapshotdir / no.Source 均非空 }对挂载类型的识别还做了一定的弹性处理fuse.nydus-overlayfs是默认挂载类型但带有nydus-overlayfs前缀的二进制如nydus-overlayfs-abcde以及形如fuse./usr/local/bin/nydus-overlayfs的绝对路径形式也会被识别见 kata_agent.go。shim 中 nydusd 的生命周期管理在设计文档描述的基础上仓库源码完整实现了 nydusd 的生命周期管理nydusd.go 的Start方法参数校验依次校验sockPath、apiSockPath、path、sourcePath对应的目录是否存在见valid()nydusd.go组装参数并启动exec.Command(nd.path, args...)启动 nydusd并把 stdout/stderr 接入 shim 日志监控退出后台 goroutine 逐行读取 nydusd 输出一旦 nydusd 退出会调用onQuit回调用于停止 sandbox 的兜底逻辑等待 API 就绪通过waitUntilNydusAPIServerReady轮询GET /api/v1/daemon直到state RUNNING最多尝试 20 次、间隔 20ms见 nydusd.go设置共享目录调用setupPassthroughFS挂载passthrough_fs到 guest 的/containers。容器创建时的挂载与清理创建容器时shim 的Mount方法构造MountRequest{nydusRafs, opt.source, opt.config}并 POST 到 nydusd将 rafs 挂到/rafs/container_id/lowerdirnydusd.go容器销毁时nydusContainerCleanup依次执行卸载 guest 内的 rafs 挂载Umount(rafsMountPath(c.id))、卸载 snapshotdir 的 bind mount、移除共享目录中的容器 rootfs 目录见 mount_linux.go整个 sandbox 销毁时bindUnmountAllRootfs会区分 Nydus rootfs 与普通 rootfs 走不同清理路径mount_linux.go。宿主机侧不做重复挂载在 shim 的checkAndMount中如果 rootfs 类型被识别为 Nydus 类型则跳过宿主机侧的 rootfs 挂载// if kata nydus, do not mount避免无意义的重复操作见 create.go。测试用例nydusd 的启动逻辑在 nydusd_test.go 中有单元测试覆盖TestNydusdStart通过注入startFn/waitFn/setupShareDirFn三个 mock 函数验证“空配置报错”“sourcePath 不存在报错”“合法配置成功”三条路径以及TestParseExtraOption对extraoptionbase64 解码与字段校验的覆盖。运行时配置与限制要让 Kata 运行时启用 Nydus 支持需在运行时配置中设置共享文件系统类型为virtio-fs-nydus。以 QEMU 配置 configuration-qemu.toml.in 为例# Shared file system type: # - virtio-fs (default) # - virtio-9p # - virtio-fs-nydus # - none shared_fs DEFSHAREDFS_QEMU_VIRTIOFS此外需要将virtio_fs_daemon指向nydusd可执行文件路径同一配置文件的virtio_fs_daemon项。当前实现限制务必注意从源码可见nydusd 目前仅支持 QEMU 与 Cloud Hypervisor 两类 hypervisor。getVirtiofsDaemonForNydus中非 QEMU/CLH 的 hypervisor 会直接返回errNydusdNotSupport错误mount_linux.go错误信息原文为errNydusdNotSupport errors.New(nydusd only supports the QEMU/CLH hypervisor currently (see https://github.com/kata-containers/kata-containers/issues/3654))另外Kata 也支持 rootless 场景当以 rootless 模式运行时guest 内的共享目录与 Nydus 镜像目录会挂载在 rootless 目录之下见 kata_agent.go 中kataGuestNydusRootDir/kataGuestNydusImageDir对rootless.IsRootless()的分支处理。总结懒加载如何让 Kata 容器“快”起来将 Nydus 集成进 Kata Containers最终形成如下闭环nydus-snapshotter为 containerd 提供 Nydus 镜像快照并通过fuse.nydus-overlayfs类型的 Mount 切片把镜像元数据source/config/snapshotdir以extraoption形式传给 Kata shimshim 在 VM 启动前拉起nydusdvirtiofs 模式通过 API 先挂载passthrough_fs建立目录共享通道创建容器时shim 解析extraoption让 nydusd 在 guest 内按需挂载rafs并与 snapshotdir 组成 overlay rootfs容器启动只拉取实际访问的数据块剩余镜像数据留在宿主机后端冷启动耗时不再随镜像体积线性增长。设计文档中的手工 curl 命令、overlay 组装方案与extraoption传递机制全部能在仓库源码nydusd.go、nydusd_linux.go、mount_linux.go、kata_agent.go中找到对应实现读者可以顺着这些路径深入阅读并结合 nydusd_test.go 的测试用例验证各环节的行为。赞分享云原生容器运行时【免费下载链接】kata-containersKata Containers is an open source project and community working to build a standard implementation of lightweight Virtual Machines (VMs) that feel and perform like containers, but provide the workload isolation and security advantages of VMs. https://katacontainers.io/项目地址https://gitcode.com/gh_mirrors/ka/kata-containers点击查看免费下载相关推荐Kata Containers 使用 virtio-fs-nydus 加速容器镜像加载独立模式与内置模式全解析Kata Containers 使用 virtio fs nydus 加速容器镜像加载独立模式与内置模式全解析 本文介绍 Kata Containers 如何云原生容器运行时nerdctl 使用 Nydus Snapshotter 实现镜像懒加载从运行到镜像转换的完整实战指南nerdctl 使用 Nydus Snapshotter 实现镜像懒加载从运行到镜像转换的完整实战指南 导读 Nydus 是 dragonflyoss/imaCLI云原生vanilla-lazyload 与 SolidJS 集成响应式框架中的懒加载实现vanilla lazyload 与 SolidJS 集成响应式框架中的懒加载实现 在现代 Web 开发中性能优化是提升用户体验的关键环节。随着页面内容日益前端上一篇如何用kohya_ss打造专属AI画师5分钟上手Stable Diffusion模型定制下一篇FlashGBX复古游戏卡带数据读写工具的技术哲学与实践应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表