利用構建緩存機制縮短Docker鏡像構建時間

在使用Docker部署PHP或者node.js應用時,經常使用的方法是將代碼和環境鏡像打包成一個鏡像而後運行,一些雲廠商提供了很是便捷的操做,只須要把咱們的代碼提交到VCS上,而後它們就會幫咱們拉取代碼並根據代碼包內的Dockerfile構建咱們的鏡像而後部署到集羣裏。node

PHP和node.js都有很是不錯的生態,有各類各樣的包,可是一旦引入的包多了咱們的項目內的文件就會變得很是多,因此在使用VCS協做的時候咱們都會忽略掉依賴包目錄(node_modules / vendor)。可是咱們忽略了包目錄後在構建鏡像的時候就要使用composer或者npm把包從新裝回去,因此Dockerfile大概長這樣docker

FROM node
COPY . /src
RUN cd /src && npm install

這樣看起來沒什麼問題,可是若是包一旦多起來安裝的時候須要花費很長的時間,修復緊急bug的狀況下等待的時間就是煎熬,那咱們有沒有什麼辦法能讓這個過程更快一些呢?express

咱們知道Docker構建是分層的,一條指令一層,在沒有帶--no-cache=true指令的狀況下若是某一層沒有改動,Docker就不會從新構建這一層而是會使用緩存,先來看看Docker官方文檔的描述npm

  • Starting with a parent image that is already in the cache, the next instruction is compared against all child images derived from that base image to see if one of them was built using the exact same instruction. If not, the cache is invalidated.
  • In most cases, simply comparing the instruction in the Dockerfile with one of the child images is sufficient. However, certain instructions require more examination and explanation.
  • For the ADD and COPY instructions, the contents of the file(s) in the image are examined and a checksum is calculated for each file. The last-modified and last-accessed times of the file(s) are not considered in these checksums. During the cache lookup, the checksum is compared against the checksum in the existing images. If anything has changed in the file(s), such as the contents and metadata, then the cache is invalidated.

簡單來講就是若是第n層有改動,則n層之後的緩存都會失效,大多數狀況下判斷有無改動的方法是判斷這層的指令和緩存中的構建指令是否一致,可是對於COPY和ADD命令會計算鏡像內的文件和構建目錄文件的校驗和而後作比較來判斷本層是否有改動。json

最理想的狀況下,咱們但願package.json或者composer.json變更的時候會從新的安裝包,在沒有變更的狀況下使用緩存縮短構建時間。segmentfault

瞭解上面的規則後咱們再來看看上面那個Dockerfile,若是咱們不修改任何代碼的話第二次構建也是能使用緩存的,可是若是咱們修改了代碼,COPY . /src這層的緩存就會失效,同時下一層的緩存也會失效。可是大多數狀況下,從新構建鏡像就意味着代碼有修改,可是package.jsoncomposer.json這兩個文件並不會頻繁的修改,因此咱們須要把這兩個文件以及install的操做分離出來,因此有緩存

FROM node

COPY package.json /src/package.json
RUN cd /src && npm install

COPY . /src

package.json裏面寫一個依賴包app

{
  "dependencies": {
    "express": "^4.16.4"
  }
}

而後再寫一個index.jscomposer

const app = require('express')();

app.listen(8080)

而後咱們進行第一次構建,看看docker history的輸出ide

LIN2UR:~ lin2ur$ docker history demo
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
3c913c9e997b        6 seconds ago       /bin/sh -c #(nop) COPY dir:e3c12f06720cf5f3b…   1.6MB               
21373087419a        6 seconds ago       /bin/sh -c cd /src && npm install               3MB                 
64896ee5240d        14 seconds ago      /bin/sh -c #(nop) COPY file:87de28b86afd2c1c…   53B

把每一層的IMAGE ID和Dockerfile裏面的指令對應起來就是
64896ee5240d => COPY package.json /src/package.json
21373087419a => RUN cd /src && npm install
3c913c9e997b => COPY . /src

如今咱們來修改一下index.js模擬咱們業務代碼變更而後再進行構建

LIN2UR:~ lin2ur$ docker history demo
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
5d697905ad0a        6 seconds ago       /bin/sh -c #(nop) COPY dir:d698db67dac047bd2…   1.6MB               
21373087419a        4 minutes ago       /bin/sh -c cd /src && npm install               3MB                 
64896ee5240d        4 minutes ago       /bin/sh -c #(nop) COPY file:87de28b86afd2c1c…   53B

能夠看到除了最上一層外其餘兩層的IMAGE ID是沒有變化的,再來看看docker build命令的輸出

LIN2UR:~ lin2ur$ docker build --rm -f "Dockerfile" -t demo .
Sending build context to Docker daemon  1.902MB
Step 1/4 : FROM node
 ---> c63e58f0a7b2
Step 2/4 : COPY package.json /src/package.json
 ---> Using cache
 ---> 64896ee5240d
Step 3/4 : RUN cd /src && npm install
 ---> Using cache
 ---> 21373087419a
Step 4/4 : COPY . /src
 ---> 5d697905ad0a
Successfully built 5d697905ad0a
Successfully tagged demo:latest

能夠看到步驟2和3都使用了緩存,比第一次構建的時間縮短很多。如今咱們在package.json裏面再加一個包模擬依賴包變更

LIN2UR:~ lin2ur$ docker history demo
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
020ce95b1987        29 seconds ago      /bin/sh -c #(nop) COPY dir:ea4d7afd475895520…   1.6MB               
d9697dfc7022        31 seconds ago      /bin/sh -c cd /src && npm install               3MB                 
71d8a2fb458a        38 seconds ago      /bin/sh -c #(nop) COPY file:87bd25345a96e6b3…   51B

此次底下兩層的IMAGE ID都變了,意味着沒有使用緩存,再來看看docker build命令的輸出

LIN2UR:~ lin2ur$ docker build --rm -f "Dockerfile" -t demo .
Sending build context to Docker daemon  1.902MB
Step 1/4 : FROM node
 ---> c63e58f0a7b2
Step 2/4 : COPY package.json /src/package.json
 ---> 71d8a2fb458a
Step 3/4 : RUN cd /src && npm install
 ---> Running in ce424d6af936
Step 4/4 : COPY . /src
 ---> 020ce95b1987
Successfully built 020ce95b1987
Successfully tagged demo:latest

因爲第二層的package.json改動致使這層及後續的緩存失效,而後從新安裝包,實現了咱們但願的結果。

來源:http://www.javashuo.com/article/p-uofibqgh-ct.html

相關文章
相關標籤/搜索