Docker container Dockerfile

「初學Docker」Dockerfile易混亂的指令

曹應忠 2019/04/08 11:19:10
776

Dockerfile Instructions

前言:

各位好,本人是樣樣學樣樣鬆樣樣不精通的不負責任講座。最近臉頰肉也開始覺得有點鬆···怎麼辦?!啊~~~~~
因為近期的案子,使用到docker、kubernetes來部署安裝;同仁們都是首次實戰build docker image。
我們來聊聊幾個在寫Dockerfile時,容易混亂搞不懂意思的指令。
 

簡介:

Dockerfile簡單介紹一下,是用來描述這個image要怎麼組成的文檔。
詳細的Dockerfile指令與介紹,可參考Dockerfile官方文檔Best practices for writing Dockerfiles
或者是去參考docker hub許多官方image的寫法,東參考西參考很快就能上手!
比如說要觀摩postgres Dockerfile是怎麼寫的:
postgres docker hub
 
postgres Dockerfile
就可以暸解許多高深的Dockerfile寫法!
 
 
主題:
底下介紹幾種在寫Dockerfile時,容易讓人混亂的指令:
 
1.  COPY vs ADD
COPY和ADD二個的功用都一樣,就是將檔案複製進去image裡!
差別在於:
  • COPY只能複製本機端的檔案或目錄
  • ADD能增加遠端url的檔案到docker image
  • ADD能順手將本機端複製進去的tar檔解開(遠端的tar不行!)
 
罷特!很重要!客倌們!
在實例上並不建議使用ADD來抓取網路上的檔案,會使用RUN curl or wget的方式。
原因是使用一次ADD指令會增加docker image layers一次,原則上layers越多,docker image size就會越大!
咱們來看看docker自己提供的範例
ADD http://example.com/big.tar.xz /usr/src/things/
RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things
RUN make -C /usr/src/things all
  1.增加遠端的tar檔
  2.解開tar檔
  3.編譯程式 
因為ADD、RUN都是會增加image layers的指令,所以上面就增加了三層layers。
 
再來看看建議的寫法:
RUN mkdir -p /usr/src/things \
    && curl -SL http://example.com/big.tar.xz \
    | tar -xJC /usr/src/things \
    && make -C /usr/src/things all
只用一個RUN指令就做完同樣的事情,功能不變,但只用了一層layers。
誒~各位人客不要小看容量減少帶來的好處,除了減少下載的時間;
在雲端環境上,網路可都是斤斤計較要計費的!一分流量一分錢,當然是能省則省呀。
 
本段重點:
  • 使用COPY複製檔案,因為意義比較明確
  • 只有需要解開本機tar檔進image時,才使用ADD
 
 
2.  EXPOSE vs publish
這又是另一個讓人摸不著重點的指令···
很多人以為加上EXPOSE 8080,docker run起來後,就可以從本機端連得到container的8080 port。
但是,孩子,這是不可能的。
 
EXPOSE概念上比較像是在告訴使用這個image的人,服 務是在那個port。
我們來下些指令
docker pull nginx
docker inspect nginx
可以看到下圖在Config裡有個ExposedPorts;
這邊就在告訴你,這個nginx的container跑起來後,服務是在port 80/tcp
 
因此我們若要在本機端能夠連的到container中的ngin x服務,
必須要publish一下,在參數加上-p <要讓外部連的到的port>:<app服務的port>
docker run -p 8888:80 -d nginx
就能夠連的到nginx啦!
 
本段重點:
  • EXPOSE是在告訴Docker,本服務是啟動在某個port
  •  要讓宿主機能連進該服務,要使用publish
 
 
3. CMD or ENTRYPOINT
CMD與ENTRYPOINT指令,都可以用來配置container啟動時要運行的命令。
 
那麼二種有什麼不同呢?來看個小小的例子。
我在Dockerfile裡寫CMD echo "Hello Weber!",執行docker run -it <image>會輸出
Hello Weber!
 
但是若啟動時另外指定docker run -it <image> echo "What's up~",會變成只輸出
What's up~
 
咦咦~~蝦咪碗糕,我設定的CMD命令怎麼不見了!
這是因為CMD的目的是讓你配置一個預設的命令內容;
啟動容器時若是有另外指定命令,就會忽略原本的CMD命令內容。
 
相反的,ENTRYPOINT設定的命令則一定會被執行,並且會將docker run帶的參數加在後面一併運行。
 
因此ENTRYPOINT跟CMD是可以互相配合的,讓CMD的參數當作ENTRYPOINT的額外參數。
讓ENTRYPOINT帶必要的參數,而CMD帶可以被替換掉的參數。
並且有多種形式可以互相搭配,但屬於比較進階的用法,這邊暫不詳加敘述以免大家又亂掉了。
 
本段重點:
  •  CMD的內容,當容器啟動若有指定其他命令,會被忽略
  •  ENTRYPOINT設定的指令,一定會被執行
  • 可以有多個CMD,不過只有最後一個會生效
 

後記:

以上內容,是小的在工作上親身經歷的心得,希望能幫助到各位。

或許也有誤解的部分,也請大家不吝提供正確的觀念,互相討論。

下次有機會來聊聊Kubernetes + Helm實戰吧!

曹應忠
石偉琪
2019/04/15 15:41:35

跪求 Kubernetes + Helm實戰

曹應忠
2019/04/17 15:27:39

這···我努力擠時間生出來。

但我今年的quota已經達標了,耶咿~~   

侯清慈(AlexHou)
2019/04/17 14:59:21

真是篇講人話的好文章...

曹應忠
2019/04/17 15:28:25

謝謝。