博文大綱:python
- dockerfile基本結構
- dockerfile經常使用指令
- FROM——指定基礎鏡像
- MAINTAINER——指定維護者信息
- RUN——運行指令
- COPY——複製文件\目錄
- ADD——更高級的複製文件\目錄
- ENV——設置環境變量
- ARG——構建參數
- EXPOSE——暴露端口
- CMD——容器啓動命令
- ENTRYPOINT——入口點
- ENTRYPOINT和CMD組合使用
- VOLUME——定義匿名卷
- USER——指定當前用戶
- WORKDIR——指定工做目錄
- ONBUILD——爲他人作嫁衣
Dockerfile由一行行命令語句組成,而且支持以#開頭的註釋行。nginx
通常Dockerfile分爲四部分:基礎鏡像信息、維護者信息、鏡像操做指令和容器啓動時執行指令。以下:git
# This dockerfile uses the ubuntu image # VERSION 2 - EDITION 1 # Author: Ray # Command format: Instruction [arguments / command] .. # Maintainer: docker_user <docker_user at email.com> (@docker_user) MAINTAINER Ray 916551518@qq.com # Commands to update the image RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list RUN apt-get update && apt-get install -y nginx RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf # Commands when creating a new container CMD /usr/sbin/nginx
其中,一開始必須指明所基於的鏡像,接下來推薦說明維護者信息,再接下來就是鏡像操做的指令,如RUN、COPY等。每運行一條指令,就會爲鏡像添加新的一層並提交,注:一個鏡像最多不容許超過127層。最後時CMD指令,是指定運行容器時的操做指令。github
格式爲:FROM <image>或者 FROM <image>:<tag>。web
第一條指令必須爲FROM指令,若是在同一個dockerfile中建立多個鏡像時,可使用多個FROM指令(每一個鏡像一次,可是通常不會這麼作)。redis
格式爲:MAINTAINER <name> <email>。用來指定維護者信息。docker
格式爲:RUN <command> 或者RUN ["executable", "param1", "param2"]。shell
前者將在 shell 終端中運行命令,即 /bin/sh -c;後者則使用 exec 執行。指定使用其它終端能夠經過第二種方式實現,例如 RUN ["/bin/bash", "-c", "echo hello"]。數據庫
每條 RUN 指令將在當前鏡像基礎上執行指定命令,並提交爲新的鏡像。當命令較長時可使用 \ 來換行。(注:若是以爲鏡像的層數可能過多,能夠一個RUN指令後面接多條指令,中間使用&&進行拼接便可)。ubuntu
格式爲:格式爲 COPY <src> <dest>。
做用:複製本地的<src> (源文件/目錄必需要與Dockerfile在相同的目錄中)到容器中的<dest>。
當使用本地目錄爲源目錄時,推薦使用COPY。
使用COPY時,所指定的源文件/目錄,也能夠是其餘鏡像中的文件,格式以下:
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
格式爲:ADD <src> <dest>。它和COPY很類似,一樣須要源文件和Dockerfile位於相同目錄中,或者是一個URL。它比COPY更爲人性化些。
該命令將複製指定的 <src> 到容器中的 <dest>。 其中 <src> 能夠是Dockerfile所在目錄的一個相對路徑;也能夠是一個 URL(自動下載URL所對應的文件);還能夠是一個 tar 文件(自動解壓爲目錄)。
在使用該指令的時候還能夠加上 --chown=<user>:<group> 選項來改變文件的所屬用戶及所屬組。
ADD --chown=55:mygroup files* /mydir/ ADD --chown=bin files* /mydir/ ADD --chown=1 files* /mydir/
ADD 指令會令鏡像構建緩存失效,從而可能會令鏡像構建變得比較緩慢。
但在某些狀況下,若是咱們真的是但願複製個壓縮文件進去,而不解壓縮,這時就不可使用 ADD 命令了。
所以在 COPY 和 ADD 指令中選擇的時候,能夠遵循這樣的原則,全部的文件複製均使用 COPY 指令,僅在須要自動解壓縮的場合使用 ADD。
格式爲 ENV <key> <value>。 指定一個環境變量,會被後續 RUN 指令使用,並在容器運行時保持。
舉個栗子:
[root@master nginx]# cat Dockerfile # test FROM nginx:latest MAINTAINER Ray <916551517@qq.com> ENV var1 hello world ENV var2 test RUN echo ${var1},${var2} > /test.txt #最終此鏡像運行的容器中test.txt文件內容以下: root@262f47a7682a:/# cat test.txt hello world,test #而且定義的變量存在該容器的環境變量中: root@262f47a7682a:/# echo $var1 hello world root@262f47a7682a:/# echo $var2 test
格式:ARG <參數名>[=<默認值>]
構建參數和 ENV 的效果同樣,都是設置環境變量。所不一樣的是,ARG 所設置的構建環境的環境變量,在未來容器運行時是不會存在這些環境變量的。可是不要所以就使用 ARG 保存密碼之類的信息,由於 docker history 仍是能夠看到全部值的。
Dockerfile 中的 ARG 指令是定義參數名稱,以及定義其默認值。該默認值能夠在構建命令 docker build 中用 --build-arg <參數名>=<值> 來覆蓋。
在 1.13 以前的版本,要求 --build-arg 中的參數名,必須在 Dockerfile 中用 ARG 定義過了,換句話說,就是 --build-arg 指定的參數,必須在 Dockerfile 中使用了。若是對應參數沒有被使用,則會報錯退出構建。從 1.13 開始,這種嚴格的限制被放開,再也不報錯退出,而是顯示警告信息,並繼續構建。這對於使用 CI 系統,用一樣的構建流程構建不一樣的 Dockerfile 的時候比較有幫助,避免構建命令必須根據每一個 Dockerfile 的內容修改。
格式爲:EXPOSE <port> [<port>...]。
該指令的做用是告訴docker服務端容器暴露的端口號,供互聯繫統使用,在啓動容器時須要經過-P,docker主機會自動分配一個端口轉發到指定的端口。
它支持如下三種格式:
- CMD ["executable","param1","param2"] 使用 exec 執行,推薦方式;
- CMD command param1 param2 在 /bin/sh 中執行,提供給須要交互的應用;
- CMD ["param1","param2"] 提供給 ENTRYPOINT 的默認參數;
做用是指定啓動容器時執行的命令,每一個dockerfile只有一條CMD命令,若是指定了多條,那麼前面的會被覆蓋,只有最後一條指令生效。
若是用戶啓動容器時指定了運行的命令,則會覆蓋掉CMD指定的命令。
以下:
[root@master nginx]# cat Dockerfile #dockerfile內容以下 # test FROM nginx:latest CMD echo hello world CMD echo hello [root@master nginx]# docker run -t ljz:v2 #只有最後一條CMD指令生效 hello [root@master nginx]# docker run -t ljz:v2 echo 123456 #啓動容器時又指定了其餘指令,則會覆蓋掉dockerfile中的全部指令 123456
通常我將CMD和ENTRYPOINT結合使用。也就是上面的第三種格式。
它支持下面兩種格式:
- ENTRYPOINT ["executable", "param1", "param2"];
- ENTRYPOINT command param1 param2(shell中執行)。
配置容器啓動後執行的命令,而且不可被docker run提供的參數覆蓋。
每一個dockerfile中只能有一個ENTRYPOINT ,當指定多個時,只有最後一個起效。
使用舉例:
[root@master nginx]# cat Dockerfile #dokerfile文件以下 # test FROM nginx:latest ENTRYPOINT echo hello world ENTRYPOINT echo hello [root@master nginx]# docker run -t ljz:v3 #運行此鏡像 hello [root@master nginx]# docker run -t ljz:v3 echo 123456 #運行時指定的命令也不會被執行 hello #可是能夠經過「--entrypoint」指令將鏡像中的ENTRYPOINT指令覆蓋,只能是命令字 [root@master nginx]# docker run --entrypoint hostname -t ljz:v3 afb421b81a7d
在某種狀況下,ENTRYPOINT和CMD組合使用能發揮更大的做用。
組合使用ENTRYPOINT和CMD, ENTRYPOINT指定默認的運行命令, CMD指定默認的運行參數。
舉個栗子:
[root@master nginx]# cat Dockerfile #Dockerfile文件以下 # test FROM centos:7 ENTRYPOINT ["/bin/ping","-c","3"] CMD ["localhost"] [root@master nginx]# docker run -t ljz:v4 #運行容器 PING localhost (127.0.0.1) 56(84) bytes of data. 64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.028 ms 64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.072 ms 64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.074 ms
查看容器最後一條執行的命令以下:
上面執行的命令是ENTRYPOINT和CMD指令拼接而成. ENTRYPOINT和CMD同時存在時, docker把CMD的命令拼接到ENTRYPOINT命令以後, 拼接後的命令纔是最終執行的命令. 可是因爲上文說docker run命令行執行時, 能夠覆蓋CMD指令的值. 若是你但願這個docker鏡像啓動後不是ping localhost, 而是ping其餘服務器,, 能夠這樣執行docker run:
下表列出了若是把Shell表示法和Exec表示法混合, 最終獲得的命令行, 能夠看到若是有Shell表示法存在, 很可貴到正確的效果:
Dockerfile Command ENTRYPOINT /bin/ping -c 3 CMD localhost #拼接後的指令以下: /bin/sh -c '/bin/ping -c 3' /bin/sh -c localhost ENTRYPOINT ["/bin/ping","-c","3"] CMD localhost #拼接後的指令以下: /bin/ping -c 3 /bin/sh -c localhost ENTRYPOINT /bin/ping -c 3 CMD ["localhost"]" #拼接後的指令以下: /bin/sh -c '/bin/ping -c 3' localhost ENTRYPOINT ["/bin/ping","-c","3"] CMD ["localhost"] #拼接後的指令以下: /bin/ping -c 3 localhost
從上面看出, 只有ENTRYPOINT和CMD都用Exec表示法, 才能獲得預期的效果。
容器運行時應該儘可能保持容器存儲層不發生寫操做,對於數據庫類須要保存動態數據的應用,其數據庫文件應該保存於卷(volume)中,爲了防止運行時用戶忘記將動態文件所保存目錄掛載爲卷,在 Dockerfile 中,能夠事先指定某些目錄掛載爲匿名卷,這樣在運行時若是用戶不指定掛載,其應用也能夠正常運行,不會向容器存儲層寫入大量數據。
指令格式爲:VOLUME ["/data"]。
做用:/data 目錄就會在運行時自動掛載爲匿名卷,任何向 /data 中寫入的信息都不會記錄進容器存儲層,從而保證了容器存儲層的無狀態化。固然,運行時能夠覆蓋這個掛載設置。好比:
docker run -d -v mydata:/data xxxx
在這行命令中,就使用了 mydata 這個命名卷掛載到了 /data 這個位置,替代了 Dockerfile 中定義的匿名卷的掛載配置。
這種方式是docker manager volumes數據持久化方式,不支持Bind mount掛載方式(也就是不支持指定本地的目錄)。
在基於鏡像運行容器後,能夠經過命令「docker inspect container_name」查看容器的詳細信息,在返回的結果中,查看MOUNT字段能夠看到容器內對應的本地目錄位置,以下:
[root@master volumes]# docker inspect web02
返回的結果以下:
命令格式爲:USER <用戶名>[:<用戶組>]。
指定運行容器時的用戶名或 UID,後續的 RUN 也會使用指定用戶。
USER 指令和 WORKDIR 類似,都是改變環境狀態並影響之後的層。WORKDIR 是改變工做目錄,USER 則是改變以後層的執行 RUN, CMD 以及 ENTRYPOINT 這類命令的身份。
固然,和 WORKDIR 同樣,USER 只是幫助你切換到指定用戶而已,這個用戶必須是事先創建好的,不然沒法切換。
RUN groupadd -r redis && useradd -r -g redis redis USER redis RUN [ "redis-server" ]
若是以 root 執行的腳本,在執行期間但願改變身份,好比但願以某個已經創建好的用戶來運行某個服務進程,不要使用 su 或者 sudo,這些都須要比較麻煩的配置,並且在 TTY 缺失的環境下常常出錯。建議使用 gosu。
# 創建 redis 用戶,並使用 gosu 換另外一個用戶執行命令 RUN groupadd -r redis && useradd -r -g redis redis # 下載 gosu RUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.7/gosu-amd64" \ && chmod +x /usr/local/bin/gosu \ && gosu nobody true # 設置 CMD,並以另外的用戶執行 CMD [ "exec", "gosu", "redis", "redis-server" ]
格式爲:WORKDIR /path/to/workdir。
爲後續的 RUN、CMD、ENTRYPOINT 指令配置工做目錄。
可使用多個 WORKDIR 指令,後續命令若是參數是相對路徑,則會基於以前命令指定的路徑。例如
WORKDIR /a WORKDIR b WORKDIR c RUN pwd
則最終路徑爲 /a/b/c。
格式爲:ONBUILD [INSTRUCTION]。
配置當所建立的鏡像做爲其它新建立鏡像的基礎鏡像時,所執行的操做指令。
例如,Dockerfile 使用以下的內容建立了鏡像 image-A。
[...] ONBUILD ADD . /app/src ONBUILD RUN /usr/local/bin/python-build --dir /app/src [...]
若是基於 image-A 建立新的鏡像時,新的Dockerfile中使用 FROM image-A指定基礎鏡像時,會自動執行ONBUILD 指令內容,等價於在後面添加了兩條指令。
FROM image-A #Automatically run the following ADD . /app/src RUN /usr/local/bin/python-build --dir /app/src
使用 ONBUILD 指令的鏡像,推薦在標籤中註明,例如 ruby:1.9-onbuild。
———————— 本文至此結束,感謝閱讀 ————————