「 Kata Containers 」源码走读 — virtcontainers/factory
based on 3.0.0
Factory
src/runtime/virtcontainers/factory.go
Factory 继承自 FactoryBase,两者的区别在于 FactoryBase 用于创建 base VM(即为模版 VM),创建后会将其暂停,而 Factory 会在 VM 使用时将其恢复,并热更新 VM 以满足运行时的规格要求。
FactoryBase 有四种实现:direct、template、grpccache 和 cache。但是它们并不会对外暴露使用,而是在 Factory 的工厂函数中根据具体的配置细节初始化对应的实现,作为统一的 Factory 对外提供接口调用,即 factory。
1 | type direct struct { |
1 | type grpccache struct { |
1 | type template struct { |
1 | type cache struct { |
1 | // VMConfig is a collection of all info that a new blackbox VM needs. |
VMConfig 针对 VM factory 场景下,聚合的配置文件中的相关信息。
1 | type factory struct { |
工厂函数
目前来看,grpccache 初始化的条件应该不存在。此外,当启用 VM factory 时,必然是 VM template 和 VM cache 二选一,所以 direct 不会作为 factory 直接对外使用,而是进一步初始化成 cache factory。
- 校验 VMConfig 配置的合法性,其中包括 [hypervisor].kernel 是否不为空,[hypervisor].image 和 [hypervisor].initrd 有且仅有一个。并设置 [hypervisor].default_vcpus 缺省时为 1(单位:Core),[hypervisor].default_memory 缺省时为 2048(单位:MiB)
- 当启用 VM template 时(即 [factory].enable_template 为 true),则初始化 template factory
- 如果 fetchOnly 为 true,则校验 [factory].template_path 目录下是否存在 state 和 memory 文件
- 如果 fetchOnly 为 false,则初始化 template factory
- 校验 [factory].template_path 目录下是否不存在 state 和 memory 文件
- 创建 [factory].template_path 目录,将 tmps 挂载到此目录下,大小为 [hypervisor].default_memory + 8 MiB(amd64 架构下为 8 MiB;arm64 架构下为 300 MiB),并在此目录下创建 memory 文件
- 调用 NewVM,基于 VMConfig 创建 VM(创建后则作为模版 VM)
- 调用 agent 的 disconnect,断开与 agent 的链接
- 调用 hypervisor 的 PauseVM,暂停 VM
- 调用 hypervisor 的 SaveVM,保存 VM 到磁盘文件
- 调用 hypervisor 的 StopVM,关停 VM
- 调用 store 的 Destroy,删除状态数据目录
- 当启用 VM cache 时(即 [factory].vm_cache_number 大于 0),则初始化 cache factory
- 初始化 direct factory(cache factory 的初始化必须依赖其他 factory)
- 反复调用 GetBaseVM(direct.GetBaseVM),直至创建暂停状态的 VM 数量等于 [factory].vm_cache_number
- 将这些事先创建好的 base VM 维护在 cache 中
*后续需要时通过 GetBaseVM(cache.GetBaseVM),获取 base VM,并通过 GetVM 热更新*
NewVM
基于 VMConfig 创建 VM
NewVM 并非 FactoryBase 定义接口,而是 virtcontainers 提供的一个基于 VMConfig 创建 VM 的工厂函数,仅用于 factory 相关流程
- 校验 VMConfig 配置的合法性,其中包括 [hypervisor].kernel 是否不为空,[hypervisor].image 和 [hypervisor].initrd 有且仅有一个。并设置 [hypervisor].default_vcpus 缺省时为 1,[hypervisor].default_memory 缺省时为 2048
- 初始化 hypervisor 和 agent
- 调用 hypervisor 的 CreateVM,创建一个不含网络信息的 VM
- 调用 agent 的 configure 和 setAgentURL,配置 agent 相关信息
- 调用 hypervisor 的 StartVM,启动 VM
- 如果 VM 不是从 template 启动(因为从 template 启动的 VM,会进入 pause 状态),则调用 agent 的 check,检测服务存活性
Config
获取 base factory 的配置信息
cache、direct、grpccache 和 template 实现方式相同。
- 返回 VMConfig
GetVMStatus
获取 base VM 的状态信息
direct、grpccache 和 template 实现下均不支持此接口,会触发 panic。
cache
- 针对 cache 中缓存的每一个 VM,获取其 CPU 和内存大小(即配置中声明的默认大小),并调用 hypervisor 的 GetPids,获取相关的 PID
GetBaseVM
获取 base VM
direct
- 调用 NewVM,创建 VM
- 调用 hypervisor 的 PauseVM,将其暂停并返回
template
- 调用 NewVM,创建 VM(该 VM 基于模版创建,创建后不作为模版 VM)
grpccache
- gRPC 调用 cache server 的 GetBaseVM,获取 VM
- 调用 hypervisor 的 fromGrpc,配置 hypervisor 信息
- 调用 agent 的 configureFromGrpc,配置 agent 信息
- 基于配置后的 hypervisor、agent 和 gRPC 返回体构建并返回 VM
cache
- 从缓存的 base VM 中返回一个
CloseFactory
关闭并销毁 factory
direct、grpccache 实现下此接口不做任何处理,直接返回即可。
template
- 移除 [factory].template_path 挂载点(默认为 /run/vc/vm/template),并删除此目录
cache
- 调用 base factory 的 ClostFactory,关闭 factory
如上所述,cache factory 也就是调用 direct factory 的 CloseFactory
GetVM
热更新 base VM 以满足需求
GetVM 不是 base factory 的接口,而是 factory 的接口
GetVM 接受一个 VMConfig 类型的参数,该参数描述了预期的 VM 配置(下称 newConfig),而 factory 实现中的 VMConfig 是 base VM 的配置(下称 baseConfig),两者的差异点补齐便是 GetVM 操作的核心逻辑
- 校验 newConfig 配置的合法性,其中包括 [hypervisor].kernel 是否不为空,[hypervisor].image 和 [hypervisor].initrd 有且仅有一个。并设置 [hypervisor].default_vcpus 缺省时为 1,[hypervisor].default_memory 缺省时为 2048
- 调用 Config,获取 baseConfig 信息,校验两个配置信息是否并不冲突
- 调用 GetBaseVM,获取 base VM
- 调用 hypervisor 的 ResumeVM,将 VM 从暂停状态恢复
- 借助 /dev/urandom 重新生成随机熵,调用 agent 的 reseedRNG,重置 guest 随机数生成器
- 为了补齐 VM 的暂停时间,调用 agent 的 setGuestDateTime,同步 host 时间至 guest 中
- 如果 base VM 中的 CPU 数量小于期望配置中的 CPU 数量,则调用 hypervisor 的 HotplugAddDevice,热添加差值 CPU;内存同理
- 当有 CPU 或内存的热添加动作后,调用 agent 的 onlineCPUMem,通知 agent 上线资源
Cache Server
src/runtime/protocols/cachecache.pb.go
cache server 并非默认启动的 gRPC 服务,而是在 VM Cache 特性启用时,通过 kata-runtime factory init 命令启动。
1 | type cacheServer struct { |
启动函数
- 基于配置,初始化一个新的 factory(即 fetchOnly 为 false)
- 启动 gRPC 服务,监听 [factory].vm_cache_endpoint 地址(默认为 /var/run/kata-containers/cache.sock)
Config
获取 base factory 的配置信息
返回体结构如下
1
2
3
4
5
6type GrpcVMConfig struct {
// VMConfig
Data []byte
// VMConfig.AgentConfig
AgentConfig []byte
}调用 factory 的 Config,获取 base factory 的配置信息
由于 base factory 在 cache server 启动后便固定规格,因此在首次调用后,会保存配置信息,之后的调用直接返回即可
GetBaseVM
获取 base VM
返回体结构如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14type GrpcVM struct {
// VM.id
Id string
// VM.hypervisor.toGrpc
Hypervisor []byte
ProxyPid int64
ProxyURL string
// VM.cpu
Cpu uint32
// VM.memory
Memory uint32
// VM.cpuDelta
CpuDelta uint32
}调用 factory 的 Config,获取 base factory 的配置信息
调用 factory 的 GetBaseVM,获取 base VM
Status
获取 base VM 的状态信息
返回体结构如下
1
2
3
4
5
6type GrpcStatus struct {
// 当前进程的 PID
Pid int64
// factory.GetVMStatus 的返回结果
Vmstatus []*GrpcVMStatus
}调用 factory 的 GetVMStatus,获取 base VM 的状态信息
Quit
关闭 cache server
- 1 秒钟后,关闭 cache gRPC server
「 Kata Containers 」源码走读 — virtcontainers/factory
http://shenxianghong.github.io/2023/03/12/2023-03-12 Kata Containers 源码走读 - virtcontainers factory/