docker graphdriver之aufs

aufs dirver的类型为:

type Driver struct {
    root       string
    sync.Mutex // Protects concurrent modification to active
    active     map[string]int
}

其中,root代表aufs驱动所在的根目录,为/var/lib/docker/aufs。active属性为map类型,key为DockerImage的ID,value为int类型,代表该层镜像layer被引用的次数总和。

aufs驱动类型的存储结构如下所示:

├── layers // Metadata of layers
│   ├── 1
│   ├── 2
│   └── 3
├── diff  // Content of the layer
│   ├── 1  // Contains layers that need to be mounted for the id
│   ├── 2
│   └── 3
└── mnt    // Mount points for the rw layers to be mounted
    ├── 1
    ├── 2
    └── 3

其中,layers、diff、mnt为目录/var/lib/docker/aufs下的三个子目录,1、2、3是镜像ID,分别代表三个镜像。三个目录下的1均代表同一个镜像ID。其中,layers目录下代表每一个镜像的元数据,这些元数据是这个镜像的祖先镜像ID列表;diff目录下存储每一个镜像所在的layer,具体包含的文件系统的内容;mnt目录下每一个文件都是一个镜像ID,代表在该层镜像之上挂载的读写的layer。


下面分析一些函数的作用:

func getParentIds(root, id string) ([]string, error) {
    f, err := os.Open(path.Join(root, "layers", id))
    ...
    out := []string{}
    s := bufio.NewScanner(f)
    for s.Scan() {
        if t := s.Text(); t != "" {
            out = append(out, s.Text())
        }
     }
     return out, s.Err()
 }

以上参数,root为/var/lib/docker/aufs,id为镜像ID,返回该镜像的所有父镜像的ID列表。


aufs这种文件系统的实现,在合并多个镜像时起到重要作用。创建镜像路径的源码如下所示:

func (a *Driver) Create(id, parent string) error {
    if err := a.createDirsFor(id); err != nil {
        return err
    }
    // Write the layers metadata
    f, err := os.Create(path.Join(a.rootPath(), "layers", id))
    if err != nil {
        return err
    }
    defer f.Close()
    
    if parent != "" {
        ids, err := getParentIds(a.rootPath(), parent)
        if err != nil {
            return err
        }
        if _, err := fmt.Fprintln(f, parent); err != nil {
            return err
        }
        for _, i := range ids {
            if _, err := fmt.Fprintln(f, i); err != nil {
                return err
            }
        }
   }
   return nil
}

1、docker daemon首先通过

f, err := os.Create(path.Join(a.rootPath(), "layers", id))

打开layers目录下镜像ID文件。

2、然后,通过

ids, err := getParentIds(a.rootPath(), parent)

获取父镜像的祖先镜像ID列表ids。

3、其次,将父镜像ID写入f。

4、最后,将父镜像的祖先镜像的ID列表写入f。



下面这个函数返回镜像的根目录,并且进行了读写层的挂载。

func (a *Driver) Get(id, mountLabel string) (string, error) {
    ids, err := getParentIds(a.rootPath(), id)
    if err != nil {
        if !os.IsNotExist(err) {
            return "", err
         }
         ids = []string{}
     }
     
     // Protect the a.active from concurrent access
     a.Lock()
     defer a.Unlock()
     
     count := a.active[id]
     
     // If a dir does not have a parent ( no layers )do not try to mount
     // just return the diff path to the data
     out := path.Join(a.rootPath(), "diff", id)
     if len(ids) > 0 {
         out = path.Join(a.rootPath(), "mnt", id)
         
         if count == 0 {
             if err := a.mount(id, mountLabel); err != nil {
                 return "", err
             }
         }
     }
     a.active[id] = count + 1
     return out, nil
}



补充:

aufs这种驱动同时实现了下面这个接口:

type Differ interface {
    Diff(id string) (archive.Archive, error)
    Changes(id string) ([]archive.Change, error)
    ApplyDiff(id string, diff archive.ArchiveReader) error
    DiffSize(id string) (bytes int64, err error)
}
// Returns an archive of the contents for the id
func (a *Driver) Diff(id string) (archive.Archive, error) {
    return archive.TarWithOptions(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
        Compression: archive.Uncompressed,
    })
}

func (a *Driver) ApplyDiff(id string, diff archive.ArchiveReader) error {
    return archive.Untar(diff, path.Join(a.rootPath(), "diff", id), nil)
}

// Returns the size of the contents for the id
func (a *Driver) DiffSize(id string) (int64, error) {
    return utils.TreeSize(path.Join(a.rootPath(), "diff", id))
}

func (a *Driver) Changes(id string) ([]archive.Change, error) {
    layers, err := a.getParentLayerPaths(id)
    if err != nil {
        return nil, err
    }
    return archive.Changes(layers, path.Join(a.rootPath(), "diff", id))
}


你可能感兴趣的:(docker graphdriver之aufs)