想要了解Docker Volume,首先我们需要知道Docker的文件系统是如何工作的。Docker镜像是由多个文件系统(只读层)叠加而成。当我们启动一个容器的时候,Docker会加载只读镜像层并在其上(译者注:镜像栈顶部)添加一个读写层。如果运行中的容器修改了现有的一个已经存在的文件,那该文件将会从读写层下面的只读层复制到读写层,该文件的只读版本仍然存在,只是已经被读写层中该文件的副本所隐藏。当删除Docker容器,并通过该镜像重新启动时,之前的更改将会丢失。在Docker中,只读层及在顶部的读写层的组合被称为(联合文件系统)。
为了能够保存(持久化)数据以及共享容器间的数据,Docker提出了Volume的概念。简单来说,Volume就是目录或者文件,它可以绕过默认的联合文件系统,而以正常的文件或者目录的形式存在于宿主机上。
Volume的接口方式:
// Volume is a place to store data. It is backed by a specific driver, and can be mounted.
type Volume interface {
// Name returns the name of the volume
Name() string
// DriverName returns the name of the driver which owns this volume.
DriverName() string
// Path returns the absolute path to the volume.
Path() string
// Mount mounts the volume and returns the absolute path to
// where it can be consumed.
Mount() (string, error)
// Unmount unmounts the volume when it is no longer in use.
Unmount() error
}
创建Root:
func New(scope string) (*Root, error) {
rootDirectory := filepath.Join(scope, volumesPathName)
if err := os.MkdirAll(rootDirectory, 0700); err != nil {
return nil, err
}
r := &Root{
scope: scope,
path: rootDirectory,
volumes: make(map[string]*localVolume),
}
dirs, err := ioutil.ReadDir(rootDirectory)
if err != nil {
return nil, err
}
//主要是初始化Root结构的Volumes的map[string]*localVolume
for _, d := range dirs {
name := filepath.Base(d.Name())
r.volumes[name] = &localVolume{
driverName: r.Name(),
name: name,
path: r.DataPath(name),
}
}
return r, nil
}
下面再来看看Root的结构:
type Root struct {
m sync.Mutex
scope string
path string
volumes map[string]*localVolume //主要是这个map结构
}
//Root 申明 Driver 的接口,方便管理volumes的创建和删除
//Driver的接口
type Driver interface {
// Name returns the name of the volume driver.
Name() string
// Create makes a new volume with the given id.
Create(name string, opts map[string]string) (Volume, error)
// Remove deletes the volume.
Remove(Volume) error
}
创建Volume:
func (r *Root) Create(name string, _ map[string]string) (volume.Volume, error) {
r.m.Lock()
defer r.m.Unlock() //创建的时候需要锁
v, exists := r.volumes[name]
if exists {
return v, nil
}
path := r.DataPath(name)
if err := os.MkdirAll(path, 0755); err != nil {
if os.IsExist(err) {
return nil, fmt.Errorf("volume already exists under %s", filepath.Dir(path))
}
return nil, err
}
v = &localVolume{
driverName: r.Name(),
name: name,
path: path,
}
r.volumes[name] = v
return v, nil
}
同理删除和获取volume的方法:
func (r *Root) Remove(v volume.Volume) error {
r.m.Lock()
defer r.m.Unlock()
lv, ok := v.(*localVolume)
if !ok {
return errors.New("unknown volume type")
}
realPath, err := filepath.EvalSymlinks(lv.path)
if err != nil {
return err
}
if !r.scopedPath(realPath) {
return fmt.Errorf("Unable to remove a directory of out the Docker root: %s", realPath)
}
if err := os.RemoveAll(realPath); err != nil {
return err
}
delete(r.volumes, lv.name)
return os.RemoveAll(filepath.Dir(lv.path))
}
// Get looks up the volume for the given name and returns it if found
func (r *Root) Get(name string) (volume.Volume, error) {
r.m.Lock()
v, exists := r.volumes[name]
r.m.Unlock()
if !exists {
return nil, ErrNotFound
}
return v, nil
}
具体什么时候调用local.New
func configureVolumes(config *Config) (*volumeStore, error) {
volumesDriver, err := local.New(config.Root)
if err != nil {
return nil, err
}
volumedrivers.Register(volumesDriver, volumesDriver.Name())
return newVolumeStore(volumesDriver.List()), nil
}
//在Daemon创建New Daemon时候Configure the volumes driver调用到
原文:http://my.oschina.net/yang1992/blog/526595