走進docker(05):docker在本地如何管理image(鏡像)?

docker裏面能夠經過docker pulldocker builddocker commitdocker loaddocker import等方式獲得一個image,獲得image以後docker在本地是怎麼存儲的呢?本篇將以git pull爲例,簡述image的獲取和存儲方式。python

鏡像相關的配置

docker裏面和image有關的目錄爲/var/lib/docker,裏面存放着image的全部信息,能夠經過下面這個dockerd的啓動參數來修改這個目錄的路徑。git

--graph, -g /var/lib/docker Root of the Docker runtime

鏡像的引用方式

在須要引用image的時候,好比docker pull的時候,或者運行容器的時候,都須要指定一個image名稱,引用一個鏡像有多種方式,下面以ubuntu爲例進行說明.github

因爲sha256碼太長,因此用bcdef...來表示完整的sha256,節約空間docker

docker hub上的官方鏡像

  • ubuntu: 官方提供的最新ubuntu鏡像,對應的完整名稱爲docker.io/library/ubuntu:latestjson

  • ubuntu:16.04: 官方提供的ubuntu 16.04鏡像,對應的完整名稱爲docker.io/library/ubuntu:16.04ubuntu

  • ubuntu:@sha256:abcdef...: 官方提供的digest碼爲sha256:abcdef...的ubuntu鏡像,對應的完整名稱爲docker.io/library/ubuntu@sha256:abcdef...segmentfault

docker hub上的非官方(我的)鏡像

引用方式和官方鏡像同樣,惟一不一樣的是須要在鏡像名稱前面帶上用戶前綴,如:api

  • user1/ubuntu: 由user1提供的最新ubuntu鏡像, 對應的完整名稱爲docker.io/user1/ubuntu:latest安全

user1/ubuntu:16.04 和 user1/ubuntu:@sha256:abcdef...這兩種方式也是和上面同樣,等同於docker.io/user1/ubuntu:16.04和docker.io/user1/ubuntu:@sha256:abcdef...服務器

本身搭建的registry裏的鏡像

引用方式和docker hub同樣,惟一不一樣的是須要在鏡像名稱最前面帶上地址,如:

  • localhost:5000/ubuntu: 本地本身搭建的registry(localhost:5000)裏面的官方ubuntu的最新鏡像,對應的完整名稱爲localhost:5000/library/ubuntu:latest

  • localhost:5000/user1/ubuntu@sha256:a123def...: 本地本身搭建的registry(localhost:5000)裏面由用戶user1提供的digest爲sha256:a123def的ubuntu鏡像

其它的幾種狀況和上面的相似。

爲何須要鏡像的digest?

對於某些image來講,可能在發佈以後還會作一些更新,好比安全方面的,這時雖然鏡像的內容變了,但鏡像的名稱和tag沒有變,因此會形成先後兩次經過一樣的名稱和tag從服務器獲得不一樣的兩個鏡像的問題,因而docker引入了鏡像的digest的概念,一個鏡像的digest就是鏡像的manifes文件的sha256碼,當鏡像的內容發生變化的時候,即鏡像的layer發生變化,從而layer的sha256發生變化,而manifest裏面包含了每個layer的sha256,因此manifest的sha256也會發生變化,即鏡像的digest發生變化,這樣就保證了digest能惟一的對應一個鏡像。

docker pull的大概過程

若是對Image manifest,Image Config和Filesystem Layers等概念不是很瞭解,請先參考image(鏡像)是什麼

取image的大概過程以下:

  • docker發送image的名稱+tag(或者digest)給registry服務器,服務器根據收到的image的名稱+tag(或者digest),找到相應image的manifest,而後將manifest返回給docker

  • docker獲得manifest後,讀取裏面image配置文件的digest(sha256),這個sha256碼就是image的ID

  • 根據ID在本地找有沒有存在一樣ID的image,有的話就不用繼續下載了

  • 若是沒有,那麼會給registry服務器發請求(裏面包含配置文件的sha256和media type),拿到image的配置文件(Image Config)

  • 根據配置文件中的diff_ids(每一個diffid對應一個layer tar包的sha256,tar包至關於layer的原始格式),在本地找對應的layer是否存在

  • 若是layer不存在,則根據manifest裏面layer的sha256和media type去服務器拿相應的layer(至關去拿壓縮格式的包)。

  • 拿到後進行解壓,並檢查解壓後tar包的sha256可否和配置文件(Image Config)中的diff_id對的上,對不上說明有問題,下載失敗

  • 根據docker所用的後臺文件系統類型,解壓tar包並放到指定的目錄

  • 等全部的layer都下載完成後,整個image下載完成,就可使用了

注意: 對於layer來講,config文件中diffid是layer的tar包的sha256,而manifest文件中的digest依賴於media type,好比media type是tar+gzip,那digest就是layer的tar包通過gzip壓縮後的內容的sha256,若是media type就是tar的話,diffid和digest就會同樣。

dockerd和registry服務器之間的協議爲Registry HTTP API V2

image本地存放位置

這裏以ubuntu的image爲例,展現docker的image存儲方式。

先看看debian的image id和digest,而後再分析image數據都存在哪裏

dev@debian:~$ docker images --digests
REPOSITORY  TAG     DIGEST            MAGE ID       CREATED      SIZE
ubuntu      latest  sha256:ea1d85...  7b9b13f7b9c0  4 weeks ago  118 MB
......

對於本地生成的鏡像來講,因爲沒有上傳到registry上去,因此沒有digest,由於鏡像的manifest由registry生成

repositories.json

repositories.json中記錄了和本地image相關的repository信息,主要是name和image id的對應關係,當image從registry上被pull下來後,就會更新該文件:

#這裏目錄中的aufs爲docker後臺所採用的存儲文件系統名稱,
#若是是其餘的文件系統的話,名字會是其餘的,好比btrfs、overlay二、devicemapper等。
root@debian:~# cat /var/lib/docker/image/aufs/repositories.json|python -m json.tool
{
    "Repositories": {
        "ubuntu": {
            "ubuntu:latest": "sha256:7b9b13f7b9c086adfb6be4d2d264f90f16b4d1d5b3ab9f955caa728c3675c8a2",
            "ubuntu@sha256:ea1d854d38be82f54d39efe2c67000bed1b03348bcc2f3dc094f260855dff368": "sha256:7b9b13f7b9c086adfb6be4d2d264f90f16b4d1d5b3ab9f955caa728c3675c8a2"
        }
        ......
    }
}
  • ubuntu: repository的名稱,前面沒有服務器信息的表示這是官方registry(docker hub)裏面的repository,裏面包含的都是image標識和image ID的對應關係

  • ubuntu:latest和debian@sha256:ea1d85...: 他們都指向同一個image(sha256:7b9b13...)

配置文件(image config)

docker根據第一步獲得的manifest,從registry拿到config文件,而後保存在image/aufs/imagedb/content/sha256/目錄下,文件名稱就是文件內容的sha256碼,即image id:

root@debian:~# sha256sum /var/lib/docker/image/aufs/imagedb/content/sha256/7b9b13f7b9c086adfb6be4d2d264f90f16b4d1d5b3ab9f955caa728c3675c8a2
7b9b13f7b9c086adfb6be4d2d264f90f16b4d1d5b3ab9f955caa728c3675c8a2  /var/lib/docker/image/aufs/imagedb/content/sha256/7b9b13f7b9c086adfb6be4d2d264f90f16b4d1d5b3ab9f955caa728c3675c8a2

#這裏咱們只關注這個image的rootfs,
#從diff_ids裏能夠看出ubuntu:latest這個image包含了5個layer,
#從上到下依次是從底層到頂層,6a8bf8...是最底層,d8b353...是最頂層
root@debian:~# cat /var/lib/docker/image/aufs/imagedb/content/sha256/7b9b13f7b9c086adfb6be4d2d264f90f16b4d1d5b3ab9f955caa728c3675c8a2|python -m json.tool
......
    "rootfs": {
        "diff_ids": [
            "sha256:6a8bf8c8edbd705f67cdc062eee3911470a38a763258c81c05da1f28d6eec896",
            "sha256:fe9a3f9c4559684b75bba751883fa084d34e418c018d687ddc25c3f23f13f657",
            "sha256:fc9e1e5e38f700997585295bd65a47e58f3da7b2f0e6a971e14a6104f199de1f",
            "sha256:f2e85bc0b7b17ab33f9060d2f24824defe600e189b05a895d60b2e8a6a7bd0d7",
            "sha256:d8b353eb3025c49e029567b2a01e517f7f7d32537ee47e64a7eac19fa68a33f3"
        ],
        "type": "layers"
    }

......

layer的diff_id和digest的對應關係

layer的diff_id存在image的配置文件中,而layer的digest存在image的manifest中,他們的對應關係被存儲在了image/aufs/distribution目錄下:

root@debian:~# tree -d /var/lib/docker/image/aufs/distribution/
/var/lib/docker/image/aufs/distribution/
├── diffid-by-digest
│   └── sha256
└── v2metadata-by-diffid
    └── sha256
  • diffid-by-digest: 存放digest到diffid的對應關係

  • v2metadata-by-diffid: 存放diffid到digest的對應關係

#這裏以最底層layer(6a8bf8...)爲例,查看其digest信息
root@debian:~# cat /var/lib/docker/image/aufs/distribution/v2metadata-by-diffid/sha256/6a8bf8c8edbd705f67cdc062eee3911470a38a763258c81c05da1f28d6eec896|python -m json.tool
[
    {
        "Digest": "sha256:bd97b43c27e332fc4e00edf827bbc26369ad375187ce6eee91c616ad275884b1",
        "HMAC": "",
        "SourceRepository": "docker.io/library/ubuntu"
    }
]

#根據digest獲得diffid
root@debian:~# cat /var/lib/docker/image/aufs/distribution/diffid-by-digest/sha256/bd97b43c27e332fc4e00edf827bbc26369ad375187ce6eee91c616ad275884b1
sha256:6a8bf8c8edbd705f67cdc062eee3911470a38a763258c81c05da1f28d6eec896

layer的元數據

layer的屬性信息都放在了image/aufs/layerdb目錄下,目錄名稱是layer的chainid,因爲最底層的layer的chainid和diffid相同,因此這裏咱們用第二層(fe9a3f...)做爲示例:

計算chainid時,用到了全部祖先layer的信息,從而能保證根據chainid獲得的rootfs是惟一的。好比我在debian和ubuntu的image基礎上都添加了一個一樣的文件,那麼commit以後新增長的這兩個layer具備相同的內容,相同的diffid,但因爲他們的父layer不同,因此他們的chainid會不同,從而根據chainid能找到惟一的rootfs。計算chainid的方法請參考image spec

#計算chainid
#這裏fe9a3f...是第二層的diffid,而6a8bf8...是fe9a3f...父層的chainid,
#因爲6a8bf8...是最底層,它沒有父層,因此6a8bf8...的chainid就是6a8bf8...
dev@debian:~$ echo -n "sha256:6a8bf8c8edbd705f67cdc062eee3911470a38a763258c81c05da1f28d6eec896 sha256:fe9a3f9c4559684b75bba751883fa084d34e418c018d687ddc25c3f23f13f657"|sha256sum -
67b5e1df1b671d4751794767b20f6973d77e91528ab475ee1118ce8dab796193  -

#根據chainid來看看相應目錄的內容
root@debian:/var/lib/docker/image/aufs/layerdb/sha256/67b5e1df1b671d4751794767b20f6973d77e91528ab475ee1118ce8dab796193# ls
cache-id  diff  parent  size  tar-split.json.gz
#每一個layer都有這樣一個對應的文件夾

#cache-id是docker下載layer的時候在本地生成的一個隨機uuid,
#指向真正存放layer文件的地方
root@debian:/var/lib/docker/image/aufs/layerdb/sha256/67b5e1df1b671d4751794767b20f6973d77e91528ab475ee1118ce8dab796193# cat cache-id
b656bf5f0688069cd90ab230c029fdfeb852afcfd0d1733d087474c86a117da3

#diff文件存放layer的diffid
root@debian:/var/lib/docker/image/aufs/layerdb/sha256/67b5e1df1b671d4751794767b20f6973d77e91528ab475ee1118ce8dab796193# cat diff
sha256:fe9a3f9c4559684b75bba751883fa084d34e418c018d687ddc25c3f23f13f657

#parent文件存放當前layer的父layer的diffid,
#注意:對於最底層的layer來講,因爲沒有父layer,因此沒有這個文件
root@debian:/var/lib/docker/image/aufs/layerdb/sha256/67b5e1df1b671d4751794767b20f6973d77e91528ab475ee1118ce8dab796193# cat parent
sha256:6a8bf8c8edbd705f67cdc062eee3911470a38a763258c81c05da1f28d6eec896

#當前layer的大小,單位是字節
root@debian:/var/lib/docker/image/aufs/layerdb/sha256/67b5e1df1b671d4751794767b20f6973d77e91528ab475ee1118ce8dab796193# cat size
745

#tar-split.json.gz,layer壓縮包的split文件,經過這個文件能夠還原layer的tar包,
#在docker save導出image的時候會用到
#詳情可參考https://github.com/vbatts/tar-split
root@debian:/var/lib/docker/image/aufs/layerdb/sha256/67b5e1df1b671d4751794767b20f6973d77e91528ab475ee1118ce8dab796193# ls -l tar-split.json.gz
-rw-r--r-- 1 root root 1429 May 28 22:57 tar-split.json.gz

layer數據

docker根據後臺所採用的文件系統不一樣,在/var/lib/docker目錄下建立了不一樣的子目錄,對於debian來講,默認文件系統是aufs,因此全部layer的文件都放在了/var/lib/docker/aufs目錄下。

root@dev:~# tree -d -L 1 /var/lib/docker/aufs
/var/lib/docker/aufs
├── diff
├── layers
└── mnt

該目錄下有三個子目錄,layers裏面存放的layer的父子關係,diff目錄存放的是每一個layer的原始數據,mnt存儲的是layer和祖先layer疊加起來的數據。

注意:因爲docker所採用的文件系統不一樣,/var/lib/docker/<storage-driver>目錄下的目錄結構及組織方式也會不同,要具體文件系統具體分析,本文只介紹aufs這種狀況。
關於aufs和btrfs的相關特性能夠參考Linux文件系統之aufsBtrfs文件系統之subvolume與snapshot

仍是以剛纔的第二層layer(fe9a3f...)爲例,看看實際的數據:

#從上面layerdb中,咱們已經找到了第二層layer對應的cache id爲b656bf...
#先看看layers目錄下的這個文件,裏面存放的是當前layer的祖先layer的cacheid
root@dev:~# cat /var/lib/docker/aufs/layers/b656bf5f0688069cd90ab230c029fdfeb852afcfd0d1733d087474c86a117da3
1e83d2ea184e08eed978127311cc96498e319426abe2fb5004d4b1454598bd76

#再來看看diff目錄下的內容,看這一層包含了哪些文件,從下面的輸出能夠看出,這一層包含的文件不多
root@dev:~# tree /var/lib/docker/aufs/diff/b656bf5f0688069cd90ab230c029fdfeb852afcfd0d1733d087474c86a117da3/
/var/lib/docker/aufs/diff/b656bf5f0688069cd90ab230c029fdfeb852afcfd0d1733d087474c86a117da3/
├── etc
│   ├── apt
│   │   └── apt.conf.d
│   │       ├── docker-autoremove-suggests
│   │       ├── docker-clean
│   │       ├── docker-gzip-indexes
│   │       └── docker-no-languages
│   └── dpkg
│       └── dpkg.cfg.d
│           └── docker-apt-speedup
├── sbin
│   └── initctl
├── usr
│   └── sbin
│       └── policy-rc.d
└── var
    └── lib
        └── dpkg
            ├── diversions
            └── diversions-old

#再來看看這一層和上一層合併後的文件目錄
root@dev:~# tree /var/lib/docker/aufs/mnt/b656bf5f0688069cd90ab230c029fdfeb852afcfd0d1733d087474c86a117da3/
/var/lib/docker/aufs/mnt/b656bf5f0688069cd90ab230c029fdfeb852afcfd0d1733d087474c86a117da3/

0 directories, 0 files
#爲何是空的呢?這和aufs文件系統有關,
#它只有在運行容器的時候,纔會將多層合併起來,提供一個統一的視圖,
#因此這裏看不到這兩層合併以後的效果。

manifest文件去哪了?

從前面介紹docker pull的過程當中得知,docker是先獲得manifest,而後根據manifest獲得config文件和layer。

前面已經介紹了config文件和layer的存儲位置,但惟獨不見manifest,去哪了呢?

manifest裏面包含的內容就是對config和layer的sha256 + media type描述,目的就是爲了下載config和layer,等image下載完成後,manifest的使命就完成了,裏面的信息對於image的本地管理來講沒什麼用,因此docker在本地沒有單獨的存儲一份manifest文件與之對應。

結束語

本篇介紹了image在本地的存儲方式,包括了/var/lib/docker/image/var/lib/docker/aufs這兩個目錄,但/var/lib/docker/image下面有兩個目錄沒有涉及:

  • /var/lib/docker/image/aufs/imagedb/metadata:裏面存放的是本地image的一些信息,從服務器上pull下來的image不會存數據到這個目錄,下次有機會再補充這部份內容。

  • /var/lib/docker/image/aufs/layerdb/mounts: 建立container時,docker會爲每一個container在image的基礎上建立一層新的layer,裏面主要包含/etc/hosts、/etc/hostname、/etc/resolv.conf等文件,建立的這一層layer信息就放在這裏,後續在介紹容器的時候,會專門介紹這個目錄的內容。

參考

相關文章
相關標籤/搜索