高效編寫Dockerfile的幾條準則


概述

  • Dockerfile 是專門用來進行自動化構建鏡像的編排文件(就像Jenkins 2.0時代的Jenkinsfile是對Jenkins的Job和Stage的編排同樣),咱們能夠經過 docker build 命令來自動化地從 Dockerfile 所描述的步驟來構建自定義的 Docker鏡像,這比咱們去命令行一條條指令執行的方式構建高效得多。docker

  • 另外一方面,因爲 Dockerfile 提供了統一的配置語法,所以經過這樣一份配置文件,咱們能夠在各類不一樣的平臺上進行分發,須要時經過 Dockerfile 構建一下就能獲得所需的鏡像。編程

  • 最後一個必須提的優勢即是:Dockerfile 經過與鏡像配合使用,使得 Docker鏡像構建之時能夠充分利用 「鏡像的緩存功能」,所以也提效很多!centos

然而寫 Dockerfile 也像寫代碼同樣,一份精心設計、Clean Code 的 Dockerfile 能在提升可讀性的同時也大大提高Docker的使用效率緩存

所以下面就結合實踐來說幾條 Dockerfile 的實踐心得!bash

注: 本文首發於 My 公衆號 CodeSheep ,可 長按掃描 下面的 當心心 來訂閱 ↓ ↓ ↓服務器

CodeSheep · 程序羊



基礎鏡像的選擇有講究

在個人文章 《利用K8S技術棧打造我的私有云(連載之:基礎鏡像製做與實驗)》 中,咱們是基於某個Linux基礎鏡像做爲底包,而後打包進我須要的功能從而造成本身的鏡像。框架

這裏選擇基礎鏡像時是有講究的:wordpress

  • 一是 應當儘可能選擇官方鏡像庫裏的基礎鏡像;
  • 二是 應當選擇輕量級的鏡像作底包

就典型的Linux基礎鏡像來講,大小關係以下:微服務

Ubuntu > CentOS > Debian
複製代碼

所以相比 Ubuntu,其實更推薦使用最輕量級的 Debian鏡像,並且它也是一個完整的Release版,能夠放心使用工具



多使用標籤Tag 有好處

  • 構建鏡像時,給其打上一個易讀的鏡像標籤有助於幫助瞭解鏡像的功能,好比:
docker build -t=「centos:wordpress" . 複製代碼

例如上面的這個centos鏡像是用來作wordpress用的,因此已經集成了wordpress功能,這一看就很清晰明瞭

  • 再者,咱們也應該在 Dockerfile 的 FROM 指令中明確指明標籤 Tag,不要再讓 Docker daemon 去猜,如
FROM debian:codesheep
複製代碼


充分利用鏡像緩存

什麼是鏡像緩存?

由 Dockerfile 最終構建出來的鏡像是在基礎鏡像之上一層層疊加而得,所以在過程當中會產生一個個新的 鏡像層。Docker daemon 在構建鏡像的過程當中會緩存一系列中間鏡像。

docker build鏡像時,會順序執行Dockerfile中的指令,並同時比較當前指令和其基礎鏡像的全部子鏡像,若發現有一個子鏡像也是由相同的指令生成,則 命中緩存,同時能夠直接使用該子鏡像而避免再去從新生成了。

爲了有效地使用緩存,須要保證 Dockerfile 中指令的 連續一致,儘可能將相同指令的部分放在前面,而將有差別性的指令放在後面

**舉例:**假如我想用 Dockerfile方式 基於最基本的 CentOS 鏡像來構建兩個不一樣的鏡像時,兩個Dockerfile的開頭能夠相同:

FROM centos:latest

# 下面安裝兩個經常使用的工具
RUN yum install -y net-tools.x86_64

RUN yum install lrzsz

######## 上面爲兩個Dockerfile文件中相同的部分######

######## 下面爲兩個Dockerfile文件中不一樣的部分######

......

複製代碼


ADD 與 COPY 指令的正確使用

雖然二者均可以添加文件到鏡像中,但在通常用法中,仍是推薦以COPY指令爲首選,緣由在於ADD指令並無COPY指令來的純粹,ADD會添加一些額外功能,典型的以下 ADD 一個壓縮包時,其不只會複製,還會自動解壓,而有時咱們並不須要這種額外的功能。

ADD codesheep.tar.gz /path
複製代碼

除此以外,在須要添加多個文件到鏡像中的時候,不要一次性集中添加,而是選擇 按需 在必要時 逐個 添加便可,由於這樣有利於利用鏡像緩存



##儘可能使用docker volume

雖然上面一條原則說推薦經過 COPY 命令來向鏡像中添加多個文件,然而實際狀況中,若文件 大而多 的時候仍是應該優先用 docker -v 命令來掛載文件,而不是依賴於 ADD 或者 COPY

最後必須說一下,這裏的「儘可能」是有個度的,適度把握才行。



CMD 和 ENTRYPOINT指令 的理解使用

Dockerfile 製做鏡像時,會組合 CMD 和 ENTRYPOINT 指令來做爲容器運行時的默認命令:即 CMD + ENTRYPOINT。此時的默認命令組成中:

  • ENTRYPOINT 指令部分「通常」固定不變,容器運行時不修改
  • 而 CMD 部分的指令也能夠改變,表如今運行容器時,docker run 命令中提供的參數會覆蓋CMD的指令內容。

舉個例子:

FROM debian:latest

MAINTAINER codesheep@163.com

ENTRYPOINT [ "ls", "-l"]
CMD ["-a"]
複製代碼

若以默認命令運行容器,能夠發現,執行的是 ls -a -l 命令:

ls -l -a

docker run 中增長參數 -t

docker run -it --rm --name test debian:codesheep -t
複製代碼

也能夠發現執行的是 ls -l -t,即 Dockerfile 中的 CMD 原參數被覆蓋了:

ls -l -t

所以推薦的使用方式是:

  • 使用exec格式的 ENTRYPOINT指令 設置固定的默認命令和參數

  • 使用 CMD指令 設置可變的參數



不推薦在 Dockerfile中 作端口映射

Dockerfile 能夠經過 EXPOSE指令 將容器端口映射到主機端口上,但這樣會致使鏡像在一臺主機上僅能啓動一個容器!

因此應該在 docker run 命令中來用 -p 參數來指定端口映射,而不要將該工做置於 Dockerfile 之中:

#儘可能避免這種方式
EXPOSE 8080:8899

#僅僅暴露端口
EXPOSE 8080
複製代碼


使用 Dockerfile 來共享鏡像

推薦經過共享 Dockerfile 的方式來共享鏡像,優勢多多:

  • 經過 Dockerfile 構建的鏡像用戶能夠清楚地看到構建的過程

  • 就像 Jenkinsfile 能夠加入版本控制從而追蹤CI系統的變遷和步驟的回滾同樣,Dockerfile 做爲一個編排文件一樣能夠入庫作版本控制,這樣也能夠回溯

  • 使用 Dockerfile 構建的鏡像具備肯定性,沒有玄學的成分



後記

若是有興趣,也能夠抽點時間看看做者一些關於容器化、微服務化方面的文章:

做者相關的SpringBt實踐文章在此:



CodeSheep · 程序羊
相關文章
相關標籤/搜索