云原生容器运行时【免费下载链接】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 仓库中的设计文档 vcpu-handling-runtime-rs.md系统讲解 Rust 版运行时runtime-rs如何为虚拟机VM计算、调整 vCPU 数量从CreateVM、CreateContainer、UpdateContainer到DeleteContainer的完整生命周期以及配置项、Annotation、OCI spec 三类数据源如何共同决定最终 vCPU 数量。阅读本文后你将掌握Boot Size与Real-time Size两套计算模型、cpu quota/cpuset的优先级规则、StaticSandboxResourceMgmt静态资源管理模式并能结合源码理解其底层实现与测试验证方法。背景为什么 runtime-rs 需要一套新的 vCPU 处理方式Kata Containers 在 3.0 版本引入了全新的 Rust 运行时runtime-rs。在设计 vCPU 处理时上层运行时生态出现了一个重要变化Kubernetes1.23 起与Containerd1.6.0-beta4 起会帮助计算 Pod/Sandbox 的 Sandbox Size沙箱资源大小信息并通过 Annotation 传递给 Kata Containers。为了适配这一变化、同时保持与过去版本的兼容性runtime-rs实现了一套与原有 Go 运行时runtime-go略有不同的 vCPU 处理方式。这套设计的目标是vCPU 的数量应当由容器工作负载workload的需求来决定而不是简单地使用配置文件中写死的默认值。文档中以两个术语区分两种场景下的 vCPU 数量Boot Size启动 VM 时的 vCPU 数量。Real-time Size收到CreateContainer/UpdateContainer/DeleteContainer请求时实时调整后的 vCPU 数量。何时需要处理 vCPU 大小四个关键时间点vCPU 的数量在整个沙箱生命周期内不是一成不变的主要有四个需要决策的时间点时间点场景动作CreateVM创建沙箱sandbox时需要决定启动 VM 时配多少个 vCPUCreateContainer在 VM 中创建新容器时根据容器 spec 中的资源需求可能需要对 vCPU 进行热插拔hot-plugUpdateContainer收到UpdateContainer请求时根据容器的新资源需求更新 vCPU 数量DeleteContainer容器从 VM 中移除时可能需要热拔hot-unplugvCPU回收该容器引入的 vCPU 资源从实现角度看CreateContainer时对 vCPU 的调整对应源码中的ResourceUpdateOp::AddUpdateContainer对应ResourceUpdateOp::UpdateDeleteContainer对应ResourceUpdateOp::Del三者都会汇聚到资源管理器ResourceManager的update_linux_resource()入口见 manager_inner.rs。vCPU 数量计算依据三类数据源与优先级Kata 计算 vCPU 数量时共有三个数据来源1. TomlConfig配置文件来自configuration-*.toml.in等运行时配置文件具体由kata_types::config::TomlConfig承载default_vcpus启动 VM 时默认的 vCPU 数量。default_maxvcpusVM 允许的最大 vCPU 数量超出该值的热插拔请求会被截断到该上限。2. Annotation上层运行时注解上层运行时如 Kubernetes Containerd/CRI-O通过 OCI Annotation 传入io.kubernetes.cri.sandbox-cpu-quota与io.kubernetes.cri.sandbox-cpu-period以及内存相关的 sandbox mem 键。文档将这一数据源传来的资源大小统称为InitialSizeKubernetes 会根据 Pod 的声明计算沙箱大小这个大小就是这里的InitialSize它是优先采用的数值。3. Container Spec容器 OCI spec容器在 spec 中声明其希望使用的 CPU 资源量。计算 vCPU 数量时主要考虑cpu quota和cpuset两类声明cpu quota最常用的 CPU 资源声明方式。由cpu quota引入的 vCPU 数量计算公式为vCPUs ceiling( quota / period )cpuset常用于绑定任务可运行的 CPU 集合。由cpuset引入的 vCPU 数量为集合中指定且与其他容器不重叠的 CPU 个数。优先级理解三者的优先级关系为Annotation 的InitialSize优先于 TomlConfig 的default_vcpus在容器层面cpu quota优先于cpuset。后续的所有计算都建立在这套优先级之上。Boot Size启动时如何确定 vCPU 数量Boot Size主要考虑InitialSize与default_vcpus两个因素遵循以下原则InitialSize优先于default_vcpus。当存在相关 Annotation 声明时原本的default_vcpus会被修改为InitialSize中的 vCPU 数量作为Boot Size。由于并非所有运行时目前都支持这类 AnnotationTomlConfig中的默认值仍然保留作为兜底。当所有容器的 spec 被聚合起来计算沙箱大小时聚合方式与这里InitialSize的计算方法保持一致。源码印证InitialSizeManagerBoot Size的落地实现是InitialSizeManager位于 initial_size.rs。核心逻辑如下数据来源归一化InitialSize实现了两个TryFrom转换一个从 AnnotationHashMapString, String构造一个从 OCI specoci::Spec构造。从 spec 构造时如果是 PodSandbox 类型通过container_type(spec)判断则递归读取 spec 的 annotations否则单容器场景如docker run直接启动从 spec 的linux.resources.cpu中解析 quota/period 并计算 vCPU从linux.resources.memory.limit解析内存。注解取值get_sizing_info()依次读取 sandbox 的 CPU period、CPU quota、内存值。特别地CRI-O 会把同样的尺寸信息写在一个 JSON Annotation 中io.kubernetes.cri-o.LinuxResources当三个键全为 0 时会回退读取该 JSON 获取cpu_period、cpu_quota、memory_limit_in_bytes。写入配置setup_config()在静态资源管理模式开启时将计算出的 vCPU 与内存写入 hypervisor 配置hv.cpu_info.default_vcpus (hv.cpu_info.overhead_vcpus self.resource.vcpu).max(1.0); hv.cpu_info.default_maxvcpus hv.cpu_info.default_vcpus.ceil() as u32;即Boot Size overhead_vcpus InitialSize.vcpu向上取整、最小为 1并同步刷新default_maxvcpus。内存对齐convert_memory_to_mb()将字节换算为 MiB并强制 2 MiB 对齐mem_size.is_multiple_of(2)不满足时mem_size 1以支持大页hugepage需求。在创建 VM 时InitialSizeManager::new_from(sandbox_config.annotations)被构造并随ResourceManager一起创建见 vm.rs。Real-time Size容器增删改时如何调整 vCPU收到 OCI 请求时请求可能只针对单个容器但需要决策的是整个 VM的 vCPU 数量。因此实现中维护了一张全容器 CPU 资源列表container_cpu_resources: HashMapString, LinuxContainerCpuResources每次需要调整时都遍历整张表重新计算一个 vCPU 总数。此外遵循以下原则原则 1不削减算力尽量保持InitialSize指定的 vCPU 数量调整后的 vCPU 数量不会低于Boot Size。在源码do_update_cpu_resources()中体现为// Prevent decreasing vCPUs on an Add operation or increasing on a Delete if (op ResourceUpdateOp::Add old_vcpus new_vcpus) || (op ResourceUpdateOp::Del old_vcpus new_vcpus) { return Ok(old_vcpus.ceil() as u32); }即在Add创建容器时不允许 vCPU 数量下降在Del删除容器时不允许 vCPU 数量上升从而保证任何时刻的 vCPU 数量都不低于引导值。原则 2cpu quota优先于cpuset且考虑设置历史设计理由quota描述的是 cgroup 可以使用的 CPU 时间片大小而cpuset只描述 cgroup 可以用哪些 CPU——cgroup 可以只用指定 CPU 上的一小部分时间片。因此quota 能更好地描述 cgroup 实际想使用的 CPU 时间片大小优先级高于 cpuset。具体规则当cpu quota与cpuset同时指定时按cpu quota计算 vCPU 数量并忽略cpuset如果过去用cpu quota控制 vCPU 数量而UpdateContainer期间只更新了cpuset则此时不调整vCPU 数量。后者在源码update_container_cpu_resources()中有直接实现见 cpu.rs// the priority of cpu-quota is higher than cpuset when determine the number of vcpus. // we should better ignore the resource update when update cpu only by cpuset if cpu-quota // has been set previously. if old_container_resource.quota() 0 container_resource.quota() 0 { resources.insert(cid.to_owned(), old_container_resource); }当旧资源记录中quota() 0曾用 quota 约束而新请求的quota() 0未设置 quota仅更新 cpuset时保留旧记录即不回退、不调整。原则 3StaticSandboxResourceMgmt控制是否允许热插拔部分 VMM 与某些架构的内核不支持 CPU 热插拔。通过StaticSandboxResourceMgmt可以适配这种情况当配置static_sandbox_resource_mgmt true时VM 启动后不再对 vCPU 数量做任何调整。源码中的门控见 manager_inner.rs// if static_sandbox_resource_mgmt, we will not have to update sandboxs cpu or mem resource if !self.toml_config.runtime.static_sandbox_resource_mgmt { // update cpu self.cpu_resource .update_cpu_resources(cid, linux_cpus, op, self.hypervisor.as_ref()) .await?; // update memory self.mem_resource .update_mem_resources(cid, linux_resources, op, self.hypervisor.as_ref()) .await?; ... }在该模式下CPU/内存资源只在启动时由InitialSizeManager::setup_config()一次性写入配置运行期不再热插拔同时setup_config()也会校验计算出的沙箱内存必须非零否则报错validate_non_zero_sandbox_memory。源码级剖析CpuResource 的完整计算链路Real-time Size的核心实现集中在 cpu.rs 的CpuResource结构体它由四个字段组成pub struct CpuResource { /// Current number of vCPUs pub(crate) current_vcpu: ArcRwLockf32, /// Default number of vCPUs pub(crate) default_vcpu: f32, /// CpuResource of each container pub(crate) container_cpu_resources: ArcRwLockHashMapString, LinuxContainerCpuResources, }current_vcpu当前 VM 的 vCPU 数初始化时取自hypervisor_config.cpu_info.default_vcpusdefault_vcpu配置文件默认值container_cpu_resourcesPod 内每个容器的 CPU 资源表Add/Update/Del三类操作都在这里增删改。对外主入口update_cpu_resources()的执行链路为update_container_cpu_resources(cid, linux_cpus, op) // 更新每容器资源表 → calc_cpu_resources() // 遍历全表计算 vCPU 需求 → 若 vcpu_required current_vcpu 则直接返回 → do_update_cpu_resources(vcpu_required, op, hypervisor) // 热插拔/热拔 → update_current_vcpu(curr_vcpus) // 更新当前值calc_cpu_resources聚合计算规则calc_cpu_resources()是决定最终 vCPU 总数的关键函数其分支逻辑完整对应文档原则资源表为空没有任何容器直接返回default_vcpu保持默认配置。统一 period 基准由于各容器可能声明不同的period先取所有容器中最大的 periodmax_period作为公共分母再将各容器 quota 按比例归一化total_quota quota * (max_period / period)避免直接相加带来的精度损失。仅 cpuset 约束无 quotatotal_quota 0.0且 cpuset 非空时返回 cpuset 中 CPU 个数。quota 约束有 quotatotal_quota 0.0时quota_vcpu total_quota / max_period最终total_vcpu quota_vcpu default_vcpu——这正是计算结果不会低于 Boot Size原则的体现。兜底既无 quota 也无 cpuset返回default_vcpu.max(1.0)。值得注意的是聚合计算全程使用f64 浮点数而非 f32源码注释解释了原因不同 period 归一化后 quota 可能出现非整数值需要保留小数部分直到最终取整避免过早舍入丢失精度同时 f64 在超过 2^53 之前不会丢失整数精度而 quota 之和几乎不可能达到该量级。do_update_cpu_resources热插拔执行计算完成后若目标值与当前值不同则调用 hypervisor 的resize_vcpu接口执行热插拔/热拔let target_vcpus_int target_vcpus.ceil() as u32; // 超管只支持整数 vCPU最后一步向上取整 let (_, new) hypervisor .resize_vcpu(old_vcpus.ceil() as u32, target_vcpus_int) .await .context(resize vcpus)?;同时强制 VM 至少保留 1 个 vCPUmin_vcpus 1.0_f32。以 QEMU 为例qemu/inner.rs 的resize_vcpu()会依次执行相等则直接返回目标为 0 则报错拒绝超过default_maxvcpus则告警并截断到上限最后通过 QMP 协议调用qmp.hotplug_vcpus()/qmp.hotunplug_vcpus()完成实际的 CPU 热插拔。配置参数详解runtime-rs的各架构配置文件如 configuration-qemu-runtime-rs.toml.in中与 vCPU 相关的参数如下default_vcpus# Default number of vCPUs per SB/VM: # unspecified or 0 -- will be set to DEFVCPUS # 0 -- will be set to the actual number of physical cores # 0 number of physical cores -- will be set to the specified number # number of physical cores -- will be set to the actual number of physical cores default_vcpus DEFVCPUS_QEMU未指定或为 0使用打包时生成的默认值小于 0使用物理核数大于 0 且不超过物理核数使用指定值超过物理核数回退为物理核数。它是 VM 启动时的基础 vCPU 数也是Real-time Size计算的兜底基准default_vcpu。overhead_vcpus# Guest-side vCPU overhead budget (fractional) used with static_sandbox_resource_mgmt. # When workload limits are present, vm_vcpus requested_vcpus overhead_vcpus # (rounded up at boot). If a workload limit is set on another dimension (for example # memory) but CPU is missing, requested_vcpus is treated as 0 and vm_vcpus equals # overhead_vcpus (minimum 1 at boot). When no workload limits are present, # default_vcpus is used instead. overhead_vcpus DEFOVERHEADVCPUS_QEMU这是在static_sandbox_resource_mgmt模式下为 guest 侧预留的 vCPU 开销预算支持小数。有工作负载限制时vm_vcpus requested_vcpus overhead_vcpus启动时向上取整只在其他维度如内存设置了限制而 CPU 缺失时requested_vcpus视为 0vm_vcpus等于overhead_vcpus启动时最少 1完全没有限制时使用default_vcpus。对应配置见 how-to-size-sandbox-overhead-runtime-rs.md。default_maxvcpus# Default maximum number of vCPUs per SB/VM: # unspecified or 0 -- will be set to the actual number of physical cores or to the maximum number # of vCPUs supported by KVM if that number is exceeded # 0 number of physical cores -- will be set to the specified number # number of physical cores -- will be set to the actual number of physical cores or to the maximum number # of vCPUs supported by KVM if that number is exceeded default_maxvcpus DEFMAXVCPUS_QEMU它定义了单沙箱/VM 可热插拔到的 vCPU 上限直接影响虚拟机内存占用与热插拔能力default_maxvcpus 240意味着最多可加到 240 个 vCPU但内存占用较大default_maxvcpus 8则内存占用小、上限也小。配置注释建议非必要不修改此值arm 平台使用 gicv2 中断控制器时需设置为 8。QEMU 的resize_vcpu正是用self.config.cpu_info.default_maxvcpus作为截断上限。static_sandbox_resource_mgmtstatic_sandbox_resource_mgmt DEFSTATICRESOURCEMGMT_QEMU置为true时启用静态资源管理VM 启动后不再调整 vCPU/内存不支持热插拔的 VMM 或架构使用置为false默认时运行期按Add/Update/Del操作动态热插拔 vCPU 与内存。InitialSizeManager::setup_config()中的写入逻辑与ResourceManager::update_linux_resource()中的门控都以此为准。测试与验证单元测试如何保证计算正确性仓库为这套计算逻辑提供了大量单元测试可以直接作为理解行为规范的参考cpu.rs 测试覆盖聚合计算的精度问题例如test_rounding验证10 次 0.1 相加结果恰为 1.0f32 实现会失败而 f64 通过test_divisible_periods/test_indivisible_periods验证不同 period 归一化后的 quota 求和test_fractional_default_vcpus验证小数default_vcpus如 0.5与容器 quota 叠加。initial_size.rs 测试test_initial_size_sandbox验证从 containerd Annotation 计算InitialSizeperiod100000、quota220000 → 2200 mCPU → 向上取整为 3 个 vCPU内存 512 MiBtest_initial_size_sandbox_from_crio_pod_resources验证 CRI-O JSON Annotation 路径及其废弃键test_setup_config_static_applies_vcpu_and_memory等用例验证default_vcpus overhead_vcpus requested_vcpus的叠加规则与default_maxvcpus截断逻辑。cpu.rskata-types测试验证LinuxContainerCpuResources::try_from的quota/period分数 vCPU 计算如 quota1001、period100 → 10.01 个 vCPU与 cpuset-only 路径cpuset 大小即 vCPU 数以及LinuxSandboxCpuResources::merge的跨容器聚合行为。总结Kata Containers 3.0 的runtime-rs将 vCPU 容量规划拆分为两个清晰阶段启动阶段Boot Size以 Annotation 传来的InitialSize优先、TomlConfig的default_vcpus兜底叠加overhead_vcpus预算由InitialSizeManager写入 hypervisor 配置运行阶段Real-time Size维护全容器 CPU 资源表每次CreateContainer/UpdateContainer/DeleteContainer都重新聚合计算遵循不削减算力、quota 优先于 cpuset、考虑设置历史三原则通过 hypervisor 的resize_vcpuQEMU 下即 QMP 热插拔/热拔动态调整static_sandbox_resource_mgmt true时则完全关闭运行期调整。这一设计与runtime-go的重要差异在于runtime-rs充分吸收了 Kubernetes/Containerd 提供的 Sandbox Size 能力将上层对工作负载的精确资源描述优先转化为 VM 的 vCPU 配置同时通过分数化 vCPUfractional vCPU与 f64 精度聚合为容器工作负载提供了更贴近实际需求的 CPU 资源调度兼顾了兼容性与未来演进空间。赞分享云原生容器运行时【免费下载链接】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点击查看免费下载相关推荐OpenMower OS 版本升级完全指南旧系统迁移与 A/B 在线更新的 6 个关键点OpenMower OS 版本升级完全指南旧系统迁移与 A/B 在线更新的 6 个关键点 OpenMower 机器人上的系统升级只有两条路往新硬件上 全新烧机器人嵌入式智能硬件硬件开发自动驾驶Kata Containers Dragonball VMM 设备模型深解析dbs-device 的 MMIO/PIO 陷阱处理、IO 分发与热插拔机制Kata Containers Dragonball VMM 设备模型深解析dbs device 的 MMIO/PIO 陷阱处理、IO 分发与热插拔机制 本篇云原生容器运行时Milvus CDC 跨集群复制Replication技术总览主备灾备拓扑、角色约束与故障切换机制Milvus CDC 跨集群复制Replication技术总览主备灾备拓扑、角色约束与故障切换机制 本文以 docs/design docs/design云原生容器运行时上一篇Elm架构教程代码实现原理揭秘Elm如何实现零运行时错误下一篇深度解析小智ESP32后端服务从架构原理到实战部署的技术解密创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
