containerd的核心Layer层存储组件content,用于存储下载的layer层数据,大家知道镜像是分层架构,一个镜像可能有很多Layer层组成,通过各Layer堆叠形成最终的文件系统,供运行时组件调用。本节详细分析content组件。
content
服务接口type Store interface {ManagerProviderIngestManagerIngester
}
1.Manager
用于Layer层数据的删改查,对应下面四个方法。Walk方法,是查询的一种,将过滤出来的content应用WalkFunc
方法。最后一个查Info,并不是读取Layer层数据,而是获取其属性信息,包括文件大小、修改时间等。
Delete(ctx context.Context, dgst digest.Digest) error
Update(ctx context.Context, info Info, fieldpaths ...string) (Info, error)
Walk(ctx context.Context, fn WalkFunc, filters ...string) error
Info(ctx context.Context, dgst digest.Digest) (Info, error)
2.Provider
用户Layer层数据的查询。下面方法读取数据。
ReaderAt(ctx context.Context, desc ocispec.Descriptor) (ReaderAt, error)
3.IngestManager
用于管理正在下载的Layer层数据,提供状态查询、中断等方法,上面两个方法是下载完成的Layer管理接口。
Status(ctx context.Context, ref string) (Status, error)
ListStatuses(ctx context.Context, filters ...string) ([]Status, error)
Abort(ctx context.Context, ref string) error
4.Ingester
用于下载Layer层数据,提供数据存储服务。
Writer(ctx context.Context, opts ...WriterOpt) (Writer, error)
content
组件设计containerd以插件形式管理各个组件,content组件生效需要提供两个组件标识。
plugin.GRPCPlugin
将 content组件注册为GRPC服务,对外提供服务接口,ctr、crictl等客户端可以访问该服务。
plugin.ServicePlugin
将content组件注册为一个功能服务,提供Layer层存储服务。
plugin.MetadataPlugin
该组件是所有组件的元数据管理中心,用于存储各组件的管理数据。
plugin.ContentPlugin
content组件的存储实现服务,真正实现存储逻辑的。
他们几个的关系:
1)客户端调用从GRPCPlugin
组件访问Content服务,GRPCPlugin
加载ServicePlugin
并调用其提供的服务。
2)ServicePlugin
调用MetadataPlugin
,调用其提供的服务。
3)MetadataPlugin
调用ContentPlugin
,调用其提供的存储服务。
containerd的源代码在这方面确实有点混乱,这几个组件很多方法名称相同,连IDE也无法正确分辨。
定义存储结构,组件包含两类数据,一类是正在下载的临时目录,一类是下载完成、验证完成的Layer层真实存储目录。
1)下载完成的Layer层存储目录:/var/lib/containerd/io.containerd.content.v1.content/blobs
2)Layer层临时存储目录:/var/lib/containerd/io.containerd.content.v1.content/ingest
即 Ingester
的 Writer(ctx context.Context, opts ...WriterOpt) (Writer, error)
实现。
1)存储Layer层数据,传入该Layer层的唯一标识,以及根据Manifest获取的Desc信息,Desc信息包含期望下载的Digest。
2)ingest
目录其{namespace/id/ref}文件夹下,定义两个子文件 ref文件、data文件,ref文件存储Layer层的唯一标识,data文件Layer层数据。
3) 根据data文件是否有内容,判断是重新写入还是接着上次的offset继续写入。将data文件的WriteIO
句柄封装到返回值Writer
服务中。
1)Writer
数据写入服务,这个好理解,写入Layer层数据到data文件
2)Digest
获取写入数据的Hash值
3)Status
获取已经写入数据状态,包括写入Size、Offset,以及期望的Digest等。
4)Truncate
文件内容清空,Reset文件的Offset等。
5)Commit
通知文件写入完毕,可以进行后续处理,例如将ingest文件存储到blobs目录。
客户端真正的访问顺序,从
MetadataPlugin
到ContentPlugin
,在调用ContentPlugin
存储Layer数据前,需要MetadataPlugin
先行对元数据进行处理。
metadata存储数据库:bolt
metadata存储目录:/var/lib/containerd/io.containerd.metadata.v1.bolt
1)该Layer层存储完毕,记录元数据
Key:v1/{namespace}/content/blob/{digest}
Key1: createdat
Key2:updatedat
Key3:size
2)content的lease信息,下载完成后设置
Key: v1/{namespace}/leases/{leaseID}/content/{Digest}
Value: nil
3)正在下载的ref记录
Key:v1/{namespace}/content/ingests/{ref}
Key1:ref Value:{namespace}/{boltID}{ref}
Key2:expireat Value:{deadline time}
Key3:expected Value:{Digest}
4)下载环节设置,GC使用
Key: v1/{namespace}/leases/{leaseID}/ingests/{ref}
Value: nil
content垃圾回收用于删除无用的content。
遍历所有的content存储,在v1/{namespace}/content/blob/
与v1/{namespace}/content/ingests
存在的content保留,其他删除。