06 Dockerfile 最佳生產(chǎn)實(shí)踐

好處

生產(chǎn)實(shí)踐中一定優(yōu)先使用 Dockerfile 的方式構(gòu)建鏡像,因?yàn)槭褂?Dockerfile 構(gòu)建鏡像可以帶來如下好處:
● 易于版本化管理,Dockerfile 本身是一個文本文件,方便存放在代碼倉庫中做版本管理,可以很方便地找到各個版本之間的變更歷史;
● 過程可追溯,Dockerfile 的每一行指令代表一個鏡像層,根據(jù) Dockerfile 的內(nèi)容即可明確地查看鏡像的完整構(gòu)建過程;
● 屏蔽構(gòu)建環(huán)境異構(gòu),使用 Dockerfile 構(gòu)建鏡像無須考慮構(gòu)建環(huán)境,基于相同 Dockerfile 無論在哪里運(yùn)行,構(gòu)建結(jié)果都一致。


書寫原則

雖然好處不少,但使用不當(dāng)也會引發(fā)很多問題,例如:
● 鏡像構(gòu)建時間過長,導(dǎo)致鏡像構(gòu)建失?。?br> ● 鏡像層數(shù)過多,導(dǎo)致鏡像文件過大。

如果要在生產(chǎn)環(huán)境中編寫出最優(yōu)的 Dockerfile,首先應(yīng)該盡量遵循相關(guān)的原則:
(1) 單一職責(zé)
由于容器的本質(zhì)是進(jìn)程,一個容器代表一個進(jìn)程,因此不同功能的應(yīng)用應(yīng)該盡量拆分為不同的容器,每個容器負(fù)責(zé)單一業(yè)務(wù)進(jìn)程。

(2) 提供注釋信息
Dockerfile 也是一種代碼,應(yīng)該保持良好的代碼編寫習(xí)慣,晦澀難懂的代碼盡量添加注釋,讓協(xié)作者可以一目了然地知道每行代碼的作用,并且方便擴(kuò)展和使用。

(3) 保持容器最小化
應(yīng)該避免安裝無用的軟件包,這樣不僅可以加快容器構(gòu)建速度,還可以避免鏡像體積過大。

(4) 合理選擇基礎(chǔ)鏡像
容器的核心是應(yīng)用,因此只要基礎(chǔ)鏡像能夠滿足應(yīng)用的運(yùn)行環(huán)境即可。

(5) 使用.dockerignore文件
使用 .dockerignore 文件可以在構(gòu)建時忽略一些不需要參與構(gòu)建的文件,從而提升構(gòu)建效率。類似于在使用 git 時,我們可以使用 .gitignore 文件忽略一些不需要做版本管理的文件。

規(guī)則 含義
# # 開頭的表示注釋
/tmp 匹配當(dāng)前目錄下任何以 tmp 開頭的文件或者文件夾
*.md 匹配以 .md 為后綴的任意文件
my? 匹配以 my 開頭并且以任意字符結(jié)尾的文件(?代表任意一個字符)
!README.md ! 表示排除忽略
例如 .dockerignore 定義如下:
*.md
!README.md
表示除了 README.md 文件外所有以 .md 結(jié)尾的文件。

(6) 盡量使用構(gòu)建緩存
Docker 構(gòu)建過程中,每一條 Dockerfile 指令都會提交為一個鏡像層,下一條指令都是基于上一條指令構(gòu)建的。如果構(gòu)建時發(fā)現(xiàn)要構(gòu)建的鏡像層的父鏡像層已經(jīng)存在,并且下一條命令使用了相同的指令,即可命中構(gòu)建緩存。

基于 Docker 構(gòu)建時的緩存特性,我們可以把不輕易改變的指令放到 Dockerfile 前面(例如安裝軟件包),而可能經(jīng)常發(fā)生改變的指令放在 Dockerfile 末尾(例如編譯應(yīng)用程序)。Docker 構(gòu)建時判斷是否需要使用緩存的規(guī)則如下:

  • 從當(dāng)前構(gòu)建層開始,比較所有的子鏡像,檢查所有的構(gòu)建指令是否與當(dāng)前完全一致,如果不一致,則不使用緩存;
  • 一般情況下,只需要對比構(gòu)建指令即可判斷是否需要使用緩存,但是有些指令除外(例如ADD和COPY);
  • 對于ADD和COPY指令不僅要校驗(yàn)命令是否一致,還要為即將拷貝到容器的文件計算校驗(yàn)和(根據(jù)文件內(nèi)容計算出的一個數(shù)值,如果兩個文件計算的數(shù)值一致,表示兩個文件內(nèi)容一致 ),命令和校驗(yàn)和完全一致,才認(rèn)為命中緩存。

(7) 正確設(shè)置時區(qū)
從 Docker Hub 拉取的官方操作系統(tǒng)鏡像大多數(shù)都是 UTC 時間(世界標(biāo)準(zhǔn)時間),如果容器應(yīng)用對時間敏感,需要使用中國區(qū)標(biāo)準(zhǔn)時間(東八區(qū)),則可以在 Dockerfile 中添加以下指令:
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/Shanghai" >> /etc/timezone

(8) 使用國內(nèi)軟件源加快鏡像構(gòu)建速度
常用的官方操作系統(tǒng)鏡像基本都是國外的,如果我們構(gòu)建鏡像的時候想要安裝一些軟件包可能會非常慢,以 CentOS 7 使用 163 軟件源為例:

# CentOS7-Base-163.repo
#
[base]
name=CentOS-$releasever - Base - 163.com
repo=os
baseurl=http://mirrors.163.com/centos/$releasever/os/$basearch/
gpgcheck=1
gpgkey=http://mirrors.163.com/centos/RPM-GPG-KEY-CentOS-7

[updates]
name=CentOS-$releasever - Updates - 163.com
repo=updates
baseurl=http://mirrors.163.com/centos/$releasever/updates/$basearch/
gpgcheck=1
gpgkey=http://mirrors.163.com/centos/RPM-GPG-KEY-CentOS-7

[extras]
name=CentOS-$releasever - Extras - 163.com
repo=extras
baseurl=http://mirrors.163.com/centos/$releasever/extras/$basearch/
gpgcheck=1
gpgkey=http://mirrors.163.com/centos/RPM-GPG-KEY-CentOS-7

[centosplus]
name=CentOS-$releasever - Plus - 163.com
baseurl=http://mirrors.163.com/centos/$releasever/centosplus/$basearch/
gpgcheck=1
enabled=0
gpgkey=http://mirrors.163.com/centos/RPM-GPG-KEY-CentOS-7

隨后在 Dockerfile 中添加如下指令:
COPY CentOS7-Base-163.repo /etc/yum.repos.d/CentOS7-Base.repo

(9) 最小化鏡像層數(shù)
在構(gòu)建鏡像時盡可能地減少 Dockerfile 指令行數(shù),主要是 RUN 指令如下:
RUN yum -y install make net-tools


書寫建議

(1) RUN
RUN 指令在構(gòu)建時將會生成一個新的鏡像層并且執(zhí)行 RUN 指令后面的內(nèi)容,使用 RUN 指令時應(yīng)該盡量遵循以下原則:

  • 當(dāng) RUN 指令后面跟的內(nèi)容比較復(fù)雜時,建議使用反斜杠(\)結(jié)尾并且換行;
  • RUN 指令后面的內(nèi)容盡量按照字母順序排序,提高可讀性。
FROM centos:7
RUN yum -y install automake \
                   curl \
                   python \
                   vim \

(2) CMD 和 ENTRYPOINT
CMD 和 ENTRYPOINT 指令都是容器運(yùn)行的命令入口,它們基本使用格式分兩種:

  • CMD/ENTRYPOINT ["command" , "param"],這種格式是使用Linux的 exec 實(shí)現(xiàn)的,一般稱為 exec 模式,這種書寫格式為指令后跟 json 數(shù)組,也是 Docker 推薦使用的格式。
  • CMD/ENTRYPOINT command param,這種格式是基于 shell 實(shí)現(xiàn)的,一般稱為 shell 模式,Docker 會以 /bin/sh -c command 的方式執(zhí)行命令。

它們之間的區(qū)別如下:

  • Dockerfile 中如果使用了 ENTRYPOINT 指令,啟動 Docker 容器時需要使用 --entrypoint 參數(shù)才能覆蓋 Dockerfile 中的 ENTRYPOINT 指令 ,而使用 CMD 設(shè)置的命令則可以被 docker run 后面的參數(shù)直接覆蓋。
  • ENTRYPOINT 指令可以結(jié)合 CMD 指令使用,也可以單獨(dú)使用,而 CMD 指令只能單獨(dú)使用。
  • 使用 exec 模式啟動容器時,指令指定的命令就是容器的 1 號進(jìn)程,而 shell 模式啟動的進(jìn)程在容器中實(shí)際并不是 1 號進(jìn)程。
  • 如果希望鏡像足夠靈活,推薦使用 CMD 指令。如果鏡像只執(zhí)行單一的具體程序,并且不希望在執(zhí)行 docker run 時覆蓋默認(rèn)程序,建議使用 ENTRYPOINT。

(3) ADD 和 COPY
ADD 和 COPY 指令功能類似,都是從外部往容器內(nèi)添加文件。但是 COPY 指令只支持基本的文件和文件夾拷貝功能,ADD 則支持更多文件來源類型,比如自動提取 tar 包,并且可以支持源文件為 URL 格式。
日常應(yīng)用,更推薦使用 COPY 指令, 因此該指令更加透明,僅支持本地文件向容器拷貝,而且可以更好地利用構(gòu)建緩存,有效減小鏡像體積。

### 當(dāng)想用 ADD 向容器中添加 URL 文件時,可使用如下寫法替代:
RUN wget -O /tmp/memtester-4.3.0.tar.gz http://pyropus.ca/software/memtester/old-versions/memtester-4.3.0.tar.gz \
&& tar -xvf /tmp/memtester-4.3.0.tar.gz -C /tmp \
&& make -C /tmp/memtester-4.3.0 && make -C /tmp/memtester-4.3.0 install

(4) WORKDIR
為了使構(gòu)建過程更加清晰明了,推薦使用 WORKDIR 來指定容器的工作路徑,盡量避免使用 RUN cd /work/path 這樣的指令來切換工作路徑。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容