【containerd 源码分析】containerd image list 源码分析

    本文分析 containerd 列出所有镜像的分析过程,包括 ctr image 命令行以及 containerd daemon 执行 过程,也包含镜像 metadata,content 等内容。

1. 执行如下命令 ctr images list

    首先分析 ctr 命令,实现在 cmd/ctr 的子命令 listCommand 实现,利用 GRPC 链接到 remote cotainerd 端请求,使用了如下两个服务,其中 imageStore 实现了文件 image/images.go 的 Store 接口,cs 实现了 content/content.go Store 接口,如下 1.1 1.2 所示:

imageStore = client.ImageService()
cs         = client.ContentStore()

   1.1 文件 image/image.go 接口 Store

    Store 接口很好理解,增删改查的操作。

// Store and interact with images
type Store interface {
	Get(ctx context.Context, name string) (Image, error)
	List(ctx context.Context, filters ...string) ([]Image, error)
	Create(ctx context.Context, image Image) (Image, error)

	// Update will replace the data in the store with the provided image. If
	// one or more fieldpaths are provided, only those fields will be updated.
	Update(ctx context.Context, image Image, fieldpaths ...string) (Image, error)

	Delete(ctx context.Context, name string, opts ...DeleteOpt) error
}

   1.2 文件 content/content.go 接口 Store

    Store 接口🈶包含四个匿名接口, Manager 提供了 content 接口, Provider 提供了指定内容的读接口, IngestManager 提供了管理 ingest 的方法, Ingester 提供了写 content 的方法。

// Store combines the methods of content-oriented interfaces into a set that
// are commonly provided by complete implementations.
type Store interface {
	Manager
	Provider
	IngestManager
	Ingester
}

2. 向 containerd 发起 list 请求 

imageList, err := imageStore.List(ctx, filters...)

   2.1 containerd 处理 GRPC ListImagesRequest 请求

    其核心处理函数在 containerd/services.images/service.go 文件中 List 方法。

func (c *imagesClient) List(ctx context.Context, in *ListImagesRequest, opts ...grpc.CallOption) (*ListImagesResponse, error) {
	out := new(ListImagesResponse)
	err := c.cc.Invoke(ctx, "/containerd.services.images.v1.Images/List", in, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

    2.2 service List 方法

    看出最终调用的是 local.List 方法, 那主要分析到 local 实现也在相同目录 containerd/services.images 中。

func (s *service) List(ctx context.Context, req *imagesapi.ListImagesRequest) (*imagesapi.ListImagesResponse, error) {
	return s.local.List(ctx, req)
}

   2.3 结构体 local

   其中又包含三个接口, store gcScheduler Publisher, local 实现了 ImagesClient 的接口

type local struct {
	store     images.Store
	gc        gcScheduler
	publisher events.Publisher
}

var _ imagesapi.ImagesClient = &local{}

   2.4 初始化 init 注册插件 io.containerd.service.v1

    io.containerd.service.v1 需要插件 io.containerd.metadata.v1 和 io.containerd.gc.v1, 可以看出来 store 接口是插件 io.containerd.metadata.v1 实现的。 在看看 metadata 初始化插件

func init() {
	plugin.Register(&plugin.Registration{
		Type: plugin.ServicePlugin,
		ID:   services.ImagesService,
		Requires: []plugin.Type{
			plugin.MetadataPlugin,
			plugin.GCPlugin,
		},
		InitFn: func(ic *plugin.InitContext) (interface{}, error) {
			m, err := ic.Get(plugin.MetadataPlugin)
			if err != nil {
				return nil, err
			}
			g, err := ic.Get(plugin.GCPlugin)
			if err != nil {
				return nil, err
			}

			return &local{
				store:     metadata.NewImageStore(m.(*metadata.DB)),
				publisher: ic.Events,
				gc:        g.(gcScheduler),
			}, nil
		},
	})
}

   2.5 插件 metadata 初始化

    可以看出来使用的 db 为 bolt, 在 io.containerd.metadata.v1.bolt 目录创建了数据库文件 meta.db。

	plugin.Register(&plugin.Registration{
		Type: plugin.MetadataPlugin,
		ID:   "bolt",
		Requires: []plugin.Type{
			plugin.ContentPlugin,
			plugin.SnapshotPlugin,
		},
		Config: &srvconfig.BoltConfig{
			ContentSharingPolicy: srvconfig.SharingPolicyShared,
		},
		InitFn: func(ic *plugin.InitContext) (interface{}, error) {
            .........................
			db, err := bolt.Open(path, 0644, &options)
			close(doneCh)
			if err != nil {
				return nil, err
			}

			var dbopts []metadata.DBOpt
			if !shared {
				dbopts = append(dbopts, metadata.WithPolicyIsolated)
			}
			mdb := metadata.NewDB(db, cs.(content.Store), snapshotters, dbopts...)

    回到正题, 还是继续看 List 流程, 可以看到调用了 store.List 方法, 实现为 boltdb 数据库。

func (l *local) List(ctx context.Context, req *imagesapi.ListImagesRequest, _ ...grpc.CallOption) (*imagesapi.ListImagesResponse, error) {
	images, err := l.store.List(ctx, req.Filters...)
	if err != nil {
		return nil, errdefs.ToGRPC(err)
	}

	return &imagesapi.ListImagesResponse{
		Images: imagesToProto(images),
	}, nil
}

    这里以 namespace 作为 bucket, 剩下的就从 boltdb 读取, key value 读取完成,  具体内容在后续 pull 镜像讲解。

func (s *imageStore) List(ctx context.Context, fs ...string) ([]images.Image, error) {
	namespace, err := namespaces.NamespaceRequired(ctx)
	if err != nil {
		return nil, err
	}

总结:

   list image 依赖 metadata 数据库 boltdb 读取 key,value

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值