Skip to content

快速启动与快照恢复

概述

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(&region_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跨节点迁移

最佳实践

  1. 预热快照池 - 高频模板预先创建快照
  2. 内存映射 - 使用 mmap 避免内存复制
  3. 并行恢复 - 设备状态并行恢复
  4. 延迟加载 - 非关键页面延迟加载
  5. 定期刷新 - 快照池定期刷新避免状态过期

下一步