鏡像在遠(yuǎn)端鏡像倉(cāng)庫(kù)和本地的存儲(chǔ)方式不同雀摘。在鏡像倉(cāng)庫(kù)中,按層壓縮后存儲(chǔ)枉层,因?yàn)樾枰紤]拉取泉褐、推送的效率;在本地是解壓后存儲(chǔ)的鸟蜡,因?yàn)樾枰紤]快速起容器膜赃,通過(guò)聯(lián)合掛載的方式構(gòu)造rootfs(聯(lián)合文件系統(tǒng)UnionFS)。下面對(duì)兩種存儲(chǔ)方式分別進(jìn)行介紹揉忘。
幾個(gè)名詞解釋
首先對(duì)后面要用到的幾個(gè)名詞做個(gè)簡(jiǎn)單解釋跳座。
digest: 摘要信息,通常是文件的SHA256哈希值泣矛。
tag: 鏡像的標(biāo)簽疲眷,通常用來(lái)表示鏡像的一個(gè)版本。
Image ID: 鏡像配置文件(config文件)的digest值您朽。docker images時(shí)顯示的鏡像ID狂丝,本地保存在
/var/lib/docker/image/overlay2/repositories.json
文件中换淆,同一個(gè)鏡像可以打多個(gè)不同的tag,但image ID都相同几颜。config文件:鏡像的配置文件倍试,保存的是鏡像的詳細(xì)描述信息,包括根文件系統(tǒng)蛋哭,容器運(yùn)行時(shí)使用的執(zhí)行參數(shù)及鏡像的元數(shù)據(jù)县习。還有容器運(yùn)行需要的相關(guān)信息,如arch谆趾、OS等躁愿。保存在
/var/lib/docker/image/overlay2/imagedb/content/sha256/\${image_ID}
中。sha256sum /var/lib/docker/image/overlay2/imagedb/content/sha256/${image_ID}
結(jié)果就是image ID的值沪蓬。layer: 鏡像的實(shí)際層彤钟,保存的是該層和上一層的差異部分,包括添加跷叉、更改和刪除样勃。保存在
/var/lib/docker/image/overlay2/layerdb/sha256/\${diff_ID}
目錄下。manifest:鏡像清單文件性芬,保存的是layer和config文件的digest峡眶。該文件保存在遠(yuǎn)端倉(cāng)庫(kù)中。
blob:鏡像在遠(yuǎn)程倉(cāng)庫(kù)的基本存儲(chǔ)單元植锉,包含layer辫樱,config,manifest等數(shù)據(jù)俊庇。
-
鏡像索引(image index):指向一組支持不同架構(gòu)的鏡像狮暑。
幾個(gè)概念之間的關(guān)系示意圖如下所示:
鏡像在遠(yuǎn)端倉(cāng)庫(kù)存儲(chǔ)
在本地起一個(gè)registry服務(wù),然后推送三個(gè)鏡像到鏡像倉(cāng)庫(kù)辉饱“崮校可以得到registry中的文件內(nèi)容如下所示。registry中包含三個(gè)鏡像: xxx/library/debian:latest彭沼,xxx/repo:tag和xxx/busybox:v1
└── registry
└── v2
├── blobs
│ └── sha256
│ ├── 0d
│ │ └── 0d96da54f60b86a4d869d44b44cfca69d71c4776b81d361bc057d6666ec0d878
│ │ └── data
│ ├── 34
│ │ └── 34efe68cca33507682b1673c851700ec66839ecf94d19b928176e20d20e02413
│ │ └── data
│ ...
└── repositories
├── busybox
│ ├── _layers
│ │ └── sha256
│ │ ├── 7138284460ffa3bb6ee087344f5b051468b3f8697e2d1427bac1a20c8d168b14
│ │ │ └── link
│ │ └── e685c5c858e36338a47c627763b50dfe6035b547f1f75f0d39753db71e319016
│ │ └── link
│ ├── _manifests
│ │ ├── revisions
│ │ │ └── sha256
│ │ │ └── 34efe68cca33507682b1673c851700ec66839ecf94d19b928176e20d20e02413
│ │ │ └── link
│ │ └── tags
│ │ └── v1
│ │ ├── current
│ │ │ └── link
│ │ └── index
│ │ └── sha256
│ │ └── 34efe68cca33507682b1673c851700ec66839ecf94d19b928176e20d20e02413
│ │ └── link
│ └── _uploads
├── library
│ └── debian
│ ├── _layers
│ │ └── sha256
│ │ ├── 41c22baa66ecf728c1ea0c5405ebe72c5b2606ef66b4565a209e23e1ab05fe80
│ │ │ └── link
│ │ ├── 67283bbdd4a0dd32f555b4279fd546b3c69251342f0c6715b075cc72049d28a1
│ │ │ └── link
│ │ ...
│ ├── _manifests
│ │ ├── revisions
│ │ │ └── sha256
│ │ │ └── 57c1e4ff150e2782a25c8cebb80b574f81f06b74944caf972f27e21b76074194
│ │ │ └── link
│ │ └── tags
│ │ └── latest
│ │ ├── current
│ │ │ └── link
│ │ └── index
│ │ └── sha256
│ │ └── 57c1e4ff150e2782a25c8cebb80b574f81f06b74944caf972f27e21b76074194
│ │ └── link
│ └── _uploads
└── repo
├── _layers
│ └── sha256
│ ├── 0d96da54f60b86a4d869d44b44cfca69d71c4776b81d361bc057d6666ec0d878
│ │ └── link
│ ├── 3790aef225b922bc97aaba099fe762f7b115aec55a0083824b548a6a1e610719
│ │ └── link
│ ...
├── _manifests
│ ├── revisions
│ │ └── sha256
│ │ └── 36cb5b157911061fb610d8884dc09e0b0300a767a350563cbfd88b4b85324ce4
│ │ └── link
│ └── tags
│ └── tag
│ ├── current
│ │ └── link
│ └── index
│ └── sha256
│ └── 36cb5b157911061fb610d8884dc09e0b0300a767a350563cbfd88b4b85324ce4
│ └── link
└── _uploads
將上面的結(jié)構(gòu)稍加整理缔逛,可以得到如下圖所示結(jié)構(gòu)
registry有兩個(gè)目錄,分別為blobs和repositories姓惑,其中blobs保存的是鏡像的manifest文件褐奴、config文件和layer文件內(nèi)容,文件名字均為data于毙,每個(gè)文件可能是manifest敦冬、config、layer中的一種唯沮。repositories保存的是鏡像的repo脖旱、tag堪遂、layer摘要等信息。其中的
_manifests
文件夾下包含著鏡像的 tags 和 revisions 信息萌庆,每一個(gè)鏡像的每一個(gè) tag 對(duì)應(yīng) tag 名相同的目錄蚤氏。每個(gè) tag名目錄下面有 current 目錄和 index 目錄, current 目錄下的 link 文件保存了該 tag 目前的 manifest 文件的 sha256 編碼踊兜,對(duì)應(yīng)在 blobs 中的 sha256 目錄下的 data 文件,而 index 目錄則列出了該 tag 歷史上傳的所有版本的 sha256 編碼信息佳恬。_revisions 目錄里存放了該 repository 歷史上上傳版本的所有 sha256 編碼信息捏境。
下面通過(guò)例子來(lái)說(shuō)明下幾個(gè)文件的關(guān)系。
- manifest文件
查看busybox:v1文件的manifest信息
cat docker/registry/docker/registry/v2/repositories/busybox/_manifests/tags/v1/current/link
sha256:34efe68cca33507682b1673c851700ec66839ecf94d19b928176e20d20e02413
可以看到link中返回的是一個(gè)digest值毁葱。
根據(jù)該digest值垫言,我們到blobs中查看其中保存的數(shù)據(jù):
cat docker/registry/docker/registry/v2/blobs/sha256/34/34efe68cca33507682b1673c851700ec66839ecf94d19b928176e20d20e02413/data
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"size": 1456,
"digest": "sha256:7138284460ffa3bb6ee087344f5b051468b3f8697e2d1427bac1a20c8d168b14"
},
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 772792,
"digest": "sha256:e685c5c858e36338a47c627763b50dfe6035b547f1f75f0d39753db71e319016"
}
]
可以看出來(lái),這是一個(gè)manifest文件倾剿,里面包含了config和layer的digest值筷频,且config文件的digest值就是執(zhí)行docker images
看到的鏡像的image ID。下面分別查看兩個(gè)文件的內(nèi)容前痘。
首先是config文件:
cat docker/registry/docker/registry/v2/blobs/sha256/71/7138284460ffa3bb6ee087344f5b051468b3f8697e2d1427bac1a20c8d168b14/d
{
"architecture": "amd64",
"config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"sh"
],
"Image": "sha256:d39a5c18a94ca076b3f9fad5b104d1b5555697280b61cbabd1eec6d89908b1b6",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": null
},
"container": "8afe392526b6fa99a3498001c95812b187123968e5a14802c9e837e1cd06d02b",
"container_config": {
"Hostname": "8afe392526b6",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"CMD [\"sh\"]"
],
"Image": "sha256:d39a5c18a94ca076b3f9fad5b104d1b5555697280b61cbabd1eec6d89908b1b6",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {}
},
"created": "2021-11-11T19:19:37.862545075Z",
"docker_version": "20.10.7",
"history": [
{
"created": "2021-11-11T19:19:37.680254655Z",
"created_by": "/bin/sh -c #(nop) ADD file:10aef872700b72808327a02dd1b22ca1ac9d3e1058cb35cfec1fcfcd1b465ab4 in / "
},
{
"created": "2021-11-11T19:19:37.862545075Z",
"created_by": "/bin/sh -c #(nop) CMD [\"sh\"]",
"empty_layer": true
}
],
"os": "linux",
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:d94c78be13527d00673093f9677f9b43d7e3a02ae6fa0ec74d3d98243b5b40e4"
]
}
}
可以看出凛捏,其中包含了容器的鏡像的架構(gòu)、默認(rèn)配置芹缔,啟動(dòng)的容器坯癣,鏡像構(gòu)建命令,操作系統(tǒng)最欠、diff_ids等信息示罗。其中的diff_ids是鏡像每一層解壓后的digest值,在拉取鏡像時(shí)芝硬,可以用來(lái)校驗(yàn)本地是否已經(jīng)存在該層蚜点。鏡像層本地保存路徑為/var/lib/docker/image/overlay2/layerdb/sha256/\${diff_id}
最后看下鏡像的layer文件:
file docker/registry/docker/registry/v2/blobs/sha256/e6/e685c5c858e36338a47c627763b50dfe6035b547f1f75f0d39753db71e319016/data
docker/registry/docker/registry/v2/blobs/sha256/e6/e685c5c858e36338a47c627763b50dfe6035b547f1f75f0d39753db71e319016/data: gzip compressed data
該文件是一個(gè)gzip的壓縮包,從前面的manifest文件中可以知道文件類型為:application/vnd.docker.image.rootfs.diff.tar.gzip拌阴。
鏡像下載流程
最后绍绘,從上面的分析中,我們可以推測(cè)到鏡像拉取的大致流程為:
- docker client發(fā)送鏡像的tag到registry迟赃。
HEAD /v2/<namespace>/<repo>/manifest/<tag>
(docker1.19之前不發(fā)HEAD脯倒,直接發(fā)GET請(qǐng)求) - docker client發(fā)送GET請(qǐng)求到registry,下載Manifest文件捺氢。
- registry根據(jù)鏡像tag藻丢,得到鏡像的manifest文件,返回給docker client摄乒。
- docker client拿到manifest文件后悠反,根據(jù)其中的config的digest残黑,也就是image ID,檢查下鏡像在本地是否存在斋否。
- 如果鏡像不存在,則下載config文件茵臭,并根據(jù)config文件中的diff_ids得到鏡像每一層解壓后的digest疫诽。
- 然后根據(jù)每層解壓后的digest文件旦委,檢查本地是否存在,如果不存在缨硝,則通過(guò)manifest文件中的layer的digest下載該層并解壓摩钙,然后校驗(yàn)解壓后digest是否匹配查辩。
GET /v2/<namespace>/<repo>/blobs/<sha256>
- 下載完所有層后,鏡像就下載完畢宜岛。
鏡像推送流程
鏡像下載流程和推送過(guò)程正好相反。
- 先發(fā)送POST請(qǐng)求萍倡,根據(jù)響應(yīng)header的Location獲取blob需要上傳到哪個(gè)位置之斯。
POST /v2/<namespace>/<repo>/blobs/uploads
- 發(fā)送HEAD請(qǐng)求遣铝,檢查每一層再鏡像倉(cāng)庫(kù)中是否存在。返回404表示不存在酿炸,200表示已存在瘫絮,307表示重定向到鏡像存儲(chǔ)位置確認(rèn),如對(duì)象存儲(chǔ)中確認(rèn)填硕。
HEAD /v2/<namespace/<repo>/blobs/<digest>
- 并發(fā)上傳鏡像層麦萤。docker client發(fā)送PATCH請(qǐng)求到registry,上傳該層數(shù)據(jù)扁眯。上傳完畢后壮莹,繼續(xù)步驟2,直到所有鏡像層傳輸完畢后姻檀,進(jìn)入下一步命满。
PATCH /v2/<namespace>/<repo>/blobs/uploads/<id>
。 - docker client發(fā)送PUT請(qǐng)求到registry绣版,將鏡像manifest發(fā)送給registry胶台,并上傳鏡像tag歼疮。至此,鏡像上傳完畢诈唬。
PUT /v2/<namespace>/<repo>/manifest/<tag>
韩脏。
本地鏡像存儲(chǔ)
我使用的存儲(chǔ)驅(qū)動(dòng)時(shí)overlay2,鏡像在本地存儲(chǔ)目錄為/var/lib/docker/image/overlay2
铸磅,查看下面的文件結(jié)構(gòu)赡矢,得到結(jié)果如下:
tree -L 4 /var/lib/docker/image/overlay2/
/var/lib/docker/image/overlay2/
├── distribution
│ ├── diffid-by-digest
│ │ └── sha256
│ │ ├── 0240c3db9dedbfe40ec02d465375aa5b059bf8ac78dc249d1f1c91b9429fce44
│ │ ├── 41c22baa66ecf728c1ea0c5405ebe72c5b2606ef66b4565a209e23e1ab05fe80
│ │ ├── 4cdd12619cf5ed0ae43b41cd51f26fbdbd1f5ded860e4188822ec29158218263
│ │ ├── ...
│ └── v2metadata-by-diffid
│ └── sha256
│ ├── 00188c48b6d80656e2344142a77bccf6927123e7492baf43df68e280b2baf7f2
│ ├── 04fefa2a1a8fefaafde3b966f11d547e3bbaa2bb36bf90c58e33c1d305052fa9
│ ├── ...
├── imagedb
│ ├── content
│ │ └── sha256
│ │ ├── 7138284460ffa3bb6ee087344f5b051468b3f8697e2d1427bac1a20c8d168b14
│ │ ├── ...
│ └── metadata
│ └── sha256
│ ├── b8604a3fe8543c9e6afc29550de05b36cd162a97aa9b2833864ea8a5be11f3e2
│ └── dabbfbe0c57b6e5cd4bc089818d3f664acfad496dc741c9a501e72d15e803b34
├── layerdb
│ ├── mounts
│ │ ├── 2d534be7517fb3efd9c14248eefdb4781924095fe304f5aa0c848f2e76c6bf08
│ │ │ ├── init-id
│ │ │ ├── mount-id
│ │ │ └── parent
│ │ ├──...
│ ├── sha256
│ │ ├── 0e16a5a61bcb4e6b2bb2d746c2d6789d6c0b66198208b831f74b52198d744189
│ │ │ ├── cache-id
│ │ │ ├── diff
│ │ │ ├── parent
│ │ │ ├── size
│ │ │ └── tar-split.json.gz
│ │ ├── 0ee0aa554b8be64c963aaaf162df152784d868d21a7414146cb819a93e4bdb9e
│ │ │ ├── cache-id
│ │ │ ├── diff
│ │ │ ├── parent
│ │ │ ├── size
│ │ │ └── tar-split.json.gz
│ │ ├── ...
│ └── tmp
└── repositories.json
對(duì)上面的文件結(jié)構(gòu)進(jìn)行整理,可以得到如下圖所示的結(jié)構(gòu):
此處我們主要關(guān)心imagedb阅仔、layerdb和repositories中的內(nèi)容吹散。
- repositories.json
該json文件部分內(nèi)容如下:
cat /var/lib/docker/image/overlay2/repositories.json | jq
{
"Repositories":{
"registry/busybox":{
"registry/busybox:v1":"sha256:7138284460ffa3bb6ee087344f5b051468b3f8697e2d1427bac1a20c8d168b14",
"registry/busybox@sha256:34efe68cca33507682b1673c851700ec66839ecf94d19b928176e20d20e02413":"sha256:7138284460ffa3bb6ee087344f5b051468b3f8697e2d1427bac1a20c8d168b14"
}
}
}
}
可以看出repositories.json文件中保存的是鏡像tag和鏡像ID的對(duì)應(yīng)關(guān)系,以及鏡像manifest的digest值和鏡像ID的對(duì)應(yīng)關(guān)系霎槐。其實(shí)我們除了通過(guò)鏡像tag拉取鏡像外,也可以直接使用manifest的digest拉取鏡像梦谜,如下:
docker pull registry/busybox@sha256:34efe68cca33507682b1673c851700ec66839ecf94d19b928176e20d20e02413
- imagedb
imagedb下的content保存的是鏡像的config文件
metadata目錄下保存的是元信息丘跌,如鏡像的最近更新時(shí)間等 - layerdb
layerdb下面的mounts目錄保存的信息暫不清楚是做什么用的唁桩。闭树。。荒澡。
sha256目錄下保存的是鏡像每一層的實(shí)際內(nèi)容报辱,包括parent、diff等单山。因?yàn)殓R像是按層構(gòu)建的碍现,需要記錄每一層的上一層是什么昼接,與上一層的差異點(diǎn)等。
鏡像索引
最后簡(jiǎn)單介紹下鏡像索引慢睡。
從前面的config文件中可以知道,一個(gè)鏡像只能在指定架構(gòu)的機(jī)器上執(zhí)行漂辐,如果要在不同架構(gòu)的機(jī)器上運(yùn)行棕硫,則需要拉取不同架構(gòu)的鏡像。以前我們通過(guò)uname -m
命令獲取機(jī)器架構(gòu)信息哈扮,然后拉取不同架構(gòu)的鏡像瘤泪,非常麻煩育八。因此,OCI推出了鏡像索引髓棋,通過(guò)鏡像索引,可以根據(jù)本地機(jī)器的架構(gòu)按声,自動(dòng)拉取對(duì)應(yīng)架構(gòu)的鏡像。
如圖所示须床,鏡像索引包含了不同架構(gòu)下鏡像的manifest的digest。在拉取鏡像的時(shí)候豺旬,就可以按照不同的OS架構(gòu)拉取不同的鏡像了柒凉。