1、layerStore
docker daemon在初始化過程中,會初始化一個layerStore,那麼layerStore是什麼呢?從名字可以看出,是用來存儲layer的,docker鏡像時分層的,一層稱爲一個layer。在之前的docker源碼中,docker的鏡像是由一個叫graph的數據結構進行管理的,現在換成了layerStore
type layerStore struct {
store MetadataStore
driver graphdriver.Driver
layerMap map[ChainID]*roLayer
layerL sync.Mutex
mounts map[string]*mountedLayer
mountL sync.Mutex
}
layStore主要包含四個主要的結構成員,store、driver、layerMap和mounts。
1.1 store
store的數據類型爲MetadataStore,主要用來存儲每個layer的元數據,存儲的目錄位於/var/lib/docker/image/{driver}/layerdb,這裏的driver包括aufs、devicemapper、overlay和btrfs。
layerdb下面有三個目錄,mounts、sha256和tmp,tmp目錄主要存放臨時性數據,因此不做介紹。主要介紹mounts和sha256兩個目錄。
1.1.1 mounts目錄
mounts主要用來存儲layerStore的mounts成員的信息,每個運行的容器,其實都是若干只讀的layers(也就是docker鏡像),加上一層只讀的init-layer,再加上一層可寫的layer構成。這層可寫的layer保存在mounts這個成員裏面,具體的元數據則保存在/var/lib/docker/image/{driver}/layerdb/mounts下面。mounts下面的子目錄名字是各個容器的uuid,在每一個子目錄下面包含三個文件,即init-id,mount-id和parent。
- mount-id 是可寫的layer的uuid
- init-id 是mount-id加上後綴'-init'
- parent 則存放可寫的layer的父layer的chain-id。
1.1.2 sha256目錄
sha256下面則存放了每一層只讀的Layer的原信息。之所以會有sha256,這是因爲這些只讀的layer的chain-id是使用sha256加密算法生成的,未來也支持sha384和sha512。sha256下面的子目錄名稱爲各個只讀layer的chain-id。每個子目錄包含以下五個文件cache-id、diff、parent、size和tar-split.json.gz
-
cache-id:layer的cache-id,它制定了該層layer的數據存放位置,假如driver爲aufs,數據存在/var/lib/docker/aufs/diff/cache-id,假如driver爲overlay,數據存放在/var/lib/docker/overlay/cache-id,假如driver爲devicemapper,數據存放的device-id的信息位於/var/lib/docker/devicemapper/metadata/cache-id下
-
diff:存放diff-id,docker下載鏡像時會首先會下載一個docker image的元數據文件,在元數據文件標識了每一個layer,就是用diff-id。
-
parent:保存了parent layer的chain-id。假如是最底層的layer,則沒有Parent這個文件
-
size:存放layer的數據大小
-
tar-split.json.gz:存放的是關於這個layer的json信息
從上面的分析,我們可以看出一個layer它有三個id,分別爲chain-id、diff-id和cache-id,很讓人困惑,我們接下來的章節會進行解釋
1.2 driver
存儲layer的具體的driver,目前主要包括四種
- aufs
- overlay
- devicemapper
- btrfs
這裏我們暫時不展開,後面會詳細介紹
1.3 layerMap
layerMap本質上是一個map,map的類型爲map[ChainID]*roLayer,即map的鍵爲ChainID(字母串),值爲roLayer。前面說store本質上是磁盤上保存了各個layer的元數據信息,當docker初始化時,它會利用這些元數據文件在內存中構造各個layer,每個Layer都用一個roLayer結構體表示,即只讀(ro)的layer
type roLayer struct {
chainID ChainID
diffID DiffID
parent *roLayer
cacheID string
size int64
layerStore *layerStore
descriptor distribution.Descriptor
referenceCount int
references map[Layer]struct{}
}
前面提到每一層layer都有三個id。chain-id、diffID、cache-id。現在我們來解釋一下三個id的區別,之所以會有三個id是由於docker從v 1.10開始採用了一種叫做content addressable storage的模型
- diff-id:通過docker pull下載鏡像時,鏡像的json文件中每一個layer都有一個唯一的diff-id
- chain-id:chain-id是根據parent的chain-id和自身的diff-id生成的,假如沒有parent,則chain-id等於diff-id,假如有parent,則chain-id等於sha256sum( "parent-chain-id diff-id")
- cache-id:隨機生成的64個16進制數。前面提到過,cache-id標識了這個layer的數據具體存放位置
cache-id的類型的ChainID,diff-id類型爲DiffID,本質上二者都是digest.Digest類型,其實都是string類型,都是sha256:uuid,uuid爲64個16進制數
roLayer還有parent數據成員、referenceCount和references成員,referentces存放的是他的子layer的信息。當刪除鏡像時,只有roLayer的referentceCount爲零時,才能夠刪除該layer。
1.4 mounts
mounts本質上是一個map,類型爲map[string]*mountedLayer。前面提到過mounts存放的其實是每個容器可寫的layer的信息,他們的元數據存放在/var/lib/docker/image/{driver}/layerdb/mounts目錄下。而mountedLayer則是這些可寫的layer在內存中的結構
type mountedLayer struct {
name string
mountID string
initID string
parent *roLayer
path string
layerStore *layerStore
references map[RWLayer]*referencedRWLayer
}
mountedLayer沒有chain-id、diff-id、cached-id,只有mountID和initID,其中mountID是由隨機生成的64個16進制數構成,initID等於mountID-init。initID和mountID表示了這個layer數據存放的位置,和cache-id一樣。
2、imageStore
imageStore存放的是各個docker image的信息。imageStore的類型爲image.Store,結構體爲
type store struct {
sync.Mutex
ls LayerGetReleaser
images map[ID]*imageMeta
fs StoreBackend
digestSet *digest.Set
}
ls類型爲LayerGetReleaser接口,初始化時將ls初始化爲layerStore。fs類型爲StoreBackend。
ifs, err := image.NewFSStoreBackend(filepath.Join(imageRoot, "imagedb"))
d.imageStore, err = image.NewImageStore(ifs, d.layerStore)
fs存放了image的原信息,存儲的目錄位於/var/lib/docker/image/{driver}/imagedb,該目錄下主要包含兩個目錄content和metadata
-
content目錄:content下面的sha256目錄下存放了每個docker image的元數據文件,除了制定了這個image由那些roLayer構成,還包含了部分配置信息,瞭解docker的人應該知道容器的部分配置信息是存放在image裏面的,如volume、port、workdir等,這部分信息就存放在這個目錄下面,docker啓動時會讀取鏡像配置信息,反序列化出image對象
-
metadata目錄:metadata目錄存放了docker image的parent信息
imageStore包含了images成員,類型爲map[ID]*imageMeta,images就是每一個鏡像的信息,看看imageMeta結構體
type imageMeta struct {
layer layer.Layer
children map[ID]struct{}
}
imageMeta包含一個layer成員,之前說過docker image是由多個只讀的roLayer構成,而這裏的layer就是最上層的layer。
此外,imageStore還包含一個digestSet成員,本質上是一個set數據結構,裏面存放的其實是每個docker的最上層layer的chain-id。
3、referenceStore
referfenceStore的類型爲reference.store,這個應該是docker用戶最熟悉的部分了。以一個ubunu鏡像爲例,ubuntu鏡像的名字就叫ubuntu,一個完成的鏡像還包括tag,於是就有了ubuntu:latest、ubuntu:14.04等。這部分信息其實就是存儲才referenceStore中。這部分信息其實保存在/var/lib/docker/image/{driver}/repositories.json這個文件中
"Repositories": {
"ubuntu": {
"ubuntu@sha256:bd00486535fd3ab00463b0572d94a62715cb790e482d5419c9179cd22c74520b": "sha256:f2d8ce9fa988ed844dda693fe260b9afd393b9a65b647aa02f62d6eecdb7b635",
"ubuntu@sha256:3235a49037919e99696d97df8d8a230717272d848ee4ddadbca8d54f97ee30cb": "sha256:45bc58500fa3d3c0d67233d4a7798134b46b486af1389ca87000c543f46c3d24",
"ubuntu:latest": "sha256:45bc58500fa3d3c0d67233d4a7798134b46b486af1389ca87000c543f46c3d24",
"ubuntu:14.04": "sha256:f2d8ce9fa988ed844dda693fe260b9afd393b9a65b647aa02f62d6eecdb7b635"
},
"busybox": {
"busybox@sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6": "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749",
"busybox:latest": "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749"
}
}
}
從這裏我們可以看出,這才機器包括兩個鏡像,ubuntu和busybox,其中ubuntu有兩個tag分別爲latest和14.04,而busybox只有latest一個tag
referfenceStore其實就是從這個文件反序列化而來的
type store struct {
mu sync.RWMutex
jsonPath string
Repositories map[string]repository
referencesByIDCache map[image.ID]map[string]Named
}
從上面的json文件我們可以看出,"ubuntu:14.04"和"ubuntu@sha256:bd00486535fd3ab00463b0572d94a62715cb790e482d5419c9179cd22c74520b"指向的其實是同一個image。其實我們pull鏡像時
即可以
docker pull ubuntu:14.04
也可以
docker pull ubuntu@sha256:bd00486535fd3ab00463b0572d94a62715cb790e482d5419c9179cd22c74520b
4、distributionMetadataStore
這個結構體沒去詳細瞭解過,它在我們下載鏡像時會用到。數據存儲在/var/lib/docker/image/{driver}/distribution
type FSMetadataStore struct {
sync.RWMutex
basePath string
}
5、storage driver
目前docker支持四種storage driver,aufs、devicemapper、overlay和btrfs。所有的driver必須支持以下接口
type Driver interface {
ProtoDriver
Diff(id, parent string) (archive.Archive, error)
Changes(id, parent string) ([]archive.Change, error)
ApplyDiff(id, parent string, diff archive.Reader) (size int64, err error)
DiffSize(id, parent string) (size int64, err error)
}
type ProtoDriver interface {
String() string
CreateReadWrite(id, parent, mountLabel string, storageOpt map[string]string) error
Create(id, parent, mountLabel string, storageOpt map[string]string) error
Remove(id string) error
Get(id, mountLabel string) (dir string, err error)
Put(id string) error
Exists(id string) bool
Status() [][2]string
GetMetadata(id string) (map[string]string, error)
Cleanup() error
}
鏡像的數據前面提過,存放在/var/lib/docker/{driver}目錄下
aufs下面有三個目錄,diff、layers、mnt。diff目錄下面存儲以各個layer的cache-id存儲各個layer的數據;layers文件存儲了各個layer的parent layer;mnt目錄主要是用於掛在layer
devicemapper下面有三個目錄,devicemapper、metadata、mnt。devicemapper目錄面有兩個文件,metadata和data,metadata爲2G的大小,而data爲100G,所有的鏡像數據都存在這兩個文件裏面,這裏主要涉及到thin-provision技術;metadata目錄下面存放的是各個layer的大小,都爲10G以及所對應的device_id
overlay下面直接以各個layer的cache-id存儲各個layer的數據
btrfs這個驅動沒有使用過,因此不瞭解