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

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

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

從上面的圖可以看到,我們的鏡像是分層的,我們的Dockerfile中新增一條指令,就會新增一層!
如果我們將多個命令合成一個,那么也只會生成一層。修改一下上面的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è)計呢?
試想一下我們?nèi)绻环謱訒惺裁磫栴}?
以拉取鏡像為例!
拉取鏡像的鏡像很大,比如Redis的鏡像有100多M
第一次我們拉取6.2版本的Redis,下載了完成的100M到本地,下次我要下載6.2.6版本的,是不是又得下載100M。
盡管可能兩個版本之間就改了幾行配置文件。
這樣是非常低效的。如果能只下載有差異的部分就好了!
這個痛點,也就是鏡像分層要解決的問題。實際上,Docker也是這么實現(xiàn)的。
第一次下載redis:6.2時,因為之前沒有下載過,所以下載了所有的層,總共113M。網(wǎng)絡(luò)慢點的話還是需要花一些時間的!

第二次下載redis:7.0-rc,就變得快了很多!因為前面3層是redis:6.2是一樣的,這些層已經(jīng)下載過了!

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

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

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

這樣做的好處是可以大大減少每一層的大??!
更具體的實現(xiàn)請參考官方文檔中:Copy-on-write策略
到此這篇關(guān)于徹底搞懂Docker鏡像分層的文章就介紹到這了,更多相關(guān)Docker鏡像分層內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
docker部署nginx訪問宿主機服務(wù)并使用緩存的操作方法
這篇文章主要介紹了docker部署nginx訪問宿主機服務(wù)并使用緩存的操作方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,感興趣的朋友一起看看吧2024-04-04
Centos7使用docker部署生產(chǎn)環(huán)境中間件的詳細(xì)步驟
這篇文章主要為大家詳細(xì)介紹了騰訊云Centos7使用docker部署生產(chǎn)環(huán)境中間件的詳細(xì)步驟,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-07-07
Docker教程之Ubuntu 安裝 Docker詳細(xì)介紹
這篇文章主要介紹了Docker教程之Ubuntu 安裝 Docker詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2016-12-12
Docker中conda環(huán)境的導(dǎo)出和導(dǎo)入
現(xiàn)在很多的應(yīng)用程序系統(tǒng)都會選擇使用docker容器進(jìn)行部署,本文主要介紹了Docker中conda環(huán)境的導(dǎo)出和導(dǎo)入,具有一定的參考價值,感興趣的可以了解一下2024-02-02
ubuntu docker搭建Hadoop集群環(huán)境的方法
這篇文章主要介紹了ubuntu docker搭建Hadoop集群環(huán)境,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-06-06

