一、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
})
}