layStore负责Docker的layer的管理,具体有镜像层的管理,容器init层的管理,容器读写层的管理。layerStore的管理目录为/var/lib/docker/image/aufs/layerdb,主要有以下目录:

  1. mounts: 存储容器挂载信息,有init-id, mount-id, parent文件。
  2. sha256: 存储镜像层的信息,有cache-id(aufs的存储位置), diff, size和tar-split.json.gz。

本次分析将介绍layStore是如何管理这些文件目录的。

首先,要先介绍直接与文件打交道的filestore。

filestore相关

fileMetadataStore

先来介绍fileMetadataStore,fileMetadataStore用来读取layerdb目录下的文件,定义在/layer/filestore.go中:

1
2
3
4
//***设置mounts目录下的内容***//
type fileMetadataStore struct {
root string
}

NewFSMetadataStore()

NewFSMetadataStore()可以创建layerdb的根目录,并生成一个新的fileMetadataStore。

1
2
3
4
5
6
7
8
9
10
11
// NewFSMetadataStore returns an instance of a metadata store
// which is backed by files on disk using the provided root
// as the root of metadata files.
func NewFSMetadataStore(root string) (MetadataStore, error) {
if err := os.MkdirAll(root, 0700); err != nil {
return nil, err
}
return &fileMetadataStore{
root: root,
}, nil
}

getLayerDirectory()

getLayerDirectory()返回/var/lib/docker/image/aufs/layerdb/sha256/xxxxxxxxxxxxxxxx目录。

1
2
3
4
5
//***dgst.Algorithm()返回"sha256"***//
func (fms *fileMetadataStore) getLayerDirectory(layer ChainID) string {
dgst := digest.Digest(layer)
return filepath.Join(fms.root, string(dgst.Algorithm()), dgst.Hex())
}

getLayerFilename()

getLayerFilename()用来获取sha256目录具体层目录下的具体文件。

1
2
3
4
//***filename为size等***//
func (fms *fileMetadataStore) getLayerFilename(layer ChainID, filename string) string {
return filepath.Join(fms.getLayerDirectory(layer), filename)
}

getMountDirectory()

getMountDirectory()返回/var/lib/docker/image/aufs/layerdb/mounts/xxxxxxxxxx目录。

1
2
3
4
//***返回/var/lib/docker/image/aufs/layerdb/mounts/xxxxxxxxxx***//
func (fms *fileMetadataStore) getMountDirectory(mount string) string {
return filepath.Join(fms.root, "mounts", mount)
}

getMountFilename()

1
2
3
4
//***获取mounts目录下的具体文件***//
func (fms *fileMetadataStore) getMountFilename(mount, filename string) string {
return filepath.Join(fms.getMountDirectory(mount), filename)
}

StartTransaction()

设置临时目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//***设置临时目录***//
func (fms *fileMetadataStore) StartTransaction() (MetadataTransaction, error) {
tmpDir := filepath.Join(fms.root, "tmp")
if err := os.MkdirAll(tmpDir, 0755); err != nil {
return nil, err
}
td, err := ioutil.TempDir(tmpDir, "layer-")
if err != nil {
return nil, err
}
// Create a new tempdir
return &fileMetadataTransaction{
store: fms,
root: td,
}, nil
}

GetSize()

GetSize()用于获取size文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//***获取size***//
func (fms *fileMetadataStore) GetSize(layer ChainID) (int64, error) {
content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "size"))
if err != nil {
return 0, err
}
size, err := strconv.ParseInt(string(content), 10, 64)
if err != nil {
return 0, err
}
return size, nil
}

GetParent()

GetParent()用于获取parent文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//***获取parent***//
func (fms *fileMetadataStore) GetParent(layer ChainID) (ChainID, error) {
content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "parent"))
if err != nil {
if os.IsNotExist(err) {
return "", nil
}
return "", err
}
dgst, err := digest.ParseDigest(strings.TrimSpace(string(content)))
if err != nil {
return "", err
}
return ChainID(dgst), nil
}

GetDiffID()

GetDiffID()用于获取diff文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//***获取diff***//
func (fms *fileMetadataStore) GetDiffID(layer ChainID) (DiffID, error) {
content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "diff"))
if err != nil {
return "", err
}
dgst, err := digest.ParseDigest(strings.TrimSpace(string(content)))
if err != nil {
return "", err
}
return DiffID(dgst), nil
}

GetCacheID()

GetCacheID()用于获取cache-id

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//***获取cache-id***//
func (fms *fileMetadataStore) GetCacheID(layer ChainID) (string, error) {
contentBytes, err := ioutil.ReadFile(fms.getLayerFilename(layer, "cache-id"))
if err != nil {
return "", err
}
content := strings.TrimSpace(string(contentBytes))
if !stringIDRegexp.MatchString(content) {
return "", errors.New("invalid cache id value")
}
return content, nil
}

GetDescriptor()

??????不知道什么用途

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//***获取descriptor.json***//
func (fms *fileMetadataStore) GetDescriptor(layer ChainID) (distribution.Descriptor, error) {
content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "descriptor.json"))
if err != nil {
if os.IsNotExist(err) {
// only return empty descriptor to represent what is stored
return distribution.Descriptor{}, nil
}
return distribution.Descriptor{}, err
}
var ref distribution.Descriptor
err = json.Unmarshal(content, &ref)
if err != nil {
return distribution.Descriptor{}, err
}
return ref, err
}

TarSplitReader()

TarSplitReader()可以读取tar-split.json.gz,但tar-split.json.gz的作用目录还不清楚。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//***处理tar-split.json.gz***//
func (fms *fileMetadataStore) TarSplitReader(layer ChainID) (io.ReadCloser, error) {
fz, err := os.Open(fms.getLayerFilename(layer, "tar-split.json.gz"))
if err != nil {
return nil, err
}
f, err := gzip.NewReader(fz)
if err != nil {
return nil, err
}
return ioutils.NewReadCloserWrapper(f, func() error {
f.Close()
return fz.Close()
}), nil
}

SetMountID()

SetMountID()可以在mounts目录下设置mount-id文件。

1
2
3
4
5
6
7
//***设置mount-id***//
func (fms *fileMetadataStore) SetMountID(mount string, mountID string) error {
if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil {
return err
}
return ioutil.WriteFile(fms.getMountFilename(mount, "mount-id"), []byte(mountID), 0644)
}

SetInitID()

SetInitID()可以在mounts目录下设置init-id文件。

1
2
3
4
5
6
7
//***设置init-id***//
func (fms *fileMetadataStore) SetInitID(mount string, init string) error {
if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil {
return err
}
return ioutil.WriteFile(fms.getMountFilename(mount, "init-id"), []byte(init), 0644)
}

SetMountParent()

SetMountParent()可以在mounts目录下设置parent文件。

1
2
3
4
5
6
7
//***设置parent***//
func (fms *fileMetadataStore) SetMountParent(mount string, parent ChainID) error {
if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil {
return err
}
return ioutil.WriteFile(fms.getMountFilename(mount, "parent"), []byte(digest.Digest(parent).String()), 0644)
}

GetMountID()

GetMountID()用于获取mounts目录下的mount-id文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//***获取mount-id***//
func (fms *fileMetadataStore) GetMountID(mount string) (string, error) {
contentBytes, err := ioutil.ReadFile(fms.getMountFilename(mount, "mount-id"))
if err != nil {
return "", err
}
content := strings.TrimSpace(string(contentBytes))
if !stringIDRegexp.MatchString(content) {
return "", errors.New("invalid mount id value")
}
return content, nil
}

GetInitID()

GetInitID()用于获取mounts目录下的init-id文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//***获取init-id***//
func (fms *fileMetadataStore) GetInitID(mount string) (string, error) {
contentBytes, err := ioutil.ReadFile(fms.getMountFilename(mount, "init-id"))
if err != nil {
if os.IsNotExist(err) {
return "", nil
}
return "", err
}
content := strings.TrimSpace(string(contentBytes))
if !stringIDRegexp.MatchString(content) {
return "", errors.New("invalid init id value")
}
return content, nil
}

GetMountParent()

GetMountParent()用于获取mounts目录下的parent文件。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//***获取parent***//
func (fms *fileMetadataStore) GetMountParent(mount string) (ChainID, error) {
content, err := ioutil.ReadFile(fms.getMountFilename(mount, "parent"))
if err != nil {
if os.IsNotExist(err) {
return "", nil
}
return "", err
}
dgst, err := digest.ParseDigest(strings.TrimSpace(string(content)))
if err != nil {
return "", err
}
return ChainID(dgst), nil
}

List()

用于获取ids和mounts。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//***获取ids, mounts***//
func (fms *fileMetadataStore) List() ([]ChainID, []string, error) {
var ids []ChainID
for _, algorithm := range supportedAlgorithms {
fileInfos, err := ioutil.ReadDir(filepath.Join(fms.root, string(algorithm)))
if err != nil {
if os.IsNotExist(err) {
continue
}
return nil, nil, err
}
for _, fi := range fileInfos {
if fi.IsDir() && fi.Name() != "mounts" {
dgst := digest.NewDigestFromHex(string(algorithm), fi.Name())
if err := dgst.Validate(); err != nil {
logrus.Debugf("Ignoring invalid digest %s:%s", algorithm, fi.Name())
} else {
ids = append(ids, ChainID(dgst))
}
}
}
}
fileInfos, err := ioutil.ReadDir(filepath.Join(fms.root, "mounts"))
if err != nil {
if os.IsNotExist(err) {
return ids, []string{}, nil
}
return nil, nil, err
}
var mounts []string
for _, fi := range fileInfos {
if fi.IsDir() {
mounts = append(mounts, fi.Name())
}
}
return ids, mounts, nil
}

Remove()

Remove()移除layerdb中的镜像层

1
2
3
4
//***删除/var/lib/docker/image/aufs/layerdb/目录下指定layer目录***//
func (fms *fileMetadataStore) Remove(layer ChainID) error {
return os.RemoveAll(fms.getLayerDirectory(layer))
}

RemoveMount()

RemoveMount()移除layerdb中mounts目录下的内容。

1
2
3
4
//***删除/var/lib/docker/image/aufs/layerdb/mounts***//
func (fms *fileMetadataStore) RemoveMount(mount string) error {
return os.RemoveAll(fms.getMountDirectory(mount))
}

fileMetadataTransaction

再来介绍fileMetadataTransaction。fileMetadataTransaction用来创建一个临时文件夹,再全部的操作完成后,再把临时文件夹重命名成正式文件夹。
fileMetadataTransaction定义在/layer/filiestore.go中:

1
2
3
4
5
//***设置layerdb/sha256目录下的内容***//
type fileMetadataTransaction struct {
store *fileMetadataStore
root string
}

fileMetaDataStore的StartTransaction()方法可以生成一个新的fileMetadataTransaction。

SetSize()

往临时文件夹中写入size。

1
2
3
4
5
//***设置size***//
func (fm *fileMetadataTransaction) SetSize(size int64) error {
content := fmt.Sprintf("%d", size)
return ioutil.WriteFile(filepath.Join(fm.root, "size"), []byte(content), 0644)
}

SetParent()

往临时文件夹中写入parent。

1
2
3
4
//***设置parent***//
func (fm *fileMetadataTransaction) SetParent(parent ChainID) error {
return ioutil.WriteFile(filepath.Join(fm.root, "parent"), []byte(digest.Digest(parent).String()), 0644)
}

SetDiffID()

往临时文件夹中写入diff。

1
2
3
4
//***设置diff***//
func (fm *fileMetadataTransaction) SetDiffID(diff DiffID) error {
return ioutil.WriteFile(filepath.Join(fm.root, "diff"), []byte(digest.Digest(diff).String()), 0644)
}

SetCacheID()

往临时文件夹中写入cache-id。

1
2
3
4
//***设置cache-id***//
func (fm *fileMetadataTransaction) SetCacheID(cacheID string) error {
return ioutil.WriteFile(filepath.Join(fm.root, "cache-id"), []byte(cacheID), 0644)
}

SetDescriptor()

往临时文件夹中写入descriptor.json。

1
2
3
4
5
6
7
func (fm *fileMetadataTransaction) SetDescriptor(ref distribution.Descriptor) error {
jsonRef, err := json.Marshal(ref)
if err != nil {
return err
}
return ioutil.WriteFile(filepath.Join(fm.root, "descriptor.json"), jsonRef, 0644)
}

TarSplitWriter()

往临时文件夹中写入tar-split.json.gz。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//***设置tar-split.json.gz***//
func (fm *fileMetadataTransaction) TarSplitWriter(compressInput bool) (io.WriteCloser, error) {
f, err := os.OpenFile(filepath.Join(fm.root, "tar-split.json.gz"), os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return nil, err
}
var wc io.WriteCloser
if compressInput {
wc = gzip.NewWriter(f)
} else {
wc = f
}
return ioutils.NewWriteCloserWrapper(wc, func() error {
wc.Close()
return f.Close()
}), nil
}

Commit()

把临时目录重命名成正式目录。

1
2
3
4
5
6
7
8
//***把临时目录重命名成正式目录***//
func (fm *fileMetadataTransaction) Commit(layer ChainID) error {
finalDir := fm.store.getLayerDirectory(layer)
if err := os.MkdirAll(filepath.Dir(finalDir), 0755); err != nil {
return err
}
return os.Rename(fm.root, finalDir)
}

Cancel()

Cancel()用来删除临时文件夹。

1
2
3
4
//***删除该layer信息***//
func (fm *fileMetadataTransaction) Cancel() error {
return os.RemoveAll(fm.root)
}

layerStore

再来看layerStore。layerStore定义了layerdb真正意义上的操作,定义在/layer/layer_store.go中:

1
2
3
4
5
6
7
8
9
10
11
12
13
type layerStore struct {
//***即fileMetadataStore,定义在/filestore.go中***//
store MetadataStore
driver graphdriver.Driver
//***缓存layer用***//
layerMap map[ChainID]*roLayer
layerL sync.Mutex
//***缓存mount用***//
mounts map[string]*mountedLayer
mountL sync.Mutex
}

可以看出,layerStore中有一个store,即fileMetadataStore;一个driver,默认为aufs driver;一个layerMap用来缓存layer;一个mounts用来缓存mountedLayer。

NewStoreFromGraphDriver()

NewStoreFromGraphDriver()可以创建一个layerStore。流程如下:

  1. 使用store.List()获取ids和mounts;
  2. 通过loadLayer()方法导入所有layer信息;
  3. 通过loadMount()方法导入所有mount信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// NewStoreFromGraphDriver creates a new Store instance using the provided
// metadata store and graph driver. The metadata store will be used to restore
// the Store.
func NewStoreFromGraphDriver(store MetadataStore, driver graphdriver.Driver) (Store, error) {
ls := &layerStore{
store: store,
driver: driver,
layerMap: map[ChainID]*roLayer{},
mounts: map[string]*mountedLayer{},
}
ids, mounts, err := store.List()
if err != nil {
return nil, err
}
for _, id := range ids {
l, err := ls.loadLayer(id)
if err != nil {
logrus.Debugf("Failed to load layer %s: %s", id, err)
continue
}
if l.parent != nil {
l.parent.referenceCount++
}
}
for _, mount := range mounts {
if err := ls.loadMount(mount); err != nil {
logrus.Debugf("Failed to load mount %s: %s", mount, err)
}
}
return ls, nil
}

loadLayer()

loadLayer()用于导入一个layer。主要流程如下:

  1. 如果layer在layerMap已缓存,则直接返回;
  2. 获取layer目录下的diff文件;
  3. 获取layer目录下的size文件;
  4. 获取layer目录下的cache-id文件;
  5. 获取layer目录下的parent文件;
  6. 获取layer目录下的descriptor文件;
  7. 构造roLayer表示只读层;
  8. 把roLayer缓存到layerMap中;
  9. 返回roLayer。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//***读取某layer信息并缓存***//
func (ls *layerStore) loadLayer(layer ChainID) (*roLayer, error) {
cl, ok := ls.layerMap[layer]
if ok {
return cl, nil
}
//***获取layerdb目录下diff***//
diff, err := ls.store.GetDiffID(layer)
if err != nil {
return nil, fmt.Errorf("failed to get diff id for %s: %s", layer, err)
}
//***获取layerdb目录下size***//
size, err := ls.store.GetSize(layer)
if err != nil {
return nil, fmt.Errorf("failed to get size for %s: %s", layer, err)
}
//***获取layerdb目录下cache-id***//
cacheID, err := ls.store.GetCacheID(layer)
if err != nil {
return nil, fmt.Errorf("failed to get cache id for %s: %s", layer, err)
}
//***获取layerdb目录下parent***//
parent, err := ls.store.GetParent(layer)
if err != nil {
return nil, fmt.Errorf("failed to get parent for %s: %s", layer, err)
}
//***获取layerdb目录下descriptor***//
descriptor, err := ls.store.GetDescriptor(layer)
if err != nil {
return nil, fmt.Errorf("failed to get descriptor for %s: %s", layer, err)
}
//***生成roLayer***//
cl = &roLayer{
chainID: layer,
diffID: diff,
size: size,
cacheID: cacheID,
layerStore: ls,
references: map[Layer]struct{}{},
descriptor: descriptor,
}
if parent != "" {
p, err := ls.loadLayer(parent)
if err != nil {
return nil, err
}
cl.parent = p
}
//***加入到layerStore的layerMap中***//
ls.layerMap[cl.chainID] = cl
return cl, nil
}

loadMount()

loadMount()用于导入一个mount。主要流程如下:

  1. 如果mouns中已有缓存,则直接返回;
  2. 获取mount目录下的mount-id文件;
  3. 获取mount目录下的init-id文件;
  4. 获取mount目录下的parent文件;
  5. 构造mountLayer;
  6. 把mountLayer缓存到mounts中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//***读取某mount信息并缓存***//
func (ls *layerStore) loadMount(mount string) error {
if _, ok := ls.mounts[mount]; ok {
return nil
}
//***获取mount-id***//
mountID, err := ls.store.GetMountID(mount)
if err != nil {
return err
}
//***获取init-id***//
initID, err := ls.store.GetInitID(mount)
if err != nil {
return err
}
//***获取parent***//
parent, err := ls.store.GetMountParent(mount)
if err != nil {
return err
}
//***生成mountedLayer***//
ml := &mountedLayer{
name: mount,
mountID: mountID,
initID: initID,
layerStore: ls,
references: map[RWLayer]*referencedRWLayer{},
}
if parent != "" {
p, err := ls.loadLayer(parent)
if err != nil {
return err
}
ml.parent = p
p.referenceCount++
}
//***加入到layerStore的mounts中***//
ls.mounts[ml.name] = ml
return nil
}

applyTar()

applyTar()通过调用driver的ApplyDiff()把镜像层的tar包解压到指定位置。具体解压过程见driver的分析过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//***处理tar文件***//
func (ls *layerStore) applyTar(tx MetadataTransaction, ts io.Reader, parent string, layer *roLayer) error {
digester := digest.Canonical.New()
//***从ts中读取内容写入到digester.Hash()中***//
tr := io.TeeReader(ts, digester.Hash())
tsw, err := tx.TarSplitWriter(true)
if err != nil {
return err
}
metaPacker := storage.NewJSONPacker(tsw)
defer tsw.Close()
// we're passing nil here for the file putter, because the ApplyDiff will
// handle the extraction of the archive
rdr, err := asm.NewInputTarStream(tr, metaPacker, nil)
if err != nil {
return err
}
//***此处调用driver的ApplyDiff()***//
//***把镜像层的内容存入diff中***//
applySize, err := ls.driver.ApplyDiff(layer.cacheID, parent, archive.Reader(rdr))
if err != nil {
return err
}
// Discard trailing data but ensure metadata is picked up to reconstruct stream
io.Copy(ioutil.Discard, rdr) // ignore error as reader may be closed
//***回填size和diffID***//
layer.size = applySize
//***diffID是镜像layer中tar包的hash值***//
layer.diffID = DiffID(digester.Digest())
logrus.Debugf("Applied tar %s to %s, size: %d", layer.diffID, layer.cacheID, applySize)
return nil
}

Register()

Register()是入口。
Register()调用了registerWithDescriptor()方法。registerWithDescriptor()主要流程如下:

  1. 调用driver.Create()在driver中创建对应目录;
  2. 调用store.StartTransaction()创建临时目录;
  3. applyTar()把镜像层的tar包解压到driver指定目录中;
  4. 调用createChainIDFromParent()计算chainID,即layerID,具体分析见Docker镜像存储分析;
  5. 调用storeLayer()把所生成的layer持久化;
  6. 调用tx.Commit()提交变更。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
//***入口***//
func (ls *layerStore) Register(ts io.Reader, parent ChainID) (Layer, error) {
return ls.registerWithDescriptor(ts, parent, distribution.Descriptor{})
}
func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, descriptor distribution.Descriptor) (Layer, error) {
// err is used to hold the error which will always trigger
// cleanup of creates sources but may not be an error returned
// to the caller (already exists).
var err error
var pid string
var p *roLayer
if string(parent) != "" {
p = ls.get(parent)
if p == nil {
return nil, ErrLayerDoesNotExist
}
pid = p.cacheID
// Release parent chain if error
defer func() {
if err != nil {
ls.layerL.Lock()
ls.releaseLayer(p)
ls.layerL.Unlock()
}
}()
if p.depth() >= maxLayerDepth {
err = ErrMaxDepthExceeded
return nil, err
}
}
// Create new roLayer
layer := &roLayer{
parent: p,
cacheID: stringid.GenerateRandomID(),
referenceCount: 1,
layerStore: ls,
references: map[Layer]struct{}{},
descriptor: descriptor,
}
//***在graph中创建对应层***//
if err = ls.driver.Create(layer.cacheID, pid, "", nil); err != nil {
return nil, err
}
tx, err := ls.store.StartTransaction()
if err != nil {
return nil, err
}
defer func() {
if err != nil {
logrus.Debugf("Cleaning up layer %s: %v", layer.cacheID, err)
if err := ls.driver.Remove(layer.cacheID); err != nil {
logrus.Errorf("Error cleaning up cache layer %s: %v", layer.cacheID, err)
}
if err := tx.Cancel(); err != nil {
logrus.Errorf("Error canceling metadata transaction %q: %s", tx.String(), err)
}
}
}()
//***调用applyTar()***//
//***把tar文件解压至到对应的目录***//
if err = ls.applyTar(tx, ts, pid, layer); err != nil {
return nil, err
}
if layer.parent == nil {
layer.chainID = ChainID(layer.diffID)
} else {
//***生成chainID***//
//***就是把parent.chainID和layer.diffID合并,然后进行hash***//
layer.chainID = createChainIDFromParent(layer.parent.chainID, layer.diffID)
}
//***把信息存入到layerdb中***//
if err = storeLayer(tx, layer); err != nil {
return nil, err
}
ls.layerL.Lock()
defer ls.layerL.Unlock()
if existingLayer := ls.getWithoutLock(layer.chainID); existingLayer != nil {
// Set error for cleanup, but do not return the error
err = errors.New("layer already exists")
return existingLayer.getReference(), nil
}
//***提交变更***//
if err = tx.Commit(layer.chainID); err != nil {
return nil, err
}
ls.layerMap[layer.chainID] = layer
return layer.getReference(), nil
}

Get()

Get()可以从layerMap中获取一个layer,并在该layer的referenceCount上+1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//***从layerMap中直接获取layer***//
func (ls *layerStore) getWithoutLock(layer ChainID) *roLayer {
l, ok := ls.layerMap[layer]
if !ok {
return nil
}
l.referenceCount++
return l
}
func (ls *layerStore) get(l ChainID) *roLayer {
ls.layerL.Lock()
defer ls.layerL.Unlock()
return ls.getWithoutLock(l)
}
func (ls *layerStore) Get(l ChainID) (Layer, error) {
ls.layerL.Lock()
defer ls.layerL.Unlock()
layer := ls.getWithoutLock(l)
if layer == nil {
return nil, ErrLayerDoesNotExist
}
return layer.getReference(), nil
}

deleteLayer()

在driver中删除layer相关信息;在filestore中删除layer相关信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//***从dirver和store中删除layer相关信息***//
func (ls *layerStore) deleteLayer(layer *roLayer, metadata *Metadata) error {
//***调用driver的Remove()方法***//
err := ls.driver.Remove(layer.cacheID)
if err != nil {
return err
}
//***调用filestore的Remove()方法***//
err = ls.store.Remove(layer.chainID)
if err != nil {
return err
}
metadata.DiffID = layer.diffID
metadata.ChainID = layer.chainID
metadata.Size, err = layer.Size()
if err != nil {
return err
}
metadata.DiffSize = layer.size
return nil
}

Release()

Release()可以层层删除没有被引用的层。由于Docker镜像是树状结构的,Release()相当于删除一条枝代表的laysers。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//***Release()入口***//
//***会释放多层引用为0的layer***//
func (ls *layerStore) Release(l Layer) ([]Metadata, error) {
ls.layerL.Lock()
defer ls.layerL.Unlock()
layer, ok := ls.layerMap[l.ChainID()]
if !ok {
return []Metadata{}, nil
}
if !layer.hasReference(l) {
return nil, ErrLayerNotRetained
}
layer.deleteReference(l)
return ls.releaseLayer(layer)
}
func (ls *layerStore) releaseLayer(l *roLayer) ([]Metadata, error) {
depth := 0
removed := []Metadata{}
//***会检查parent***//
for {
if l.referenceCount == 0 {
panic("layer not retained")
}
l.referenceCount--
//***只有referenceCount为0时才可删除***//
//***如果遇到有引用的layer,则中断回收***//
if l.referenceCount != 0 {
return removed, nil
}
if len(removed) == 0 && depth > 0 {
panic("cannot remove layer with child")
}
if l.hasReferences() {
panic("cannot delete referenced layer")
}
var metadata Metadata
//***删除某layer***//
if err := ls.deleteLayer(l, &metadata); err != nil {
return nil, err
}
//***从layerMap中删除layer***//
delete(ls.layerMap, l.chainID)
//***已经删除的layer***//
removed = append(removed, metadata)
if l.parent == nil {
return removed, nil
}
depth++
l = l.parent
}
}

CreateRWLayer()

CreateRWLayer()先在layerdb中创建一个layer,然后调用driver.CreateReadWrite()在driver中创建读写目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//***创建读写层***//
func (ls *layerStore) CreateRWLayer(name string, parent ChainID, mountLabel string, initFunc MountInit, storageOpt map[string]string) (RWLayer, error) {
ls.mountL.Lock()
defer ls.mountL.Unlock()
m, ok := ls.mounts[name]
if ok {
return nil, ErrMountNameConflict
}
var err error
var pid string
var p *roLayer
if string(parent) != "" {
p = ls.get(parent)
if p == nil {
return nil, ErrLayerDoesNotExist
}
pid = p.cacheID
// Release parent chain if error
defer func() {
if err != nil {
ls.layerL.Lock()
ls.releaseLayer(p)
ls.layerL.Unlock()
}
}()
}
//***构造mountedLayer***//
m = &mountedLayer{
name: name,
parent: p,
mountID: ls.mountID(name),
layerStore: ls,
references: map[RWLayer]*referencedRWLayer{},
}
if initFunc != nil {
pid, err = ls.initMount(m.mountID, pid, mountLabel, initFunc, storageOpt)
if err != nil {
return nil, err
}
m.initID = pid
}
//***创建读写层***//
if err = ls.driver.CreateReadWrite(m.mountID, pid, "", storageOpt); err != nil {
return nil, err
}
if err = ls.saveMount(m); err != nil {
return nil, err
}
return m.getReference(), nil
}

ReleaseRWLayer()

ReleaseRWLayer()可以删除一个读写层。主要流程如下:

  1. 删除driver的容器读写层;
  2. 删除driver的容器init层;
  3. 从fileStore上删除该层的信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
func (ls *layerStore) ReleaseRWLayer(l RWLayer) ([]Metadata, error) {
ls.mountL.Lock()
defer ls.mountL.Unlock()
m, ok := ls.mounts[l.Name()]
if !ok {
return []Metadata{}, nil
}
if err := m.deleteReference(l); err != nil {
return nil, err
}
if m.hasReferences() {
return []Metadata{}, nil
}
//***删除ef9fb8e1c2790cc6aeb0eaaf6f1abe08bd9949c6e9b53f229f729b3848d28a59***//
if err := ls.driver.Remove(m.mountID); err != nil {
logrus.Errorf("Error removing mounted layer %s: %s", m.name, err)
m.retakeReference(l)
return nil, err
}
if m.initID != "" {
//***删除ef9fb8e1c2790cc6aeb0eaaf6f1abe08bd9949c6e9b53f229f729b3848d28a59-init***//
if err := ls.driver.Remove(m.initID); err != nil {
logrus.Errorf("Error removing init layer %s: %s", m.name, err)
m.retakeReference(l)
return nil, err
}
}
if err := ls.store.RemoveMount(m.name); err != nil {
logrus.Errorf("Error removing mount metadata: %s: %s", m.name, err)
m.retakeReference(l)
return nil, err
}
delete(ls.mounts, m.Name())
ls.layerL.Lock()
defer ls.layerL.Unlock()
if m.parent != nil {
return ls.releaseLayer(m.parent)
}
return []Metadata{}, nil
}

saveMount()

saveMount()可以持久化mount信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//***持久化mount信息***//
func (ls *layerStore) saveMount(mount *mountedLayer) error {
//***存储mount-id***//
if err := ls.store.SetMountID(mount.name, mount.mountID); err != nil {
return err
}
//***存储init-id***//
if mount.initID != "" {
if err := ls.store.SetInitID(mount.name, mount.initID); err != nil {
return err
}
}
//***存储parent***//
if mount.parent != nil {
if err := ls.store.SetMountParent(mount.name, mount.parent.chainID); err != nil {
return err
}
}
ls.mounts[mount.name] = mount
return nil
}

CreateChainID()

最后来看下计算chainID的函数,定义在/layer/layer.go中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// CreateChainID returns ID for a layerDigest slice
func CreateChainID(dgsts []DiffID) ChainID {
return createChainIDFromParent("", dgsts...)
}
func createChainIDFromParent(parent ChainID, dgsts ...DiffID) ChainID {
if len(dgsts) == 0 {
return parent
}
if parent == "" {
return createChainIDFromParent(ChainID(dgsts[0]), dgsts[1:]...)
}
// H = "H(n-1) SHA256(n)"
//***parent: sha256:ea9f151abb7e06353e73172dad421235611d4f6d0560ec95db26e0dc240642c1***//
//***dgsts[0]: sha256:37cd1954e92bbbf23db02215ded6d1da958747dd642b43268a0ad73866592a7b***//
//***dgst: sha256:860708544664c614b8ab025d5d01ba8f37694e939fd02ce861538bfd7c64cc43***//
dgst := digest.FromBytes([]byte(string(parent) + " " + string(dgsts[0])))
return createChainIDFromParent(ChainID(dgst), dgsts[1:]...)
}

可见,整个过程是把上一次的哈希结果和下一个diff合并成字符串,接着计算哈希值。

总结

方法很多,分析中的代码也很长。只要记着以下几点:

  1. 可以使用Register()向layerdb注册一个layer;
  2. layerStore对应的目录为/var/lib/docker/image/aufs/layerdb;
  3. layerStore中有driver连接镜像存储;