前言
首先我們來思考這樣一個問題:如果将改變了一些配置的Container在生成一個鏡像?
正文
就前言的問題,我做一下解答:
通過docker commit指令,這個指令的目的是将我們的最新修改作為鏡像的一層進行建構,指令詳情參考:
https://docs.docker.com/engine/reference/commandline/commit/❌但是這種方式方式我們并不提倡,因為這種建構方式相當于一個黑盒的建構,别人也不知道你具體做了那些操作,這個時候就需要引出我們的"建構好助手"——DockerFile
dockerfile是把你所有想要需要的地方都表現在了紙面上,這樣我們可以明确知道所有的修改内容。
dockerfile的具體寫法我們在後面進行詳細的讨論。 但是在這裡我們要明确一個問題,Dockerfile其實并不是向鏡像裡直接寫入的,因為鏡像是隻讀的。docker在這個時候建立了一個臨時的容器,然後寫入内容之後,再把臨時容器删除。
DockerFile使用說明
我們建立自己需要的鏡像的時候,可以通過commit和dockerfile的形式進行建構,但是前面也說了,官方推薦的還是dockerfile的形式。我們其實很容易的把它了解為一個建構腳本,docker為我們提供了很多可以使用的指令,下面我會一一說明。
基本指令說明
-
ARG指令**
定義建立鏡像過程中使用的變量,相當于我們為docker build - -build-arg指派。鏡像編譯結束後,這個變量将不會被儲存
ARG version=1.0
-
FROM指令
指定我們要在哪個image之上再進行建構,盡量使用官方image進行base image,為了安全。并且一個Dockerfile,必須要以From指令作為開頭(ARG是唯一一個可以先于From指令的)
FROM debian:latest
-
LABLE指令
像是代碼裡的注釋一樣,一些概括的維護者資訊。
LABLE author=harry
-
ENV指令
定義變量,可以在dockerfile下方進行使用,例如我們定義了 ENV USER harry,那麼下面可以這樣使用 "${USER}"
ENV FILE_LOCATION /usr/local/file
-
USER指令
指定運作容器時的使用者是誰
-
WORKDIR指令
進入到我們指定的目錄中,如果沒有這個目錄會自動進行建立,用WORKDIR,代替 RUN cd。盡量使用絕對目錄,不要使用相對目錄。
WORKDIR /usr/local WORKDIR tomcat/config # 可以連續指定路徑,如果像上述一樣,指定的路徑為/usr/local/tomcat/configs
-
RUN指令
每執行一次RUN就是就會産生鏡像的一層,使用 "&&" 将多個指令串聯起來,如果需要換行在最後需要使用" " 反斜杠。環境的運作與搭建,大多數情況下需要這個指令
RUN yum update \ && yum install -y nginx #上述操作先更新yum,然後下載下傳nginx
-
CMD指令
設定啟動後預設執行的指令和參數。如果docker run 進行了指定了指令,例如 docker run -it … /bin/bash。則不會運作CMD中的指令,而且CMD定義多個,後面會覆寫之前的。
啟動tomcat指令 CMD ['catalina.sh', 'run']
-
ENTRYPOINT指令
設定容器啟動時預設執行的指令和參數,該指令會在啟動容器後作為根指令執行,通過名稱可以看出來是入口。讓容器以應用程式或者服務去執行。并且ENTRYPOINT一定會執行
将一個shell腳本作為docker啟動的入口。 ENTRYPOINT ['/entry.sh']
-
COPY指令
把本地檔案拷貝到docker裡去,COPY指令優于ADD指令,如果需要添加遠端檔案可以使用 curl或者wget
COPY . /temp
-
ADD指令
是把本地的檔案複制到docker裡去,不過不光如此,還會對壓縮檔案自動進行解壓縮
ADD . /temp
-
VOLUME指令
啟動容器時,可以在本地或者是其他容器建立資料卷挂載點,用于存放資料庫和持久化資料
#指定挂載點為 /temp/mount VOLUME /temp/mount
-
EXPOSE指令
聲明鏡像内部服務監聽的端口,一次可以暴露多個端口
#暴露22端口,和8888端口 EXPOSE 22 8888
-
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中的則是用來指定參數的。