溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Docker Dockerfile

發(fā)布時(shí)間:2020-06-12 13:29:42 來源:網(wǎng)絡(luò) 閱讀:1682 作者:騎士救兵 欄目:云計(jì)算

鏡像的生成途徑:

  • Dockerfile
  • 基于容器制作

本篇介紹Dockerfile。

文件說明

Dockerfile是一個(gè)包含用于組合映像的命令的文本文檔。Docker通過讀取Dockerfile中的指令自動(dòng)生成鏡像。

基本結(jié)構(gòu)

基本格式:

# Comment
INSTRUCTION arguments

主要就2類一句,第一行是注釋,第二行是指令。
指令(INSTRUCTION)大小寫不敏感,為了將指令和參數(shù)或其他內(nèi)容區(qū)分,通常指令使用全大寫。
Docker以從上到下的順序運(yùn)行Dockerfile的指令。為了指定基本映像,第一條指令必須是FROM。一個(gè)聲明以#字符開頭則被視為注釋??梢栽贒ocker文件中使用RUN,CMD,F(xiàn)ROM,EXPOSE,ENV等指令。

工作目錄

制作鏡像,首先要有一個(gè)文件目錄,即工作目錄。
制作鏡像所引用的文件,都必須在工作目錄下。
Dockerfile文件,文件名就是Dockerfile(首字母大寫),寫docker指令。
.dockeringore文件,寫在該文件中的路徑在打包時(shí)都不會(huì)打包。

docker build命令用于從Dockerfile構(gòu)建鏡像。可以在docker build命令中使用-f標(biāo)志指向文件系統(tǒng)中任何位置的Dockerfile。

環(huán)境變量

制作鏡像的過程中,可以使用環(huán)境變量。
直接為一個(gè)變量名賦值,這種只會(huì)在當(dāng)前shell中有效。但是對(duì)在當(dāng)前shell(父shell)中啟動(dòng)的其他shell(子shell)無(wú)效。比如賦值之后,調(diào)用了一個(gè).sh腳本,在這個(gè)腳本中沒有之前賦值的變量。
一般就用export命令來創(chuàng)建環(huán)境變量,這樣是全局都有效的。
無(wú)論哪種情況,只要會(huì)話關(guān)閉,就全部失效了。要想永久有效需要編輯文件。

引用變量
可以使用$KEY${KEY}來使用環(huán)境變量。不帶大括號(hào)的格式之后必須有空格,如果之后沒有空格而是要接著其他內(nèi)容,就需要用帶大括號(hào)的格式。
另外,${KEY}這種形式還支持變量替換的特殊格式:

  • \${KEY:-VALUE}: VALUE是默認(rèn)值,如果變量不存在就使用默認(rèn)值
  • \${KEY:+VALUE}: 如果VALUE有值,則返回VALUE,否則返回空

Dockerfile 指令

在這里列出了一些常用的指令。

FROM

FROM指定是最重要的一個(gè)指定,并且必須為Dockerfile文件開篇的第一個(gè)非注釋行,用于為映像文件構(gòu)建過程指定基本鏡像,后續(xù)的指令運(yùn)行于此基準(zhǔn)鏡像所提供的運(yùn)行環(huán)境中。

實(shí)踐中,基準(zhǔn)鏡像可以是任何可用鏡像文件。默認(rèn)情況下,dockerfile會(huì)在docker主機(jī)上查找指定的鏡像文件,如果不存在,會(huì)自動(dòng)從Registry上拉取。

語(yǔ)法格式:

FROM <repository>[:<tag>]
FROM <repository>@<digest>

上面兩種格式都可以。第一行和拉取鏡像或者運(yùn)行鏡像一樣,一個(gè)鏡像可以有多個(gè)版本,可以通過tag指定。第二行的格式是通過鏡像的ID來指定,因?yàn)镮D是唯一的,所以這種方式可以對(duì)鏡像進(jìn)行校驗(yàn)?;诿忠苗R像可能會(huì)有安全問題,防止引用被修改了名字的含有惡意代碼的鏡像。

MAINTAINER

維護(hù)者信息。該指令在舊版本中使用,建議用下面的LABEL來指定。

語(yǔ)法格式:

MAINTAINER <name>

可以是任何形式的文本信息,但約定俗成地使用作者名及郵箱地址:

MAINTAINER Steed Xu <x749b@163.com>

LABEL

用戶可以為鏡像指定各種元數(shù)據(jù)。

語(yǔ)法格式:

LABEL <key1>=<value1> <key2>=<value2> <key3>=<value3> ...

使用LABEL指定元數(shù)據(jù)時(shí),一條LABEL指定可以指定一或多條元數(shù)據(jù),指定多條元數(shù)據(jù)時(shí)不同元數(shù)據(jù)之間通過空格分隔。推薦將所有的元數(shù)據(jù)通過一條LABEL指令指定,以免生成過多的中間鏡像。

COPY

用于從Docker主機(jī)復(fù)制文件至創(chuàng)建的新映像文件。

語(yǔ)法格式:

COPY <src> ... <dest>
COPY ["<src>", ... "<dest>"]

src是要復(fù)制的源文件或目錄,支持使用通配符問號(hào)(?)和星號(hào)(*)。一般使用相對(duì)路徑,起始路徑就是工作目錄。
dest為目標(biāo)路徑,一般為絕對(duì)路徑。如果是相對(duì)路徑,起始路徑以WORKDIR(后面會(huì)講)指定。

上面的兩種格式都可以,第二行的列表格式可以支持包含空格的路徑。

文件復(fù)制準(zhǔn)則:

  • src必須在工作目錄中
  • 如果src是目錄,其內(nèi)部文件和子目錄會(huì)遞歸復(fù)制,但src本身不會(huì)被復(fù)制
  • 如果指定多個(gè)src,或src中使用了通配符,則dest必須是一個(gè)目錄,必須以/結(jié)尾
  • 如果dest不存在,它將會(huì)被自動(dòng)創(chuàng)建,包括其父目錄的路徑

ADD

ADD與COPY類似,ADD支持使用tar文件和url路徑。tar文件會(huì)自動(dòng)解壓,url會(huì)下載類似wget。

語(yǔ)法格式:

ADD <src> ... <dest>
ADD ["<src>", ... "<dest>"]

操作準(zhǔn)則:

  • 如果src為url,且dest不以/結(jié)尾,則下載文件并創(chuàng)建為dest。如果dest以/結(jié)果,則將文件下載到dest的目錄下。
  • 只有本地的tar文件,會(huì)自動(dòng)展開。如果是url,下載下來的文件是tar文件,這個(gè)不會(huì)再自動(dòng)展開。
  • 如果src有多個(gè),或者使用了通配符,dest以/結(jié)尾,則所有文件下載到dest目錄下。如果dest不以/結(jié)尾,則被視為文件,src的內(nèi)容將直接寫入到dest。

WORKDIR

用于為Dockerfile中所有的RUN、CMD、ENTRYPOINT、COPY和ADD指令設(shè)定工作目錄。
工作目錄不僅影響之后命令的起始路徑,也會(huì)影響容器啟動(dòng)后的工作目錄,默認(rèn)是根目錄。

語(yǔ)法格式:

WORKDIR <dirpath>

WORKDIR可以出現(xiàn)多次,路徑也可以是相對(duì)路徑。相對(duì)路徑就是相對(duì)前一個(gè)WORKDIR指定指定的路徑。
WORKDIR還可以調(diào)用由ENV(后面會(huì)講)指令定義的變量。

示例:

# 工作目錄為/a
WORKDIR /a
# 工作目錄為/a/b
WORKDIR b
# 工作目錄為/a/b/c
WORKDIR c

VOLUME

用于在image中創(chuàng)建一個(gè)掛載點(diǎn)目錄。
在dockerfle中只能指定掛載點(diǎn),無(wú)法指定宿主機(jī)上的路徑。所以這里的掛載的只能是docker管理的卷。

語(yǔ)法格式:

VOLUME <mounpoint>
VOLUME ["<mounpoint1>", "<mounpoint2>", "<mounpoint3>"]

如果掛載點(diǎn)目錄路徑下之前存在文件,docker run 命令會(huì)在卷掛載完成后將此前的所有文件復(fù)制到新掛載的卷中。

EXPOSE

用于為容器打開指定要監(jiān)聽的端口以實(shí)現(xiàn)對(duì)外部通信。

語(yǔ)法格式:

EXPOSE <port> [<port> ...]

默認(rèn)是TCP協(xié)議,可以使用/tcp或/udp來指定。指令還可以一次指定多個(gè)端口,示例:

EXPOSE 11211/tcp 11211/udp

ENV

用于為鏡像定義所需的環(huán)境變量,并可被Dockerfile文件中位于其后的其它指令所調(diào)用。調(diào)用環(huán)境變量在開頭已經(jīng)講過了。

語(yǔ)法格式:

ENV <key> <value>
ENV <key1>==<value1> <key2>==<value2> ...

第一種格式,key之后的所有內(nèi)容都會(huì)被視為value,這樣一次只能設(shè)置一個(gè)變量。
第二種格式,可以一次設(shè)置多個(gè)變量,如果value中包含空格,就用反斜杠(\)轉(zhuǎn)義,也可以通過對(duì)value加引號(hào)進(jìn)行標(biāo)識(shí)。另外反斜杠也可用于續(xù)行。
建議使用第二種格式時(shí),用上反斜杠續(xù)行,一行設(shè)置一對(duì)鍵值對(duì):

ENV key1=value1 \
    key2=value2

容器的環(huán)境變量
除了制作鏡像時(shí)可以設(shè)置環(huán)境變量,在啟動(dòng)容器時(shí)也可以設(shè)置環(huán)境變量。docker container run啟動(dòng)容器是使用-e參數(shù):

-e, --env list                       Set environment variables

制作鏡像時(shí)的設(shè)置的環(huán)境變量會(huì)影響ENV語(yǔ)句后面的指令。并且會(huì)一直保留這個(gè)環(huán)境變量,在容器運(yùn)行時(shí)依然有效。在啟動(dòng)容器時(shí)可以設(shè)置新的環(huán)境變量或者把之前的環(huán)境變量的值替換掉。這不會(huì)影響到之前鏡像制作的過程中使用環(huán)境變量的值。鏡像制作時(shí)遇到環(huán)境變量,會(huì)直接獲取到當(dāng)前該變量額值并進(jìn)行操作。
小結(jié):

  • 制作鏡像時(shí)設(shè)置的環(huán)境變量不但在制作鏡像過程中有效,也可以把容器運(yùn)行時(shí)需要的環(huán)境變量提前設(shè)置好。
  • 容器運(yùn)行時(shí)設(shè)置的環(huán)境變量,只會(huì)對(duì)容器運(yùn)行的過程有影響,不會(huì)影響到之前鏡像制作過程中的操作。
  • 上面的情況也有例外,比如容器默認(rèn)執(zhí)行的命令中帶有環(huán)境變量,這個(gè)命令的內(nèi)容就是環(huán)境變量而不是環(huán)境變量的值,所以直接把環(huán)境變量的引用寫到默認(rèn)命令中。等到容器啟動(dòng)的時(shí)候再運(yùn)行這個(gè)命令,此時(shí)才會(huì)去獲取當(dāng)前環(huán)境變量的值,也就是被run命令的-e參數(shù)改變之后的值。

printenv 查看環(huán)境變量
使用printenv命令可以查看環(huán)境變量,env命令不帶參數(shù)也一樣:

$ docker container run --rm -e k1=v1 -e k2=v2 tinyweb1 printenv
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=ee6793a43c8c
k1=v1
k2=v2
HOME=/root
$ 

這里就是啟動(dòng)容器時(shí),指定運(yùn)行printenv命令即可。

RUN

用于指定docker build過程中運(yùn)行的程序,可以是任何命令。

語(yǔ)法格式:

RUN <command>
RUN ["executable", "param1", "param2"]

RUN指令可以使用多次。不過對(duì)于前后有關(guān)聯(lián)的一系列指令,建議多條指令之間使用&&連接成為一條指令,再用續(xù)行符讓一條指令寫一行增加可讀性。

通過shell來啟動(dòng)指令
第一種格式中,command是一條完整的shell命令,并且是以/bin/sh -c來運(yùn)行的。
第二種格式是一個(gè)數(shù)組,executable是要運(yùn)行的命令,后面都是要傳遞的參數(shù)。此種格式的命令不會(huì)以/bin/sh -c來發(fā)起,而是直接以內(nèi)核創(chuàng)建進(jìn)程。因此,常見的shell特性如變量替換以及通配符都無(wú)效。不過,如果需要依賴shell特性的話,可以用下面的格式:

RUN ["/bin/bash", "-c", "executable param1 param2"]

這里注意,最終要執(zhí)行的命令包括這個(gè)命令的參數(shù)需要寫成一個(gè)完整的字符串作為列表的一個(gè)元素,不能拆開。后面的制作鏡像的示例中會(huì)有分析。

安裝應(yīng)用程序
大多數(shù)鏡像在基于基礎(chǔ)鏡像安裝應(yīng)用程序時(shí),都是編譯安裝的。這個(gè)編譯安裝的過程就需要RUN指令。
當(dāng)然也可以使用yum安裝。不過需要注意,yum安裝過程中是會(huì)生成緩存的。這些緩存是不會(huì)自動(dòng)刪除,應(yīng)該清除掉以節(jié)約鏡像空間。下面的命令可以清除yum緩存:

yum clean all

另外,緩存的內(nèi)容是保存在/var/cache/yum/目錄下的。所以也可以把目錄下的文件刪除。

清除緩存
這部分的內(nèi)容沒有驗(yàn)證過,可能和上面是一樣的作用。
RUN指令創(chuàng)建的中間鏡像會(huì)被緩存,并會(huì)在下次構(gòu)建中使用(這里的使用不是真的要用,而是因?yàn)檫@些緩存沒有用了,卻要占用鏡像空間,應(yīng)該要清除掉)。如果不想使用這些緩存鏡像,可以在構(gòu)建時(shí)指定--no-cache參數(shù):

docker build --no-cache

CMD

為啟動(dòng)容器指定默認(rèn)要運(yùn)行的程序。這個(gè)就是容器中PID為1的進(jìn)程,其運(yùn)行結(jié)束后,容器也將終止。CMD指定類似于RUN指令,也是運(yùn)行任何命令或應(yīng)用程序,不過二者的運(yùn)行時(shí)間點(diǎn)不同。RUN是在制作鏡像時(shí)執(zhí)行的。CMD是在容器啟動(dòng)后執(zhí)行的,并且只有最后一條CMD指令有效,就是指定容器的主進(jìn)程。

語(yǔ)法格式:

CMD <command>
CMD ["executable", "param1", "param2"]
CMD ["param1", "param2"]

語(yǔ)法格式也和RUN是一樣的,去對(duì)比查看RUN的指令格式說明和兩種格式的差別即可。
這里還有第三種格式,內(nèi)容形式上和第二種是沒有差別的(形式上都是字符串,體現(xiàn)不出是命令還是參數(shù))。在設(shè)置了ENTRYPOINT(后面會(huì)講)時(shí),CMD列表里的元素就都作為ENTRYPOINT指令的參數(shù)了,所以就沒有命令了。

下面兩小段是引申出來的內(nèi)容,和使用容器無(wú)關(guān)。

exec命令
這一小段的內(nèi)容只是要了解一下容器中啟動(dòng)的命令是如何成為PID為1的主進(jìn)程的。
同RUN命令一樣,第一種格式默認(rèn)是用過/bin/sh -c啟動(dòng)的。這意味著啟動(dòng)的進(jìn)程在容器中的PID不為1,不能接收Unix信號(hào)。因此,當(dāng)使用docker stop命令停止容器時(shí),此進(jìn)程接收不到SIGTERM信號(hào)。并且shell才是PID為1的那個(gè)進(jìn)程,那么程序啟動(dòng)完之后,容器就停止了。默認(rèn)應(yīng)該是使用了exec命令的機(jī)制,調(diào)用要啟動(dòng)的進(jìn)程并且新的進(jìn)程會(huì)替代父進(jìn)程,也就是成為PID為1的進(jìn)程。

在后臺(tái)運(yùn)行程序
這一小段的內(nèi)容和容器無(wú)關(guān),是講一下傳統(tǒng)的做法是如何直接在宿主機(jī)上啟動(dòng)后臺(tái)程序的。
傳統(tǒng)的方式在宿主機(jī)上運(yùn)行應(yīng)用,要啟動(dòng)這些服務(wù),可以使用systemctl start命令。
還有一種方式,可以手動(dòng)啟動(dòng),就是在命令行中執(zhí)行命令。每一個(gè)進(jìn)程都應(yīng)該是某一個(gè)進(jìn)程的子進(jìn)程。手動(dòng)啟動(dòng)的程序默認(rèn)是作為shell的子進(jìn)程存在的。有些程序啟動(dòng)后會(huì)占據(jù)當(dāng)前shell的終端,可以在命令后加&符號(hào),讓程序在后臺(tái)運(yùn)行。不過這并沒有改變這個(gè)應(yīng)用程序是shell的子進(jìn)程,一旦退出當(dāng)前shell,任何一個(gè)進(jìn)程終止時(shí),都會(huì)先把它的子進(jìn)程終止。要避免這種情況,還需要nobub命令,讓之后的命令啟動(dòng)的進(jìn)程剝離于當(dāng)前shell進(jìn)程的關(guān)系,使得程序可以在退出shell時(shí)繼續(xù)運(yùn)行。
所以可以使用下面的方式啟動(dòng)程序并且保持運(yùn)行:

nohup COMMAND &
nohup COMMAND > /dev/null 2>&1 &

ENTRYPOINT

類似CMD指令的功能,用于為容器指定默認(rèn)運(yùn)行程序。但是有ENTRYPOINT啟動(dòng)的程序不會(huì)被docker run命令指定的參數(shù)所覆蓋。而且命令行參數(shù)會(huì)被當(dāng)做參數(shù)傳遞給ENTRYPOINT指令指定的程序。
在docker run的時(shí)候,還是可以通過--entrypoint參數(shù)來覆蓋ENTRYPOINT指令指定的程序。

語(yǔ)法格式:

ENTRYPOINT <command>
ENTRYPOINT ["executable", "param1", "param2"]

docker run命令傳入的命令參數(shù)會(huì)覆蓋CMD指令的內(nèi)容并且附加到ENTRYPOINT命令最后作為其參數(shù)使用。

關(guān)于ENTRYPOINT的應(yīng)用,還是看下之后的動(dòng)態(tài)生成配置文件的示例。

USER

用于指定運(yùn)行image時(shí),或運(yùn)行Dockerfile中任何RUN、CMD、ENTRYPOINT指令指定的程序時(shí)的用戶名或UID。默認(rèn)情況下,container運(yùn)行身份為root用戶。

語(yǔ)法格式:

USER user
USER user:group
USER uid
USER uid:gid
USER user:gid
USER uid:group

這里的uid、gid可以是任意數(shù)字,但是必須為有效的,否則docker run命令將會(huì)失敗。

HEALTHCHECK

HEALTHCHECK指令告訴Docker如何測(cè)試容器以檢查它是否仍在工作。即使服務(wù)器進(jìn)程仍在運(yùn)行,這也可以檢測(cè)到陷入無(wú)限循環(huán)且無(wú)法處理新連接的Web服務(wù)器等情況。

語(yǔ)法格式:

HEALTHCHECK [OPTIONS] CMD <command>
HEALTHCHECK NONE

第一行,通過在容器內(nèi)運(yùn)行命令來檢查容器運(yùn)行狀況。
第二行,禁用從基礎(chǔ)映像繼承的任何運(yùn)行狀況檢查。
容器默認(rèn)就可以有健康狀態(tài)檢測(cè)的方法。只是默認(rèn)的方式有的場(chǎng)景不適用,這就需要自定義。CMD是關(guān)鍵字,后面跟用于健康監(jiān)測(cè)的命令。

參數(shù)說明:

  • --interval: 輪詢時(shí)間,默認(rèn)30秒
  • --timeout: 超時(shí)時(shí)間,默認(rèn)30秒
  • --start-priod: 容器啟動(dòng)多久后開始檢測(cè),默認(rèn)0秒。如果主進(jìn)程啟動(dòng)比較慢,需要設(shè)置一下一個(gè)參數(shù)
  • retries: 確認(rèn)健康檢查失敗,需要檢查失敗的次數(shù),默認(rèn)3次。

CMD關(guān)鍵字后面跟的就是健康狀態(tài)檢查的命令,docker根據(jù)這個(gè)命令的返回值來判斷健康狀態(tài)。運(yùn)行結(jié)果返回值的含義:

  • 0: 成功
  • 1: 不健康
  • 2: 預(yù)留值,無(wú)意義

指令示例
這是個(gè)簡(jiǎn)單的例子:

HEALTHCHECK --interval=5m --timeout=3s \
    CMD curl -f http://localhost/ || exit 1

可能沒有curl命令,使用wget命令也是一樣的:

    CMD wget -O - -q http://localhost/

這個(gè)命令可能還需要修改,每次wget成功都會(huì)輸出一條消息。雖然使用了-q靜默了日志輸出,但是wget下載的內(nèi)容還是要輸出的。這里用了-O參數(shù)指定到標(biāo)準(zhǔn)輸出了。希望沒有任何輸出的話還是通過重定向到/dev/null來解決。

如果檢查的邏輯不是那么簡(jiǎn)單,那么可能需要寫一個(gè)腳本來調(diào)用:

HEALTHCHECK --interval=10s --timeout=5s --retries=3 \
    CMD /bin/sh /opt/health_test.sh

健康檢查腳本的內(nèi)容:

#!/bin/sh
ss -antl | grep 80
if [ $? ==0 ]; then
    exit 0
else
    exit 1
fi

檢查容器健康狀態(tài)
當(dāng)容器指定了運(yùn)行狀況檢查時(shí),除了正常狀態(tài)外,它還具有運(yùn)行狀況。這個(gè)狀態(tài)最初是starting。每當(dāng)健康檢查通過時(shí),它就會(huì)變成healthy(以前的狀態(tài))。經(jīng)過一定數(shù)量的連續(xù)失敗后,它就變成了unhealthy。

使用docker ps命令查看容器狀態(tài),可以看到健康檢查的狀態(tài):

[root@Docker img4]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                           PORTS                   NAMES
b6dea4e8a808        ngx1-1              "nginx -g 'daemon of…"   2 seconds ago       Up 1 second (health: starting)   0.0.0.0:32776->80/tcp   ngx1
[root@Docker img4]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                   PORTS                   NAMES
b6dea4e8a808        ngx1-1              "nginx -g 'daemon of…"   4 seconds ago       Up 3 seconds (healthy)   0.0.0.0:32776->80/tcp   ngx1
[root@Docker img4]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS                   NAMES
21c80cbd2898        ngx1-1              "nginx -g 'daemon of…"   19 seconds ago      Up 18 seconds (unhealthy)   0.0.0.0:32777->80/tcp   ngx1
[root@Docker img4]#

這里有3種狀態(tài),啟動(dòng)后未做檢查的(health: starting),檢查成功的(healthy),確認(rèn)失敗的(unhealthy)。

SHELL

SHELL指令允許覆蓋用于命令行的默認(rèn)shell。Linux上的默認(rèn)shell是["/bin/sh", “-c”],而在Windows上[“cmd”, “/S”, “/C”]。

語(yǔ)法格式:

SHELL [“executable”, “parameters”]

這里只有一種格式,必須使用JSON的格式。
了解一下,一般用不上

STOPSIGNAL

STOPSIGNAL指令設(shè)置將發(fā)送到容器的系統(tǒng)調(diào)用信號(hào)以退出。此信號(hào)可以是與內(nèi)核的系統(tǒng)調(diào)用表中的位置匹配的有效無(wú)符號(hào)數(shù),例如9,或SIGNAME格式的信號(hào)名,例如SIGKILL。

語(yǔ)法格式:

STOPSIGNAL signal

也是了解一下,一般用不上,并且這里也沒有講清楚。

ARG

設(shè)置變量命令,用于指定傳遞給構(gòu)建運(yùn)行時(shí)的變量。ARG命令定義了一個(gè)變量,在docker build創(chuàng)建鏡像的時(shí)候,使用如下的build命令的選項(xiàng)來指定參數(shù):

--build-arg <varname>=<value>

有多個(gè)變量,就多次使用這個(gè)選項(xiàng)。

語(yǔ)法格式:

ARG <name>[=<default value>]

使用ARG定義變量的時(shí)候,可以加上默認(rèn)值。

在docker build調(diào)用時(shí),是沒有--env選項(xiàng)來傳環(huán)境變量的值的。所以要使用環(huán)境變量就只能使用環(huán)境變量默認(rèn)值。而使用ARG變量,就可以向ARG變量傳值了。
這個(gè)功能使得一個(gè)dockerfile可以適用于多個(gè)不同的場(chǎng)景。

ONBUILD

用于在Dockerfile中定義一個(gè)觸發(fā)器。當(dāng)所構(gòu)建的鏡像被用做其它鏡像的基礎(chǔ)鏡像時(shí),該鏡像中的觸發(fā)器將會(huì)被觸發(fā)。

語(yǔ)法格式:

ONBUILD <INSTRUCTION>

關(guān)鍵字后面跟的是一條正常的dockerfile的指令。

使用包含ONBUILD指令的Dockerfile構(gòu)建的鏡像應(yīng)該使用特殊的標(biāo)簽,比如:ruby:2.0-onbuild

制作鏡像

這里來制作幾個(gè)鏡像,把上面的指令試一下。

httpd鏡像

創(chuàng)建工作目錄:

[root@Docker ~]# mkdir img1
[root@Docker ~]# cd img1/
[root@Docker img1]# echo "<h2>Dockerfile httpd v1</h2>" >> index.html```

進(jìn)入到工作目錄中,創(chuàng)建一個(gè)index.html文件。

創(chuàng)建Dockerfile文件:

[root@Docker img1]# vim Dockerfile
# Description: First image
FROM busybox
LABEL maintainer="Steed Xu <x749b@163.com>"
COPY index.html /var/www/
VOLUME /var/www/
EXPOSE 80

工作目錄創(chuàng)建鏡像:

[root@Docker img1]# pwd
/root/img1
[root@Docker img1]# docker build -t tinyweb1 .
Sending build context to Docker daemon  3.072kB
Step 1/5 : FROM busybox
 ---> db8ee88ad75f
Step 2/5 : LABEL maintainer="Steed Xu <x749b@163.com>"
 ---> Running in 786069882d16
Removing intermediate container 786069882d16
 ---> 890d4ab97cd1
Step 3/5 : COPY index.html /var/www/
 ---> 5ba063c97abb
Step 4/5 : VOLUME /var/www/
 ---> Running in 18351ed13a43
Removing intermediate container 18351ed13a43
 ---> 50c1ae0d6350
Step 5/5 : EXPOSE 80
 ---> Running in 2e7885323425
Removing intermediate container 2e7885323425
 ---> 0656cb8b15bc
Successfully built 0656cb8b15bc
Successfully tagged tinyweb1:latest
[root@Docker img1]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
tinyweb1            latest              0656cb8b15bc        About a minute ago   1.22MB
busybox             latest              db8ee88ad75f        7 days ago           1.22MB
[root@Docker img1]# 

運(yùn)行鏡像:

[root@Docker img1]# docker container run --name app1 --rm -d -P tinyweb1 httpd -f -h /var/www/
[root@Docker img1]# docker container port app1
80/tcp -> 0.0.0.0:32769
[root@Docker img1]# 

這里使用了-P參數(shù),映射的端口是隨機(jī)的,使用命令查看到端口映射的情況。

進(jìn)入到鏡像內(nèi)部。這個(gè)容器內(nèi)執(zhí)行的命令是httpd,里面沒有shell,所以要用exec起一個(gè)shell然后才能進(jìn)入到內(nèi)部進(jìn)行操作:

[root@Docker img1]# docker container exec -it app1 /bin/sh
/ # ps
PID   USER     TIME  COMMAND
    1 root      0:00 httpd -f -h /var/www/
    7 root      0:00 /bin/sh
   11 root      0:00 ps
/ # 

在容器內(nèi)部使用mount命令查看掛載的卷:

[root@Docker ~]# docker container exec app1 mount | grep /var/www
/dev/mapper/centos-root on /var/www type xfs (rw,seclabel,relatime,attr2,inode64,noquota)
[root@Docker ~]#

使用環(huán)境變量

和上面的例子差不多,這次用到了環(huán)境變量,并且指定了CMD的命令:

[root@Docker img2]# pwd
/root/img2
[root@Docker img2]# vi Dockerfile 
# Description: Second image
FROM busybox
LABEL maintainer="Steed Xu <x749b@163.com>" \
      app="httpd"
ENV WEB_DOC_ROOT="/var/www/"
COPY index.html $WEB_DOC_ROOT
RUN echo '<h3>Hello</h3>' >> ${WEB_DOC_ROOT}/index.html
CMD httpd -f -h ${WEB_DOC_ROOT}
VOLUME $WEB_DOC_ROOT
EXPOSE 80

創(chuàng)建鏡像:

[root@Docker img2]# docker build -t tinyweb2 .
Sending build context to Docker daemon  3.072kB
Step 1/8 : FROM busybox
 ---> db8ee88ad75f
Step 2/8 : LABEL maintainer="Steed Xu <x749b@163.com>"       app="httpd"
 ---> Running in e32a21495f34
Removing intermediate container e32a21495f34
 ---> 67f721eaedfa
Step 3/8 : ENV WEB_DOC_ROOT="/var/www/"
 ---> Running in ba2d4b1b2cf1
Removing intermediate container ba2d4b1b2cf1
 ---> 844a0dc0bbcc
Step 4/8 : COPY index.html $WEB_DOC_ROOT
 ---> 1af7b350289f
Step 5/8 : RUN echo '<h3>Hello</h3>' >> ${WEB_DOC_ROOT}/index.html
 ---> Running in f61511fe6020
Removing intermediate container f61511fe6020
 ---> 2305f131626e
Step 6/8 : CMD httpd -f -h ${WEB_DOC_ROOT}
 ---> Running in aff5ee08fb46
Removing intermediate container aff5ee08fb46
 ---> e24a76680fb0
Step 7/8 : VOLUME $WEB_DOC_ROOT
 ---> Running in b59115d147c3
Removing intermediate container b59115d147c3
 ---> a2e176849eaf
Step 8/8 : EXPOSE 80
 ---> Running in c981ab7e0137
Removing intermediate container c981ab7e0137
 ---> b8f7225afb7a
Successfully built b8f7225afb7a
Successfully tagged tinyweb2:latest
[root@Docker img2]# 

運(yùn)行容器:

[root@Docker img2]# docker container run --name app2 --rm -dP tinyweb2
7cbf240f0b323c329deb3dd36fd2ae6f1fe630c394f035f723f6124d5d6c5094
[root@Docker img2]# docker container port app2
80/tcp -> 0.0.0.0:32770
[root@Docker img2]# 

這里沒設(shè)么問題。

使用inspect命令查看容器默認(rèn)啟動(dòng)額命令??梢圆榭寸R像的inspect:

[root@Docker img2]# docker image inspect tinyweb2

也可以查看容器的inspect:

[root@Docker img2]# docker container inspect app2

查看Cmd的內(nèi)容如下:

"Cmd": [
    "/bin/sh",
    "-c",
    "httpd -f -h ${WEB_DOC_ROOT}"
],

這里回過來看下Dockerfile中的CMD指令:

CMD httpd -f -h ${WEB_DOC_ROOT}

這里自動(dòng)加上了/bin/sh -c。因?yàn)樯厦娴腃MD指令使用了第一種格式。如果使用指令的第二種格式,需要這么寫:

CMD ["/bin/sh", "-c", "httpd -f -h ${WEB_DOC_ROOT}"]

或者避免使用環(huán)境變量,就可以不使用/bin/sh -c,這樣是直接通過內(nèi)核啟動(dòng):

CMD ["httpd", "-f", "-h", "/var/www/"]

CMD錯(cuò)誤的指定參數(shù)
CMD的第二種格式,列表的第一個(gè)元素是要執(zhí)行的命令,之后的元素都是這個(gè)命令的參數(shù)。一個(gè)參數(shù)作為一個(gè)元素。這里的httpd命令包括它的參數(shù)全部加一起才是一個(gè)完整的/bin/sh命令的參數(shù)。
下面這條指令是不對(duì)的:

CMD ["/bin/sh", "-c", "httpd", "-f", "-h ${WEB_DOC_ROOT}"]

使用docker ps -a --no-trunc查看COMMAND字段,

"/bin/sh -c httpd -f '-h ${WEB_DOC_ROOT}'"
"/bin/sh -c 'httpd -f -h ${WEB_DOC_ROOT}'"

第一行是這里的錯(cuò)誤格式解析出來的樣子,這里的邏輯是執(zhí)行命令/bin/sh,并且這個(gè)命令有4個(gè)參數(shù)。然而實(shí)際的情況是/bin/sh命令只有2個(gè)參數(shù),一個(gè)是-c,還有一個(gè)是后面所有的內(nèi)容。
第二行才是正確解析出來的樣子,一個(gè)命令2個(gè)參數(shù)。

下面這條指令也是錯(cuò)誤的:

CMD ["httpd", "-f", "-h /var/www/"]

需要把路徑作為一個(gè)單獨(dú)的參數(shù)。

遇到問題,有一下方式檢查。
使用不帶--rm參數(shù)的方式啟動(dòng)容器,這樣保證容器啟動(dòng)失敗后,不會(huì)自動(dòng)刪除。
使用docker logs命令查看容器內(nèi)的日志,主要是默認(rèn)啟動(dòng)命令執(zhí)行后的錯(cuò)誤消息。
使用docker ps -a --no-trunc查看完整的啟動(dòng)命令,這里能看到命令執(zhí)行時(shí)的樣子。
使用docker run命令的-it參數(shù),并且修改默認(rèn)啟動(dòng)命令為/bin/sh,這樣可以進(jìn)入到容器內(nèi)部,執(zhí)行命令。調(diào)試一個(gè)可以被正確執(zhí)行的命令。

動(dòng)態(tài)生成配置文件

這里是實(shí)現(xiàn)的是根據(jù)環(huán)境變量動(dòng)態(tài)生成配置文件。首先用ENTRYPOINT指令運(yùn)行一個(gè)腳本完成配置文件的動(dòng)態(tài)生成,并且最后通過調(diào)用exec "$@"命令,使得CMD指令的內(nèi)容可以被調(diào)用并替換稱為主進(jìn)程。

自定義一個(gè)腳本,完成動(dòng)態(tài)寫入配置文件的功能:

[root@Docker img3]# pwd
/root/img3
[root@Docker img3]# vi entrypoint.sh 
#!/bin/sh
cat > /etc/nginx/conf.d/www.conf << EOF
server {
    server_name ${HOSTNAME};
    listen ${IP:-0.0.0.0}:${PORT:-80};
    root ${NGX_DOC_ROOT};
}
EOF
exec "$@"

首先用cat配合EOF將內(nèi)容寫入到文件。cat在調(diào)用的寫入內(nèi)容的時(shí)候使用到了環(huán)境變量,最終寫入的內(nèi)容是根據(jù)環(huán)境變量替換后的內(nèi)容。
完成腳本寫入后,調(diào)用了exec "$@"。這句命令將后面的參數(shù)作為命令調(diào)用并且替換掉當(dāng)前的進(jìn)程。
還要給這個(gè)腳本賦予可執(zhí)行權(quán)限:

[root@Docker img3]# chmod a+x entrypoint.sh 

上面的腳本是要放在ENTRYPOINT中的,Dockerfile文件內(nèi)容如下:

[root@Docker img3]# vi Dockerfile
# Description: Nginx image
FROM nginx:alpine
LABEL maintainer="Steed Xu <x749b@163.com>" \
      app="nginx"
ADD entrypoint.sh /bin/
CMD ["nginx", "-g", "daemon off;"]
ENTRYPOINT ["/bin/entrypoint.sh"]
EXPOSE 80

創(chuàng)建鏡像:

[root@Docker img3]# docker build -t ngx1 .
Sending build context to Docker daemon  3.072kB
Step 1/7 : FROM nginx:alpine
 ---> 55ceb2abad47
Step 2/7 : LABEL maintainer="Steed Xu <x749b@163.com>"       app="nginx"
 ---> Running in 41050cf8bb89
Removing intermediate container 41050cf8bb89
 ---> 3bfa564b1ac3
Step 3/7 : ENV NGX_DOC_ROOT="/usr/share/nginx/html/"
 ---> Running in b8c929746310
Removing intermediate container b8c929746310
 ---> 2cb3fc5f5c8d
Step 4/7 : ADD entrypoint.sh /bin/
 ---> ecf314c9c29c
Step 5/7 : CMD ["nginx", "-g", "daemon off;"]
 ---> Running in 93d44caaa473
Removing intermediate container 93d44caaa473
 ---> eaf4da71e3c7
Step 6/7 : ENTRYPOINT ["/bin/entrypoint.sh"]
 ---> Running in ecffdcda0486
Removing intermediate container ecffdcda0486
 ---> 71f4222fc33e
Step 7/7 : EXPOSE 80
 ---> Running in 4582fa6067d2
Removing intermediate container 4582fa6067d2
 ---> 24de242b6dfc
Successfully built 24de242b6dfc
Successfully tagged ngx1:latest
[root@Docker img3]# 

看一下配置文件:

[root@Docker img3]# docker container run --name app3 --rm ngx1 cat /etc/nginx/conf.d/www.conf
server {
    server_name 38dd4a661a6f;
    listen 0.0.0.0:80;
    root /usr/share/nginx/html/;
}
[root@Docker img3]# docker container run --name app3 --rm -e HOSTNAME=ngx1 ngx1 cat /etc/nginx/conf.d/www.conf
server {
    server_name ngx1;
    listen 0.0.0.0:80;
    root /usr/share/nginx/html/;
}
[root@Docker img3]# 

配置文件會(huì)根據(jù)環(huán)境變量來動(dòng)態(tài)改變。

大多數(shù)的dockerfile都是通過ENTRYPOINT腳本接受參數(shù)以后,再啟動(dòng)服務(wù)的。也就是這里的做法。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI