如何理解Docker鏡像分層完丽?且聽(tīng)百度高級(jí)研發(fā)工程師細(xì)細(xì)道來(lái)

目錄

  • 關(guān)于base鏡像
  • 關(guān)于存儲(chǔ)結(jié)構(gòu)(About storage drivers)
    • 先來(lái)創(chuàng)建一個(gè)自己的鏡像
    • docker鏡像的分層結(jié)構(gòu)
    • 容器的大小
    • 修改時(shí)復(fù)制策略 copy-on-write (CoW)
    • Copying makes containers efficient

關(guān)于base鏡像

base 鏡像有兩層含義:

  • 不依賴其他鏡像捺僻,從 scratch 構(gòu)建乡洼。
  • 其他鏡像可以之為基礎(chǔ)進(jìn)行擴(kuò)展。

所以匕坯,能稱作 base 鏡像的通常都是各種 Linux 發(fā)行版的 Docker 鏡像束昵,比如 Ubuntu, Debian, CentOS 等。

base 鏡像提供的是最小安裝的 Linux 發(fā)行版葛峻。

我們大部分鏡像都將是基于base鏡像構(gòu)建的锹雏。所以,通常使用的是官方發(fā)布的base鏡像术奖”普欤可以在docker hub里找到匿辩。比如centos: https://hub.docker.com/_/centos

點(diǎn)擊版本可以看到github里的Dockerfile

FROM scratch
ADD centos-7-docker.tar.xz /

LABEL org.label-schema.schema-version="1.0" \
    org.label-schema.name="CentOS Base Image" \
    org.label-schema.vendor="CentOS" \
    org.label-schema.license="GPLv2" \
    org.label-schema.build-date="20181205"

CMD ["/bin/bash"]

ADD命令將本地的centos7的tar包添加到鏡像,并解壓到根目錄/下榛丢。生成/dev,/proc/,/bin等。

我們可以自己構(gòu)建docker base鏡像挺庞,也可以直接使用已有的base鏡像晰赞。比如centos。我們可以直接從docker hub上拉取选侨。

拉取

docker pull centos

查看

# docker images centos 
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos              latest              1e1148e4cc2c        2 months ago        202MB

可以看到最新的centos鏡像只有200mb掖鱼,是不是覺(jué)得太小了?這是因?yàn)閐ocker鏡像在運(yùn)行的時(shí)候直接使用docker宿主機(jī)器的kernel援制。

Linux操作系統(tǒng)由內(nèi)核空間和用戶空間組成戏挡。

內(nèi)核空間是kernel,用戶空間是rootfs, 不同Linux發(fā)行版的區(qū)別主要是rootfs.比如 Ubuntu 14.04 使用 upstart 管理服務(wù)晨仑,apt 管理軟件包褐墅;而 CentOS 7 使用 systemd 和 yum。這些都是用戶空間上的區(qū)別洪己,Linux kernel 差別不大妥凳。

所以 Docker 可以同時(shí)支持多種 Linux 鏡像,模擬出多種操作系統(tǒng)環(huán)境答捕。

需要注意的是

  • base鏡像只是用戶空間和發(fā)行版一致逝钥。kernel使用的是docker宿主機(jī)器的kernel。例如 CentOS 7 使用 3.x.x 的 kernel拱镐,如果 Docker Host 是 Ubuntu 16.04(比如我們的實(shí)驗(yàn)環(huán)境)艘款,那么在 CentOS 容器中使用的實(shí)際是是 Host 4.x.x 的 kernel。
  • ① Host kernel 為 4.4.0-31
  • ② 啟動(dòng)并進(jìn)入 CentOS 容器
  • ③ 驗(yàn)證容器是 CentOS 7
  • ④ 容器的 kernel 版本與 Host 一致

關(guān)于存儲(chǔ)結(jié)構(gòu)(About storage drivers)

上文里展示了如何下載一個(gè)base鏡像沃琅。我們通常是基于這份base鏡像來(lái)構(gòu)建我們自己的鏡像哗咆。比如,在centos里添加一個(gè)nginx負(fù)載均衡阵难。首先岳枷,得需要了解鏡像的結(jié)構(gòu)是什么。

官方文檔: https://docs.docker.com/storage/storagedriver/

先來(lái)創(chuàng)建一個(gè)自己的鏡像

首先呜叫,base鏡像是基于docker宿主機(jī)器kernel之上的Linux發(fā)行版空繁。

現(xiàn)在,我們給這臺(tái)機(jī)器安裝一個(gè)vim朱庆,一個(gè)httpd. 基于Dockerfile來(lái)創(chuàng)建一個(gè)新的鏡像盛泡。

我們的Dockerfile

FROM centos:7
RUN yum install -y vim
RUN yum install -y httpd
CMD ["/bin/bash"]

含義:

  • 基于centos7的base鏡像構(gòu)建
  • 安裝vim
  • 安裝httpd
  • 執(zhí)行bash

在當(dāng)前目錄下新建一個(gè)文件Dockerfile, 填充上述內(nèi)容。然后執(zhí)行

# docker build -t ryan/httpd:v1.0 .
Sending build context to Docker daemon  6.144kB
Step 1/4 : FROM centos:7
 ---> 1e1148e4cc2c
Step 2/4 : RUN yum install -y vim
 ---> Using cache
 ---> 74bdbea98f73
Step 3/4 : RUN yum install -y httpd
 ---> Using cache
 ---> 17d8c4095dc4
Step 4/4 : CMD /bin/bash
 ---> Using cache
 ---> f2b58b1192de
Successfully built f2b58b1192de
Successfully tagged ryan/httpd:latest
  • -t 指定我們創(chuàng)建的鏡像名稱娱颊,鏡像名稱可以用組織/id:version的方式標(biāo)記
  • 最后一個(gè)參數(shù)是Dockerfile所在的路徑., 表示當(dāng)前目錄

然后我們添加一個(gè)tag latest

docker tag ryan/httpd:v1.0 ryan/httpd:latest
  • 即給鏡像ryan/httpd:v1.0標(biāo)記為ryan/httpd:latest

構(gòu)建完成之后傲诵,查看

# docker images  | grep -E '(ryan|centos)'
ryan/httpd                                                               latest                     f2b58b1192de        About an hour ago   444MB
ryan/httpd                                                               v1.0                       f2b58b1192de        About an hour ago   444MB
centos                                                                   7                          1e1148e4cc2c        2 months ago        202MB
centos                                                                   latest                     1e1148e4cc2c        2 months ago        202MB

可以運(yùn)行我們創(chuàng)建的鏡像:

# docker run -d  --privileged=true -it ryan/httpd:v1.0 /usr/sbin/init
48a4a128cd7b6924149cd97670919d4e2af6cb96c73c901af60d05fe4478225a
# docker ps | grep ryan
48a4a128cd7b        ryan/httpd:v1.0                                                          "/usr/sbin/init"         8 seconds ago       Up 8 seconds       

現(xiàn)在我們的基于原生base centos7的httpd服務(wù)器已經(jīng)啟動(dòng)了凯砍。可以通過(guò)docker exec -it zealous_kirch /bin/bash來(lái)進(jìn)入容器內(nèi)部拴竹,查看啟動(dòng)httpd悟衩。

docker鏡像的分層結(jié)構(gòu)

我們可以查看鏡像的歷史,用上一步的鏡像id f2b58b1192de

# docker history f2b58b1192de
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
f2b58b1192de        About an hour ago   /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B                  
17d8c4095dc4        About an hour ago   /bin/sh -c yum install -y httpd                 110MB               
74bdbea98f73        About an hour ago   /bin/sh -c yum install -y vim                   133MB               
1e1148e4cc2c        2 months ago        /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B                  
<missing>           2 months ago        /bin/sh -c #(nop)  LABEL org.label-schema....   0B                  
<missing>           2 months ago        /bin/sh -c #(nop) ADD file:6f877549795f479...   202MB   

啟動(dòng)鏡像的時(shí)候栓拜,一個(gè)新的可寫層會(huì)加載到鏡像的頂部座泳。這一層通常稱為“容器層”, 之下是“鏡像層”幕与。

容器層可以讀寫挑势,容器所有發(fā)生文件變更寫都發(fā)生在這一層。鏡像層read-only,只允許讀取啦鸣。

(上圖來(lái)自官方文檔潮饱,和本次實(shí)驗(yàn)內(nèi)容略有不同,但原理一樣)

第一列是imageid, 最上面的id就是我們新創(chuàng)建ryan/httpd:latest. 下面幾行都是我們dockerfile里定義的步驟堆棧诫给。由此可以看出香拉,每個(gè)步驟都將創(chuàng)建一個(gè)imgid, 一直追溯到1e1148e4cc2c正好是我們的base鏡像的id。關(guān)于<missing>的部分蝙搔,則不在本機(jī)上缕溉。

最后一列是每一層的大小。最后一層只是啟動(dòng)bash吃型,所以沒(méi)有文件變更证鸥,大小是0. 我們創(chuàng)建的鏡像是在base鏡像之上的,并不是完全復(fù)制一份base勤晚,然后修改枉层,而是共享base的內(nèi)容。這時(shí)候赐写,如果我們新建一個(gè)新的鏡像鸟蜡,同樣也是共享base鏡像。

那修改了base鏡像挺邀,會(huì)不會(huì)導(dǎo)致我們創(chuàng)建的鏡像也被修改呢揉忘? 不會(huì)!因?yàn)椴辉试S修改歷史鏡像端铛,只允許修改容器泣矛,而容器只可以在最上面的容器層進(jìn)行寫和變更。

容器的大小

創(chuàng)建鏡像的時(shí)候禾蚕,分層可以讓docker只保存我們添加和修改的部分內(nèi)容您朽。其他內(nèi)容基于base鏡像,不需要存儲(chǔ)换淆,讀取base鏡像即可哗总。如此几颜,當(dāng)我們創(chuàng)建多個(gè)鏡像的時(shí)候,所有的鏡像共享base部分讯屈。節(jié)省了磁盤空間蛋哭。

對(duì)于啟動(dòng)的容器,查看所需要的磁盤空間可以通過(guò)docker ps -s

# docker run -d -it centos
4b0df4bc3e705c540144d545441930689124ade087961d01f56c2ac55bfd986d
# docker ps -s | grep -E '(ryan|centos)'
4b0df4bc3e70        centos                                                                   "/bin/bash"              23 seconds ago      Up 23 seconds                           vigorous_elion                                                                                                                           0B (virtual 202MB)
b36421d05005        ryan/httpd:v1.0                                                          "/usr/sbin/init"         32 minutes ago      Up 32 minutes                           gracious_swirles                                                                                                                         61.6kB (virtual 444MB)
  • 首先啟動(dòng)一個(gè)base鏡像用來(lái)對(duì)比
  • 可以看到第一行就是base鏡像centos涮母,第2列的size是0和202MB, 0表示容器層可寫層的大小具壮,virtual則是容器層+鏡像層的大小。這里對(duì)比可以看到一共202M,正好是最初centos鏡像的大小哈蝇。
  • 第二行是我們自己創(chuàng)建的鏡像。virtual達(dá)到了444MB攘已。對(duì)比前面的history部分炮赦,可以發(fā)現(xiàn)這個(gè)數(shù)字是每一層大小之和。同時(shí)样勃,由于共享base吠勘,其中的202M是和第一行的鏡像共享的。

修改時(shí)復(fù)制策略 copy-on-write (CoW)

docker通過(guò)一個(gè)叫做copy-on-write (CoW) 的策略來(lái)保證base鏡像的安全性峡眶,以及更高的性能和空間利用率剧防。

Copy-on-write is a strategy of sharing and copying files for maximum efficiency. If a file or directory exists in a lower layer within the image, and another layer (including the writable layer) needs read access to it, it just uses the existing file. The first time another layer needs to modify the file (when building the image or running the container), the file is copied into that layer and modified. This minimizes I/O and the size of each of the subsequent layers. These advantages are explained in more depth below.

Copying makes containers efficient

When you start a container, a thin writable container layer is added on top of the other layers. Any changes the container makes to the filesystem are stored here. Any files the container does not change do not get copied to this writable layer. This means that the writable layer is as small as possible.

When an existing file in a container is modified, the storage driver performs a copy-on-write operation. The specifics steps involved depend on the specific storage driver. For the aufs, overlay, and overlay2 drivers, the copy-on-write operation follows this rough sequence:

Search through the image layers for the file to update. The process starts at the newest layer and works down to the base layer one layer at a time. When results are found, they are added to a cache to speed future operations.

Perform a copy_up operation on the first copy of the file that is found, to copy the file to the container’s writable layer.

Any modifications are made to this copy of the file, and the container cannot see the read-only copy of the file that exists in the lower layer.

Btrfs, ZFS, and other drivers handle the copy-on-write differently. You can read more about the methods of these drivers later in their detailed descriptions.

Containers that write a lot of data consume more space than containers that do not. This is because most write operations consume new space in the container’s thin writable top layer.

簡(jiǎn)單的說(shuō),啟動(dòng)容器的時(shí)候辫樱,最上層容器層是可寫層峭拘,之下的都是鏡像層,只讀層狮暑。

當(dāng)容器需要讀取文件的時(shí)候

從最上層鏡像開(kāi)始查找鸡挠,往下找搬男,找到文件后讀取并放入內(nèi)存拣展,若已經(jīng)在內(nèi)存中了,直接使用缔逛。(即备埃,同一臺(tái)機(jī)器上運(yùn)行的docker容器共享運(yùn)行時(shí)相同的文件)。

當(dāng)容器需要添加文件的時(shí)候

直接在最上面的容器層可寫層添加文件褐奴,不會(huì)影響鏡像層按脚。

當(dāng)容器需要修改文件的時(shí)候

從上往下層尋找文件,找到后歉糜,復(fù)制到容器可寫層乘寒,然后,對(duì)容器來(lái)說(shuō)匪补,可以看到的是容器層的這個(gè)文件伞辛,看不到鏡像層里的文件烂翰。容器在容器層修改這個(gè)文件。

當(dāng)容器需要?jiǎng)h除文件的時(shí)候

從上往下層尋找文件蚤氏,找到后在容器中記錄刪除甘耿。即,并不會(huì)真正的刪除文件竿滨,而是軟刪除佳恬。這將導(dǎo)致鏡像體積只會(huì)增加,不會(huì)減少于游。

綜上毁葱,Docker鏡像通過(guò)分層實(shí)現(xiàn)了資源共享,通過(guò)copy-on-write實(shí)現(xiàn)了文件隔離贰剥。

對(duì)于文件只增加不減少問(wèn)題倾剿,我們應(yīng)當(dāng)在同一層做增刪操作,從而減少鏡像體積蚌成。比如前痘,如下測(cè)試。

Dockerfile.A: 分層刪除文件

FROM centos:7
RUN yum install -y vim
RUN yum install -y httpd
WORKDIR /home
RUN dd if=/dev/zero of=50M.file bs=1M count=50
#創(chuàng)建大小為50M的測(cè)試文件
RUN rm -rf 50M.file
CMD ["/bin/bash"]

構(gòu)建

docker build -t test:a -f Dockerfile.A .

Dockerfile.B: 同層刪除

FROM centos:7
RUN yum install -y vim
RUN yum install -y httpd
WORKDIR /home
RUN dd if=/dev/zero of=50M.file bs=1M count=50 && rm -rf 50M.file

構(gòu)建

docker build -t test:b -f Dockerfile.B .

比較二者大小

[root@sh-k8s-001 tmp]# docker images | grep test
test                                                                     a                          ae673aa7db48        9 minutes ago       497MB
test                                                                     b                          21b2bc49f0bd        12 minutes ago      444MB

顯然担忧,分層刪除操作并沒(méi)有真正刪除掉文件芹缔。

寫在最后

點(diǎn)關(guān)注,不迷路瓶盛;持續(xù)更新Java相關(guān)技術(shù)及資訊文章

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末最欠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蓬网,更是在濱河造成了極大的恐慌窒所,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件帆锋,死亡現(xiàn)場(chǎng)離奇詭異吵取,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)锯厢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門皮官,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人实辑,你說(shuō)我怎么就攤上這事捺氢。” “怎么了剪撬?”我有些...
    開(kāi)封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵摄乒,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)馍佑,這世上最難降的妖魔是什么斋否? 我笑而不...
    開(kāi)封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮拭荤,結(jié)果婚禮上茵臭,老公的妹妹穿的比我還像新娘。我一直安慰自己舅世,他們只是感情好旦委,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著雏亚,像睡著了一般缨硝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上罢低,一...
    開(kāi)封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天追葡,我揣著相機(jī)與錄音,去河邊找鬼奕短。 笑死,一個(gè)胖子當(dāng)著我的面吹牛匀钧,可吹牛的內(nèi)容都是我干的翎碑。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼之斯,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼日杈!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起佑刷,我...
    開(kāi)封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤莉擒,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后瘫絮,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體涨冀,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年麦萤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鹿鳖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡壮莹,死狀恐怖翅帜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情命满,我是刑警寧澤涝滴,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響歼疮,放射性物質(zhì)發(fā)生泄漏杂抽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一腋妙、第九天 我趴在偏房一處隱蔽的房頂上張望默怨。 院中可真熱鬧,春花似錦骤素、人聲如沸匙睹。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)痕檬。三九已至,卻和暖如春送浊,著一層夾襖步出監(jiān)牢的瞬間梦谜,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工袭景, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留唁桩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓耸棒,卻偏偏與公主長(zhǎng)得像荒澡,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子与殃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • docker基本概念 1. Image Definition 鏡像 Image 就是一堆只讀層 read-only...
    慢清塵閱讀 8,748評(píng)論 1 21
  • 一单山、Docker 簡(jiǎn)介 Docker 兩個(gè)主要部件:Docker: 開(kāi)源的容器虛擬化平臺(tái)Docker Hub: 用...
    R_X閱讀 4,386評(píng)論 0 27
  • 百嶺溝里秋色濃
    放下_c1af閱讀 151評(píng)論 0 0
  • 概述 Worker的啟動(dòng)都是通過(guò)啟動(dòng)shell腳本 Master啟動(dòng) master啟動(dòng)從main函數(shù)開(kāi)始,主要啟動(dòng)...
    scandly閱讀 439評(píng)論 0 1
  • 今天上午上完主題課之后,悅悅老師在接小朋友幅疼,在接小朋友的時(shí)候米奸,恩恩和王子一起來(lái)的,今天是我第一次見(jiàn)到王子呢爽篷!...
    月落孤倚閱讀 162評(píng)論 0 0