layStore负责Docker的layer的管理,具体有镜像层的管理,容器init层的管理,容器读写层的管理。layerStore的管理目录为/var/lib/docker/image/aufs/layerdb,主要有以下目录:
- mounts: 存储容器挂载信息,有init-id, mount-id, parent文件。
- sha256: 存储镜像层的信息,有cache-id(aufs的存储位置), diff, size和tar-split.json.gz。
本次分析将介绍layStore是如何管理这些文件目录的。
首先,要先介绍直接与文件打交道的filestore。
filestore相关
先来介绍fileMetadataStore,fileMetadataStore用来读取layerdb目录下的文件,定义在/layer/filestore.go中:
1 2 3 4
| type fileMetadataStore struct { root string }
|
NewFSMetadataStore()可以创建layerdb的根目录,并生成一个新的fileMetadataStore。
1 2 3 4 5 6 7 8 9 10 11
| 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
| 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
| 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
| func (fms *fileMetadataStore) getMountDirectory(mount string) string { return filepath.Join(fms.root, "mounts", mount) }
|
getMountFilename()
1 2 3 4
| 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 } return &fileMetadataTransaction{ store: fms, root: td, }, nil }
|
GetSize()
GetSize()用于获取size文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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
| 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
| 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
| 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
| 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) { 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
| 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
| 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
| 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
| 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
| 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
| 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
| 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
| 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
| func (fms *fileMetadataStore) Remove(layer ChainID) error { return os.RemoveAll(fms.getLayerDirectory(layer)) }
|
RemoveMount()
RemoveMount()移除layerdb中mounts目录下的内容。
1 2 3 4
| func (fms *fileMetadataStore) RemoveMount(mount string) error { return os.RemoveAll(fms.getMountDirectory(mount)) }
|
再来介绍fileMetadataTransaction。fileMetadataTransaction用来创建一个临时文件夹,再全部的操作完成后,再把临时文件夹重命名成正式文件夹。
fileMetadataTransaction定义在/layer/filiestore.go中:
1 2 3 4 5
| type fileMetadataTransaction struct { store *fileMetadataStore root string }
|
fileMetaDataStore的StartTransaction()方法可以生成一个新的fileMetadataTransaction。
SetSize()
往临时文件夹中写入size。
1 2 3 4 5
| 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
| 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
| 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
| 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
| 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
| 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 { store MetadataStore driver graphdriver.Driver layerMap map[ChainID]*roLayer layerL sync.Mutex mounts map[string]*mountedLayer mountL sync.Mutex }
|
可以看出,layerStore中有一个store,即fileMetadataStore;一个driver,默认为aufs driver;一个layerMap用来缓存layer;一个mounts用来缓存mountedLayer。
NewStoreFromGraphDriver()
NewStoreFromGraphDriver()可以创建一个layerStore。流程如下:
- 使用store.List()获取ids和mounts;
- 通过loadLayer()方法导入所有layer信息;
- 通过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
| 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。主要流程如下:
- 如果layer在layerMap已缓存,则直接返回;
- 获取layer目录下的diff文件;
- 获取layer目录下的size文件;
- 获取layer目录下的cache-id文件;
- 获取layer目录下的parent文件;
- 获取layer目录下的descriptor文件;
- 构造roLayer表示只读层;
- 把roLayer缓存到layerMap中;
- 返回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
| func (ls *layerStore) loadLayer(layer ChainID) (*roLayer, error) { cl, ok := ls.layerMap[layer] if ok { return cl, nil } diff, err := ls.store.GetDiffID(layer) if err != nil { return nil, fmt.Errorf("failed to get diff id for %s: %s", layer, err) } size, err := ls.store.GetSize(layer) if err != nil { return nil, fmt.Errorf("failed to get size for %s: %s", layer, err) } cacheID, err := ls.store.GetCacheID(layer) if err != nil { return nil, fmt.Errorf("failed to get cache id for %s: %s", layer, err) } parent, err := ls.store.GetParent(layer) if err != nil { return nil, fmt.Errorf("failed to get parent for %s: %s", layer, err) } descriptor, err := ls.store.GetDescriptor(layer) if err != nil { return nil, fmt.Errorf("failed to get descriptor for %s: %s", layer, err) } 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 } ls.layerMap[cl.chainID] = cl return cl, nil }
|
loadMount()
loadMount()用于导入一个mount。主要流程如下:
- 如果mouns中已有缓存,则直接返回;
- 获取mount目录下的mount-id文件;
- 获取mount目录下的init-id文件;
- 获取mount目录下的parent文件;
- 构造mountLayer;
- 把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
| func (ls *layerStore) loadMount(mount string) error { if _, ok := ls.mounts[mount]; ok { return nil } mountID, err := ls.store.GetMountID(mount) if err != nil { return err } initID, err := ls.store.GetInitID(mount) if err != nil { return err } parent, err := ls.store.GetMountParent(mount) if err != nil { return err } 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++ } 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
| func (ls *layerStore) applyTar(tx MetadataTransaction, ts io.Reader, parent string, layer *roLayer) error { digester := digest.Canonical.New() tr := io.TeeReader(ts, digester.Hash()) tsw, err := tx.TarSplitWriter(true) if err != nil { return err } metaPacker := storage.NewJSONPacker(tsw) defer tsw.Close() rdr, err := asm.NewInputTarStream(tr, metaPacker, nil) if err != nil { return err } applySize, err := ls.driver.ApplyDiff(layer.cacheID, parent, archive.Reader(rdr)) if err != nil { return err } io.Copy(ioutil.Discard, rdr) layer.size = applySize 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()主要流程如下:
- 调用driver.Create()在driver中创建对应目录;
- 调用store.StartTransaction()创建临时目录;
- applyTar()把镜像层的tar包解压到driver指定目录中;
- 调用createChainIDFromParent()计算chainID,即layerID,具体分析见Docker镜像存储分析;
- 调用storeLayer()把所生成的layer持久化;
- 调用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) { 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 defer func() { if err != nil { ls.layerL.Lock() ls.releaseLayer(p) ls.layerL.Unlock() } }() if p.depth() >= maxLayerDepth { err = ErrMaxDepthExceeded return nil, err } } layer := &roLayer{ parent: p, cacheID: stringid.GenerateRandomID(), referenceCount: 1, layerStore: ls, references: map[Layer]struct{}{}, descriptor: descriptor, } 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) } } }() if err = ls.applyTar(tx, ts, pid, layer); err != nil { return nil, err } if layer.parent == nil { layer.chainID = ChainID(layer.diffID) } else { layer.chainID = createChainIDFromParent(layer.parent.chainID, layer.diffID) } 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 { 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
| 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
| func (ls *layerStore) deleteLayer(layer *roLayer, metadata *Metadata) error { err := ls.driver.Remove(layer.cacheID) if err != nil { return err } 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
| 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{} for { if l.referenceCount == 0 { panic("layer not retained") } l.referenceCount-- 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 if err := ls.deleteLayer(l, &metadata); err != nil { return nil, err } delete(ls.layerMap, l.chainID) 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 defer func() { if err != nil { ls.layerL.Lock() ls.releaseLayer(p) ls.layerL.Unlock() } }() } 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()可以删除一个读写层。主要流程如下:
- 删除driver的容器读写层;
- 删除driver的容器init层;
- 从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 } 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 != "" { 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
| func (ls *layerStore) saveMount(mount *mountedLayer) error { if err := ls.store.SetMountID(mount.name, mount.mountID); err != nil { return err } if mount.initID != "" { if err := ls.store.SetInitID(mount.name, mount.initID); err != nil { return err } } 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
| 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:]...) } dgst := digest.FromBytes([]byte(string(parent) + " " + string(dgsts[0]))) return createChainIDFromParent(ChainID(dgst), dgsts[1:]...) }
|
可见,整个过程是把上一次的哈希结果和下一个diff合并成字符串,接着计算哈希值。
总结
方法很多,分析中的代码也很长。只要记着以下几点:
- 可以使用Register()向layerdb注册一个layer;
- layerStore对应的目录为/var/lib/docker/image/aufs/layerdb;
- layerStore中有driver连接镜像存储;