1. 程式人生 > 實用技巧 >10.Docker File製作映象

10.Docker File製作映象

Docker File構建映象

關於DockerFile

在Docker中建立映象最常用的方式,就是使用Dockerfile。Dockerfile是一個Docker映象的描述檔案,我們可以理解成火箭發射的A、B、C、D…的步驟。Dockerfile其內部包含了一條條的指令,每一條指令構建一層,因此每一條指令的內容,就是描述該層應當如何構建。

Dockerfile結構大致分為四個部分:

  1. 基礎映象資訊
  2. 維護者資訊
  3. 映象操作指令
  4. 容器啟動時執行指令

Dockerfile每行支援一條指令,每條指令可帶多個引數,支援使用以#號開頭的註釋。下面會對上面使用到的一些常用指令做一些介紹。

Dockerfile常用指令

指令 物件 含義
FROM 映象 指定新映象所基於的映象,必須為第一條指令
MAINTAINER 名字 新映象的維護人資訊
RUN 命令 在所基於的映象上執行命令,並提交到新映象中
EXPOSE 埠號 指定新映象載入到Docker時開啟的埠號
ENV 環境變數 變數值 設定一個環境變數的值,會被後面的RUN使用
ADD 原始檔/目錄 目標檔案/目錄 將原始檔複製到目標檔案,原始檔要與Docker位於同一目錄下,或者為一個URL
COPY 原始檔/目錄 目標檔案/目錄 將本地主機上的原始檔/目錄複製到目標地點,原始檔/目錄要與Dockerfile在同一目錄下
VOLUME ["目錄"] 在容器中建立一個掛載點
USER 使用者名稱 /UID 指定執行容器時的使用者
WORKDIR 路徑 為後續的RUN、CMD、ENTRYPOINT指定工作目錄
ONBUILD 命令 指定所生成的映象作為一個基礎映象時所要執行的命令
CMD ["要執行的程式","引數1","引數2"] 指定啟動容器時執行的命令或指令碼,只能有一條CMD命令,多條時只有最後一條被執行

來一張通俗易懂的全景圖:

FROM

指明構建的新映象是來自於哪個基礎映象

FROM <repository>[:<tag>]
#較為安全
FROM <repository>@<digest>
#<repository>:指定作為base image的名稱
#<tag>:base image的標籤,為可選性,省略時預設為latest
#<digest>:雜湊碼

例如:

FROM centos:7

MAINTAINER(depreacted)

指明映象維護著及其聯絡方式(一般是郵箱地址),已被LABEL所替代

MAINTAINER<author's detail>

例如:

MAINTAINER "MiaosenG <[email protected]>"

不過,MAINTAINER並不推薦使用,更推薦使用LABEL來指定映象作者,例如:

LABEL maintainer="MiaosenG <[email protected]>"

RUN

#第一種格式<command>通常是一個shell命令,且以"/bin/sh -c"執行
#此程序在容器的PID不為1,當使用docker stop <container>命令停止容器時,此程序接收不到SIGTERM訊號
RUN <command>
#第二種格式中的引數是一個JSON格式陣列,其中<executable>為要執行命令,<paramN>為傳遞命令選項或引數
RUN ["<executable>","<param1>","<param2>"]
#此格式不會以"/bin/sh -c"執行,無法使用變數替換或萬用字元等shell操作符,可寫成
RUN ["/bin/sh" "-c","<exrcutable>","<param1>"]

構建映象時執行的Shell命令,針對於基礎映象,僅可使用基礎映象環境,和命令;

例如:

RUN ["yum", "install", "httpd"]
RUN yum install httpd

jason陣列中,要是用雙引號

CMD

#意義等同RUN
CMD <command>
CMD ["<executable>","<param1>","<param2>"]
#第三種用於ENTRYPOINT指令提供預設引數
CMD ["<param1>","<param2>"]

啟動容器時執行的Shell命令,針對映象預設執行命令,多個CMD僅最後一個生效

例如:

CMD ["-C", "/start.sh"] 
CMD ["/usr/sbin/sshd", "-D"] 
CMD /usr/sbin/sshd -D

EXPOSE

EXPOSE <port>[/<protocol>][ <port>[/<protocol>]...]
#<protocol>用於指定傳輸層協議,可為tcp或udp,預設為TCP

宣告容器執行的(待暴露)預設服務埠,但執行容器時不會暴露該埠;需要配合docker run -P命令,會暴露DockerFile內寫定的全部埠

例如:

EXPOSE 11211/udp 11211/tcp

ENV

ENV <key> <value>
ENV <key>=<value>...

設定環境內環境變數,可被檔案中位於其後的其他指令所呼叫

例如:

#單一變數賦值
ENV MYSQL_ROOT_PASSWORD 123456
#多變數賦值
ENV JAVA_HOME=/usr/local/jdk1.8.0_45

#使用
ENV DOC_ROOT=/data/web/html/ \
    WEB_SERVER_PACKAGE="nginx-1.15.2"
COPY index.html ${DOC_ROOT:-/data/web/html}	#設定預設值
ADD ${WEB_SERVER_PACKAGE}.tar.gz /usr/local/src/

第一種格式,<key>之後所有內容均被視為<value>

第二種格式,<value>中包含空格,可以利用\進行轉義或換行,也可以加""進行表示

可配合docker run -e設定變數值使用,在初始化執行容器時改變預設設定變數

ADD

拷貝檔案或目錄到映象中,例如:

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

支援URL路徑,用法同COPY

#展開檔案
ADD nginx-1.15.2.tar.gz /usr/local/src/
#僅下載不解壓
ADD http://nginx.org/download/nginx-1.15.2.tar.gz /usr/local/src/

如果<src>為URL

  • <dest>不以/結尾,則<src>指定的檔案將被下載並直接被建立為<dest>;
  • <dest>以/結尾,則檔名URL指定的檔案將被直接下載並儲存為<dest>/<filename>

如果<src>是一個

  • 本地系統上的壓縮格式的tar檔案;他將會被展開為一個目錄,類似於tar -x
  • URL獲取的tar檔案;將不會被展開

如果<src>為多個,或期間使用了萬用字元,則<dest>必須是一個以/的目錄路徑;

如果不以/結尾,則其被視作一個普通檔案,<src>的內容將被直接寫入到<dest>

COPY

拷貝主機檔案或目錄到映象中

COPY <src>...<dest>
COPY ["<src>"..."<dest>"]
#<src>:需要複製的原始檔,支援萬用字元
#<dest>:目標路徑;建議使用絕對路徑;否則,COPY指定則以WORKDIR為其其實路徑

用法同ADD,只是不支援自動下載和解壓,例如:

COPY index.html /data/web/html
COPY yum.repos.d /etc/yum.repos.d

<src>必須是build上下文中的路徑,不能是其父目錄中的檔案

如果<src>是目錄,則其內部檔案或子目錄會被遞迴複製,但<src>目錄自身不會被複制

如果指定了多個<src>,或在<src>中使用了萬用字元,則<dest>必須是一個目錄,且必須以/結尾

如果<dest>實現不存在,他將會被自動建立,這包括其父目錄路徑

ENTRYPOINT

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

啟動容器時執行的Shell命令,同CMD類似,只是由ENTRYPOINT啟動的程式不會被docker run命令列指定的引數所覆蓋,而且,這些命令列引數會被當作引數傳遞給ENTRYPOINT指定指定的程式,例如:

ENTRYPOINT ["/bin/bash", "-C", "/start.sh"]
ENTRYPOINT /bin/bash -C '/start.sh'

PS:Dockerfile檔案中也可以存在多個ENTRYPOINT指令,但僅有最後一個會生效。

除非使用docker run --entrypoint string才可強制覆蓋dockerfile內有ENTRYPOINT命令

當CMD與ENTRYPOINT同時出現在Dockerfile中;CMD使用第三種寫法,為ENTRYPOINT指令提供預設引數

CMD ["/bin/httpd","-f","-h","/data/web/html"]
ENTRYPOINT ["/bin/sh","-c"]

但當使用docker run對容器內部傳參或輸入指令,會對CMD進行覆蓋,而不會覆蓋ENTRYPOINT

VOLUME

VOLUME <mountpoint>
VOLUME ["<mountpoint>"]

指定容器掛載點到宿主機自動生成的目錄或其他容器

例如:

VOLUME ["/data/mysql/"]

PS:一般不會在Dockerfile中用到,更常見的還是在docker run的時候指定-v資料卷。

USER

USER <UID>|<UserName>

為RUN、CMD和ENTRYPOINT執行Shell命令指定執行使用者,預設容器中使用root使用者

例如:

USER <user>[:<usergroup>]
USER <UID>[:<UID>]
USER miaoseng

UID可以為任意數字,但實踐中其必須為/etc/passwd(容器)中某使用者的有效UID,否則docker run命令將失敗

WORKDIR

WORKDIR <dirpath>

為RUN、CMD、ENTRYPOINT以及COPY和AND設定工作目錄,理解為cd命令

例如:

WORKDIR /usr/local/src/
ADD nginx-1.15.2.tar.gz ./
#等效於
ADD nginx-1.15.2.tar.gz /usr/local/src/

HEALTHCHECK

HEALTHCHECK [options] CMD command

告訴Docker如何測試容器以檢查它是否仍在工作,即健康檢查,例如:

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

其中,一些選項的說明:

  • --interval=DURATION (default: 30s):每隔多長時間探測一次,預設30秒
  • -- timeout= DURATION (default: 30s):服務響應超時時長,預設30秒
  • --start-period= DURATION (default: 0s):服務啟動多久後開始探測,預設0秒
  • --retries=N (default: 3):認為檢測失敗幾次為宕機,預設3次

  一些返回值的說明:

  • 0:容器成功是健康的,隨時可以使用
  • 1:不健康的容器無法正常工作
  • 2:保留不使用此退出程式碼

SHELL

修改shell型別

#Linux
SHELL ["/bin/sh","-c"]
#Windows
SHELL ["cmd","/S","/C"]

STOPSIGNAL

定義停止訊號

STOPSIGNAL signal

ARG

AGE <name>[=<default value>]

在構建映象時,指定一些引數,例如:

FROM centos:7
ARG author="MiaosenG <[email protected]>"
LABEL maintainer="${author}"

這時,我們在docker build時可以帶上自定義引數user了,如下所示:

docker build --build-arg author=Tony Dockerfile .

ONBUILD

ONBUILD <INSTRUCTION>

用於在Dockerfile中定義一個觸發器,延遲觸發;當其他使用者,基於含有ONBUILD映象構建新映象,再次構建並執行build時執行命令;留下後門

ONBUILD不可自我巢狀,且不會觸發FROM和MAINTAINER指令;通常使用RUN和ADD,使用ADD,COPY時可能缺少指定檔案;無法拷貝

CMD與RUN區別

指令 CMD RUN
區別 基於Dockerfile構建出新映像檔案啟動一個容器時 運行於映像檔案構建過程中
可被docker run命令選項進行覆蓋,針對映象預設執行命令 使用基礎映象環境,和命令
可存在多個CMD僅最後一個生效 可執行多個

例如:

Dockerfile1

vim Dockerfile
FROM busybox
LABEL maintainer="MiaosenG <[email protected]>" app="http"

ENV WEB_DOC_ROOT="/data/web/html/"

RUN mkdir -p $ENV WEB_DOC_ROOT && echo '<h1>hello busybox httpd server</h1>' > $ENV WEB_DOC_ROOT/indec.html

CMD /bin/httpd -f -h ${WEB_DOC_ROOT}

執行後檢視容器內PID=1程序為/bin/httpd -f -h /data/web/html/

Dockerfile2

vim Dockerfile
FROM busybox
LABEL maintainer="MiaosenG <[email protected]>" app="http"

ENV WEB_DOC_ROOT="/data/web/html/"

RUN mkdir -p $ENV WEB_DOC_ROOT && echo '<h1>hello busybox httpd server</h1>' > $ENV WEB_DOC_ROOT/indec.html

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

執行後報錯httpd:can't change directory to '${WEB_DOC_ROOT}': No such file or directory