Dockerfile 指令詳解
在上一節(jié)的 Dockerfile 例子中,我們用到了幾個指令,比如:FROM, MAINTAINER,RUN,EXPOSE等等,Dockerfile 構(gòu)建鏡像需要用到的指令當(dāng)然不止這些,下面是我們將介紹常用的構(gòu)建指令 。
1. FROM:指明當(dāng)前的鏡像基于哪個鏡像構(gòu)建
用法:
FROM <基礎(chǔ)鏡像:版本>
示例:
FROM alpine:latest
寫上這一行指令后,我們的 Dockerfile 就可以構(gòu)建鏡像了,構(gòu)建出的鏡像就是未做任何修改、沒有執(zhí)行任何命令的 alpine:latest
。
在 Docker 官方倉庫里有很多高質(zhì)量的服務(wù)鏡像如 redis、mongo、mysql、httpd、php、tomcat 等;也有一些方便開發(fā)、構(gòu)建、運行各種語言應(yīng)用的鏡像,如 node、openjdk、python、ruby、golang 等。我們可以在其中尋找一個最符合我們最終目標(biāo)的鏡像為基礎(chǔ)鏡像進行定制。如果沒有找到對應(yīng)服務(wù)的鏡像,官方鏡像中還提供了一些更為基礎(chǔ)的操作系統(tǒng)鏡像,如 ubuntu、debian、centos、fedora、alpine 等,這些操作系統(tǒng)的軟件庫為我們提供了更廣闊的擴展空間。盡可能使用當(dāng)前官方倉庫作為構(gòu)建鏡像的基礎(chǔ)。
2. LABEL: 標(biāo)記鏡像信息
給鏡像添加標(biāo)簽來標(biāo)記鏡像信息,每個標(biāo)簽一行。
用法:
LABEL <標(biāo)簽>=<描述>
示例:
LABEL MYLABEL="First Test"
3. MAINTAINER:指定鏡像的作者信息,包含鏡像的所有者和聯(lián)系人信息
用法:
MAINTAINER <NAME>
示例:
MAINTAINER nickname@domain.com
這是一種語義化的表達方式,也可以用LABEL來標(biāo)記
LABEL maintainer="nickname@domain.com"
4. RUN : 運行命令
用法:
RUN <命令>
示例:
RUN echo 'text' > test.txt
為了保持 Dockerfile 文件的可讀性,以及可維護性,建議將長的或復(fù)雜的RUN
指令用反斜杠\
分割成多行。
例如:
RUN apt update && apt install -y \
vim \
emacs
這里需要注意一個關(guān)于軟件源更新安裝軟件的問題。
如果我們需要更新軟件源并安裝軟件源中的軟件vim,在Linux環(huán)境中我們一般會執(zhí)行這個的命令:
apt update
apt install -y vim
如果需要在鏡像中安裝軟件,我們會想當(dāng)然地在 Dockerfile 寫成這樣
RUN apt update
RUN apt install -y vim
Dockerfile 構(gòu)建一次之后,apt update
構(gòu)建的鏡像層就會緩存到本地,無論后面這個 Dockerfile 如何更新 apt install
的內(nèi)容,apt update
鏡像緩存也不會更新,這會導(dǎo)致安裝的始終是第一次 Dockerfile構(gòu)建時獲取的軟件源版本,除非你手動刪除這些緩存鏡像層。
解決的方法很簡單,用 RUN apt-get update && apt-get install -y
可以確保 Dockerfiles
每次安裝的都是包的最新的版本。
5. CMD:指定容器的默認(rèn)執(zhí)行的命令。
建議用法:
CMD ["可執(zhí)行命令", "參數(shù)1", "參數(shù)2"...]
示例:
CMD ["echo" "hello"]
docker run 沒有指定其他命令時,CMD 指令會在容器執(zhí)行。Dockerfile 中 CMD 只能有一個,如果寫了多個 CMD,則以最后一個為準(zhǔn)。
Tips:ENTRYPOINT 與 CMD 類似,但不會被
docker run
指定的命令覆蓋。
6. EXPOSE:指定容器將要監(jiān)聽的端口
用法:
EXPOSE 端口號
示例:
EXPOSE 8080
啟動容器時,如果我們使用自動映射 -P
或 --net=host
宿主機網(wǎng)絡(luò)模式,容器中 EXPOSE
標(biāo)記暴露的端口與宿主機網(wǎng)絡(luò)會自動建立關(guān)聯(lián)。
如果沒有指定 EXPOSE
,使用 -p
手動指定端口映射參數(shù)也可以訪問到容器內(nèi)提供服務(wù)的端口。
EXPOSE
顯式地標(biāo)明鏡像開放端口,一定程度上提供了操作的便利,也提高了 Dockerfile 的可讀性和可維護性。
7. ENV:定義環(huán)境變量
用法:
ENV 環(huán)境變量名 環(huán)境變量值
示例:
ENV PATH /usr/local/nginx/bin:$PATH
Tips:
- 通過 ENV 定義的環(huán)境變量,可以被后面的所有指令中使用,但是不能被 CMD 指令使用。
- 通過 ENV 定義的環(huán)境變量,會永久的保存到該鏡像創(chuàng)建的任何容器中,我們可以在 docker run 命令中通過 -e 標(biāo)記來傳遞環(huán)境變量,啟動的容器將會使用我們指定的變量值。
- ARG 指令與 ENV 作用基本一致,區(qū)別在于它僅在構(gòu)建過程中使用,不會保留到容器中。
8. COPY: 將宿主機文件拷貝到鏡像中
用法:
COPY <宿主機文件路徑> <鏡像文件路徑>
示例:
COPY app.py /web/
除了指定完整的文件名外,COPY 命令還支持 Go 風(fēng)格的通配符,比如:
# * 是任意字符的占位符,匹配文件 test11 test22
COPY test* /tmp
# ? 是單個字符的占位符,匹配文件 test1.txt test2.txt
COPY test?.txt /tmp
對于目錄而言,COPY 只復(fù)制目錄中的內(nèi)容而不包含目錄自身。 如下目錄結(jié)構(gòu):
testdir/
├── file1
└── file2
COPY testdir /tmp
鏡像的 /tmp 目錄下,將得到這樣的文件結(jié)構(gòu):
tmp/
├── file1
└── file2
如果要帶目錄拷貝到鏡像中,需要使用:
COPY testdir /tmp/testdir
ADD
和COPY
用法類似,一般優(yōu)先使用 COPY。COPY 只支持簡單將本地文件拷貝到容器中,而 ADD 還有從壓縮包中提取文件的功能,如:
# 宿主機壓縮包test.tar 解壓到 鏡像/tmp中
ADD test.tar /tmp
9. VOLUME:指定目錄為數(shù)據(jù)卷存儲方式
為了防止運行時用戶忘記將需要保存數(shù)據(jù)的目錄掛載為卷,我們可以事先指定某些目錄掛載為匿名卷,這樣在運行時如果用戶不指定掛載,其應(yīng)用也不會向容器存儲層寫入大量數(shù)據(jù)。
用法:
VOLUME ["<路徑1>", "<路徑2>"...]
示例:
VOLUME ["/data"]
這里的 /data
目錄就會在運行時自動掛載為匿名卷,容器運行時使用 -v mydata:/data
可以覆蓋這個掛載設(shè)置。
10. USER:指定運行容器時的用戶名或 UID
用法:
USER <user>[:<group>]
或
USER <UID>[:<GID>]
示例:
USER www
當(dāng)容器中運行的服務(wù)不需要管理員權(quán)限時,可以先建立一個特定的用戶和用戶組,為它分配必要的權(quán)限,然后通過該命令,使用 USER 切換到這個用戶。
Tips:
- 使用USER指定用戶時,可以使用用戶名、UID或GID,或是兩者的組合。
- 使用USER指定用戶后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT都將使用該用戶。
我們可以在docker run
中使用-u
參數(shù)指定用戶執(zhí)行命令,來替代默認(rèn)設(shè)定,如果為了精確控制用戶的id,也可以傳入uid。
docker run -i -t -u 1001 busybox sh
11. WORKDIR: 切換到鏡像中的指定路徑
在WORKDIR
中需要使用絕對路徑,如果鏡像中對應(yīng)的路徑不存在,會自動創(chuàng)建此目錄。
我們使用 WORKDIR 來替代 RUN cd <path> && <do something>
的這類切換目錄進行操作的指令。
WORKDIR指令對ADD COPY等指令也生效,如下操作會將宿主機的test.txt 文件復(fù)制到 鏡像的/tmp/test.txt。
WORKDIR /tmp
COPY test.txt .
12. ONBUILD:引用后構(gòu)建指令
用法:
ONBUILD <其他指令>
示例:
ONBUILD COPY . /tmp/
ONBUILD
是一個特殊的指令,它后面跟的是其它指令,比如 RUN, COPY 等。
FROM alpine:latest
ONBUILD RUN mkdir /app
ONBUILD COPY . /app/
CMD [ "echo", "hello" ]
使用上面 Dockerfile 在構(gòu)建基礎(chǔ)鏡像的時候,這兩行 ONBUILD 并不會被執(zhí)行。它的效果等價于:
FROM alpine:latest
CMD [ "echo", "hello" ]
構(gòu)建出來的鏡像作為基礎(chǔ)鏡像,在其他 Dockerfile 的 FROM
指令中被引用,去構(gòu)建新鏡像的時候,ONBUILD 后的指令會執(zhí)行。
13. 小結(jié)
本節(jié)介紹了我們使用 Dockerfile 構(gòu)建鏡像的過程中經(jīng)常會用到的指令,這些指令大多簡單易懂的。但本節(jié)中也提到,其中有些指令的用法比較特殊,在某些看似“理所當(dāng)然”的使用方法下,可能會出現(xiàn)"bug",請大家一定要留意。后面也有實戰(zhàn)章節(jié),幫助大家加深理解。