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 idfunc (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 idfunc (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))}