Docker鏡像存儲相關數據結構


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這個驅動沒有使用過,因此不瞭解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章