【containerd 2.1.8】(Part 3)containerd 2.1.8 超深度分析 — Metadata存储(BoltDB) + Content(CAS) + Snapshot + Di
·
containerd 2.1.8 超深度分析 — Metadata存储(BoltDB) + Content(CAS) + Snapshot + Diff + Mount
源码:
core/metadata/(6217行) +core/content/(617行) +core/snapshots/(397行) +core/diff/(684行) +core/mount/(2155行)
一、Metadata — BoltDB 元数据存储
1.1 BoltDB 架构
containerd 使用 BoltDB (现在叫 bbolt) 作为元数据存储引擎。所有容器、镜像、快照等元数据都存储在 BoltDB 中。
Bucket 层次结构
1.2 DB 结构体
type DB struct {
db *bolt.DB // BoltDB 实例
co *containerOpts // 容器选项
wlock *kmutex.KeyedMutex // 写锁 (per-key)
store map[string]content.Store // Content Store 集合
ss map[string]snapshots.Snapshotter // Snapshotter 集合
}
DB.View / DB.Update — 事务封装
func (m *db) View(fn func(*bolt.Tx) error) error {
return m.db.View(fn) // 只读事务
}
func (m *db) Update(fn func(*bolt.Tx) error) error {
m.wlock.Lock(string(tx.WriteTo(nil))) // 按 namespace 加写锁
defer m.wlock.Unlock(string(tx.WriteTo(nil)))
return m.db.Update(fn) // 读写事务
}
设计:kmutex.KeyedMutex 提供按 namespace 的细粒度锁,避免不同 namespace 的事务互相阻塞。
1.3 Container 存储
func (m *containerStore) Create(ctx context.Context, container containers.Container) (containers.Container, error) {
return m.db.Update(ctx, func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(namespace)).Bucket([]byte("containers"))
// 1. 检查是否已存在
existing := bucket.Get([]byte(container.ID))
if existing != nil {
return errdefs.ErrAlreadyExists
}
// 2. 序列化
data, _ := proto.Marshal(containerToProto(&container))
// 3. 写入 BoltDB
return bucket.Put([]byte(container.ID), data)
})
}
1.4 GC — 垃圾收集
func (m *db) GarbageCollect(ctx context.Context) (gc.Stats, error) {
// ─── 标记阶段 ───
// 1. 从所有 leases 开始标记
// 2. 标记所有引用的 content blobs
// 3. 标记所有引用的 snapshots
// 4. 标记所有 images 引用的 manifests/blobs
// ─── 清除阶段 ───
// 5. 删除未被标记的 content blobs
// 6. 删除未被标记的 snapshots
// 7. 删除未被标记的 ingest 临时数据
}
GC 标记-清除流程
二、Content — 内容寻址存储 (CAS)
2.1 Content Store 接口
type Store interface {
Info(ctx context.Context, dgst digest.Digest) (Info, error) // 查询 blob 信息
Walk(ctx context.Context, fn WalkFunc, filters ...string) error // 遍历所有 blob
Delete(ctx context.Context, dgst digest.Digest) error // 删除 blob
Update(ctx context.Context, info Info, fieldpaths ...string) (Info, error) // 更新元数据
Writer(ctx context.Context, opts ...WriterOpt) (Writer, error) // 获取写入器
ReaderAt(ctx context.Context, dgst digest.Digest) (io.ReaderAt, error) // 读取 blob
Status(ctx context.Context, ref string) (Status, error) // 查询上传状态
Abort(ctx context.Context, ref string) error // 中止上传
}
2.2 Content 存储路径
/var/lib/containerd/io.containerd.content.v1.content/
├── blobs/
│ └── sha256/
│ ├── a1b2c3d4... ← blob 文件 (以 digest 命名)
│ └── e5f6g7h8...
└── ingest/
├── ref-123/ ← 正在上传的临时数据
└── ref-456/
Content 写入流程
三、Snapshot — 快照管理
3.1 Snapshotter 接口
type Snapshotter interface {
Stat(ctx context.Context, key string) (Info, error) // 查询快照信息
Update(ctx context.Context, info Info, fieldpaths ...string) (Info, error)
Usage(ctx context.Context, key string) (Usage, error) // 查询磁盘使用
Mounts(ctx context.Context, key string) ([]mount.Mount, error) // 获取 mount 参数
Prepare(ctx context.Context, key, parent string, opts ...Opt) ([]mount.Mount, error) // 准备可写快照
View(ctx context.Context, key, parent string, opts ...Opt) ([]mount.Mount, error) // 准备只读快照
Commit(ctx context.Context, name, key string, opts ...Opt) error // 提交快照
Remove(ctx context.Context, key string) error // 删除快照
Walk(ctx context.Context, fn WalkFunc, filters ...string) error // 遍历
Close() error // 关闭
}
3.2 快照类型与转换
| 类型 | Kind | 描述 |
|---|---|---|
| Active | KindActive |
可写快照 (Prepare 创建) |
| Committed | KindCommitted |
只读快照 (Commit 创建) |
| View | KindView |
只读视图 (View 创建) |
快照父子关系
3.3 overlayfs snapshotter 实现原理
Prepare("container-1", "layer-2") → 返回 mount 参数:
type: "overlay"
options: lowerdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/2,
upperdir=/var/lib/containerd/.../snapshots/3/fs,
workdir=/var/lib/containerd/.../snapshots/3/work
Commit("nginx-rootfs", "container-1"):
1. 将 upperdir 的内容变为只读
2. 删除 workdir
3. 标记 snapshot 3 为 Committed
四、Diff — 差异计算
4.1 Differ 接口
type Differ interface {
Apply(ctx context.Context, desc ocispec.Descriptor, mounts []mount.Mount) (ocispec.Descriptor, error)
DiffMounts(ctx context.Context, lower, upper []mount.Mount, opts ...Opt) (ocispec.Descriptor, error)
Compare(ctx context.Context, a, b ocispec.Descriptor, opts ...Opt) (ocispec.Descriptor, error)
}
4.2 Walking Diff 实现
func (d *walkingDiff) DiffMounts(ctx context.Context, lower, upper []mount.Mount, opts ...Opt) (ocispec.Descriptor, error) {
// 1. Mount lower 和 upper 到临时目录
// 2. 遍历 upper 目录,与 lower 比较
// 3. 新增/修改/删除的文件 → 写入 tar 流
// 4. 计算 digest
// 5. 存储 content blob
// 6. 返回 descriptor
}
Diff 类型
| 场景 | 方法 | 描述 |
|---|---|---|
| Layer diff | DiffMounts(lower, upper) |
两个 mount 间的差异 |
| Layer apply | Apply(desc, mounts) |
将 diff 应用到 mount |
| Manifest compare | Compare(a, b) |
两个镜像的差异 |
五、Mount — 挂载操作封装
5.1 Mount 结构体
type Mount struct {
Type string // 文件系统类型 (overlay, bind, proc, ...)
Source string // 源路径
Options []string // 挂载选项 (rw, lowerdir=..., upperdir=...)
}
5.2 关键操作
func (m *Mount) Mount(target string) error {
// 调用 unix.Mount(m.Source, target, m.Type, flags, data)
}
func All(mounts []Mount, target string) error {
// 依次挂载所有 mount 点
}
六、核心协作关系
6.1 Pull → Unpack → Snapshot 协作
七、设计模式总结
| # | 模式 | 体现 |
|---|---|---|
| 1 | CAS (Content Addressable) | Content Store 以 digest 为 key |
| 2 | Copy-on-Write | overlayfs snapshotter |
| 3 | 标记-清除 GC | 从 leases/images 标记,清除未引用资源 |
| 4 | Namespace 隔离 | BoltDB per-namespace bucket |
| 5 | Ingest 临时区 | Writer → ingest → commit → blobs |
| 6 | 父子快照链 | Prepare → Active → Commit → Committed |
| 7 | Walking Diff | 逐文件遍历比较 |
| 8 | Per-key Mutex | kmutex 按 namespace 加锁 |
| 9 | Proxy 模式 | content/snapshot proxy (gRPC 代理) |
| 10 | 双层存储 | Metadata (BoltDB) + Content (文件系统) |
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)