Dockerfile是一個(gè)文本文件,包含了一條條指令,每條指令對應構建一層鏡像,Docker基于它來(lái)構建一個(gè)完整鏡像。本文介紹Dockerfile的常用指令及相應的最佳實(shí)踐建議。
Docker鏡像通過(guò)docker build指令構建,該指令執行時(shí)當前的工作目錄就是docker構建的上下文,即build context,上下文中的文件及目錄都會(huì )作為構建上下文內容發(fā)送給Docker Daemon。
docker build --no-cache -t helloapp:v2 -f dockerfiles/Dockerfile context如上 –no-cache 表示鏡像構建時(shí)不使用緩存,-f 指定Dockerfile文件位置, context 指定build context目錄。
將一些非必要的文件包含到build context中,會(huì )導致build context過(guò)大,從而導致鏡像過(guò)大,會(huì )增加鏡像構建、推送及拉取的時(shí)間,以及容器運行時(shí)的大小。
執行docker build時(shí)會(huì )顯示build context的大小,
Sending build context to Docker daemon 187.8MB最佳實(shí)踐建議
作用FROM指定基礎鏡像,每一個(gè)定制鏡像,必須以一個(gè)現有鏡像為基礎。因此一個(gè)Dockerfile中FROM是必須的指令,并且必須是第一條。使用格式,
FROM <image>:<tag>
# 注釋以#開(kāi)頭?;A鏡像的tag可不指定,默認使用latest
# 示例:FROM mysql:5.7最佳實(shí)踐建議
作用用來(lái)執行命令行命令,是最常用的指令之一。使用格式,
# shell格式,跟直接在命令行輸入命令一行
RUN <命令>
# 示例:RUN mkdir -p /usr/src/redis
# exec格式,類(lèi)似于函數調用
RUN ["可執行文件", "參數1", "參數2"]RUN指令創(chuàng )建的中間鏡像會(huì )被緩存,并會(huì )在下次構建中使用。如果不想使用這些緩存鏡像,可以在構建指令中指定–no-cache參數,如:docker build --no-cache
最佳實(shí)踐建議
比如先按如下Dockerfile創(chuàng )建了一個(gè)鏡像
FROM ubuntu:18.04
RUN apt-get update
RUN apt-get install -y curl一段時(shí)間后,再按以下Dockerfile創(chuàng )建另一個(gè)鏡像
FROM ubuntu:18.04
RUN apt-get update
RUN apt-get install -y curl nginx因為RUN指令創(chuàng )建的鏡像層會(huì )被緩存,所以下面鏡像的RUN apt-get update并不會(huì )執行,直接使用了前面構建的鏡像層,這樣,curl、nginx就可能安裝已經(jīng)過(guò)時(shí)的版本。
因此 在 apt-get update 之后立即接 && apt-get install -y ,這叫做“ cache busting”(緩存破壞),也可以通過(guò)指定包的版本,來(lái)達到同樣的目的,這叫“ version pinning” (版本指定)示例:
RUN apt-get update && apt-get install -y reprepro ruby1.9.1 ruby1.9.1-dev #刪除apt 緩存減少鏡像層的大小
&& rm -rf /var/lib/apt/lists/*Docker使用/bin/sh -c 解釋器來(lái)執行這些指令,只會(huì )評估管道最后一條命令的退出碼來(lái)確定是否成功,如上例中只要wc -l成功了就算wget失敗,也會(huì )認為是成功的。如果要使管道命令的任何一步報錯都導致指令失敗,則可通過(guò)加 set -o pipefile && 來(lái)實(shí)現,如
RUN set -o pipefail && wget -O - https://some.site | wc -l > /number不是所有的shell都支持-o pipefail選項,如果不支持的話(huà)可以使用如下形式,顯式地指定一個(gè)支持的shell
RUN ["/bin/bash", "-c", "set -o pipefail && wget -O - https://some.site | wc -l > /number"]作用COPY從構建上下文的目錄中復制文件/目錄到鏡像層的目標路徑。使用格式,
COPY [--chown=<user>:<group>] <源路徑>... <目標路徑>
COPY [--chown=<user>:<group>] ["<源路徑1>",... "<目標路徑>"]同RUN一樣,也有兩種格式。源文件可以多個(gè),甚至可以是通配符,目標路徑是容器的絕對路徑,可以是相對工作目錄(WORKDIR指定)的相對路徑,目標路徑不存在時(shí)會(huì )自動(dòng)創(chuàng )建。使用--chown=<user>:<group>來(lái)改變文件的所屬用戶(hù)與組。ADD與COPY的使用格式與性質(zhì)差不多,但功能更豐富,如源路徑可以是URL(下載后放到目標路徑下,文件權限為600),也可以為tar壓縮包,壓縮格式為gzip,bzip2及xz的情況下,ADD 指令將會(huì )自動(dòng)解壓縮這個(gè)壓縮文件到目標路徑去
最佳實(shí)踐建議
如果把COPY . /tmp/ 放在RUN上面,將使RUN層鏡像緩存失效的場(chǎng)景更多——因為 . 目錄(當前目錄)中任何一個(gè)文件的改變都會(huì )導致緩存失效。
而應使用
RUN mkdir -p /usr/src/things && curl -SL http://example.com/big.tar.xz | tar -xJC /usr/src/things && make -C /usr/src/things all作用CMD指定容器的啟動(dòng)命令。容器實(shí)質(zhì)就是進(jìn)程,進(jìn)程就需要啟動(dòng)命令及參數,CMD指令就是用于指定默認的容器主進(jìn)程的啟動(dòng)命令的。使用格式
# shell格式
CMD <命令>
# exec格式
CMD ["可執行文件", "參數1", "參數2"...]
# 參數列表格式,在指定了ENTRYPOINT指令后,用CMD來(lái)指定具體的參數
CMD ["參數1", "參數2"...]在容器運行時(shí)可以指定新的命令來(lái)覆蓋Dockerfile中設置的這個(gè)默認命令
最佳實(shí)踐建議
作用ENTRYPOINT的目的和CMD一樣,都是在指定容器啟動(dòng)時(shí)要運行的程序及參數。ENTRYPOINT在運行時(shí)也可以替代,不過(guò)比CMD要略顯繁瑣,需要通過(guò)docker run的參數 –entrypoint 來(lái)指定。如果指定了ENTRYPOINT,則CMD將只是提供參數,傳遞給ENTRYPOINT。使用ENTRYPOINT可以在容器運行時(shí)直接為默認啟動(dòng)程序添加參數。與RUN指令格式一樣,ENTRYPOINT也分為exec格式和shell格式。
最佳實(shí)踐建議
直接run時(shí),相當于執行了s3cmd --help。也可以使用shell腳本,在腳本中做一些預處理的工作,如
COPY ./docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["postgres"]作用為鏡像添加label以方便組織鏡像,記錄licensce信息,幫助自動(dòng)化實(shí)現等等。字符串中包含空格需要轉義或包含在引號中, 如
# Set one or more individual labels
LABEL com.example.version="0.0.1-beta"
LABEL vendor1="ACME Incorporated"
LABEL com.example.release-date="2019-09-12"
LABEL com.example.version.is-production=""
# Set multiple labels on one line
LABEL com.example.version="0.0.1-beta" com.example.release-date="2019-09-12"
# Set multiple labels at once, using line-continuation characters to break long lines
LABEL vendor=ACME\ Incorporated com.example.is-beta= com.example.is-production="" com.example.version="0.0.1-beta" com.example.release-date="2019-09-12"作用ENV設置環(huán)境變量,無(wú)論是后面的其它指令,如 RUN(使用 $環(huán)境變量key 的形式) ,還是運行時(shí)的應用,都可以直接使用這里定義的環(huán)境變量。使用格式有兩種,
#只能設置一個(gè)key value
ENV <key> <value>
#可以設置多個(gè),value中如果包含空格可以使用\來(lái)進(jìn)行轉義,也可以通過(guò)""括起來(lái);也可以用反斜線(xiàn)來(lái)續行
ENV <key1>=<value1> <key2>=<value2>...除了RUN,還有這些指令可以引用環(huán)境變量:ADD 、 COPY 、 ENV 、 EXPOSE 、 LABEL 、 USER 、 WORKDIR 、 VOLUME 、STOPSIGNAL 、 ONBUILD
最佳實(shí)踐建議
作用ARG設置構建參數,即docker build命令時(shí)傳入的參數。和ENV的效果差不多,都是設置環(huán)境變量,不同的是,ARG設置的是構建環(huán)境的環(huán)境變量,在容器運行時(shí)是不會(huì )存在這些環(huán)境變量的。Dockerfile中的ARG指令是定義參數名稱(chēng),以及默認值(可選)。該默認值可以在執行構建命令docker build時(shí)用 –build-arg <參數名>=<值> 來(lái)覆蓋。使用格式,
ARG <參數名>[=<默認值>]
```
**最佳實(shí)踐建議**
1. 不要使用ARG來(lái)保存密碼之類(lèi)的信息,因為通過(guò)docker history還是可以看到docker build執行時(shí)的所有值
2. 使用ARG,對于使用CI系統(持續集成),用同樣的構建流程構建不同的 Dockerfile 的時(shí)候比較有幫助,避免構建命令必須根據每個(gè) Dockerfile 的內容修改
## 10. WORKDIR
**作用**
WORKDIR用于指定工作目錄(或當前目錄),以后各層的當前目錄就被改為指定的目錄,如該目錄不存在,會(huì )自動(dòng)創(chuàng )建。使用格式,
```shell
WORKDIR <工作目錄路徑>
```
**最佳實(shí)踐建議**
1. WORKDIR應該使用絕對路徑,顯得更為清楚、可靠
2. 使用WORKDIR,避免使用`RUN cd … && do-something`,可讀性差,難以維護
## 11. VOLUME
**作用**
VOLUME用于定義匿名卷。容器運行時(shí)應該盡量保持容器存儲層不發(fā)生寫(xiě)操作,應該將數據寫(xiě)入存儲卷。VOLUME就是為了防止運行時(shí)用戶(hù)忘記將動(dòng)態(tài)文件所保存的目錄掛載為卷,我們事先在Dockerfile中指定某些目錄掛載為匿名卷,這樣在運行時(shí)如果用戶(hù)不指定掛載,其應用也可以正常運行,不會(huì )向容器存儲層寫(xiě)入大量數據。使用格式,
```shell
VOLUME ["<路徑1>", "<路徑2>"...]
VOLUME <路徑>如 VOLUME /data, 任何向/data目錄寫(xiě)入的數據都會(huì )寫(xiě)入匿名卷??梢赃\行容器時(shí)覆蓋這個(gè)掛載設置 docker run -d -v host-path:/data xxxx
最佳實(shí)踐建議
作用EXPOSE指令是聲明運行時(shí)容器提供的服務(wù)端口,也只是一個(gè)聲明,在容器運行時(shí)并不會(huì )因為這個(gè)聲明應用就一定會(huì )開(kāi)啟這個(gè)端口的服務(wù),容器啟動(dòng)時(shí),還是需要通過(guò) -p host-port:container-port來(lái)實(shí)現映射。EXPOSE主要是幫助鏡像使用者了解這個(gè)鏡像服務(wù)的監聽(tīng)端口,以方便進(jìn)行映射配置,另一個(gè)用處是在運行時(shí)如果是使用隨機端口映射,也就是通過(guò) docker run -P的形式時(shí),會(huì )自動(dòng)隨機映射EXPOSE聲明的端口。使用格式,
EXPOSE <端口1> [<端口2>...]
```
**最佳實(shí)踐建議**
1. 應該使用常用的慣用的端口,如nginx 80,mongoDB 27017
## 13. USER
**作用**
USER指令和WORKDIR相似,都是改變環(huán)境狀態(tài)并影響以后的層。WORKDIR是改變工作目錄, USER則是改變之后的層在執行RUN , CMD以及ENTRYPOINT這類(lèi)命令時(shí)的身份。USER幫助你切換到指定的用戶(hù),這個(gè)用戶(hù)必
須是事先建立好的,否則無(wú)法切換。使用格式
```shell
USER <用戶(hù)名>[:<用戶(hù)組>]最佳實(shí)踐建議
作用HEALTHCHECK用于檢查容器的健康狀態(tài),Docker可通過(guò)健康狀態(tài)來(lái)決定是否對容器進(jìn)行重新調度。使用格式
HEALTHCHECK [選項] CMD <命令>支持的選項為
命令的返回值決定了該次健康檢查的成功與否—— 0 :成功;1 :失??;2 :保留(不要使用這個(gè)值),如:
FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib
/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s CMD curl -fs http://localhost/ || exit 1
```
可以使用docker ps 或docker inspect來(lái)查看容器的健康狀態(tài)。
**最佳實(shí)踐建議**
1. 如果基礎鏡像有健康檢查指令,想要屏蔽掉其健康檢查,可以使用`HEALTHCHECK NONE`
2. 對一些可能造成假死(進(jìn)程還在, 但提供不了服務(wù)了)的服務(wù)建議提供健康檢查,以便及時(shí)重新調度恢復服務(wù)
## 15. ONBUILD
**作用**
ONBUILD后跟的指令,只有當以當前鏡像為基礎鏡像,去構建下一級鏡像的時(shí)候才會(huì )被執行。使用格式
```shell
ONBUILD <其它指令>它后面跟的是其它指令,比如 RUN , COPY 等,這些指令在當前鏡像構建時(shí)并不會(huì )被執行。ONBUILD命令在本鏡像的子鏡像中執行,把ONBUILD想象為父鏡像為子鏡像聲明的一條指令,Docker會(huì )在子鏡像所有命令之前執行ONBUILD指令。
最佳實(shí)踐建議
Docker在構建鏡像時(shí)會(huì )復用緩存中已經(jīng)存在的鏡像,如果明確不使用緩存,則可加參數docker build --no-cache=true使用緩存鏡像的規則
連字符 - 作為文件名告訴Docker從stdin讀取Dockerfile
聯(lián)系客服