Docker學習——Dockerfile中的構建命令
目錄
前言
docker的有些文件寫的真的有點糟糕.............
Dockerfile的作用
相當於docker構建映象的說明書,docker會根據Dockerfile中的構建命令一步一步的構建映象,每一步構建指令都會產生一個映象,在這個映象的基礎上在執行下一步構建指令,本部落格不會涉及docker構建映象的細節,只會介紹Dockerfile中的命令,執行dokcer build命令可以構建映象,此時會將工作目錄下的所有檔案傳送給docker守護程序(不管構建過程是不是需要)
Parser directives
Parser directives會影響docker後續處理行的方式,它不會在構建中填加額外的層,關於構建過程中層的概念,請檢視:docker 的image是什麼
使用格式:#directive=value
注意事項:以下使用方式會導致Parser directives無效
- 換行
# direc \ tive=value
- 重複定義
# directive=value1 # directive=value2 FROM ImageName
- 出現在構建命令之後
FROM ImageName # directive=value
- 出現在非parser directive之後
# About my dockerfile # directive=value FROM ImageName
- 不是合法的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}具有更多的用法:
- ${variable:-word}:如果variable值沒有被指定,那麼將使用word作為值
- ${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>]
要點:
- FROM為接下來的構建指令指定基礎映象,一個有效的Dockerfile檔案必須以FROM指令開頭
- 除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
- FROM指令允許新增映象的別名(指令中的[AS <name>]),在接下來的FROM指令或是COPY --from=<name|index>指令中可以引用這個映象
- 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指令的關係
- 兩者均制定了容器執行時執行的命令
- Dockerfile應該至少指定CMD或是ENTRYPOINT中的一個
- CMD可以為ENTRYPOINT指定引數值
- 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"]