Docker 最佳實踐:5個方法精簡你的鏡像

創建短生命週期容器

基於Dockerfile生成鏡像,使用這個鏡像生成的容器,我們要儘可能的縮短容器的生命週期。這裡我的理解是,不要將容器當做vm 來使用, 這個容器可以被停止或者銷燬, 然後可以根據設置和配置的變動重新生成新的容器。

理解構建上下文

當你觸發docker build 命令時,當前目錄就被稱為構建上下文(build context)。默認情況下 Dockerfile文件就在這個目錄下, 但是可以通過 -f 參數來指定Dockerfile的位置。不管Dockerfile在哪裡,當前目錄中的所有文件和目錄都會作為構建上下文發送到 docker daemon 進程。

Docker 最佳實踐:5個方法精簡你的鏡像

構建上下文示例

創建一個目錄並且使用cd進入該目錄。在hello文件中寫”hello”,同時創建 Dockerfile文件並且cat hello文件。在當前上下文(.)中構建鏡像:

Docker 最佳實踐:5個方法精簡你的鏡像

在構建過程中導入了不必要的文件將會導致更大的構建上下文,從而會構建出更大的鏡像。這會增加構建鏡像的時間,拉取和上傳鏡像的時間以及容器的大小。當你使用Dockerfile構建鏡像時,可通過如下信息查看你的構建上下文的大小:

Sending build context to Docker daemon 187.8MB

使用.dockerignore 排除不需要加入鏡像的文件

有的時候我們會需要排除一些與我們構建鏡像不相關的文件,這時候我們可以通過編寫.dockerignore在不改變代碼結構的情況下達到這一目的。這個文件的實現方式與.gitignore很像,關於如何創建一個.dockerignore,可以參考.dockerignore file

使用多階段構建

multi-stage builds 技術可以大幅度減少最終鏡像的大小,而不是想辦法去減少構建過程中的層級數和文件。因為鏡像是在構建過程最後階段生成的,因此我們可以通過leveraging build cache來最小化鏡像層。舉個例子來說,如果構建一個鏡像,這個鏡像有很多層,可以按照鏡像層的修改頻率來排序(就是將不經常更新的層作為最底層,這樣可以複用構建緩存):

  1. 安裝工具
  2. 安裝或者更新依賴
  3. 生成你的應用

一個 Go 應用的 Dockerfile示例:

FROM golang:1.11-alpine AS build

Docker 最佳實踐:5個方法精簡你的鏡像

不安裝不需要的包

為了減小鏡像的複雜度和大小, 我們應當避免安裝一些我們不需要的 packages。舉個例子來說,你不需要在數據庫鏡像中安裝文本編輯器。

應用解耦

每個容器應當只含有一個應用實例, 將多個應用解耦至多個容器可以很方便的對應用進行水平擴展,並且可以複用容器。舉個例子來說,一個 web 應用應當包含三個容器(web容器, 數據庫容器, 緩存容器),每一個容器對應一個鏡像。

每個容器中限制只能有一個進程是一個很好的經驗法則, 但這也不是一個硬性的規定。容器中的進程不僅可以由 init 創建, 一些程序可能會額外的生成一些他們自己的進程。比如, Celery會生成多個 worker 進程, Apache 對每一個請求創建一個進程。

每種場景不一樣,規則也不一樣。但是應該儘可能的保證我們的容器功能明確和模塊化。如果容器之間相互依賴(容器之間可能需要通信), 你可以使用Docker container networks 確保容器間通信。

減小鏡像層數

減少鏡像層數對於鏡像構建非常重要。在更老的版本的 docker 中需要特別注意,現在通過下面的這些特性我們可以方便的對鏡像層數進行限制:

  1. 只有 ONLY, COPY,ADD這三個命令增加層數,其他的命令只會創建一些臨時的鏡像,並不會增加構建的鏡像的層數
  2. 使用 multi-stage builds只拷貝真正需要的artifaces(製品) 到最終的鏡像。這可以使你在構建過程中使用工具和打印調試信息,但不會增加最終的鏡像大小。

對多行參數排序

只要有可能, 將參數按照字母進行排序是一種非常好的實踐,這種方式可以避免重複安裝包(特指apt-get命令),也可以是開發人員更加容易的閱讀和審查。

下面是 buildpack-deps鏡像的例子 images:

Docker 最佳實踐:5個方法精簡你的鏡像

藉助構建緩存

在構建鏡像的時候,docker 會按照dockerfile中的指令順序來一次執行。每一個指令被執行的時候 docker 都會去緩存中檢查是否有已經存在的鏡像可以複用,而不是去創建一個新的鏡像複製。

如果不想使用構建緩存,可以使用docker build參數選項—no-cache=true來禁用構建緩存。在使用鏡像緩存時,要弄清楚緩存合適生效,何時失效。構建緩存最基本規則如下:

  1. 如果引用的父鏡像在構建緩存中,下一個命令將會和所有從該父進程派生的子鏡像做比較,如果有子鏡像使用相同的命令,那麼緩存命中,否則緩存失效。
  2. 在大部分情況下,通過比較Dockerfile中的指令和子鏡像已經足夠了。但是有些指令需要進一步的檢查。
  3. 對於ADD和COPY指令, 文件的內容會被檢查,並且會計算每一個文件的校驗碼。但是文件最近一次的修改和訪問時間不在校驗碼的考慮範圍內。在構建過程中,docker 會比對已經存在的鏡像,只要有文件內容和元數據發生變動,那麼緩存就會失效。
  4. 除了ADD和COPY指令,鏡像緩存不會檢查容器中文件來判斷是否命中緩存。例如,在處理RUN apt-get -y update命令時,不會檢查容器中的更新文件以確定是否命中緩存,這種情況下只會檢查命令字符串是否相同。

本文轉載自:作者:jssyjam。來源:簡書


分享到:


相關文章: