之前在”Docker镜像存储分析”中已经介绍过Docker是如何组织镜像存储目录的。在Docker中,主要有三个store来负责对镜像进行管理:
- imageStore: 负责imagedb目录管理;
 
- layerStore: 负责content目录管理;
 
- referenceStore: 负责repositories.json文件管理。
 
本次分析将介绍imageStore相关的知识。
imagedb
Docker1.12.3把镜像相关的内容入在/var/lib/docker/image/aufs/imagedb目录下。主要有content和metadata两个目录,content目录存储镜像config文件,metadata目录存储镜像的parent文件。
fs
先来看fs。fs是直接和imagedb目录打交道的模块。fs定义在/image/fs.go中:
1 2 3 4 5 
  | type fs struct { 	sync.RWMutex 	root string } 
  | 
fs的root字段表示imagedb目录,即/var/lib/docker/image/aufs/imagedb/。
fs实现了StorageBackend接口:
1 2 3 4 5 6 7 8 9 10 
  | type StoreBackend interface { 	Walk(f IDWalkFunc) error 	Get(id ID) ([]byte, error) 	Set(data []byte) (ID, error) 	Delete(id ID) error 	SetMetadata(id ID, key string, data []byte) error 	GetMetadata(id ID, key string) ([]byte, error) 	DeleteMetadata(id ID, key string) error } 
  | 
NewFSStoreBackend()
NewFSStoreBackend()可以生成一个新的fs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 
  | func NewFSStoreBackend(root string) (StoreBackend, error) { 	return newFSStore(root) } func newFSStore(root string) (*fs, error) { 	s := &fs{ 		root: root, 	} 	 	if err := os.MkdirAll(filepath.Join(root, contentDirName, string(digest.Canonical)), 0700); err != nil { 		return nil, err 	} 	 	if err := os.MkdirAll(filepath.Join(root, metadataDirName, string(digest.Canonical)), 0700); err != nil { 		return nil, err 	} 	return s, nil } 
  | 
可以看出,NewFSStoreBackend()调用了newFSStore(),newFSStore()也只是创建了content和metadata目录。
路径组装方法
路径组装有两个方法:contentFile()和metadataDir(),分别可以获取content和metadata目录下的文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 
  | func (s *fs) contentFile(id ID) string { 	dgst := digest.Digest(id) 	return filepath.Join(s.root, contentDirName, string(dgst.Algorithm()), dgst.Hex()) } func (s *fs) metadataDir(id ID) string { 	dgst := digest.Digest(id) 	return filepath.Join(s.root, metadataDirName, string(dgst.Algorithm()), dgst.Hex()) } 
  | 
Walk()
Walk()该当实现了目录的遍历。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 
  | func (s *fs) Walk(f IDWalkFunc) error { 	 	s.RLock() 	dir, err := ioutil.ReadDir(filepath.Join(s.root, contentDirName, string(digest.Canonical))) 	s.RUnlock() 	if err != nil { 		return err 	} 	for _, v := range dir { 		 		 		dgst := digest.NewDigestFromHex(string(digest.Canonical), v.Name()) 		if err := dgst.Validate(); err != nil { 			logrus.Debugf("Skipping invalid digest %s: %s", dgst, err) 			continue 		} 		if err := f(ID(dgst)); err != nil { 			return err 		} 	} 	return nil } 
  | 
Get()
Get()可以通过id获取镜像的config文件。获取config文件的流程很简单,从content目录下读取对应id的文件即可。
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 (s *fs) Get(id ID) ([]byte, error) { 	s.RLock() 	defer s.RUnlock() 	return s.get(id) } func (s *fs) get(id ID) ([]byte, error) { 	content, err := ioutil.ReadFile(s.contentFile(id)) 	if err != nil { 		return nil, err 	} 	 	 	if ID(digest.FromBytes(content)) != id { 		return nil, fmt.Errorf("failed to verify image: %v", id) 	} 	return content, nil } 
  | 
Set()
Set()可以设置镜像层的config文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 
  | func (s *fs) Set(data []byte) (ID, error) { 	s.Lock() 	defer s.Unlock() 	if len(data) == 0 { 		return "", fmt.Errorf("Invalid empty data") 	} 	id := ID(digest.FromBytes(data)) 	 	if err := ioutils.AtomicWriteFile(s.contentFile(id), data, 0600); err != nil { 		return "", err 	} 	return id, nil } 
  | 
Delete()
Delete()可以依据id删除镜像层对应的文件或目录。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 
  | func (s *fs) Delete(id ID) error { 	s.Lock() 	defer s.Unlock() 	if err := os.RemoveAll(s.metadataDir(id)); err != nil { 		return err 	} 	if err := os.Remove(s.contentFile(id)); err != nil { 		return err 	} 	return nil } 
  | 
SetMetadata()可以把内容写入到metadata目录下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 
  | func (s *fs) SetMetadata(id ID, key string, data []byte) error { 	s.Lock() 	defer s.Unlock() 	if _, err := s.get(id); err != nil { 		return err 	} 	baseDir := filepath.Join(s.metadataDir(id)) 	if err := os.MkdirAll(baseDir, 0700); err != nil { 		return err 	} 	return ioutils.AtomicWriteFile(filepath.Join(s.metadataDir(id), key), data, 0600) } 
  | 
从metadata目录下获取key对应的内容。
1 2 3 4 5 6 7 8 9 10 11 
  | // GetMetadata returns metadata for a given ID. //***获取镜像的metadata***// func (s *fs) GetMetadata(id ID, key string) ([]byte, error) { 	s.RLock() 	defer s.RUnlock() 	if _, err := s.get(id); err != nil { 		return nil, err 	} 	return ioutil.ReadFile(filepath.Join(s.metadataDir(id), key)) } 
  | 
DeleteMetadata()移除某id的metadata内容。
1 2 3 4 5 6 7 8 
  | func (s *fs) DeleteMetadata(id ID, key string) error { 	s.Lock() 	defer s.Unlock() 	return os.RemoveAll(filepath.Join(s.metadataDir(id), key)) } 
  | 
image
接着来看image,image定义在/image/image.go中:
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 
  | type V1Image struct { 	 	ID string `json:"id,omitempty"` 	 	Parent string `json:"parent,omitempty"` 	 	Comment string `json:"comment,omitempty"` 	 	Created time.Time `json:"created"` 	 	Container string `json:"container,omitempty"` 	 	ContainerConfig container.Config `json:"container_config,omitempty"` 	 	DockerVersion string `json:"docker_version,omitempty"` 	 	Author string `json:"author,omitempty"` 	 	Config *container.Config `json:"config,omitempty"` 	 	Architecture string `json:"architecture,omitempty"` 	 	OS string `json:"os,omitempty"` 	 	Size int64 `json:",omitempty"` } type Image struct { 	V1Image 	Parent     ID        `json:"parent,omitempty"` 	RootFS     *RootFS   `json:"rootfs,omitempty"` 	History    []History `json:"history,omitempty"` 	OSVersion  string    `json:"os.version,omitempty"` 	OSFeatures []string  `json:"os.features,omitempty"` 	 	rawJSON []byte 	 	 	computedID ID } 
  | 
image的各字段可以和image的config对应起来。
RawJSON()
RawJSON()返回rawJSON。
1 2 3 4 
  | func (img *Image) RawJSON() []byte { 	return img.rawJSON } 
  | 
ID()
1 2 3 4 5 
  | func (img *Image) ID() ID { 	return img.computedID } 
  | 
 
ImageID()
返回镜像层的ID。
1 2 3 4 
  | func (img *Image) ImageID() string { 	return string(img.ID()) } 
  | 
RunConfig()
1 2 3 4 5 
  | func (img *Image) RunConfig() *container.Config { 	return img.Config } 
  | 
 
MarshalJSON()
MarshalJSON()把镜像层的config序列化成json。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 
  | func (img *Image) MarshalJSON() ([]byte, error) { 	type MarshalImage Image 	pass1, err := json.Marshal(MarshalImage(*img)) 	if err != nil { 		return nil, err 	} 	var c map[string]*json.RawMessage 	if err := json.Unmarshal(pass1, &c); err != nil { 		return nil, err 	} 	return json.Marshal(c) } 
  | 
rootfs
rootfs表明Docker是如何组织镜像的,目前只有”layers”一种。rootfs定义在/image/rootfs_unix.go中:
1 2 3 4 5 6 7 8 9 10 11 12 
  | type RootFS struct { 	Type    string         `json:"type"` 	DiffIDs []layer.DiffID `json:"diff_ids,omitempty"` } func (r *RootFS) ChainID() layer.ChainID { 	return layer.CreateChainID(r.DiffIDs) } 
  | 
可以看出,RootFS可以依据DiffIDs(由layer.tar哈希计算得出)计算layer的值。具体见镜像存储分析。
Store
Store可以操作整个imagedb。
Store定义在/image/store.go中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 
  | type Store interface { 	Create(config []byte) (ID, error) 	Get(id ID) (*Image, error) 	Delete(id ID) ([]layer.Metadata, error) 	Search(partialID string) (ID, error) 	SetParent(id ID, parent ID) error 	GetParent(id ID) (ID, error) 	Children(id ID) []ID 	Map() map[ID]*Image 	Heads() map[ID]*Image } type store struct { 	sync.Mutex 	ls        LayerGetReleaser 	images    map[ID]*imageMeta 	fs        StoreBackend 	digestSet *digest.Set } 
  | 
可以看出,store中有images map维护ID和imageMeta的关系。
imageMeta定义如下:
1 2 3 4 5 
  | type imageMeta struct { 	layer layer.Layer 	 	children map[ID]struct{} } 
  | 
imageMeta中存有layer信息及该镜像层的children信息。
NewImageStore()
来看下store的生成函数NewImageStore()。
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 
  | func NewImageStore(fs StoreBackend, ls LayerGetReleaser) (Store, error) { 	is := &store{ 		ls:        ls, 		images:    make(map[ID]*imageMeta), 		fs:        fs, 		digestSet: digest.NewSet(), 	} 	 	if err := is.restore(); err != nil { 		return nil, err 	} 	 	 	return is, nil } func (is *store) restore() error { 	err := is.fs.Walk(func(id ID) error { 		 		img, err := is.Get(id) 		if err != nil { 			logrus.Errorf("invalid image %v, %v", id, err) 			return nil 		} 		var l layer.Layer 		if chainID := img.RootFS.ChainID(); chainID != "" { 			l, err = is.ls.Get(chainID) 			if err != nil { 				return err 			} 		} 		if err := is.digestSet.Add(digest.Digest(id)); err != nil { 			return err 		} 		imageMeta := &imageMeta{ 			layer:    l, 			children: make(map[ID]struct{}), 		} 		is.images[ID(id)] = imageMeta 		return nil 	}) 	if err != nil { 		return err 	} 	 	 	for id := range is.images { 		if parent, err := is.GetParent(id); err == nil { 			if parentMeta := is.images[parent]; parentMeta != nil { 				parentMeta.children[id] = struct{}{} 			} 		} 	} 	return nil } 
  | 
其中restore()是直接从imagedb目录构建imageStore。
Create()
Create()可以从config生成一个镜像层。
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 
  | func (is *store) Create(config []byte) (ID, error) { 	var img Image 	 	err := json.Unmarshal(config, &img) 	if err != nil { 		return "", err 	} 	 	 	rootFSLayers := make(map[layer.DiffID]struct{}) 	for _, diffID := range img.RootFS.DiffIDs { 		rootFSLayers[diffID] = struct{}{} 	} 	layerCounter := 0 	for _, h := range img.History { 		if !h.EmptyLayer { 			layerCounter++ 		} 	} 	if layerCounter > len(img.RootFS.DiffIDs) { 		return "", errors.New("too many non-empty layers in History section") 	} 	 	dgst, err := is.fs.Set(config) 	if err != nil { 		return "", err 	} 	imageID := ID(dgst) 	is.Lock() 	defer is.Unlock() 	if _, exists := is.images[imageID]; exists { 		return imageID, nil 	} 	layerID := img.RootFS.ChainID() 	var l layer.Layer 	if layerID != "" { 		l, err = is.ls.Get(layerID) 		if err != nil { 			return "", err 		} 	} 	imageMeta := &imageMeta{ 		layer:    l, 		children: make(map[ID]struct{}), 	} 	is.images[imageID] = imageMeta 	if err := is.digestSet.Add(digest.Digest(imageID)); err != nil { 		delete(is.images, imageID) 		return "", err 	} 	return imageID, nil } 
  | 
Get()
Get()先从fs中获取config,然后调用NewFromJSON(config)创建一个img,再设置img的computedID(就是该image的config的hash值)及Parent。
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 
  | func (is *store) Get(id ID) (*Image, error) { 	 	 	 	config, err := is.fs.Get(id) 	if err != nil { 		return nil, err 	} 	 	img, err := NewFromJSON(config) 	if err != nil { 		return nil, err 	} 	 	img.computedID = id 	 	img.Parent, err = is.GetParent(id) 	if err != nil { 		img.Parent = "" 	} 	return img, nil } 
  | 
Delete()
Delete()可以删除一个镜像层。
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 
  | func (is *store) Delete(id ID) ([]layer.Metadata, error) { 	is.Lock() 	defer is.Unlock() 	imageMeta := is.images[id] 	if imageMeta == nil { 		return nil, fmt.Errorf("unrecognized image ID %s", id.String()) 	} 	for id := range imageMeta.children { 		is.fs.DeleteMetadata(id, "parent") 	} 	if parent, err := is.GetParent(id); err == nil && is.images[parent] != nil { 		delete(is.images[parent].children, id) 	} 	if err := is.digestSet.Remove(digest.Digest(id)); err != nil { 		logrus.Errorf("error removing %s from digest set: %q", id, err) 	} 	delete(is.images, id) 	is.fs.Delete(id) 	if imageMeta.layer != nil { 		return is.ls.Release(imageMeta.layer) 	} 	return nil, nil } 
  | 
SetParent()
Setparent()可以建立两个id之前的父子层关系。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 
  | func (is *store) SetParent(id, parent ID) error { 	is.Lock() 	defer is.Unlock() 	parentMeta := is.images[parent] 	if parentMeta == nil { 		return fmt.Errorf("unknown parent image ID %s", parent.String()) 	} 	if parent, err := is.GetParent(id); err == nil && is.images[parent] != nil { 		delete(is.images[parent].children, id) 	} 	parentMeta.children[id] = struct{}{} 	return is.fs.SetMetadata(id, "parent", []byte(parent)) } 
  | 
GetParent()
GetParent()可以获取镜像层的父层。
1 2 3 4 5 6 7 8 9 
  | func (is *store) GetParent(id ID) (ID, error) { 	d, err := is.fs.GetMetadata(id, "parent") 	if err != nil { 		return "", err 	} 	return ID(d), nil  } 
  | 
Children()
Children()可以获取镜像层的所有子层。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 
  | func (is *store) Children(id ID) []ID { 	is.Lock() 	defer is.Unlock() 	return is.children(id) } func (is *store) children(id ID) []ID { 	var ids []ID 	if is.images[id] != nil { 		for id := range is.images[id].children { 			ids = append(ids, id) 		} 	} 	return ids } 
  | 
Map()
Map()返回所有的镜像层。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 
  | func (is *store) Map() map[ID]*Image { 	return is.imagesMap(true) } func (is *store) imagesMap(all bool) map[ID]*Image { 	is.Lock() 	defer is.Unlock() 	images := make(map[ID]*Image) 	for id := range is.images { 		if !all && len(is.children(id)) > 0 { 			continue 		} 		img, err := is.Get(id) 		if err != nil { 			logrus.Errorf("invalid image access: %q, error: %q", id, err) 			continue 		} 		images[id] = img 	} 	return images } 
  |