1. 程式人生 > >Docker學習——Dockerfile中的構建命令

Docker學習——Dockerfile中的構建命令

目錄

前言

ENV

FROM

RUN

CMD

LABEL

ENV

ADD

COPY

USER

ARG

SHELL

前言

docker的有些文件寫的真的有點糟糕.............

Dockerfile的作用

相當於docker構建映象的說明書,docker會根據Dockerfile中的構建命令一步一步的構建映象,每一步構建指令都會產生一個映象,在這個映象的基礎上在執行下一步構建指令,本部落格不會涉及docker構建映象的細節,只會介紹Dockerfile中的命令,執行dokcer build命令可以構建映象,此時會將工作目錄下的所有檔案傳送給docker守護程序(不管構建過程是不是需要)

Parser directives

Parser directives會影響docker後續處理行的方式,它不會在構建中填加額外的層,關於構建過程中層的概念,請檢視:docker 的image是什麼

使用格式:#directive=value

注意事項:以下使用方式會導致Parser directives無效

  1. 換行
    # direc \
    tive=value
  2. 重複定義
    # directive=value1
    # directive=value2
    
    FROM ImageName
  3. 出現在構建命令之後
    FROM ImageName
    # directive=value
  4. 出現在非parser directive之後
    # About my dockerfile
    # directive=value
    FROM ImageName
  5. 不是合法的parser directive,目前docker支援的Parser directives只有escape,所以我在這裡廢話總結
    # unknowndirective=value
    # knowndirective=value

escape

定義轉義符號,目前支援兩種形式

# escape=\ (backslash)
# escape=` (backtick)

如果在dockerfile中未指定,則預設為\,這會導致一些麻煩,因為\是windows的檔案路徑分割符,如果我們如下使用:

FROM microsoft/nanoserver
COPY testfile.txt c:\\
RUN dir c:\

二、三行命令會被翻譯成:

COPY testfile.txt c:\RUN dir c:

因此在windows上最好使用  ` 號

ENV

dockerfile中可以使用環境變數:$variable_name或是${variable_name},這兩種格式被同等對待,但是括號可以處理空格的狀態,

${variable_name}具有更多的用法:

  1. ${variable:-word}:如果variable值沒有被指定,那麼將使用word作為值
  2. ${variable:+word}:如果variable值被指定,那variable最終的值將是word,如果沒有被指定,則預設為空字串

環境變數也會被轉義字元轉義:

COPY \$foo /quux 等效於 COPY $foo /quux

環境變數的值可以通過ENV指令指定,ENV定義的值只有在ENV命令外才會生效,例如:

ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abc

def的值將是hello(由於abc=hello在第一條ENV命令中,位於第二條ENV命令的def外),ghi的值將是bye

.dockerignore檔案

前面說過docker build指令會將工作目錄的所有檔案傳送給docker守護程序,在此之前,docker會檢查工作目錄下是否存在.dockerignore檔案,通過.dockerignore檔案,我們可以指定哪些檔案不用傳送給docker守護程序,.dockerignore檔案以一行為單位,表示哪些檔案不需要新增到docker守護程序,.dockerignore中的檔案路徑都是相對於工作目錄而言,如果我們在一行的前面標記#,則改行被當作註釋:

# comment
*/temp*
*/*/temp*
temp?

上述檔案每行意義如下:

.dockerignore也支援萬用字元,**表示匹配任意數目的目錄,!表示排除在外的意思,例如:

*.md
!README.md

除了README.md以外的所有.md檔案都會被排除在外,.dorckerignore檔案的最後一行具有最高的優先順序,它會覆蓋之前的匹配,例如:

    *.md
    !README*.md
    README-secret.md

即使README-secret.md匹配!README*.md,由於README-secret.md在最後一行,因此仍然會將README—secret.md移除,再如:

   *.md
    README-secret.md
    !README*.md

README-secret.md不會被移除,因為!README*.md在最後一行

docker一定會見工作目錄的Dockerfile檔案發完docker守護程序,即使在.dockeringnore中宣告Dockerfile

FROM

FROM指令具有三種格式:

FROM <image> [AS <name>]
FROM <image>[:<tag>] [AS <name>]
FROM <image>[@<digest>] [AS <name>]

要點:

  1. FROM為接下來的構建指令指定基礎映象,一個有效的Dockerfile檔案必須以FROM指令開頭
  2. 除arg指令以外的構建指令都不能出現在FROM之前,Dockerfile檔案可以出現多個FROM指令,此時一個Dockerfile可以構建多個image,並且輸出這些image的ID,在執行FROM指令時,之前構建命令生成的狀態會被清空,若arg指令位於FROM之外,FROM內部的指令不能引用arg,如果想使用,必須宣告一遍:
    ARG VERSION=latest
    FROM busybox:$VERSION
    #宣告過後,才能替換RUN指令為的$VERSION為latest
    ARG VERSION
    RUN echo $VERSION > image_version
  3. FROM指令允許新增映象的別名(指令中的[AS <name>]),在接下來的FROM指令或是COPY --from=<name|index>指令中可以引用這個映象
  4. tag與digest指映象的版本資訊,如果不指定,則預設為latest

RUN

RUN指令有兩種形式:

#shell形式,命令會執行在shell指令碼上,在linux上預設為/bin/sh -c,在windwos上預設為cmd /S /C
RUN <command>

#exec形式
RUN["executable","param1","param2"]

要點:

  • RUN指令會在當前映象的基礎上執行命令
  • 如果當前映象不支援預設的shell,可以使用第二種形式,exec形式會解析成JSON陣列,所以不能使用單引號
RUN ["/bin/bash", "-c", "echo hello"]
  • 可以通過SHELL指令更改預設的shell環境
  • exec形式不會預設呼叫shell,類似於RUN [ "echo", "$HOME" ]的指令(需要環境變數解析)將不會得到解析,環境變數解析的工作由shell進行,不是docker
  • RUN命令的執行結果會進行快取,以便加快二次構建的速度,可以使用docker build --no-cache,禁止使用快取

CMD

CMD指令具有三種形式

#exec形式,推薦使用
CMD ["executable","param1","param2"]

#作為ENTRYPOINT指令的引數
CMD ["param1","param2"]

#shell形式
CMD command param1 param2

要點:

  • dockerfile中只有一條CMD指令會生效,如果有多條,只有最後一條CMD指令生效
  • CMD指令主要為ENTRYPOINT提供預設值,此時CMD指令與ENTRYPOINT指令都必須使用exec格式
  • exec指令會解析成JSON格式,因此必須使用雙引號
  • exec形式不會呼叫shell,這意味著CMD [ "echo", "$HOME" ]不會進行環境變數解析,如果想要使用,更改為CMD [ "sh", "-c", "echo $HOME" ],環境變數的解析均有shell負責,而不是docker
  • 當映象執行時才會執行CMD指令,而RUN是在構建階段執行
  • 如果使用shell形式,命令會執行在/bin/sh -c上
  • 如果不想執行在shell上,就使用exec形式,但是要指明可執行檔案的位置
  • 如果在docker run命令上添加了引數,會覆蓋CMD中對應的預設值

LABEL

LABEL <key>=<value> <key>=<value> <key>=<value> ...

要點:

  • LABEL命令增加元資料到image中(描述作用)
  • LABEL命令使用key-value模式
  • 子image會繼承父image的LABEL,同時可以覆蓋其中的值
  • 可以使用docker inspect命令檢視Labels

MAINTAINER

MAINTAINER <name>

要點:

  • 指定image的創作者

EXPOSE

EXPOSE <port> [<port>/<protocol>...]

要點:

  • EXPOSE讓container在執行時監聽特定的埠,可以指定埠執行tcp或是udp命令,預設情況下為tcp
EXPOSE 80/udp
  • EXPOSE命令不會真正釋出埠,只是image構建者告訴container執行者應該如何對映埠
  • docker run指令的-p引數可以指定埠對映,以及對應的協議,此時才會釋出埠
docker run -p 80:80/tcp -p 80:80/udp ...

ENV

#一行只能設定一個全域性變數,雙引號會被去除
ENV <key> <value>
#一行可以設定多個全域性變數
ENV <key>=<value> ...

要點:

  • ENV指令設定的key-value相當於全域性變數
  • ENV指令設定的全域性變數會持久化,可以使用docker inspect檢視,可以使用docker run --env <key>=<value>更改其中的值

ADD

ADD [--chown=<user>:<group>] <src>... <dest>
#當存在空格時,只能使用這種形式
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]

要點:

  • [--chown=<user>:<group>]只在linux上有效,用於指定使用者以及使用者組
  • ADD指令將檔案、資料夾、URL處的檔案(夾)拷貝到<dest>指示的位置
  • 如果<src>不是URL,則檔案與資料夾的位置都是是相對於工作目錄的,即相對目錄
  • <src>可以使用萬用字元
    ADD hom* /mydir/        # adds all files starting with "hom"
    ADD hom?.txt /mydir/    # ? is replaced with any single character, e.g., "home.txt"
  • .<dest>只能是絕對路徑或相對於WORDIR(稍後會介紹的指令)
    ADD test relativeDir/          # adds "test" to `WORKDIR`/relativeDir/
    ADD test /absoluteDir/         # adds "test" to /absoluteDir/
  • 除非指定了--chown,否則所有新建立的檔案以及目錄的UID與GID均為0,--chown允許UID以及GID為數字或是字串,也可以是兩者的組合,/etc/passwd和/etc/group檔案會被用來將字串轉換為對應的數字形式的UID以及GID,如果只指定了UID,GID預設取UID的值
  • 如果URL需要驗證登陸,ADD指令需要與RUN wget、RUN curl指令搭配使用(提供賬號密碼)
  • 如果使用STDIN給出Dockerfile,可能會沒有上下文,此時ADD指令只能通過URL獲取資源
    #沒有上下文
    docker build -< Dockerfile
    
    #解壓後的檔案作文上下文
    docker build - < archive.tar.gz
  • 如果<src>發生了改變,包括ADD在內的接下來的所有構建指令的快取都會無效化
  • 如果<src>是URL,而<dest>是資料夾,則檔名有URL中自動推算得知,例如ADD http://example.com/foobar /,在/下對應的檔名為foobar
  • 如果<src>是資料夾,則資料夾的所有內容都會被拷貝到<dest>
  • 如果<src>是壓縮檔案,則ADD會將其解壓,URL對應的壓縮檔案不會被解壓,一個檔案會不會被解壓不是由檔案字尾決定,而是檔案內容
  • 將多個檔案或是資料夾ADD到<dest>,<dest>必須是一個資料夾,並且以/結尾,否則會被認為是一個檔案
  • 如果<dest>不存在,則ADD指令會建立對應的檔案路徑

COPY

copy有兩種形式:

COPY [--chown=<user>:<group>] <src>... <dest>
#如果路徑包括空格,則使用這種格式
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]

要點:

  • copy將檔案從<src>拷貝到<dest>,與ADD不同,它不支援從URL獲取檔案或資料夾,其餘使用與ADD基本一致
  • copy指令可以使用--from=<name|index>引用之前構建階段的映象(FROM ..AS<name>建立的映象),如果在之前的構建階段找不到對應的映象,則會嘗試使用相同名字的映象(從遠端下載),也可以指定index,從而引用某個映象(name指定)某個構建階段(index指定)對應的映象

ENTRYPOINT

ENTRYPOINT具有兩種寫法:

#exec形式
ENTRYPOINT ["executable", "param1", "param2"]
#shell形式
ENTRYPOINT command param1 param2

要點:

  • 執行docker run指令後會執行的指令
  • docker run指令後面的引數或是命令會附加到ENTRYPOINT上,會覆蓋cmd對應的值,例如cmd指令指定了-name的值,如果使用docker run -name....,則容器最終的name為docker run指定的值
  • 通過docker run --entrypoint會替換ENTRYPOINT的值
  • shell形式不接受cmd指定的引數,shell形式有一個缺點,就是啟動的可執行程式不會接收訊號,如果我們執行docker stop指令,可執行程式將不會接收到停止訊號
  • 如果定義了多條entrypoint指令,只有最後一條可以生效
  • cmd指令與ENTRYPOINT指令的關係
  1. 兩者均制定了容器執行時執行的命令
  2. Dockerfile應該至少指定CMD或是ENTRYPOINT中的一個
  3. CMD可以為ENTRYPOINT指定引數值
  4. CMD指定的值可以被命令中對應的引數值替換,兩者的協作效果如下

VOLUME

命令格式:

#定義匿名資料卷
VOLUME ["/data"]

要點:

  • VOLUME指令用於建立一個掛載點,掛載點用於儲存持久化資料,一般不建議將資料儲存在Docker容器的可寫層(原因我還沒看),掛載點可以理解為就是一個用於儲存資料的檔案,只是這個檔案由docker統一管理,當然,也可以將本地檔案對映成掛載點,此時資料將寫入到本地檔案中
  • volume分為兩種,一種是匿名卷,一種是實名卷,匿名資料卷就是未對映到本文將的卷,一般儲存在/var/lib/docker/volumes中,當容器被刪除時,對應的卷也會被刪除,實名卷對映到了本地檔案,當容器被刪除時也不會被刪除
  • 實名卷只可以通過docker run -v指定,例如docker run -v /var/temp:/app
  • docker run指令會保留作為卷的檔案之前的資料,例如:
    FROM ubuntu
    RUN mkdir /myvol
    RUN echo "hello world" > /myvol/greeting
    VOLUME /myvol
    
    /myvol中將存在greeting檔案,並且該檔案保留hello world的字元
  • 在windows上使用卷時,掛載檔案必須儲存在空資料夾或是一開始不存在的資料夾下,並且資料夾不能儲存在C盤
  • 使用實名卷需要注意,實名卷可能會影響容器的移植性,因為實名卷對應的檔案結構受作業系統的影響,因此,我們不能在Dockerfile中指定使用實名卷(影響移植性),只能通過docker run -v指令

USER

格式:

USER <user>[:<group>] or
USER <UID>[:<GID>]

要點:

  • 用於指定執行image、RUN、CMD、ENTRYPOINT的user和group,只有對應的user以及group可以執行image
  • 如果user沒有對應的group,則預設使用root

WORKDIR

命令格式:

WORKDIR /path/to/workdir

要點:

  • RUN、CMD、ENTRYPOINT、COPY、ADD指令可以使用,當這些指令未指出工作目錄時,則預設使用WORKDIR指定的目錄作為工作目錄
  • 如果WORKDIR指定的目錄不存在,則會預設建立
  • WORKDIR指令可以使用多次,如果使用相對路徑,會承接到之前的WORKDIR指定的路徑中,例如:
    WORKDIR /a
    WORKDIR b
    WORKDIR c
    RUN pwd
    
    RUN指令指定的檔案為 /a/b/c/pwd
  • WORKDIR指令可以使用ENV指定的環境變數,例如:
    ENV DIRPATH /path
    WORKDIR $DIRPATH/b
    RUN pwd
    
    RUN指令指定的檔案為/path/b/pwd

ARG

命令格式:

ARG <name>[=<default value>]

要點:

  • ARG定義引數的值必須通過docker build --build-arg <varname>=<value> 指定
  • dockerfile檔案可以使用多個ARG引數
    FROM busybox
    ARG user1
    ARG buildno
    ...
  • 一般不建議將賬號密碼等敏感資訊作為ARG引數的值,因為可以通過docker history命令檢視到
  • ARG指令可以指定引數的預設值
  • ARG指令只對位於自己下方的指令有效,映象構建完畢後就會失效,如果想在多個映象的構建過程中使用,則在多個映象的構建過程中指定相同的ARG:
    FROM busybox
    ARG SETTINGS
    RUN ./run/setup $SETTINGS
    
    FROM busybox
    ARG SETTINGS
    RUN ./run/other $SETTINGS
  • ENV指令會覆蓋ARG指令中相應的引數,例如:
    1 FROM ubuntu
    2 ARG CONT_IMG_VER
    3 ENV CONT_IMG_VER v1.0.0
    4 RUN echo $CONT_IMG_VER
    
    執行指令docker build --build-arg CONT_IMG_VER=v2.0.1 .
    
    CONT_IMG_VER的值將是v1.0.0
  • 有些ARG引數是預定義的:
    HTTP_PROXY
    http_proxy
    HTTPS_PROXY
    https_proxy
    FTP_PROXY
    ftp_proxy
    NO_PROXY
    no_proxy
    
    可以直接使用,這些引數的值不會被docker history輸出,我們也可以自己覆蓋這些引數
  • 如果指定的ARG指令與之前構建階段的不同,如果在其他指令中使用過ARG,則會出現快取缺失,但如果它的值杯ENV指令覆蓋,則不會出現:
    1 FROM ubuntu
    2 ARG CONT_IMG_VER
    3 ENV CONT_IMG_VER hello
    4 RUN echo $CONT_IMG_VER
    
    因為ARG指令的CONT_IMG_VER杯ENV覆蓋了,所以不會出現快取確實

ONBUILD

ONBUILD [INSTRUCTION]

要點:

  • 指定執行映象後緊接著執行的命令
  • 這個指令是如何起作用的呢?在構建映象時,builder會將ONBUILD指令的值新增到映象的OnBuild中,可以通過執行docker inspect檢視,當其他映象使用這個映象時,builder會檢視OnBuild區域,並且按順序執行它們,OnBuild區域的值不會被繼承

STOPSIGNAL

STOPSIGNAL signal

要點:

  • 使用這個指令允許使用者自定義應用在收到 docker stop 時所傳送的訊號

HEALTHCHECK

#通過執行容器中的指令來判斷容器是否健康,CMD可以是shell或是exec形式
HEALTHCHECK [OPTIONS] CMD command

#禁止所有父映象的健康檢查
HEALTHCHECK NONE

要點:

  • 這條指令用於檢查容器的健康狀況
  • 如果容器指定了健康檢查,會新增額外的health欄位,這個欄位一開始是starting,當健康檢查通過以後,會更改為healthy,如果幾次嘗試都失敗,則會設定為unhealthy
  • OPTIONS欄位
    #兩次健康檢查的間隔
    --interval=DURATION(預設為30s)
    
    健康檢查命令執行超時時間,如果超過這個時間,本次健康檢查視為失敗
    --timeout=DURATION(預設為30s)
    
    #應用啟動的初始化時間,在啟動過程中的健康檢查失效不會計入
    --start--period=DURATION(預設為0s)
    
    #當連續失敗指定次數後,則將容器狀態視為unhealthy
    --retries=N(預設為3次)
    
    
  • 如果有多條HEALTHCHECK,只有最後一條才會起作用

  • 這條命令執行完畢後,會輸出一個數字:

    #成功
    0:success
    #失敗
    1:unhealthy
    #保留欄位,沒有意義
    2:reserved

SHELL

SHELL ["executable", "parameters"]

要點:

  • 用於指定shell形式的命令執行的shell環境,linux預設的shell環境為["/bin/sh","-c"],windwos預設為["cmd","/S","/C"]