10.Docker File製作映象
Docker File構建映象
關於DockerFile
在Docker中建立映象最常用的方式,就是使用Dockerfile。Dockerfile是一個Docker映象的描述檔案,我們可以理解成火箭發射的A、B、C、D…的步驟。Dockerfile其內部包含了一條條的指令,每一條指令構建一層,因此每一條指令的內容,就是描述該層應當如何構建。
Dockerfile結構大致分為四個部分:
- 基礎映象資訊
- 維護者資訊
- 映象操作指令
- 容器啟動時執行指令
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