今天來看看如何利用 cache 讓 build image 更加順利。
Build image 第一次會正常執行每一個指令,第二次如果發現是同一個 commit 上,執行同一個指令,且結果推測不會變的時候,會把前一次執行結果的 commit 直接拿來用,並標上 Using cache
訊息。
---> Using cache
有效利用 cache 可以提升開發或測試 build image 的效率,這也是最佳化 Dockerfile 的一環。
延續使用昨天的 Dockerfile:
FROM php:7.3
WORKDIR /source
COPY . .
RUN curl -sS https://getcomposer.org/installer | php && mv composer.phar /usr/local/bin/composer
RUN apt update && apt install unzip
RUN composer install
CMD ["php", "artisan", "serve", "--host", "0.0.0.0"]
上面這個範例無法有效利用 cache,可以用以下指令做個實驗:
# 先確認 build image 可以抓 cache
make build
# 隨意新增檔案,修改檔案也會有一樣的效果
touch some
# 先確認 build image 無法抓 cache
make build
從這個範例可以發現,程式有做任何修改,build image 都會需要全部重跑,浪費時間也浪費網路頻寬。
而根據 cache 生效的規則,我們只要找到第一個沒有 Using cache
的指令,就有機會知道問題在哪。
Step 1/7 : FROM php:7.3
---> d9b8167b4a1c
Step 2/7 : WORKDIR /source
---> Using cache
---> 6db08839d8a0
Step 3/7 : COPY . .
---> 70a56357ee18
從這個輸出資訊來看,是 COPY . .
沒有使用 cache。這是因為 touch 新檔案也包含在 COPY . .
的範圍裡,但新檔案 Docker 也不知道跟 build image 過程有關係,因此會讓 COPY . .
重新執行,接著後面全部都需要一起重新執行。
類似的問題,COPY
是針對 host 檔案,另外還有一個很像的指令是 ADD
,後面可以接下載檔案的端口,如:
ADD https://example.com/download.zip .
它會在 build image 的時候下載連結的檔案並複製進 container。這功能看似很方便,實際上是會嚴重拖累 build image 時間的。因為一開始有提到:「結果推測不會變的時候」才會使用 cache。上面 ADD
例子的問題點在於,必須要把檔案下載回來才知道檔案內容有沒有被修改過,於是變成每次 build image 都需要下載檔案。
回到範例的 Dockerfile,調整方法很簡單,記住一個原則:
不常變動的 能早做就早做
常變動的 能晚做就晚做
我們把 Dockerfile 分做幾個部分:
概念上來說,環境與工具會是變動最少的,最常變動的則是程式碼。筆者通常全域設定會放在一開始,偶爾在常調整設定的時候,會放在最後方便測試,最終還是要看使用情境與習慣。
根據上述順序調整如下:
FROM php:7.3
# 全域設定
WORKDIR /source
# 安裝環境、安裝工具
RUN curl -sS https://getcomposer.org/installer | php && mv composer.phar /usr/local/bin/composer
RUN apt update && apt install unzip
# 程式碼
COPY . .
RUN composer install
CMD ["php", "artisan", "serve", "--host", "0.0.0.0"]
用一樣的流程測看看,會看到 Using cache
變多,速度也變快很多。
Step 1/7 : FROM php:7.3
---> d9b8167b4a1c
Step 2/7 : WORKDIR /source
---> Using cache
---> 6db08839d8a0
Step 3/7 : RUN curl -sS https://getcomposer.org/installer | php && mv composer.phar /usr/local/bin/composer
---> Using cache
---> c5a5b2b80982
Step 4/7 : RUN apt update && apt install unzip
---> Using cache
---> ddf04cc99574
Step 5/7 : COPY . .
---> d1bc2db13e9f
但一樣卡在時間花最久的 composer install
,它不能放到 COPY . .
的上面,因為它會需要複製進去的 composer.json
與 composer.lock
。
咦?既然它需要這兩個檔,那就先複製進去再執行 composer install
就好了呀!改法如下:
FROM php:7.3
# 全域設定
WORKDIR /source
# 安裝環境、安裝工具
RUN curl -sS https://getcomposer.org/installer | php && mv composer.phar /usr/local/bin/composer
RUN apt update && apt install unzip
# 安裝程式依賴套件
COPY composer.* ./
RUN composer install --no-scripts
# 複製程式碼
COPY . .
RUN composer run post-autoload-dump
CMD ["php", "artisan", "serve", "--host", "0.0.0.0"]
這次會發現 composer install
有用到 cache,而且速度已經快到可以全程錄進 GIF 了。
Sending build context to Docker daemon 501.8kB
Step 1/9 : FROM php:7.3
---> d9b8167b4a1c
Step 2/9 : WORKDIR /source
---> Using cache
---> 6db08839d8a0
Step 3/9 : RUN curl -sS https://getcomposer.org/installer | php && mv composer.phar /usr/local/bin/composer
---> Using cache
---> c5a5b2b80982
Step 4/9 : RUN apt update && apt install unzip
---> Using cache
---> ddf04cc99574
Step 5/9 : COPY composer.* ./
---> Using cache
---> 7e6b6308c79c
Step 6/9 : RUN composer install --no-scripts
---> Using cache
---> 6c8d6d6d168d
Step 7/9 : COPY . .
---> 63ab9135e738
這個 Dockerfile 對 cache 的最佳化就到此結束,從剛實驗看起來,目前改程式重 build image 都非常快速,已達成今天所預期的目標。
最後補充一點,有些情況會希望 Docker 強制重新 build image 而不要使用 cache,這時可以使用之前提供的 Makefile 裡的 make rebuild
指令:
rebuild:
docker build -t=$(IMAGE):$(VERSION) --no-cache .
相信不需要解釋,--no-cache
正是不使用 cache。
今天介紹的最佳化方法,是著重在利用 cache 加速 build image。但不要忘了今天開頭提到的,cache 就是 commit,有時候必須要把它視為 commit 來做最佳化,這將會是明天的主題。
Using cache
在 build image 是怎麼運作的