在上面的內(nèi)容中介紹了如何使用docker commit的方法來構(gòu)建鏡像,相反推薦使用被稱為Dockerfile的定義文件和docker build命令來構(gòu)建鏡像凡简。Dockerfile使用基本的基于DSL語法的指令來構(gòu)建一個Docker鏡像筐眷,之后使用docker build命令基于該Dockerfile中的指令構(gòu)建一個新的鏡像武翎。
(一)第一個Dockerfile
現(xiàn)在來創(chuàng)建一個最簡單的Dockerfile文件樣例霹疫,先創(chuàng)建一個空的Dockerfile文件屠阻,在任意目錄下都行蛉加,在Dockerfile文件中填入以下內(nèi)容丁眼。
FROM alpine:3.14
//設(shè)置容器內(nèi)的數(shù)據(jù)卷
VOLUME ["/var/html"]
EXPOSE 80
該Dockerfile由一系列指令和參數(shù)組成卢厂,每條指令融柬,如FROM,都必須為大寫字母且后面跟隨一個參數(shù):FROM alpine:3.14锅纺,Dockerfile中的指令會按順序從上到下執(zhí)行,所以應(yīng)該根據(jù)需要合理安排指令的順序驱入。
使用build命令構(gòu)建鏡像的步驟如下 :
第一步:Docker從基礎(chǔ)鏡像運行一個容器。
第二步:執(zhí)行數(shù)據(jù)卷指令來創(chuàng)建一個數(shù)據(jù)卷宴凉。
第三步:設(shè)置訪問端口
—— 最后所有指令執(zhí)行完畢窘行。
每個Dockerfile的第一條指令都應(yīng)該是FROM,F(xiàn)ROM指令指定一個已經(jīng)存在的鏡像后續(xù)指令都將基于該鏡像進行劫窒,這個鏡像被稱為基礎(chǔ)鏡像(base iamge)。在上面的Dockerfile示例中散怖,我們指定了alpine:3.14作為鏡像的基礎(chǔ)鏡像偏灿,基于這個Dockerfile構(gòu)建的新鏡像將以alpine:3.14操作系統(tǒng)為基礎(chǔ)枚荣,在運行一個容器時,必須要指明是基于哪個基礎(chǔ)鏡像在進行構(gòu)建害碾。
接著是VOLUME指令,用于指定數(shù)據(jù)卷的設(shè)置。
最后設(shè)置EXPOSE指令剃袍,這條指令告訴Docker該容器內(nèi)的應(yīng)用程序?qū)褂萌萜鞯闹付ǘ丝冢@并不意味著可以自動訪問任意容器運行中服務(wù)的端口,這里指定的是80端口棵红。
(二)Dockerfile指令
本小節(jié)主要針對Dockerfile文件中使用到的指令進行介紹致板,介紹Dockerfiler文件中常用的指令素征,在Dockerfile文件中所有的指令都必須使用大寫。
1.FROM
FROM指令的格式如下:
FROM [--platform=<platform>] <image> [AS <name>]
OR
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
OR
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
FROM指令表示初始化一個新構(gòu)建并作為后續(xù)指令的基本映像今豆,所以一個有效的Dockerfile文件必須以FROM指令開始情组,為個鏡像也必須是一個有效鏡像,可以從一些公用庫中來獲取相應(yīng)的鏡像。
但ARG可以放在FROM指令前面印蓖,關(guān)于ARG和FROM如何交互將會在后面介紹堕汞。
FROM可以在單個Dockerfile中多出現(xiàn)投放,這樣可以創(chuàng)建多個映像蛤肌,或?qū)⒁粋€構(gòu)建階段用作另一個構(gòu)建階段的依賴庫向胡。
FROM指令可以支持由其上一個指令A(yù)RG指令聲明的變量,并傳遞給FROM指令使用。
ARG CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD /code/run-app
FROM extras:${CODE_VERSION}
CMD /code/run-extras
2.RUN
RUN指令有兩種運行方式:
#shell格式:
RUN <command>
#exec格式:
RUN ["executable", "param1", "param2"]
# 例如:
# RUN ["./test.php", "dev", "offline"] 等價于 RUN ./test.php dev offline
注意:Dockerfile的指令每執(zhí)行一次都會在docker上新建一層若专,所以過多無意義的層淮逻,會造成鏡像膨脹過大车胡。
如上瘩将,以 && 符號連接命令欲险,這樣執(zhí)行后,只會創(chuàng)建 1 層鏡像。
3.CMD
CMD指令用于指定一個容器啟動時所運行的命令蚯斯,這有點類似于RUN指令狈蚤,但RUN指令是指定鏡像被構(gòu)建時要運行的命令,而CMD是指容器啟動時所要運行的命令锭魔,CMD指令有三種形式搂抒。
#exec格式 CMD ["executable","param1","param2"]
# 該寫法是為 ENTRYPOINT 指令指定的程序提供默認參數(shù) CMD ["param1","param2"]
#shell格式 CMD command param1 param2
一般推薦使用第一種格式容客,執(zhí)行過程清晰明確寓辱。第三種格式其實在運行過程中也會轉(zhuǎn)換成第一種格式運行缭保,并且默認可執(zhí)行文件是sh。
CMD和使用docker run命令啟動容器時指定運行命令幾乎一致碴巾。
#如在Dockerfile文件中寫以下指令
CMD ["/bin/true"]
#上面這個CMD等同以下面的run指令
root@ubunhomhome/test# docker run -it nginx /bin/true
在Dockerfile中只能有一個CMD指令,即使如果有多個CMD指令倘要,也只有最后一個CMD才會生效缰趋。
CMD指令主要是為執(zhí)行中的容器提供默認值,這些默認值可以包含可執(zhí)行文件,也可以省略可執(zhí)行文件,在這種情況下捕犬,就必須指定一條ENTRYPOINT指令。
如果用戶在使用docker run指定參數(shù)時,則它們將會覆蓋Dockerfile文件中的CMD參數(shù)默認值啸澡。
4.LABEL
LABEL指令用來給鏡像添加一些元數(shù)據(jù)(metadata)上沐,以鍵值對的形式龄广,語法格式如下:
LABEL <key>=<value> <key>=<value> <key>=<value> ...
如果LABEL值中需要跨多行敲才,則需要加入反斜杠和引號。
一個鏡像可以有多個標簽,可以在一行上指定多個標簽番舆,有以下兩種方式可以實現(xiàn)呛讲。
5.EXPOSE
該指令用于聲明監(jiān)聽的端口號雷逆,在監(jiān)聽是可以指定是TCP還是UDP協(xié)議,默認值為TCP膀哲,其主要有以下作用:
幫助鏡像使用理解這個鏡像服務(wù)的守護端口往产,以方便配置映射。
在運行時使用隨時端口映射某宪,也就是docker run -P時仿村,會自動隨機映射EXPOSE的端口蔼囊。
EXPOSE指令實際上并未發(fā)布指定端口,它是將我們訪問時輸入的端口和運行容器之間做一種關(guān)聯(lián)或通信滔灶,具體發(fā)布哪些端口表箭,要在運行容器時發(fā)布實際端口,可以使用docker run中-p或-P來設(shè)置映射的端口號溪掀。
默認情況下一般如果我們未指定協(xié)議的話骚勘,那么都是使用TCP協(xié)議殉疼,當然也可以指定具體的協(xié)議,也可以同時指定TCP和UDP協(xié)議淤井。
EXPOSE 80/tcp
EXPOSE 80/udp
6.ENV
設(shè)置環(huán)境變量,定義了環(huán)境變量,這樣在后續(xù)的指令中,就可以使用這個環(huán)境變量。如果在環(huán)境變量引用過程中包含空格,那么需要使用到反斜杠。
其語法格式如下:
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
ENV MY_NAME="John Doe"
ENV MY_DOG=Rex\ The\ Dog
ENV MY_CAT=fluffy
使用ENV設(shè)置的環(huán)境變量會保留下來,當容器運行時可以使用docker inspect查看值泪酱,并且可以使用docker run --env = 更改環(huán)境變量的值张弛。
如果僅僅是在構(gòu)建過程中需要環(huán)境變量,而在最終映像中不需要想邦,可以考慮為單個命令設(shè)置一個值或使用ARG婴洼,ARG指令不會人保留在最終鏡像中。
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y ...
7.ARG
ARG構(gòu)建參數(shù)與ENV作用一致,不過作用域不一樣,ARG設(shè)置的環(huán)境變量僅對Dockerfile內(nèi)有效科展,也就是說只有docker build的過程中有效浙于,構(gòu)建好的鏡像內(nèi)不存在此環(huán)境變量讹挎。
構(gòu)建命令docker build中可以用--build-arg <參數(shù)名>=<值>來覆蓋,ARG指令格式如下:
ARG <參數(shù)名>[=默認值]
如果ARG指令具有默認值,并且在構(gòu)建時未傳遞任何值国葬,則構(gòu)建時會使用默認值贤徒,相反如果未設(shè)置默認值,并且在構(gòu)建中未傳遞任何值胃惜,那么構(gòu)建時會輸出警告信息泞莉。
8.ADD
ADD指令是指將宿主機的文件或目錄復(fù)制到鏡像文件系統(tǒng)中指定的路徑哪雕,其語法格式有兩種:
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
每個可以使用通配符船殉,并且匹配將使用Go的filepath.Match規(guī)則進行,例如:
要添加所有以“hom”開頭的文件:
ADD hom* /mydir/
“斯嚎?”可以替換任意一個字符利虫,例如:
ADD hom?.txt /mydir/
是指構(gòu)建容器的路徑,可以是絕對路徑堡僻,也可以是相對路徑糠惫,但這個相對路徑是相對于WORKDIR的路徑。如以下示例是將“test.txt”文件添加到/relativeDir/钉疫。
ADD test.txt relativeDir/
下面的示例則是將“test.txt”添加到相對路徑/absoluteDie/中硼讽。
ADD test.txt /absoluteDir/
如果路徑中包含一些特字符,那么需要按照Golang規(guī)則轉(zhuǎn)義那些路徑牲阁,以防止在解析過程中將它們視為匹配的模式固阁,如要添加名為sarr[1].txt的文件。
ADD sarr[[]1].txt /mydir/
路徑必須是構(gòu)建內(nèi)容的一個內(nèi)部路徑城菊,不能添加類似于ADD ../path /path备燃,因為docker build的第一步是將上下文目錄和子目錄發(fā)送到docker守護進程。
如果是URL凌唬,而不以斜杠結(jié)尾并齐,則從URL下載文件并將其復(fù)制到。
如果是URL,而以斜杠結(jié)尾况褪,則從URL推斷文件名撕贞,并將文件下載到/,例如ADD http://chuanshi.com/foobar /窝剖,將創(chuàng)建文件/foobar麻掸,該URL必須具有正確的路徑,以便在這種情況下可以找到適當?shù)奈募?/p>
如果是目錄赐纱,則復(fù)制目錄的整個內(nèi)容脊奋,包括文件系統(tǒng)元數(shù)據(jù)。
如果是任何其他類型的文件疙描,則會將其及其元數(shù)據(jù)一起單獨復(fù)制诚隙,在這種情況下,如果以斜杠結(jié)尾起胰,則它將被視為目錄久又,并且的內(nèi)容將被寫在/base()中。
如果直接或由于使用通配符而指定多個資源效五,則必須是目錄地消,并且必須以斜杠結(jié)尾。
如果不以斜杠結(jié)尾畏妖,則將其視為常規(guī)文件脉执,并且的內(nèi)容將被寫入。
如果不存在戒劫,則會與路徑中所有缺少的目錄一起創(chuàng)建它半夷。
1.ADD的優(yōu)點:在執(zhí)行為tar壓縮文件的話,壓縮格式為gzip迅细、gzip2以及xz的情況下巫橄,會自動復(fù)制并解壓到。
2.ADD的缺點:在不解壓的前提下茵典,無法復(fù)制tar壓縮文件湘换,會令鏡像構(gòu)建緩存失效,從而可能會令鏡像構(gòu)建變得比較緩慢统阿,具體是否使用彩倚,可以根據(jù)是否需要自動解壓來決定。
9.COPY
復(fù)制指令砂吞,從上下文目錄中復(fù)制文件或者目錄到容器里指定路徑署恍,通常有以下兩種格式:
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
COPY指令表示從復(fù)制新文件或目錄,并將它們添加到容器的文件系統(tǒng)中蜻直,路徑為盯质。
可以指定多個資源袁串,但是文件和目錄的路徑將被解釋為相對于構(gòu)建上下文。
每個可能包含通配 符呼巷,并且匹配將合作Go人filepath.Match規(guī)則進行囱修。
例如要添加所有"hom"開頭的文件:
COPY home* /mydir/
"?"表示可以匹配任意一個字符:
COPY hom?.txt /mydir/
是指構(gòu)建容器的路徑,可以是絕對路徑王悍,也可以是相對路徑破镰,但這個相對路徑是相對于WORKDIR的路徑。如以下示例是將“test.txt”文件添加到/relativeDir/压储。
COPY test.txt relativeDir/
下面的示例則是將“test.txt”添加到相對路徑/absoluteDie/中鲜漩。
COPY test.txt /absoluteDir/
如果路徑中包含一些特字符,那么需要按照Golang規(guī)則轉(zhuǎn)義那些路徑集惋,以防止在解析過程中將它們視為匹配的模式孕似,如要添加名為sarr[1].txt的文件。
COPY sarr[[]1].txt /mydir/
路徑必須是構(gòu)建內(nèi)容的一個內(nèi)部路徑刮刑,不能添加類似于COPY ../path /path喉祭,因為docker build的第一步是將上下文目錄和子目錄發(fā)送到docker守護進程。
如果是目錄雷绢,則復(fù)制目錄的整個內(nèi)容泛烙,包括文件系統(tǒng)無數(shù)據(jù),目錄本身不會被復(fù)制翘紊,只是其內(nèi)容被復(fù)制蔽氨。
如果是任何其他類型的文件,則會將其及其元數(shù)據(jù)一起單獨復(fù)制霞溪,在這種情況下孵滞,如果以斜杠結(jié)尾中捆,則它將被視為目錄鸯匹,并且的內(nèi)容將被寫在/base()中。
如果直接或由于使用通配符而指定多個資源泄伪,則必須是目錄殴蓬,并且必須以斜杠結(jié)尾。
如果不以斜杠結(jié)尾蟋滴,則將其視為常規(guī)文件染厅,并且的內(nèi)容將被寫入。
如果不存在津函,則會與路徑中所有缺少的目錄一起創(chuàng)建它肖粮。
10.ENTRYPOINT
類似于CMD指令,但其不會被docker run指令運行參數(shù)所覆蓋尔苦,并且這些命令行參數(shù)會被當作參數(shù)送給ENTRYPOINT指令指定的程序涩馆。
但是如果運行docker run時使用了--entrypoint選項行施,將覆蓋CMD指令指定的程序。
其語法格式有以下兩種:
#exec格式
ENTRYPOINT ["executable", "param1", "param2"]
#shell格式
ENTRYPOINT command param1 param2
ENTRYPOINT的優(yōu)點在執(zhí)行docker run的時候可以指定ENTRYPOINT運行所需要的參數(shù)魂那。
但如果Dockerfile中如果存在多個ENTRYPOINT指令時蛾号,只有最后一個會生效。
可以將ENTRYPOINT與CMD命令搭配使用涯雅,一般是變參才會使用CMD鲜结,這里的CMD等于是在給ENTRYPOINT傳參。
例如活逆,通過Dockerfile構(gòu)建了一個nginx:V1.0的鏡像精刷。
FROM nginx
ENTRYPOINT ["nginx", "-c"] # 定參
CMD ["/etc/nginx/nginx.conf"] # 變參
如果在運行容器時,不傳參運行蔗候,
root@ubunhomhome/test# docker run -it nginx:v1.0
容器內(nèi)則會默認運行以下命令贬养,啟動主進程。
nginx -c /etc/nginx/nginx.conf
如果在運行容器時傳參運行琴庵,
root@ubunhomhome/test# docker run -it nginx:v1.0 -c /etc/nginx/new_nginx.conf
容器內(nèi)會默認運行以下命令误算,啟動主進程。
nginx -c /etc/nginxnew_nginx.conf
11.VOLUME
VOLUME指令在后面的章節(jié)會進行詳細的介紹迷殿,在本小節(jié)中不做介紹
12.USER
USER指令用于指定執(zhí)行后續(xù)命令的用戶和用戶組儿礼,這邊只是切換后續(xù)指令執(zhí)行的用戶(用戶和用戶組必須提前已經(jīng)存在)。其語法格式如下:
USER <user>[:<group>]
#or
USER <UID>[:<GID>]
13.WORKDIR
WORKDIR指令表示指定工作目錄庆寺,該指定的目錄必須提前創(chuàng)建好蚊夫,docker build構(gòu)建鏡像過程中的,每個RUN命令都是新建的一層懦尝,只有通過WORKDIR創(chuàng)建才會一直存在知纷。其語法格式如下:
WORKDIR /path/to/workdir
WORKDIR指令可在Dockerfile中多次使用,如果提供了相對路徑陵霉,則它將相對于上一個WORKDIR指令的路徑來創(chuàng)建目錄琅轧,如:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
該Dockerfile中最后一個pwd命令的輸出為/a/b/c。
WORKDIR指令也可以解析ENV設(shè)置的環(huán)境變量踊挠,如:
ENV DIRPATH=/path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd
該Dockerfile中最后一個pwd命令的輸出為/path/$DIRNAME乍桂。