快速启动与快照恢复
概述
Cube Hypervisor 通过快照技术实现毫秒级冷启动,这是 Serverless 场景的关键优化。本文介绍快速启动的原理和实现。
快照机制
快照数据结构
rust
// vmm/src/vm.rs
pub struct VmSnapshot {
pub config: VmConfig,
pub state: VmState,
pub device_states: HashMap<String, Snapshot>,
pub memory_snapshot: MemorySnapshotData,
}
pub enum VmState {
Created,
Running,
Shutdown,
Paused,
Snapshot,
}快照创建流程
快照恢复流程
源码解析
快照创建
rust
// vmm/src/vm.rs
impl Vm {
pub fn snapshot(&mut self) -> Result<Snapshot> {
// 1. 暂停 VM
self.pause()?;
// 2. 创建快照
let mut snapshot = Snapshot::new(VM_SNAPSHOT_ID);
// 3. 保存 VM 配置
snapshot.add_snapshot(
self.snapshot_config()?
);
// 4. 保存内存状态
snapshot.add_snapshot(
self.memory_manager.snapshot()?
);
// 5. 保存设备状态
snapshot.add_snapshot(
self.device_manager.snapshot()?
);
// 6. 保存 CPU 状态
snapshot.add_snapshot(
self.cpu_manager.snapshot()?
);
Ok(snapshot)
}
}内存快照优化
rust
// vmm/src/memory_manager.rs
impl MemoryManager {
pub fn snapshot(&mut self) -> Result<Snapshot> {
let mut snapshot = Snapshot::new(MEMORY_MANAGER_SNAPSHOT_ID);
// 使用 copy-on-write 优化
for region in self.guest_memory.iter() {
let region_snapshot = MemoryRegionSnapshot {
base: region.start_addr().raw_value(),
length: region.len() as u64,
// 使用文件映射而不是复制内存
file: Some(region.file().unwrap().clone()),
};
snapshot.add_data_section(
SnapshotDataSection::new_from_state(®ion_snapshot)?
);
}
Ok(snapshot)
}
}设备状态保存
rust
// vmm/src/device_manager.rs
impl DeviceManager {
pub fn snapshot(&mut self) -> Result<Snapshot> {
let mut snapshot = Snapshot::new(DEVICE_MANAGER_SNAPSHOT_ID);
// 遍历所有设备
for (id, device) in self.devices.iter() {
if let Some(snapshottable) = device.as_snapshottable() {
snapshot.add_snapshot(
snapshottable.snapshot()?
);
}
}
Ok(snapshot)
}
}快照存储格式
文件结构
snapshot/
├── config.json # VM 配置
├── state.json # VM 状态
├── memory-ranges/ # 内存快照
│ ├── region-0.bin
│ ├── region-1.bin
│ └── ...
└── devices/ # 设备状态
├── virtio-blk-0.json
├── virtio-net-0.json
└── ...config.json 示例
json
{
"cpus": {
"boot_vcpus": 2,
"max_vcpus": 2
},
"memory": {
"size": 536870912,
"shared": false,
"hugepages": false
},
"kernel": {
"path": "/path/to/vmlinux"
},
"disks": [
{
"path": "/path/to/rootfs.ext4"
}
],
"net": [
{
"tap": "tap0",
"mac": "00:11:22:33:44:55"
}
]
}性能优化
1. 内存映射优化
使用 mmap 直接映射快照文件,避免内存复制:
rust
// vmm/src/memory_manager.rs
fn restore_memory_region(
&mut self,
snapshot: &MemoryRegionSnapshot,
) -> Result<()> {
// 直接 mmap 快照文件
let file = File::open(&snapshot.file)?;
let region = MmapRegion::from_file(
FileOffset::new(file, 0),
snapshot.length as usize,
)?;
// 映射到 Guest 地址空间
self.guest_memory.insert_region(
GuestAddress(snapshot.base),
region,
)?;
Ok(())
}2. 并行恢复
多个设备并行恢复状态:
rust
// vmm/src/device_manager.rs
fn restore_devices(
&mut self,
snapshot: &Snapshot,
) -> Result<()> {
let handles: Vec<_> = snapshot.snapshots.iter()
.map(|(id, device_snapshot)| {
let device = self.get_device(id)?;
thread::spawn(move || {
device.restore(device_snapshot)
})
})
.collect();
// 等待所有设备恢复完成
for handle in handles {
handle.join().unwrap()?;
}
Ok(())
}3. 延迟加载
非关键内存页延迟加载:
热启动场景
模板预热
实现代码
rust
// Cubelet 中的快照池管理
pub struct SnapshotPool {
pool: Vec<PooledSnapshot>,
template_id: String,
max_size: usize,
}
impl SnapshotPool {
pub fn acquire(&mut self) -> Result<PooledSnapshot> {
// 从池中获取预热快照
if let Some(snapshot) = self.pool.pop() {
return Ok(snapshot);
}
// 池为空,创建新快照
self.create_snapshot()
}
pub fn release(&mut self, snapshot: PooledSnapshot) {
if self.pool.len() < self.max_size {
self.pool.push(snapshot);
}
}
}性能指标
| 场景 | 耗时 | 说明 |
|---|---|---|
| 冷启动(无快照) | 2-5s | 完整启动流程 |
| 快照恢复 | 50-100ms | 从快照恢复 |
| 预热快照 | <50ms | 从快照池获取 |
| 热迁移 | 100-200ms | 跨节点迁移 |
最佳实践
- 预热快照池 - 高频模板预先创建快照
- 内存映射 - 使用 mmap 避免内存复制
- 并行恢复 - 设备状态并行恢复
- 延迟加载 - 非关键页面延迟加载
- 定期刷新 - 快照池定期刷新避免状态过期
下一步
- 了解 设备直通与 Virtio
- 深入 迁移与快照源码