Docker實戰篇-詳述Dockerfile

前面咱們經過編寫Dockerfile文件建立鏡像,這是咱們最多見的使用docker部署應用的方式,也間接說明了熟練使用Dockerfile文件的重要程度。那麼本章咱們就來重點講解下Dockerfile經常使用的指令使用和說明以及編寫Dockerfile最佳實踐。下面咱們先看幾個常見的自定義Dockerfile文件:java

一、自定義JDK鏡像mysql

 

  •  
FROM ubuntu:16.04 RUN mkdir -p /opt/software COPY jdk1.8.0_161 /opt/software/jdk1.8.0_161  ENV JAVA_HOME /opt/software/jdk1.8.0_161ENV PATH $JAVA_HOME/bin:$PATH

 

二、前一章的自定義springboot 應用鏡像git

 

  •  
FROM ubuntu:16.04 RUN mkdir -p /opt/applications/helloworld &&\    mkdir -p /opt/software COPY jdk1.8.0_161 /opt/software/jdk1.8.0_161 COPY lazy-study-docker-0.0.1-SNAPSHOT.jar /opt/applications/helloworld/ ENV JAVA_HOME /opt/software/jdk1.8.0_161ENV PATH $JAVA_HOME/bin:$PATH CMD java -jar /opt/applications/helloworld/lazy-study-docker-0.0.1-SNAPSHOT.jar

 

 

下面咱們已問答的方式講解Dockerfile經常使用的指令github

問:Dockerfile經常使用有哪些指令?web

答:經常使用指令以下:spring

一、FROMsql

語法:docker

  •  
FROM <image>[:<tag>] [AS <name>]

說明:shell

爲後續指令設置基礎鏡像,有效Dockerfile必須以FROM指令開頭。僅ARG能夠在FROM前面。FROM能夠在單個Dockerfile鏡像中屢次出現以建立多個鏡像,或者使用其中一個構建階段做爲另外一個構建階段的依賴項。只需在每條新FROM指令以前 。每條FROM指令都清除先前指令建立的任何狀態。ubuntu

例子1:

FROM openjdk:latest

例子2:

 

  •  
ARG version=3.7FROM alpine:${version}

 

#若是在FROM後面想試用ARG定義的變量,則須要這一行

 

  •  
ARG version RUN echo "alpine version is: ${version}"

 

例子3:

 

  •  
ARG  CODE_VERSION=latestFROM base:${version}CMD  /code/run-appFROM extras:${version}CMD  /code/run-extras

 

 

二、RUN

語法:

 

  •  
RUN <command>RUN ["executable", "param1", "param2"]

 

說明:

RUN指令將在當前鏡像之上的新圖層中執行任何命令並提交結果。生成一個新的鏡像。RUN在下一次構建期間,指令的緩存不會自動失效。相似指令的緩存 RUN apt-get dist-upgrade -y將在下一次構建期間重用。例如,RUN能夠經過使用--no-cache 標誌使指令的高速緩存無效docker build --no-cache。

更多優化信息能夠參考官方文檔:

https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

 

例子1:

  •  
RUN mkdir -p /usr/local/applications

 

例子2:

 

  •  
RUN ["mkdir ", "-p", "/usr/local/applications"]

 

上面只能用雙引號,不能用單引號

三、CMD

語法:

 

  •  
CMD ["executable","param1","param2"](這是首選形式)CMD ["param1","param2"](做爲ENTRYPOINT的默認參數)CMD command param1 param2(shell形式)

 

說明:

該CMD指令指定在運行映像時要執行的命令。一個Dockerfile只能有一個CMD命令,若是有多個,只有最後一個CMD命令生效。

更多優化信息能夠參考官方文檔:

https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

 

 

  •  
例子1:CMD java -jar xxxx.jar例子2:CMD ["/usr/local/jdk8/bin/java", "-jar", "/usr/local/applications/xxxx.jar"]例子3:CMD ["/usr/local/jdk8/bin/java", "--version"]

 

 

上面只能用雙引號,不能用單引號

四、LABEL

語法:

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

說明:

該LABEL指令將元數據添加到鏡像。 LABEL是鍵值對。要在LABEL值中包含空格,請使用引號和反斜槓.

例子1:

 

  •  
LABEL "com.example.vendor"="ACME Incorporated"LABEL com.example.label-with-value="foo"LABEL version="1.0"LABEL description="This text illustrates \that label-values can span multiple lines."

 

 

五、MAINTAINER(再也不推薦使用)

語法:

  •  
MAINTAINER <name>

說明:

該MAINTAINER指令設置生成的鏡像的Author字段。該LABEL指令是一個更靈活的版本,您應該使用它,由於它能夠設置您須要的任何元數據,而且能夠輕鬆查看,例如使用docker inspect。要設置與MAINTAINER您可使用的字段對應的標籤 :

LABEL maintainer="lazy"

而後能夠從docker inspect其餘標籤中看到這一點。

六、EXPOSE

語法:

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

說明:

該EXPOSE指令通知Docker容器在運行時偵聽指定的網絡端口。您能夠指定端口是偵聽TCP或者UDP,若是未指定協議,則默認爲TCP。

該EXPOSE指令實際上不發佈端口。它起到一種文檔描述的做用,關於哪些端口要發佈。要在運行容器時經過-p publish_port:target_port方式進行實際發佈端口。

 

  •  
例子1:EXPOSE 80/tcp例子2:EXPOSE 80/upd

 

七、ENV

語法:

 

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

 

說明:

該ENV指令將環境變量<key>設置爲該值 <value>。此環境變量將在構建階段中的全部後續指令使用

例子1:

 

  •  
ENV JAVA_HOME /opt/software/jdk1.8.0_161ENV PATH ${JAVA_HOME}/bin:$PATHCMD echo "java home env is:${JAVA_HOME}"

 

八、ADD

語法:

 

  •  
ADD [--chown=<user>:<group>] <src>... <dest>ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]

 

說明:

與 COPY 相似,從 build context 複製文件到鏡像。不一樣的是,若是 src 是歸檔文件(tar, zip, tgz, xz 等),使用ADD指令的化文件會被自動解壓到 dest。

使用ADD須要注意細節問題:

1)src路徑必須是構建上下文路徑,不能 ADD ../xxxx/xxx /

2)若是src是URL,而且dest不以尾部斜槓結尾,則從URL推斷文件名並將文件下載到<dest>/<filename>

3)若是<src>是目錄,則複製目錄的所有內容,包括文件系統元數據。注意,不復制目錄自己,只複製其內容。

4)若是<src>是以識別的壓縮格式(identity,gzip,bzip2或xz)的本地 tar存檔,則將其解壓縮爲目錄

5)若是<src>直接或因爲使用通配符指定了多個資源,則<dest>必須是目錄,而且必須以斜槓結尾/

6)若是<dest>不存在,則會在其路徑中建立全部缺乏的目錄。

 

 

  •  
例子1:ADD jdk1.8.0_161.tar.gz /opt/software/jdk1.8.0_161例子2:ADD --chown=root:root jdk1.8.0_161.tar.gz /opt/software/

 

九、COPY

語法:

 

  •  
COPY [--chown=<user>:<group>] <src>... <dest>COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]

 

說明:

與 ADD 相似,從 build context 複製文件到鏡像。

例子1:

 

  •  
COPY jdk1.8.0_161 /opt/software/jdk1.8.0_161

 

例子2:

 

  •  
COPY --chown=root:root jdk1.8.0_161 /opt/software/jdk1.8.0_161

 

十、ENTRYPOINT

語法:

 

  •  
ENTRYPOINT ["executable", "param1", "param2"] (執行形式,首選)ENTRYPOINT command param1 param2 (shell形式)

 

說明:

設置容器啓動時運行的命令。Dockerfile 中能夠有多個 ENTRYPOINT 指令,但只有最後一個生效。CMD 或 docker run 以後的參數會被當作參數傳遞給 ENTRYPOINT。

 

  •  
例子1:ENTRYPOINT ["top","-b", "-H"]例子2:ENTRYPOINT top -b -H

 

十一、VOLUME

語法:

  •  
VOLUME ["/data"]

說明:

VOLUME 指令能夠在鏡像中建立掛載點,這樣只要經過該鏡像建立的容器都有了掛載點。經過 VOLUME 指令建立的掛載點,沒法指定宿主機上對應的目錄。對應到宿主機的目錄是由docker管理的,默認路徑爲/var/lib/docker/隨機id/_data.咱們能夠經過命令:

  •  
docker inspect myhelloworld | grep Mounts -A 10

查看鏡像掛載在宿主機的路徑列表。

例子1:

 

  •  
VOLUME["/logs"]

 

十二、USER

語法:

 

  •  
USER <user>[:<group>] orUSER <UID>[:<GID>]

 

說明:

指定後面RUN CMD ENTRYPOINT指令運行的用戶權限,當用戶沒有組時,默認爲root組

例子1:

USER mysql

1三、WORKDIR

語法:

  •  
WORKDIR /path/to/workdir

說明:

指定後面RUN CMD ENTRYPOINT指令將在那個目錄下進行,一旦指定,WORKIDR後面的目錄必須會建立

例子1:

 

  •  
WORKDIR /helloWORKDIR worldRUN pwd

 

最終pwd命令的輸出將是 /hello/world

1四、ARG

語法:

  •  
ARG <name>[=<default value>]

說明:

該ARG指令定義了一個變量,用戶能夠docker build使用該--build-arg <varname>=<value> 標誌在構建時將該變量傳遞給構建器

例子1:

 

  •  
FROM openjdkARG var1ARG var2=defaultval

 

若是 ARG指令具備默認值,而且在構建時沒有傳遞值,則構建器將使用默認值。

以上爲經常使用的Dockerfile指令,想了解更多詳細指令說明參考官方文檔:

https://docs.docker.com/engine/reference/builder/

 

問:編寫Dockerfile文件用來作什麼?

答:咱們的應用程序若是須要以docker容器方式來部署的話,通常須要本身編寫Dockerfile文件,經過docker build -t xxxxx -f Dockerfile  . 命令構建出docker鏡像。注意後面有個「.」 這個「.」表明構建上下文爲當前路徑。例如COPY 或ADD命令通常都是從當前構建上下文獲取文件資源複製到構建的鏡像中的。

問:編寫Dockerfile通常套路是什麼?

答:編寫Dockerfile以前咱們須要更多的瞭解鏡像的知識,鏡像是有層(layer)的概念,通常的鏡像都是一層一層堆疊起來的,這些層對容器來講是隻讀的。當經過鏡像建立容器的時候,會在當前建立的容器最頂層再建立一個簡小的可寫層,這個可寫層能夠給實際運行程序寫入,建立目錄等操做,一旦容器中止且刪除,那麼這個可寫層隨之被刪除,也就是說這時容器的程序數據不會被保存,會丟失,因此一般咱們會經過volume或掛載等方式將須要保留的數據綁定/映射到宿主機上。

編寫Dockerfile通常套路是基於某個層建立本身層的時候,使用FROM語法,就像上面那樣FROM openjdk:latest,這裏是基於openjdk鏡像層的基礎上增長咱們的層。

固然,也可使用多階段構建方式,多階段構建方式通常是在同一個Dockerfile文件中有多個FROM xxx命令,而後後面的FROM 下面的COPY --from=0這種方式應用前面的FROM,這屬於優化層面的東西,這裏暫時不過多討論,有興趣讀者能夠查看官方文檔:

https://docs.docker.com/develop/develop-images/multistage-build/

問:咱們怎麼找到好的基礎鏡像,好比上面的openjdk

答:不少基礎鏡像都是由其官方提供,咱們執行docker search xxxx命令查找鏡像,這是會返回一個列表,此時咱們關心的是STARTS和OFFICIAl這兩列,STARTS是被標星或點贊數量,OFFICIAl爲OK通常就是其官方發佈的,以下圖所示。

 

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

另外若是咱們須要在基礎鏡作些個性化安裝的話(好比須要在操做系統上安裝某些字體),咱們也能夠自定義基礎鏡像,好比自定義一個myjdk8鏡像Dockerfile文件內容以下:

 

  •  
FROM ubuntu:16.04 MAINTAINER lazy RUN mkdir -p /opt/softwareCOPY jdk1.8.0_161 /opt/software/jdk1.8.0_161 ENV JAVA_HOME /opt/software/jdk1.8.0_161ENV PATH ${JAVA_HOME}/bin:$PATH

 

上面咱們自定義了目錄/opt/software,而後把jdk放到該目錄下,而後配置了jdk環境變量。若是須要作其餘自定義操做的話,也是相似的套路。

問:編寫Dokcerfile須要注意哪些東西

答:編寫Dockerfile就像寫代碼,須要考慮優化問題。說到優化,咱們知道,都是有目標和指標的,好比web應用的優化一般是優化速度,指標通常有響應時間、吞吐量、併發數等。想要知道如何編寫好的Dockerfile文件就要知道Dockerfile的優化目標和指標。

Dockerfile優化目標爲:減小構建出最終的鏡像的大小

Dockerfile優化指標爲:減小構建出最終的鏡像的大小

1)最小化層數

Dockerfile文件每一條指令如RUN,COPY等都會建立一個新的層,因此在寫的時候儘可能減小命令,能夠利用shell &&的方式減小命令次數好比下面:

 

  •  
RUN mkdir -p /dir1RUN mkdir -p /dir2RUN mkdir -p /dir3優化後爲:RUN mkdir -p /dir1 && \    mkdir -p /dir1 && \mkdir -p /dir1

 

 

這樣就會由三層變成一層,能夠減小構建時間和最終的鏡像大小

2)不要安裝沒必要要的包

3)必要時也可使用STDIN中的DOCKERFILE從本地構建上下文構建

語法以下:

 

  •  
mkdir examplecd exampletouch somefile.txt docker build -t myimage:latest -f- . <<EOFFROM busyboxCOPY somefile.txt .RUN cat /somefile.txtEOF

 

4)若是想從某個不包含Dockerfile文件的的項目git倉庫構建鏡像,可使用STDIN中的DOCKERFILE從遠程構建上下文,就像下面這樣:

 

  •  
docker build -t myimage:latest \ -f- https://github.com/xxx/xxx.git <<EOFFROM busyboxCOPY README.md . EOF

 

 

5)推薦使用多階段構建

多階段構建容許您大幅減少最終圖像的大小,官方文檔:

https://docs.docker.com/develop/develop-images/multistage-build/

 

6)利用構建緩存

在檢查每條指令時,Docker會在其緩存中查找能夠重用的現有映像,而不是建立新的(重複)映像。

若是您根本不想使用緩存,則可使用docker build命令中的--no-cache=true 選項

問:如何忽略構建上下文的某些文件或目錄?

答:能夠在構建上下文的目錄中建立.dockerignore文件,裏面記錄須要排除的文件或目錄內容

更多優化建議能夠自行參考官方文檔:

https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

相關文章
相關標籤/搜索