Multi-stage Build
在說明 Multi-stage Build 之前,先來簡單了解持續整合(Continuous Integration,以下簡稱 CI)的 Build 與 DevOps 的 Pipeline。
CI 裡面用了 Build 這個關鍵字,實際它背後做的事包含了 compilation、testing、inspection 與 deployment 等;而 DevOps Pipeline 則提到軟體生命週期有 development、testing、deployment 不同的階段(stage),同時每個階段都有可能會產生 artifacts。
對 CI 有興趣可以參考:CI 從入門到入坑
綜合上述說明,在 build 的過程會做不同的任務,並產生 artifacts,而在不同階段又會做不同的任務。
接著來看範例,延續最佳化 Dockerfile 完之後的結果如下:
FROM php:7.3-alpine |
這個 Dockerfile 已可以建置出 Laravel image,但思考以下這兩個奇妙的問題:
- Image 最終要部署到線上環境,任何開發或建置工具(如 Composer)都不需要,該怎麼做?
- Image 若需要做為開發測試共用環境,需要開發工具(如 PHPUnit),該怎麼辦?
目前 Dockerfile 的最佳化方法,面對這兩個問題會是矛盾大對決,要嘛偏維運,要嘛偏開發,無法同時解決。
幾乎所有語言都會有這個問題,所以回頭看完各種框架如何 build image 後,現在再來看這個問題,相信更有感覺。
Dapper
最單純直接的解決方法,就是針對維運與開發寫兩個 Dockerfile,但這在使用上非常不方便,於是第三方 Rancher 就寫了一個工具--Dapper,主要 Dockerfile 作為維運用,而用另一個指令來處理開發用的 Dockerfile。
Multi-stage Build 概念
若需要在開發用的環境上,處理不固定的任務(如:依狀況進入 container 下不同的指令),Dapper 是非常好用的;如果在 container 上是處理固定的任務(如:執行 phpunit
),則使用 Docker 17.05 開始推出的 Multi-stage Build 會更方便。
概念其實很簡單,參考下面的 Dockerfile:
FROM alpine AS build |
這個 Dockerfile 有三個特別的地方跟過去不大一樣:
- 有兩個
FROM
指令,每個FROM
指令都代表一個 stage,每個 stage 的結果都是 image - 第一個
FROM
指令使用AS
可以為 image 取別名 COPY
多了一個選項--from
,選項要給的值是 image,實際行為是從該 image 把對應路徑的檔案或 artifacts,複製進 container
執行過程如下:
- 第一個 stage 產生了 test 檔案
- 第二個 stage 把第一個 stage 產生的 test 檔案複製過來,並使用 ls 觀察
這兩個 stage 是有互相依賴的,因此 touch test
指令若移除的話,就會出現找不到檔案的錯誤。
COPY image 檔案
上面有提到 COPY
的 --form
選項要給值是 image,實際上不只可以使用 stage image,而是連 remote repository 都能使用。因此像 Composer 有 image,且執行檔單一檔案,所以安裝 Composer 的方法,可以改成下面這個寫法:
COPY --from=composer:1 /usr/bin/composer /usr/bin/composer |
FROM stage image
因 stage image 的 alias 可以當作 image 用,所以下面這個寫法是可行的:
FROM alpine AS curl |
實作 Multi-stage Build
最後這裡就舉複雜的範例:一開始提的最佳化 Dockerfile,我們把拿它套用 Multi-stage Build。這個 Dockerfile 它可以分作下面幾個 stage:
# PHP 環境基礎 |
Composer 與上線環境依賴 base 是因為像 bcmath
和 redis
套件依賴是屬於底層共用的套件,所以打一個共用 image 會比較方便一點;另外 Laravel 框架 skeleton 有內帶 npm 相關檔案,這次也加入成一個 stage。
先看 base image,這段應該沒問題,因為它只做安裝 extension:
FROM php:7.3-alpine AS base |
再來 npm image 應該也沒有太大問題:
FROM node:14-alpine AS npm_builder |
Composer image,包括安裝 Composer 的調整,與安裝依賴套件。在這個 stage 還可以做單元測試:
FROM base AS composer_builder |
最後是最難的,正如昨天最後的回顧所提到的,對框架要非常了解,才知道如何哪些檔案該複製,還有先後順序等。
FROM base |
Build image 是以最後一個 stage 為主。結果又會再更小一些。
$ docker images laravel |
今日自我回顧
使用 Multi-stage Build 不但可以減少容量,同時還能使用 Docker 創造多個環境執行建置階段的任務。
當建置階段與執行階段間,使用 artifacts 做區隔的時候,通常都適合使用 Multi-stage Build。如:Golang、Java 等,所有編譯語言,需要編譯並產生 artifacts 才能執行。
- 了解 Multi-stage Build 的概念
- 練習使用 Multi-stage Build 最佳化 Dockerfile