Docker鏡像由只讀層組成,每個(gè)層都代表一個(gè)Dockerfile指令勃救。這些層是堆疊的邦蜜,每一層都是前一層變化的增量独泞。示例Dockerfile
:
FROM ubuntu:15.04
COPY . /app
RUN make /app
CMD python /app/app.py
每條指令創(chuàng)建一個(gè)層:
-
FROM
從ubuntu:15.04
Docker鏡像創(chuàng)建一個(gè)圖層啰挪。 -
COPY
從Docker客戶端的當(dāng)前目錄添加文件。 -
RUN
用你的應(yīng)用程序構(gòu)建make
嘲叔。 -
CMD
指定在容器中運(yùn)行的命令亡呵。
運(yùn)行圖像并生成容器時(shí),可以 在基礎(chǔ)圖層的頂部添加新的可寫層(“容器圖層”)硫戈。對(duì)正在運(yùn)行的容器所做的所有更改(例如寫入新文件锰什,修改現(xiàn)有文件和刪除文件)都將寫入此可寫容器層。
1.使用標(biāo)簽
? 給鏡像打上標(biāo)簽丁逝,易讀的鏡像標(biāo)簽可以幫助了解鏡像的功能汁胆。
2.使用統(tǒng)一的Base鏡像,比如busybox或者alpine霜幼,謹(jǐn)慎選擇基礎(chǔ)鏡像嫩码,盡量選擇當(dāng)前官方的鏡像庫(kù)中的鏡像。
? 很多教程中建議大家使用alpine鏡像罪既,更建議大家使用centos铸题,Ubuntu這樣的鏡像。同時(shí)琢感,在構(gòu)建自己的Docker鏡像時(shí)丢间,只安裝和更新必須使用的包,FROM
指令應(yīng)該包含的參數(shù)tag猩谊,比如使用centos:7.5.1504
而不是FROM centos
千劈。
3.充分利用緩存
? 在鏡像的構(gòu)建過(guò)程中,Docker 會(huì)遍歷 Dockerfile
文件中的指令牌捷,然后按順序執(zhí)行墙牌。在執(zhí)行每條指令之前,Docker 都會(huì)在緩存中查找是否已經(jīng)存在可重用的鏡像暗甥,如果有就使用現(xiàn)存的鏡像喜滨,不再重復(fù)創(chuàng)建。如果你不想在構(gòu)建過(guò)程中使用緩存撤防,你可以在 docker build
命令中使用 --no-cache=true
選項(xiàng)虽风。
但是,如果你想在構(gòu)建的過(guò)程中使用緩存寄月,你得明白什么時(shí)候會(huì)辜膝,什么時(shí)候不會(huì)找到匹配的鏡像,遵循的基本規(guī)則如下:
- 從一個(gè)基礎(chǔ)鏡像開始(
FROM
指令指定)漾肮,下一條指令將和該基礎(chǔ)鏡像的所有子鏡像進(jìn)行匹配厂抖,檢查這些子鏡像被創(chuàng)建時(shí)使用的指令是否和被檢查的指令完全一樣。如果不是克懊,則緩存失效忱辅。 - 在大多數(shù)情況下七蜘,只需要簡(jiǎn)單地對(duì)比
Dockerfile
中的指令和子鏡像。然而墙懂,有些指令需要更多的檢查和解釋橡卤。 - 對(duì)于
ADD
和COPY
指令,鏡像中對(duì)應(yīng)文件的內(nèi)容也會(huì)被檢查损搬,每個(gè)文件都會(huì)計(jì)算出一個(gè)校驗(yàn)和碧库。文件的最后修改時(shí)間和最后訪問(wèn)時(shí)間不會(huì)納入校驗(yàn)。在緩存的查找過(guò)程中场躯,會(huì)將這些校驗(yàn)和和已存在鏡像中的文件校驗(yàn)和進(jìn)行對(duì)比谈为。如果文件有任何改變,比如內(nèi)容和元數(shù)據(jù)踢关,則緩存失效伞鲫。 - 除了
ADD
和COPY
指令,緩存匹配過(guò)程不會(huì)查看臨時(shí)容器中的文件來(lái)決定緩存是否匹配签舞。例如秕脓,當(dāng)執(zhí)行完RUN apt-get -y update
指令后,容器中一些文件被更新儒搭,但 Docker 不會(huì)檢查這些文件吠架。這種情況下,只有指令字符串本身被用來(lái)匹配緩存搂鲫。
一旦緩存失效傍药,所有后續(xù)的 Dockerfile
指令都將產(chǎn)生新的鏡像,緩存不會(huì)被使用魂仍。
4.正確使用ADD和COPY指令
這兩者很相似拐辽,推薦有限選擇 COPY,它比 ADD 透明度更高擦酌。
- COPY俱诸,只支持將本地文件復(fù)制到容器中
- ADD,除了 COPY 的功能外赊舶,還支持遠(yuǎn)程 URL睁搭。但最好的用途是將本地 tar 文件提取到鏡像中
ADD rootfs.tar.xz /
。
如果在 Dockerfile 中使用不用的文件笼平,那么 COPY 它們可以單獨(dú)使用园骆。這樣,特定文件的更改寓调,將確保每一步的構(gòu)建緩存無(wú)效遇伞,如:
DOCKERFILECOPY requirements.txt /tmp/
RUN pip install --requirement /tmp/requirements.txt
COPY . /tmp/
將 COPY . /tmp/
放在后面,這能夠使 RUN
的緩存無(wú)效的數(shù)量減少捶牢。盡量使用docker volume共享文件鸠珠,而不是用ADD指令添加文件。
5. 不要在 Dockerfile 中單獨(dú)修改文件的權(quán)限
? 因?yàn)?docker 鏡像是分層的秋麸,任何修改都會(huì)新增一個(gè)層渐排,修改文件或者目錄權(quán)限也是如此。如果有一個(gè)命令單獨(dú)修改大文件或者目錄的權(quán)限灸蟆,會(huì)把這些文件復(fù)制一份驯耻,這樣很容易導(dǎo)致鏡像很大。
? 解決方案也很簡(jiǎn)單炒考,要么在添加到 Dockerfile 之前就把文件的權(quán)限和用戶設(shè)置好可缚,要么在容器啟動(dòng)腳本(entrypoint)做這些修改,或者拷貝文件和修改權(quán)限放在一起做(這樣最終也只是增加一層)斋枢。
6. 版本控制和自動(dòng)構(gòu)建
? 最好把 Dockerfile 和對(duì)應(yīng)的應(yīng)用代碼一起放到版本控制中帘靡,然后能夠自動(dòng)構(gòu)建鏡像。這樣的好處是可以追蹤各個(gè)版本鏡像的內(nèi)容瓤帚,方便了解不同鏡像有什么區(qū)別描姚,對(duì)于調(diào)試和回滾都有好處。
? 另外戈次,如果運(yùn)行鏡像的參數(shù)或者環(huán)境變量很多轩勘,也要有對(duì)應(yīng)的文檔給予說(shuō)明,并且文檔要隨著 Dockerfile 變化而更新怯邪,這樣任何人都能參考著文檔很容易地使用鏡像绊寻,而不是下載了鏡像不知道怎么用。
7.RUN指令
? 為了使Dockerfile易讀悬秉、易理解和可維護(hù)澄步,在使用比較長(zhǎng)的RUN指令是可以使用反斜杠\
分隔多行。將多行參數(shù)按字母順序排序(比如要安裝多個(gè)包時(shí))搂捧。這可以幫助你避免重復(fù)包含同一個(gè)包驮俗,更新包列表時(shí)也更容易。也便于 PRs
閱讀和審查允跑。建議在反斜杠符號(hào) \
之前添加一個(gè)空格王凑,以增加可讀性。
示例:
RUN yum update && yum install -y \
vim \
ntpdate \
git \
nginx
6.CMD和ENTRYPOINT指令
? CMD和ENTRYPOINT指令指定了容器運(yùn)行的默認(rèn)命令聋丝,推薦二者結(jié)合使用索烹。使用exec格式ENTRYPOINT指令設(shè)置固定的默認(rèn)命令和參數(shù),然后使用CMD指令設(shè)置可變的參數(shù)弱睦。
7.不要在Dockerfile中做端口映射
? Docker的兩個(gè)核心概念是可重復(fù)性和可移植性百姓,鏡像應(yīng)該可以在任何主機(jī)上運(yùn)行多次。映射端口會(huì)破壞鏡像的可移植性况木,且這樣的鏡像只能在一臺(tái)主機(jī)上啟動(dòng)一個(gè)容器垒拢。所以端口映射應(yīng)在docker run
命令中用-p
參數(shù)指定旬迹。
# 不要在Dockerfile中做如下映射
EXPOSE 80:8080
# 僅僅暴露80端口,需要另做映射
EXPOSE 80
8.使用多階段構(gòu)建
在 Docker 17.05
以上版本中求类,你可以使用 多階段構(gòu)建 來(lái)減少所構(gòu)建鏡像的大小奔垦。
9.避免安裝不必要的包
? 為了降低復(fù)雜性、減少依賴尸疆、減小文件大小椿猎、節(jié)約構(gòu)建時(shí)間,你應(yīng)該避免安裝任何不必要的包寿弱。例如犯眠,不要在數(shù)據(jù)庫(kù)鏡像中包含一個(gè)文本編輯器。
10.一個(gè)容器只運(yùn)行一個(gè)進(jìn)程
? 應(yīng)該保證在一個(gè)容器中只運(yùn)行一個(gè)進(jìn)程症革。將多個(gè)應(yīng)用解耦到不同容器中筐咧,保證了容器的橫向擴(kuò)展和復(fù)用。例如 web 應(yīng)用應(yīng)該包含三個(gè)容器:web應(yīng)用地沮、數(shù)據(jù)庫(kù)嗜浮、緩存。
? 如果容器互相依賴摩疑,你可以使用 Docker 自定義網(wǎng)絡(luò) 來(lái)把這些容器連接起來(lái)危融。
12.鏡像層數(shù)盡可能少
? 你需要在 Dockerfile
可讀性(也包括長(zhǎng)期的可維護(hù)性)和減少層數(shù)之間做一個(gè)平衡。