天天看點

第二口docker的感覺 —— Dockerfile

前言

首先我們來思考這樣一個問題:如果将改變了一些配置的Container在生成一個鏡像?

正文

就前言的問題,我做一下解答:

通過docker commit指令,這個指令的目的是将我們的最新修改作為鏡像的一層進行建構,指令詳情參考:

https://docs.docker.com/engine/reference/commandline/commit/

❌但是這種方式方式我們并不提倡,因為這種建構方式相當于一個黑盒的建構,别人也不知道你具體做了那些操作,這個時候就需要引出我們的"建構好助手"——DockerFile

dockerfile是把你所有想要需要的地方都表現在了紙面上,這樣我們可以明确知道所有的修改内容。

dockerfile的具體寫法我們在後面進行詳細的讨論。 但是在這裡我們要明确一個問題,Dockerfile其實并不是向鏡像裡直接寫入的,因為鏡像是隻讀的。docker在這個時候建立了一個臨時的容器,然後寫入内容之後,再把臨時容器删除。

DockerFile使用說明

我們建立自己需要的鏡像的時候,可以通過commit和dockerfile的形式進行建構,但是前面也說了,官方推薦的還是dockerfile的形式。我們其實很容易的把它了解為一個建構腳本,docker為我們提供了很多可以使用的指令,下面我會一一說明。

基本指令說明
  1. ARG指令**

    定義建立鏡像過程中使用的變量,相當于我們為docker build - -build-arg指派。鏡像編譯結束後,這個變量将不會被儲存

    ARG version=1.0           
  2. FROM指令

    指定我們要在哪個image之上再進行建構,盡量使用官方image進行base image,為了安全。并且一個Dockerfile,必須要以From指令作為開頭(ARG是唯一一個可以先于From指令的)

    FROM debian:latest           
  3. LABLE指令

    像是代碼裡的注釋一樣,一些概括的維護者資訊。

    LABLE author=harry           
  4. ENV指令

    定義變量,可以在dockerfile下方進行使用,例如我們定義了 ENV USER harry,那麼下面可以這樣使用 "${USER}"

    ENV FILE_LOCATION /usr/local/file           
  5. USER指令

    指定運作容器時的使用者是誰

  6. WORKDIR指令

    進入到我們指定的目錄中,如果沒有這個目錄會自動進行建立,用WORKDIR,代替 RUN cd。盡量使用絕對目錄,不要使用相對目錄。

    WORKDIR /usr/local
    WORKDIR tomcat/config
    # 可以連續指定路徑,如果像上述一樣,指定的路徑為/usr/local/tomcat/configs           
  7. RUN指令

    每執行一次RUN就是就會産生鏡像的一層,使用 "&&" 将多個指令串聯起來,如果需要換行在最後需要使用" " 反斜杠。環境的運作與搭建,大多數情況下需要這個指令

    RUN yum update \
            && yum install -y nginx
    #上述操作先更新yum,然後下載下傳nginx           
  8. CMD指令

    設定啟動後預設執行的指令和參數。如果docker run 進行了指定了指令,例如 docker run -it … /bin/bash。則不會運作CMD中的指令,而且CMD定義多個,後面會覆寫之前的。

    啟動tomcat指令
    CMD ['catalina.sh', 'run']           
  9. ENTRYPOINT指令

    設定容器啟動時預設執行的指令和參數,該指令會在啟動容器後作為根指令執行,通過名稱可以看出來是入口。讓容器以應用程式或者服務去執行。并且ENTRYPOINT一定會執行

    将一個shell腳本作為docker啟動的入口。
    ENTRYPOINT ['/entry.sh']           
  10. COPY指令

    把本地檔案拷貝到docker裡去,COPY指令優于ADD指令,如果需要添加遠端檔案可以使用 curl或者wget

    COPY . /temp           
  11. ADD指令

    是把本地的檔案複制到docker裡去,不過不光如此,還會對壓縮檔案自動進行解壓縮

    ADD . /temp           
  12. VOLUME指令

    啟動容器時,可以在本地或者是其他容器建立資料卷挂載點,用于存放資料庫和持久化資料

    #指定挂載點為 /temp/mount
    VOLUME /temp/mount           
  13. EXPOSE指令

    聲明鏡像内部服務監聽的端口,一次可以暴露多個端口

    #暴露22端口,和8888端口
    EXPOSE 22 8888           
  14. ONBUILD指令

    指定自己的子鏡像都會執行哪些指令

    #把目前目錄下的所有東西拷貝到/app/src目錄下
    ONBUILD COPY . /app/src           

DockerFile的寫法的關鍵在于:環境 + 工程代碼 + 運作

DockerFile的最佳實踐,請看官網:

https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
ARG和ENV的差別

兩個看起來都是生命變量用的,他們之間的差別在于ARG時建立鏡像過程中使用的變量,在啟動後的容器中不能使用。而ENV在容器中可以使用

RUN,CMD以及ENTRYPOINT的相同點以及差別

這裡要着重說一下RUN 和 CMD和ENTRYPOINT,他們都可以使用exec格式和shell格式

exec格式舉例:

CMD ['/bin/echo', 'hello world']           

shell格式舉例:

CMD echo 'hello world'           

但是要注意:如果使用exec格式,列印一個環境變量

CMD['/bin/echo', 'hello world $name'],列印的會是 hello world name的。

這個時候可以考慮使用shell格式,或者說把exec格式進行改造一下,改成如下格式:

CMD['/bin/bash','-c','echo hello world $name ']

另外值得注意的是,RUN指令用于建構鏡像,CMD和ENTRYPOINT用于指定容器啟動時的一些預設指令和參數。

CMD與ENTRYPOINT的共同點

兩者在dockerfile中各自都隻能聲明一次,聲明多次,不會報錯,但是隻有最後一條指令會生效。

CMD與ENTRYPOINT的差別

二者既然都是作為容器啟動時的指令,那麼他們的差別在哪裡呢?

通過我去查閱官網,官網的意思是說ENTRYPOINT是docker容器的主指令,而預設的一些參數會在CMD中進行指定。

請看下方代碼:

ENTRYPOINT ['/bin/echo', 'hello']
CMD ['world']           

如果我們運作

docker run -it < image > 會輸出 hello world

而如果我們運作

docker run -it < image > harry 會輸出 hello harry

這就是因為我上面說過的,如果run的時候沒有指定CMD會執行,如果指定了指令就不會執行CMD了。

是以總結起來他們兩者的關鍵差別在于:

CMD會被作為指令或者參數在ENTRYPOINT 參數後追加。

CMD可被覆寫,ENTRYPOINT不會被覆寫

CMD結合ENTRYPOINT的使用

想象一下這樣一個簡單的場景,我們隻希望我們的docker,不作為一個應用程式啟動,而是用做一個工具。假設為一個壓力測試的工具,這個工具需要被指定一些參數例如說 --vm 之類的我們可以通過 ->

CMD["/usr/bin/strees",'--vm 1']這種形式進行啟動。但是有沒有想過,這樣的話變量值就被限制死了,有什麼好辦法做到docker啟動的時候動态傳入嗎?我都這麼說了,當然是有的:

FROM ubuntu
RUN apt-get update && apt-install -y stress
ENTRYPOINT ["/usr/bin/stress"]
CMD[]           

這樣在啟動的使用就可以動态的将變量傳入:

docker run -it dockerImage名稱 --vm 1           

最後總結一下,其實如果CMD和ENTRYPOINT結合的來,那麼ENTRYPOINT是用來指定指令的,而CMD中的則是用來指定參數的。