Docker基础之VolumeStore初始化

一、VolumeStore初始化

VolumeStore结构体函数记录着所有的卷volumes,同时跟踪它们的使用情况,相当于volume的一个cache,其文件目录为:/var/lib/docker/volumes
,同时metadata.db记录volume的元数据, 是一个文件型的数据库,其文件地址为:/var/lib/docker/volumes/metadata.db

二、创建一个VolumeStore对象

	//安装卷驱动器
	volStore, err := d.configureVolumes(rootUID, rootGID)
	
	func (daemon *Daemon) configureVolumes(rootUID, rootGID int) (*store.VolumeStore, error) {
	//创建一个volume驱动
	volumesDriver, err := local.New(daemon.configStore.Root, rootUID, rootGID)
	if err != nil {
		return nil, err
	}
	volumedrivers.RegisterPluginGetter(daemon.PluginStore)
	if !volumedrivers.Register(volumesDriver, volumesDriver.Name()) {
		return nil, fmt.Errorf("local volume driver could not be registered")
	}
	return store.New(daemon.configStore.Root)
}

configureVolumes()函数的工作流程如下:
1、创建一个volume驱动,volumesDriver
2、把驱动向volumedrivers注册
3、创建一个type VolumeStore struct对象,VolumeStore

三、volume驱动

核心由两个部分组成:
1、 Root构造函数是volume的驱动
2、localVolume构造函数则是一个volume

//New函数使用提供的范围实例化一个新的Root实例。Scope是Root实例用来存储其卷的基本路径。如果基本路径不存在,则在这里创建它。 
func New(scope string, rootUID, rootGID int) (*Root, error) {
	//scope: /var/lib/docker
	//数据盘的工作目录 /var/lib/docker/volumes
	rootDirectory := filepath.Join(scope, volumesPathName)
	if err := idtools.MkdirAllAs(rootDirectory, 0700, rootUID, rootGID); err != nil {
		return nil, err
	}
	//type Root struct 是volume的驱动
	//type localVolume struct 则是一个volume
	r := &Root{
		scope:   scope,
		path:    rootDirectory,
		volumes: make(map[string]*localVolume),
		rootUID: rootUID,
		rootGID: rootGID,
	}
	//ioutil.ReadDir(dirmane) 读取目录 dirmane 中的所有目录和文件(不包括子目录)
	//返回读取到的文件的信息列表和读取过程中遇到的任何错误
	//返回的文件列表是经过排序的
	dirs, err := ioutil.ReadDir(rootDirectory)
	if err != nil {
		return nil, err
	}
	//解析系统的/proc/self/mountinfo文件,获取docker daemon所有的挂载信息
	mountInfos, err := mount.GetMounts()
	if err != nil {
		logrus.Debugf("error looking up mounts for local volume cleanup: %v", err)
	}
	for _, d := range dirs {
		if !d.IsDir() {
			continue
		}
		//路径返回最后一个元素d.Name()的值
		name := filepath.Base(d.Name())
		//创建了一个localVolume对象
		v := &localVolume{
			driverName: r.Name(),
			name:       name,
			path:       r.DataPath(name),
		}
		//记录数据卷name和数据卷对象的关系
		r.volumes[name] = v
		//数据卷配置信息opt.json的路径:optsFilePath:/var/lib/docker/volumes/{volumeId}/opts.json
		optsFilePath := filepath.Join(rootDirectory, name, "opts.json")
		if b, err := ioutil.ReadFile(optsFilePath); err == nil {
			opts := optsConfig{}
			if err := json.Unmarshal(b, &opts); err != nil {
				return nil, errors.Wrapf(err, "error while unmarshaling volume options for volume: %s", name)
			}
			//确保这是不是空的optsConfig
			//因为这个可能为空,由于旧版本Docker的错误行为。
			if !reflect.DeepEqual(opts, optsConfig{}) {
				v.opts = &opts
			}
			// 卸载可能仍然挂载的任何东西(例如,不干净的关闭)这里重新挂载一个新的节点
			for _, info := range mountInfos {
				if info.Mountpoint == v.path {
					mount.Unmount(v.path)
					break
				}
			}
		}
	}

	return r, nil
}

1、Root构造函数
Root构造函数实现了Driver接口,主要负责管理卷(volumes)的创建/删除。仅支持标准的vfs命令,在其scope范围内进行目录的创建/删除。

// Root实现了卷包的Driver接口,并管理卷的创建/删除。它只使用标准vfs命令在其提供的范围内创建/删除dirs。

type Root struct {
	m       sync.Mutex
	scope   string
	path    string
	volumes map[string]*localVolume //记录数据卷name和数据卷对象的映射关系
	rootUID int
	rootGID int
}
// DataPath返回该卷的构造路径。
func (r *Root) DataPath(volumeName string) string {
	return filepath.Join(r.path, volumeName, VolumeDataPathName)
}
// Name返回根的名称,在DefaultDriverName常量的卷包中定义。
func (r *Root) Name() string {
	return volume.DefaultDriverName
}
const (
	VolumeDataPathName = "_data" //VolumeDataPathName是存储卷数据的目录名。
	volumesPathName    = "volumes" //
)

2、localVolume构造函数
localVolume代表了一个由volume驱动Root构造函数创建的卷(volume),实现了Volume接口。

// localVolume实现了来自卷包的Volume接口,并表示Root创建的卷。
type localVolume struct {
	m sync.Mutex
	name string //卷名称
	path string //Path是数据所在主机上的路径
	driverName string //driverName是创建卷的驱动程序的名称。
	opts *optsConfig //Opts是用于创建卷的选项的解析列表
	active activeMount //活动重新计数活动挂载
}

四、驱动向volumedrivers注册

驱动向volumedrivers注册的过程本质上就是建立一个映射关系

// 注册将给定的驱动关联到给定的名称,检查名称是否已经关联
func Register(extension volume.Driver, name string) bool {
	if name == "" {
		return false
	}
	drivers.Lock()
	defer drivers.Unlock()
	_, exists := drivers.extensions[name]
	if exists {
		return false
	}
	if err := validateDriver(extension); err != nil {
		return false
	}
	//建立一个映射关系
	drivers.extensions[name] = extension
	return true
}

五、VolumeStore构造函数

VolumeStore构造函数:记录着所有的volumes,同时跟踪它们的使用情况,相当于volume的一个管理器。

// 初始化一个VolumeStore来保持系统中卷的引用计数。.
func New(rootPath string) (*VolumeStore, error) {
	vs := &VolumeStore{
		locks:   &locker.Locker{},
		names:   make(map[string]volume.Volume),
		refs:    make(map[string][]string),
		labels:  make(map[string]map[string]string),
		options: make(map[string]map[string]string),
	}
	if rootPath != "" {
		//初始化元数据存储
		volPath := filepath.Join(rootPath, volumeDataDir)
		if err := os.MkdirAll(volPath, 750); err != nil {
			return nil, err
		}
		dbPath := filepath.Join(volPath, "metadata.db")
		var err error
		//创建和启动数据库
		vs.db, err = bolt.Open(dbPath, 0600, &bolt.Options{Timeout: 1 * time.Second})
		if err != nil {
			return nil, errors.Wrap(err, "error while opening volume store metadata database")
		}
		//初始化卷桶
		if err := vs.db.Update(func(tx *bolt.Tx) error {
			//创建一个名为volumes的bucket对象
			if _, err := tx.CreateBucketIfNotExists(volumeBucketName); err != nil {
				return errors.Wrap(err, "error while setting up volume store metadata database")
			}
			return nil
		}); err != nil {
			return nil, err
		}
	}
	//把metadata.db中的元数据信息填充到VolumeStore vs中
	vs.restore()
	return vs, nil
}

1、VolumeStore定义声明

//VolumeStore构造函数记录着所有的volumes,同时跟踪它们的使用情况
type VolumeStore struct {	
	locks *locker.Locker// locks 确保一次只在一个特定卷上执行一个动作,而不会锁定整个存储区,因为卷上的动作可能相当缓慢,这确保了存储区可以自由处理其他卷的请求。
	globalLock sync.RWMutex //globalLock用于保护对store对象使用的可变结构的访问
	names map[string]volume.Volume //存储volume name和volume本身的映射关系
	refs map[string][]string //记录volume name和引用了该volume的对象的映射关系
	labels map[string]map[string]string//记录每一个volume的标签
	options map[string]map[string]string//记录了每一个volume的属性
	db      *bolt.DB
}

2、restore函数
restore()函数负责把/var/lib/docker/volumes/metadata.db中的元数据信息填充到VolumeStore的vs中

//它的主要目的是确保重启后所有驱动程序的refcounts都是基于已知的卷进行设置。 I
//这是尝试跟踪实际存储在磁盘上的db中的卷。
// 它不探测可用的驱动程序来发现任何可能已经添加到带之外的东西。
func (s *VolumeStore) restore() {
	var ls []volumeMetadata
	s.db.View(func(tx *bolt.Tx) error {
		ls = listMeta(tx)
		return nil
	})
	chRemove := make(chan *volumeMetadata, len(ls))
	var wg sync.WaitGroup
	//遍历每一个元数据(k,v)键值对
	for _, meta := range ls {
		wg.Add(1)
		// 这可能是一个非常缓慢的操作,所以用goroutine来做
		go func(meta volumeMetadata) {
			defer wg.Done()
			var v volume.Volume
			var err error
			if meta.Driver != "" {
				//从指定的驱动中获取对应volume名称的Volume
				v, err = lookupVolume(meta.Driver, meta.Name)
				if err != nil && err != errNoSuchVolume {
					logrus.WithError(err).WithField("driver", meta.Driver).WithField("volume", meta.Name).Warn("Error restoring volume")
					return
				}
				if v == nil {
					// 该drive中找到不该volume,准备删除该volume
					chRemove <- &meta
					return
				}
			} else {
				//如果没有指定驱动则从VolumeStore中获取Volume
				v, err = s.getVolume(meta.Name)
				if err != nil {
					if err == errNoSuchVolume {
						chRemove <- &meta
					}
					return
				}
				//更新元数据内容meta到数据库
				meta.Driver = v.DriverName()
				if err := s.setMeta(v.Name(), meta); err != nil {
					logrus.WithError(err).WithField("driver", meta.Driver).WithField("volume", v.Name()).Warn("Error updating volume metadata on restore")
				}
			}

			// increment函数驱动refcount函数
			volumedrivers.CreateDriver(meta.Driver)
			//卷的缓冲池
			s.globalLock.Lock()
			s.options[v.Name()] = meta.Options
			s.labels[v.Name()] = meta.Labels
			s.names[v.Name()] = v
			s.globalLock.Unlock()
		}(meta)
	}

	wg.Wait()
	close(chRemove)
	//异常处理:删除找不到的volume
	s.db.Update(func(tx *bolt.Tx) error {
		for meta := range chRemove {
			if err := removeMeta(tx, meta.Name); err != nil {
				logrus.WithField("volume", meta.Name).Warnf("Error removing stale entry from volume db: %v", err)
			}
		}
		return nil
	})
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

绝域时空

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值