徹底搞懂Docker鏡像分層的實(shí)現(xiàn)
創(chuàng)建測(cè)試鏡像
我們創(chuàng)建一個(gè)最簡(jiǎn)單的鏡像:
1.構(gòu)建測(cè)試鏡像v1.0:docker build -t image_test:1.0 .
FROM alpine:3.15.0 #除了繼承基礎(chǔ)鏡像,啥也不做
2.構(gòu)建測(cè)試鏡像v2.0:docker build -t image_test:2.0 .
FROM alpine:3.15.0 RUN dd if=/dev/zero of=file1 bs=10M count=1 #添加一個(gè)10M的文件file1
3.構(gòu)建測(cè)試鏡像v3.0:docker build -t image_test:3.0 .
FROM alpine:3.15.0 RUN dd if=/dev/zero of=file1 bs=10M count=1 #添加一個(gè)10M的文件file1 RUN dd if=/dev/zero of=file2 bs=10M count=1 #添加一個(gè)10M的文件file2
這樣本地就構(gòu)建了3個(gè)測(cè)試鏡像:

查看鏡像
我們有2種方法查看鏡像:
- 使用
docker inspect:獲取鏡像的元數(shù)據(jù) - 使用
docker history:查看鏡像的構(gòu)建歷史 使用docker inspect
使用docker inspect
查看鏡像的元數(shù)據(jù)。
其中Parent可以看到父鏡像, Layers這一項(xiàng)下面可以看到鏡像的所有層。
使用docker history
使用docker history可以看到鏡像的構(gòu)建歷史。
我們每一行列出了鏡像包含的層。

使用
docker history我們看到有一行很特別,就是鏡像ID為的行,這一行是什么呢?
看了 官方文檔 的描述,我們知道這些構(gòu)建步驟要么是構(gòu)建在另一個(gè)系統(tǒng),要么是鏡像的部分是從DockerHub上拉取下來(lái)的,要么是使用的是另一種構(gòu)建工具BuildKit構(gòu)建的。
很顯然,我們這里就是第二種情況,因?yàn)槲覀兊腄ockerfile中第一句指令就是FROM alpine:3.15.0.
鏡像分層圖
根據(jù)上面的docker history命令,我們可以輕松的畫(huà)出三個(gè)鏡像的分層圖:

從上面的圖可以看到,我們的鏡像是分層的,我們的Dockerfile中新增一條指令,就會(huì)新增一層!
如果我們將多個(gè)命令合成一個(gè),那么也只會(huì)生成一層。修改一下上面的image_test:3.0,把兩條RUN合并成一條:
FROM alpine:3.15.0
RUN dd if=/dev/zero of=file1 bs=10M count=1 && \
dd if=/dev/zero of=file2 bs=10M count=1使用docker history 查看image_test:4.0,可以看到,只有2層了!

鏡像分層的好處
知道了鏡像是分層的,那么我們是不是好奇為啥要這么設(shè)計(jì)呢?
試想一下我們?nèi)绻环謱訒?huì)有什么問(wèn)題?
以拉取鏡像為例!
拉取鏡像的鏡像很大,比如Redis的鏡像有100多M
第一次我們拉取6.2版本的Redis,下載了完成的100M到本地,下次我要下載6.2.6版本的,是不是又得下載100M。
盡管可能兩個(gè)版本之間就改了幾行配置文件。
這樣是非常低效的。如果能只下載有差異的部分就好了!
這個(gè)痛點(diǎn),也就是鏡像分層要解決的問(wèn)題。實(shí)際上,Docker也是這么實(shí)現(xiàn)的。
第一次下載redis:6.2時(shí),因?yàn)橹皼](méi)有下載過(guò),所以下載了所有的層,總共113M。網(wǎng)絡(luò)慢點(diǎn)的話(huà)還是需要花一些時(shí)間的!

第二次下載redis:7.0-rc,就變得快了很多!因?yàn)榍懊?層是redis:6.2是一樣的,這些層已經(jīng)下載過(guò)了!

這種思想和我們常用的版本管理工具git也是一樣的!
如果版本2是基于版本1的基礎(chǔ)上,那么版本2不需要copy一份全量的數(shù)據(jù),只需一份和版本1差異化的增量數(shù)據(jù)即可!
這樣的最終好處是,可以體現(xiàn)在以下方面:
- 拉取更快:因?yàn)榉謱恿?,只需拉取本地不存在的層即可?/li>
- 存儲(chǔ)更少:因?yàn)楣餐膶又恍璐鎯?chǔ)一份即可!
- 運(yùn)行時(shí)存儲(chǔ)更少:容器運(yùn)行時(shí)可以共享相同的層!
對(duì)于第3點(diǎn),多個(gè)基于相同鏡像運(yùn)行的容器,都可以直接使用相同的鏡像層,每個(gè)容器只需一個(gè)自己的可寫(xiě)層即可:

鏡像分層的實(shí)現(xiàn)
前面說(shuō)過(guò),Docker鏡像分層和Git的版本很像!我們不妨以此類(lèi)比!只是為了方便我們理解:
| Git | Docker |
|---|---|
| 版本 | 分層 |
| 在前一個(gè)版本的基礎(chǔ)上 | 在前一層的基礎(chǔ)上 |
| 修改了代碼 | Dockerfile中加了RUN等指令 |
| commit了修改,新增了一個(gè)版本 | 新建一個(gè)鏡像層 |
| 新版本只包含差異 | 新鏡像只包含了差異修改 |
| 已提交的commit是不能修改的 | 舊的鏡像層是不能修改的 |
總而言之,鏡像層是只讀的,新的鏡像層是基于前一個(gè)鏡像層的修改,只保留了增量修改的部分!
使用了聯(lián)合文件系統(tǒng),對(duì)文件系統(tǒng)的修改作為一次提交來(lái)一層層的疊加!
容器本質(zhì)上也是在鏡像的基礎(chǔ)上加了一層可寫(xiě)層!這個(gè)在另外的章節(jié)再詳細(xì)討論!

Copy-on-write策略
Copy-on-write是一種提高文件共享和復(fù)制效率的策略。
如果一個(gè)文件和目錄在低一層的鏡像層中存在,并且其它層想要讀取這個(gè)文件,就直接使用這個(gè)文件。
如果其它層想要修改這個(gè)文件(不管是構(gòu)建鏡像時(shí),還是在容器運(yùn)行的過(guò)程中),這個(gè)文件都會(huì)被先拷貝到新的一層中,然后再修改它。

這樣做的好處是可以大大減少每一層的大?。?br />更具體的實(shí)現(xiàn)請(qǐng)參考官方文檔中:Copy-on-write策略
到此這篇關(guān)于徹底搞懂Docker鏡像分層的文章就介紹到這了,更多相關(guān)Docker鏡像分層內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于Docker的Mysql主備搭建的實(shí)現(xiàn)步驟
本文主要介紹了基于Docker的Mysql主備搭建的實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
使用docker部署mysql并開(kāi)啟binlog的方法
本文介紹了如何使用Docker部署MySQL服務(wù)并配置開(kāi)啟binlog,以便通過(guò)Flink CDC Connector實(shí)現(xiàn)對(duì)MySQL數(shù)據(jù)的實(shí)時(shí)同步,有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-02-02
Docker 通過(guò)端口來(lái)連接一個(gè)容器的實(shí)現(xiàn)
這篇文章主要介紹了Docker 通過(guò)端口來(lái)連接一個(gè)容器的實(shí)現(xiàn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-11-11
Docker復(fù)制現(xiàn)有容器的實(shí)現(xiàn)方法
在使用Docker進(jìn)行應(yīng)用開(kāi)發(fā)和部署時(shí),我們經(jīng)常需要基于現(xiàn)有的容器創(chuàng)建相似的環(huán)境,本文主要介紹了Docker復(fù)制現(xiàn)有容器的實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03
詳解Docker私有倉(cāng)庫(kù)最簡(jiǎn)便的搭建方法
本篇文章主要介紹了Docker私有倉(cāng)庫(kù)最簡(jiǎn)便的搭建方法,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-02-02
在vscode中使用ssh運(yùn)行docker:從下載到運(yùn)行全流程
首先在本機(jī)或者服務(wù)器上下載docker并運(yùn)行,本文目的旨在本機(jī)下載docker并打包,然后在服務(wù)器上進(jìn)行加載,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-08-08
docker安裝tomcat8的實(shí)現(xiàn)方法
這篇文章主要介紹了docker安裝tomcat8的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02

