存储与快照:rootfs 怎么来的
每个沙箱都需要一个独立的 rootfs(根文件系统)。Cubelet 使用 containerd 的快照机制 + 自研存储池来高效管理 rootfs 的创建和回收。
1. 存储架构全景
2. 存储插件初始化
Cubelet/storage/plugin.go 注册了存储插件:
go
// storage/plugin.go
func init() {
registry.Register(&plugin.Registration{
Type: constants.InternalPlugin,
ID: constants.StorageID.ID(),
Config: &Config{},
Requires: []plugin.Type{
constants.CubeStorePlugin,
constants.CubeMetaStorePlugin,
},
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
localStorage.config = ic.Config.(*Config)
// 初始化存储池
// 初始化快照管理
},
})
}配置项
| 配置 | 说明 | 默认值 |
|---|---|---|
root_path | 存储根目录 | containerd state dir |
data_path | 数据目录 | root_path |
disksize | 磁盘大小限制 | - |
pool_type | 存储池类型 | cp (copy-on-write) |
pool_size | 池大小 | - |
pool_worker_num | 池工作线程数 | - |
pool_default_format_size_list | 预格式化大小列表 | - |
3. 存储池:快照复用
3.1 池的工作原理
存储池预先创建一批格式化好的 ext4 快照,沙箱创建时直接从池中取出,避免每次都要格式化:
3.2 Reflink 拷贝
Cubelet/storage/pool_withreflink.go 实现了基于 reflink 的快速拷贝:
go
// pool_withreflink.go
func (p *pool) createWithReflink(src, dst string) error {
// 使用 FICLONE ioctl 实现 copy-on-write 拷贝
// 速度极快(几乎瞬间),且不占用额外磁盘空间
// 直到写入时才真正分配空间
}Reflink 的优势:
- 速度快:只需复制元数据,不复制数据块
- 空间省:共享未修改的数据块
- 原子性:操作是原子的
3.3 池回收
沙箱销毁后,快照不会被删除,而是被回收到池中:
4. 快照创建流程
4.1 从模板创建快照
当创建沙箱时,Cubelet 需要从模板镜像创建一个可写的 rootfs:
4.2 Overlay 挂载
go
// Cubelet/plugins/snapshots/overlay/
// 使用 overlayfs 将只读模板层和可写层合并Overlay 的层级结构:
5. 磁盘管理
5.1 磁盘检查
Cubelet/storage/local.go 实现了磁盘空间检查:
go
func (s *localStorage) checkDisk() error {
stat := syscall.Statfs_t{}
syscall.Statfs(s.config.DataPath, &stat)
freePercent := int64(stat.Bfree * 100 / stat.Blocks)
if freePercent < s.config.WarningPercent {
return fmt.Errorf("disk usage warning: %d%% free", freePercent)
}
return nil
}5.2 磁盘回收
当磁盘空间不足时,Cubelet 会:
- 停止创建新沙箱
- 清理过期的模板缓存
- 回收已销毁沙箱的存储
6. 卷挂载
除了 rootfs,沙箱还需要挂载各种卷:
卷插件
Cubelet/plugins/mount/manager.go 管理卷的生命周期:
| 卷类型 | 实现方式 | 生命周期 |
|---|---|---|
| emptyDir | tmpfs | 随沙箱销毁 |
| configMap | bind mount | 随沙箱销毁 |
| secret | bind mount | 随沙箱销毁 |
| hostPath | 直接挂载 | 需手动清理 |
7. 完整存储流程
延伸阅读
- 沙箱在节点上的一生 — 完整生命周期
- 工作流引擎:Cubelet 如何编排插件 — 插件调用机制
- CubeShim:Shim v2 协议实现 — 容器运行时桥接