层级存储仓库(LayerStore)存储docker容器镜像的基础单位,一个镜像是由多个Layer组成,各个存储体之间有重叠的部分,这两个部分之间存储有差别化信息,这些来自于用户的修改。下面我将从Layer的构造函数和NewStoreFromOptions函数的角度来了解LayerStore的初始化。

一、LayerStore构造函数

layerStore结构体记录一个hots主机上面存储着的所有的layer层信息,包括只读layer和读写layer。
其工作目录是/var/lib/docker/image/${graphDriverName}/layerdb,其构造函数如下:

type layerStore struct {
	store  MetadataStore
	driver graphdriver.Driver //文件系统的驱动
	layerMap map[ChainID]*roLayer //存放镜像的只读layer信息,/var/lib/docker/image/overlay/layerdb/sha256/
	layerL   sync.Mutex
	mounts map[string]*mountedLayer //存放可读写层信息,/var/lib/docker/image/overlay/layerdb/mounts/
	mountL sync.Mutex
}

1、ChainID和DiffID申明

//ChainID 是一个用来寻找一个layer层内容的ID编号
type ChainID digest.Digest
//DiffID是一个layer.tar的hash,计算方法 sha256sum ./layer.tar
type DiffID digest.Digest

2、MetadataStore接口函数
MetadataStore定义操作layer及其元数据的接口

// MetadataStore 表示一个后端,用于持久保存有关层级的元数据并提供用于还原存储的元数据。
type MetadataStore interface {
	//StartTransaction 会启动新元数据的更新,这些元数据将用于在提交时表示 ID。
	StartTransaction() (MetadataTransaction, error)

	GetSize(ChainID) (int64, error) //获取层级数据大小
	GetParent(ChainID) (ChainID, error)
	GetDiffID(ChainID) (DiffID, error)
	GetCacheID(ChainID) (string, error) //获取缓存大小
	GetDescriptor(ChainID) (distribution.Descriptor, error) //获取描述信息
	TarSplitReader(ChainID) (io.ReadCloser, error)

	SetMountID(string, string) error //设置挂载ID
	SetInitID(string, string) error //设置初始化ID
	SetMountParent(string, ChainID) error 

	GetMountID(string) (string, error) //获取挂载ID
	GetInitID(string) (string, error)
	GetMountParent(string) (ChainID, error)
	//返回所有的只读layer和读写layer
	List() ([]ChainID, []string, error)

	Remove(ChainID) error //移除ChainID
	RemoveMount(string) error //移除挂载
}

3、Driver接口
下面我介绍驱动接口的函数

//下面设计驱动接口的定义
type Driver interface {
	ProtoDriver
	DiffDriver
}

type ProtoDriver interface {
	// 返回驱动程序的字符串表示形式。  
	String() string
	// CreateReadWrite创建了一个新的空文件系统层,可以用作容器的存储层。 额外的选项可以在选项中传递。 Parent可以是""而opts可以是nil。
	CreateReadWrite(id, parent string, opts *CreateOpts) error
	//Create创建一个新的、空的文件系统层,使用指定的id和父级以及传递给opts的选项。 Parent可以是""而opts可以是nil。  
	Create(id, parent string, opts *CreateOpts) error
	// Remove attempts to remove the filesystem layer with this id.
	Remove(id string) error
	// Get返回这个id所引用的分层文件系统的挂载点。您可以选择指定一个mountLabel或""。返回安装的分层文件系统的绝对路径。
	Get(id, mountLabel string) (dir string, err error)
	// 释放指定id的系统资源
	Put(id string) error
	// Exists返回驱动器上是否存在指定ID的文件系统层
	Exists(id string) bool
	// Status返回一组键值对,这些键值对给出关于这个驱动程序的低级诊断状态。
	Status() [][2]string
	// 返回一组键值对,这些键值对给出映像/容器驱动程序正在管理的低层信息。
	GetMetadata(id string) (map[string]string, error)
	// 清理执行必要的任务来释放驱动程序所拥有的资源,例如,卸载这个驱动程序所知道的所有分层文件系统。
	Cleanup() error
}

	// DiffDriver接口是继承于graph diffs
type DiffDriver interface {
	// Diff生成指定层与父层(可能是“”)之间更改的存档。
	Diff(id, parent string) (io.ReadCloser, error)
	// Changes在指定的层和它的父层之间产生一个变化列表。如果parent是"",那么所有的更改都将是ADD更改。
	Changes(id, parent string) ([]archive.Change, error)
	// ApplyDiff从给定的diff提取变更集到具有指定id和父层的层中,以字节为单位返回新层的大小。存档。读取器必须是未压缩的流。
	ApplyDiff(id, parent string, diff io.Reader) (size int64, err error)
	// DiffSize计算指定id与其父目录之间的更改,并返回相对于其基文件系统目录的更改的大小(以字节为单位)。
	DiffSize(id, parent string) (size int64, err error)
}

二、NewStoreFromOptions函数

创建daemon的过程中,正是调用此方法。其流程如下:
1、新建一个Driver
2、新建一个MetadataStore fms,这个比较简单,就是建立文件夹 /var/lib/docker/image/overlay/layerdb,同时提供操作接口。
3、 基于type fileMetadataStore struct和graph driver构建一个type layerStore struct对象

// NewStoreFromOptions函数创建了新的Store接口
func NewStoreFromOptions(options StoreOptions) (Store, error) {
	driver, err := graphdriver.New(options.GraphDriver, options.PluginGetter, graphdriver.Options{
		Root:                options.StorePath,
		DriverOptions:       options.GraphDriverOptions,
		UIDMaps:             options.UIDMaps,
		GIDMaps:             options.GIDMaps,
		ExperimentalEnabled: options.ExperimentalEnabled,
	})
	if err != nil {
		return nil, fmt.Errorf("error initializing graphdriver: %v", err)
	}
	logrus.Debugf("Using graph driver %s", driver)
//建立文件夹 /var/lib/docker/image/overlay/layerdb,同时提供操作接口(实现type MetadataStore interface)
	fms, err := NewFSMetadataStore(fmt.Sprintf(options.MetadataStorePathTemplate, driver))
	if err != nil {
		return nil, err
	}
//基于type fileMetadataStore struct和graph driver构建一个type layerStore struct对象
	return NewStoreFromGraphDriver(fms, driver)
}

1、 创建一个graphdriver
根据优先级priority来得到文件系统驱动,然后调用已经注册好的文件系统驱动初始化函数initFunc来初始化驱动

// New函数在指定的根目录下创建驱动程序并初始化它。
func New(name string, pg plugingetter.PluginGetter, config Options) (Driver, error) {
	if name != "" {
		/*用户自行自定了graphdriver*/
		logrus.Debugf("[graphdriver] trying provided driver: %s", name) //日志显示了指定的驱动程序
		return GetDriver(name, pg, config)
	}
//获取文件系统驱动,首先根据优先级priority来得到文件系统驱动,然后调用已经注册好的文件系统驱动初始化函数initFunc来初始化驱动。driver和initFunc映射关系的构建是通过各个驱动初始化的时候,自行调用func Register()来注册
	driversMap := scanPriorDrivers(config.Root)
	for _, name := range priority {
		// of the state found from prior drivers, check in order of our priority which we would prefer
		driver, err := getBuiltinDriver(name, config.Root, config.DriverOptions, config.UIDMaps, config.GIDMaps)
		...
		...
	}
	...
	...
}

各个文件系统会在初始化的时候调用func Register,把自身的func Init()注册到drivers中

//Register为驱动程序注册一个InitFunc。
func Register(name string, initFunc InitFunc) error {
	if _, exists := drivers[name]; exists {
		return fmt.Errorf("Name already registered %s", name)
	}
	drivers[name] = initFunc

	return nil
}

以overlay为例子,/daemon/graphdriver/overlay/overlay.go

func init() {
	graphdriver.Register("overlay", Init)
}

func NewStoreFromGraphDriver

目录/var/lib/docker/image/overlay/layerdb下,有着sha256目录、mounts目录

// NewStoreFromGraphDriver creates a new Store instance using the provided
// metadata store and graph driver. The metadata store will be used to restore
// the Store.
func NewStoreFromGraphDriver(store MetadataStore, driver graphdriver.Driver) (Store, error) {
	ls := &layerStore{
		store:    store,
		driver:   driver,
		layerMap: map[ChainID]*roLayer{},
		mounts:   map[string]*mountedLayer{},
	}

	/*
		/layer/filestore.go
			==>func (fms *fileMetadataStore) List() ([]ChainID, []string, error)
		根据fileMetadataStore中的root目录(即/var/lib/docker/image/overlay/layerdb)加载
		其下的sha256目录(ids)和mounts目录(mounts)中的信息
	*/
	ids, mounts, err := store.List()
	if err != nil {
		return nil, err
	}
	/*
		开始遍历sha256目录(ids),所有的只读layer		
	*/
	for _, id := range ids {
		/*
			根据sha256下的id信息加载镜像的只读layer信息:
			包括diff(所有镜像层diff是根据镜像内容使用sha256算法得到),size,cacheID,parent,descriptor。
			然后存放到ls.layerMap[ChainID]中去。
		*/
		l, err := ls.loadLayer(id)
		if err != nil {
			logrus.Debugf("Failed to load layer %s: %s", id, err)
			continue
		}
		if l.parent != nil {
			l.parent.referenceCount++
		}
	}
	/*
		同理,
		开始遍历mounts目录(mounts),所有的读写layer
		目录名一般为容器id
		目录下有三份文件,分别是init-id,mounts-id,parent
		分别对应可读写层初始化层id,可读写层id以及父镜像层的ChainID
		通过init-id,mounts-id,能在 /var/lib/docker/overlay/ 下找到对应的目录
		通过parent能在 /var/lib/docker/image/overlay/layerdb/sha256/ 下找到对应目录
	*/
	for _, mount := range mounts {
		if err := ls.loadMount(mount); err != nil {
			logrus.Debugf("Failed to load mount %s: %s", mount, err)
		}
	}

	return ls, nil
}

简单看一下loadLayer()和loadMount()

func (ls *layerStore) loadLayer(layer ChainID) (*roLayer, error) {
	cl, ok := ls.layerMap[layer]
	if ok {
		//如果该层layer已经被记录到ls.layerMap中,直接return即可
		return cl, nil
	}

	diff, err := ls.store.GetDiffID(layer)
	if err != nil {
		return nil, fmt.Errorf("failed to get diff id for %s: %s", layer, err)
	}

	size, err := ls.store.GetSize(layer)
	if err != nil {
		return nil, fmt.Errorf("failed to get size for %s: %s", layer, err)
	}

	cacheID, err := ls.store.GetCacheID(layer)
	if err != nil {
		return nil, fmt.Errorf("failed to get cache id for %s: %s", layer, err)
	}

	parent, err := ls.store.GetParent(layer)
	if err != nil {
		return nil, fmt.Errorf("failed to get parent for %s: %s", layer, err)
	}

	descriptor, err := ls.store.GetDescriptor(layer)
	if err != nil {
		return nil, fmt.Errorf("failed to get descriptor for %s: %s", layer, err)
	}

	cl = &roLayer{
		chainID:    layer,
		diffID:     diff,
		size:       size,
		cacheID:    cacheID,
		layerStore: ls,
		references: map[Layer]struct{}{},
		descriptor: descriptor,
	}

	if parent != "" {
		p, err := ls.loadLayer(parent)
		if err != nil {
			return nil, err
		}
		cl.parent = p
	}
	/*
		把新的一层layer记录到ls.layerMap[chainID]中
	*/
	ls.layerMap[cl.chainID] = cl

	return cl, nil
}

func (ls *layerStore) loadMount(mount string) error {
	if _, ok := ls.mounts[mount]; ok {
		return nil
	}

	mountID, err := ls.store.GetMountID(mount)
	if err != nil {
		return err
	}

	initID, err := ls.store.GetInitID(mount)
	if err != nil {
		return err
	}

	parent, err := ls.store.GetMountParent(mount)
	if err != nil {
		return err
	}

	ml := &mountedLayer{
		name:       mount,
		mountID:    mountID,
		initID:     initID,
		layerStore: ls,
		references: map[RWLayer]*referencedRWLayer{},
	}

	if parent != "" {
		p, err := ls.loadLayer(parent)
		if err != nil {
			return err
		}
		ml.parent = p

		p.referenceCount++
	}
	/*
		加载一层读写layer到ls.mounts[container_id]
	*/
	ls.mounts[ml.name] = ml

	return nil
}