referenceStore用来管理镜像名称和id之前的关系,主要和/var/lib/docker/image/aufs/repositories.json打交道。本次分析将介绍关于referenceStore的代码。

referenceStore

来看下referenceStore(即store)的定义,在/reference/store.go中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type store struct {
mu sync.RWMutex
// jsonPath is the path to the file where the serialized tag data is
// stored.
jsonPath string
// Repositories is a map of repositories, indexed by name.
//***map[dirage:map[dirage:v1:sha256:987fbbdd3e5ded64e38b15f3ee32ed5e59982313dc5331249cb47c07866b9746] etcd-cluster:map[etcd-cluster:v1:sha256:cf6ce5c776de4d51725077009fc473576ae04befed4c73bb049ae2e9376e20df]***//
Repositories map[string]repository
// referencesByIDCache is a cache of references indexed by ID, to speed
// up References.
//***sha256:002883be70c0fb86ee87a97ac9066091adabe370efb9d9bfeb858fb3d69596e4:map[mysql:v1:0xc42047d120]***//
//***0xc42047d120为结构体地址,Named就是image名字和tag***//
referencesByIDCache map[image.ID]map[string]Named
}
// Repository maps tags to image IDs. The key is a stringified Reference,
// including the repository name.
type repository map[string]image.ID

可以看出,store维护两个map:从镜像名称到id的map及从id到镜像名称的map。

NewReferenceStore()

来看下如何生成ReferenceStore:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// NewReferenceStore creates a new reference store, tied to a file path where
// the set of references are serialized in JSON format.
func NewReferenceStore(jsonPath string) (Store, error) {
abspath, err := filepath.Abs(jsonPath)
if err != nil {
return nil, err
}
store := &store{
jsonPath: abspath,
Repositories: make(map[string]repository),
referencesByIDCache: make(map[image.ID]map[string]Named),
}
// Load the json file if it exists, otherwise create it.
//***从repositories.json中读取信息***//
if err := store.reload(); os.IsNotExist(err) {
if err := store.save(); err != nil {
return nil, err
}
} else if err != nil {
return nil, err
}
return store, nil
}

NewReferenceStore()先构造空store,然后调用reload()方法把内容填充到store的两个map中。

来看下reload():

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
func (store *store) reload() error {
//***store.jsonPath: /var/lib/docker/image/aufs/repositories.json***//
f, err := os.Open(store.jsonPath)
if err != nil {
return err
}
defer f.Close()
if err := json.NewDecoder(f).Decode(&store); err != nil {
return err
}
for _, repository := range store.Repositories {
for refStr, refID := range repository {
ref, err := ParseNamed(refStr)
if err != nil {
// Should never happen
continue
}
if store.referencesByIDCache[refID] == nil {
store.referencesByIDCache[refID] = make(map[string]Named)
}
//***refID, refStr, ref: sha256:b33c1b9f51a1c29432def013531f8c45c5910c5138698d8408742bfa79295fba mpich-worker:v1 mpich-worker:v1***//
store.referencesByIDCache[refID][refStr] = ref
}
}
return nil
}

reload()读取/var/lib/docker/image/aufs/repositories.json,然后把内容直接解码到store中,并重新构造referencesByIDCache。

所以来看下repositories.json的内容:

1
2
3
4
5
6
7
8
9
10
{
"Repositories": {
"nginx": {
"nginx:v1": "sha256:2765df43aea203fa44f069a44b984a27cb942551493e58b0d02b04ae22e5d644"
},
"ubuntu": {
"ubuntu:16.04": "sha256:ac526a356ca46f915e822e2f8051b9cf3404c756b725661c51191564ae4e6ea7"
}
}
}

可以看出,repositories.json的格式和referenceStore中的Repositories一样。

添加

我们可以在referenceStore中加入内容:

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
// AddTag adds a tag reference to the store. If force is set to true, existing
// references can be overwritten. This only works for tags, not digests.
func (store *store) AddTag(ref Named, id image.ID, force bool) error {
if _, isCanonical := ref.(Canonical); isCanonical {
return errors.New("refusing to create a tag with a digest reference")
}
return store.addReference(WithDefaultTag(ref), id, force)
}
// AddDigest adds a digest reference to the store.
func (store *store) AddDigest(ref Canonical, id image.ID, force bool) error {
return store.addReference(ref, id, force)
}
//***添加ref,即name和tag***//
func (store *store) addReference(ref Named, id image.ID, force bool) error {
if ref.Name() == string(digest.Canonical) {
return errors.New("refusing to create an ambiguous tag using digest algorithm as name")
}
store.mu.Lock()
defer store.mu.Unlock()
repository, exists := store.Repositories[ref.Name()]
if !exists || repository == nil {
repository = make(map[string]image.ID)
store.Repositories[ref.Name()] = repository
}
refStr := ref.String()
oldID, exists := repository[refStr]
if exists {
// force only works for tags
if digested, isDigest := ref.(Canonical); isDigest {
return fmt.Errorf("Cannot overwrite digest %s", digested.Digest().String())
}
if !force {
return fmt.Errorf("Conflict: Tag %s is already set to image %s, if you want to replace it, please use -f option", ref.String(), oldID.String())
}
if store.referencesByIDCache[oldID] != nil {
delete(store.referencesByIDCache[oldID], refStr)
if len(store.referencesByIDCache[oldID]) == 0 {
delete(store.referencesByIDCache, oldID)
}
}
}
repository[refStr] = id
if store.referencesByIDCache[id] == nil {
store.referencesByIDCache[id] = make(map[string]Named)
}
store.referencesByIDCache[id][refStr] = ref
return store.save()
}

添加的过程就是在referenceStore中加入内容,然后把整个referenceStore的内容存储到repositories.json中。

删除

当然,也可以从referenceStore上删除资源。

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
// Delete deletes a reference from the store. It returns true if a deletion
// happened, or false otherwise.
func (store *store) Delete(ref Named) (bool, error) {
ref = WithDefaultTag(ref)
store.mu.Lock()
defer store.mu.Unlock()
repoName := ref.Name()
repository, exists := store.Repositories[repoName]
//***不存在,则直接返回***//
if !exists {
return false, ErrDoesNotExist
}
refStr := ref.String()
if id, exists := repository[refStr]; exists {
delete(repository, refStr)
//***Fankang***//
//***如果repository为空,则删除***//
if len(repository) == 0 {
delete(store.Repositories, repoName)
}
//***清理referencesByIDCache中的资源***//
if store.referencesByIDCache[id] != nil {
delete(store.referencesByIDCache[id], refStr)
if len(store.referencesByIDCache[id]) == 0 {
delete(store.referencesByIDCache, id)
}
}
return true, store.save()
}
return false, ErrDoesNotExist
}

删除资源就是从referenceStore的两个map中把资源删除,然后把内容存储到repositories.json中。

获取

Get()

Get可以通过imageName和tag获取镜像id。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//***通过imageName和tag获取镜像id***//
func (store *store) Get(ref Named) (image.ID, error) {
ref = WithDefaultTag(ref)
store.mu.RLock()
defer store.mu.RUnlock()
repository, exists := store.Repositories[ref.Name()]
if !exists || repository == nil {
return "", ErrDoesNotExist
}
id, exists := repository[ref.String()]
if !exists {
return "", ErrDoesNotExist
}
return id, nil
}

References()

References()可以返回某镜像id对应的镜像列表,如[nginx:20170820, nginx:v1]。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// References returns a slice of references to the given image ID. The slice
// will be nil if there are no references to this image ID.
func (store *store) References(id image.ID) []Named {
store.mu.RLock()
defer store.mu.RUnlock()
// Convert the internal map to an array for two reasons:
// 1) We must not return a mutable
// 2) It would be ugly to expose the extraneous map keys to callers.
var references []Named
//***只取Named***//
for _, ref := range store.referencesByIDCache[id] {
references = append(references, ref)
}
sort.Sort(lexicalRefs(references))
return references
}

ReferencesByName()

通过镜像名字返回Association。

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
//***查找associaltions,包含image:tag和image-id***//
func (store *store) ReferencesByName(ref Named) []Association {
store.mu.RLock()
defer store.mu.RUnlock()
repository, exists := store.Repositories[ref.Name()]
if !exists {
return nil
}
var associations []Association
for refStr, refID := range repository {
ref, err := ParseNamed(refStr)
if err != nil {
// Should never happen
return nil
}
associations = append(associations,
Association{
Ref: ref,
ImageID: refID,
})
}
sort.Sort(lexicalAssociations(associations))
return associations
}
// An Association is a tuple associating a reference with an image ID.
type Association struct {
Ref Named
ImageID image.ID
}

持久化

save()可以把referenceStore持久化到repositories.json中。

1
2
3
4
5
6
7
8
9
//***把信息存储到/var/lib/docker/image/aufs/repositories.json***//
func (store *store) save() error {
// Store the json
jsonData, err := json.Marshal(store)
if err != nil {
return err
}
return ioutils.AtomicWriteFile(store.jsonPath, jsonData, 0600)
}

总结

referenceStore就是repositories.json在内存中的对象,用来管理镜像名称和镜像id的关系。