優化加速 docker build 的秘訣 —— 使用 Cache mounts
Posted on Nov 26, 2023 in Docker by Amo Chen ‐ 3 min read
現代開發應用(application)或多或少都會使用到第三方套件,不僅減少自行開發的成本,也加速開發應用的速度,所以各種程式語言都會有所謂的 package manager, 幫助開發者安裝/管理各種第三方套件與套件相依性,例如 npm, yarn, pip, go mod 等等,甚至作業系統也有 package manager 例如著名的 APT(Advanced Packaging Tool), Homebrew 等等。
但是隨著第三方套件越用越多,每次重新編譯(build) Docker 映像檔(image)的時間也會越來越長,因為每次重新 build docker image 都要花費不少時間重新下載並安裝第三方套件⋯⋯。
針對這個問題, Docker 官方文件有提到可以使用 Cache mounts 優化 build 的速度。
一起認識什麼是 Cache mounts 吧!
本文環境
- macOS
- OrbStack
什麼是 Cache mounts?
Cache mounts let you specify a persistent package cache to be used during builds. The persistent cache helps speed up build steps, especially steps that involve installing packages using a package manager. Having a persistent cache for packages means that even if you rebuild a layer, you only download new or changed packages.
Cache mounts 是 1 個可以提供套件(package)快取的功能,這個功能可以在重新編譯 image 時,不需要再次重新下載所有的套件,因為這些套件都已經被 Docker 快取了,如果有新增套件或更新部分套件版本, Docker 也會幫忙下載並快取起來,所以重新編譯時在下載套件的速度會加快非常多!
p.s. 如果是初次編譯 image, 還是需要下載時間,但後續的重新編譯會因為快取省下許多時間
Cache mounts 的使用方式很簡單,就是在 Dockerfile 裡負責安裝套件的 RUN
指令之後加上 --mount=type=cache,target=<path>
參數,例如:
RUN --mount=type=cache,target=/go/pkg/mod go mod download
前述參數中 <path>
部分是指要快取哪個 Docker container 內的路徑,這個路徑必須是 packager manager 所使用的快取路徑,如此一來 Docker container 內的 packager manager 才能正確載入快取,以加快編譯速度。
Cache mounts 使用範例
以下針對 Golang, JavaScript, Python 這 3 個程式語言以及 APT 指令與 Docker cache mounts 的使用方式。
Golang & go mod
Go modules 的 cache 路徑設定在環境變數( GOMODCACHE
)之中,可以用以下指令找到 cache 的路徑:
$ go env GOMODCACHE
如果想知道你所使用的 Docker image 的 GOMODCACHE
的話,可以輸入以下 Docker 指令取得:
$ docker run golang:<版本> go env GOMODCACHE
例如:
$ docker run golang:1.21-alpine go env GOMODCACHE
不出意外的話,上述指令結果會是 /go/pkg/mod
, 也就是 Golang 的 Docker image 預設的 go modules cache 路徑:
接下來只要將 Dockerfile 中下載 go modules 的 RUN
指令,例如:
RUN go mod download
加上 --mount=type=cache,target=/go/pkg/mod
就可以啟用 cache mounts:
RUN --mount=type=cache,target=/go/pkg/mod go mod download
編譯 Go 執行檔的部分,例如:
RUN go build -o /tmp/app .
也要加上 --mount=type=cache,target=/go/pkg/mod
, 使用 cache mounts, 例如:
RUN --mount=type=cache,target=/go/pkg/mod go build -o /tmp/app .
JavaScript & NPM
JavaScript 生態系中的 NPM 也有 cache 的功能,可使用以下指令取得 cache 位置,再依 Go 的範例同樣炮製即可:
$ npm config get cache
不過 npm
指令也有提供設定 cache 的功能,其指令為:
$ npm set cache <path>
因此 RUN npm install
可以進一步改為下列形式,直接將 cache 路徑設定好之後再進行安裝:
RUN --mount=type=cache,target=/usr/src/npm-cache/.npm \
npm set cache /usr/src/npm-cache/.npm &&
npm install
JavaScript & Yarn
除了 npm
之外, yarn
也相當多人使用,取得 yarn
的 cache 路徑指令如下:
$ yarn cache dir
yarn
與 npm
同樣都能夠設定 cache 路徑,其指令如下:
$ yarn config set cache-folder <path>
不過使用 yarn
安裝套件時,可以直接指定 --cache-folder
設定快取,因此使用 yarn
啟用 cache mounts 的方式更簡潔,範例如下:
RUN --mount=type=cache,target=/usr/src/yarn-cache/ \
yarn install --cache-folder /usr/src/yarn-cache/
Python & pip
Pip 20.1 之後提供以下指令得到 cache 的路徑:
$ pip cache dir
除了將上述指令的位置指定為 cache mounts 之外,也可以像 yarn
一樣在安裝時套件時指定 cache 的路徑,其參數為 --cache-dir <路徑>
, 只要將該路徑設定為 cache mounts 即可,例如:
RUN --mount=type=cache,target=/usr/src/pip-cache \
pip install --cache-dir /usr/src/pip-cache -r requirements.txt
apt (Debian-based images)
apt
指令也是 Debian 相關作業系統(Debian, Ubuntu 等等)會使用的套件安裝指令,很多相依套件、工具也都會用 apt
指令進行安裝,因此以 apt
指令安裝套件時,也可以指定 cache mounts 以加快 Docker image 編譯速度。
apt
指令取得 cache 路徑的指令如下:
$ apt-config shell Cache Dir::Cache
預設會是 /var/cache/apt
, 因此為 apt
指令啟用 cache mounts 的話,會是類似以下的範例:
RUN --mount=type=cache,target=/var/cache/apt \
apt-get update && apt-get install -y git
總結
Cache mounts 是 1 個極其實用的技巧,特別是經常需要重新編譯 Docker image 的情況下,使用 cache mounts 可以節省下載時間、加快編譯速度,避免人生都浪費在等編譯的過程!千金難買寸光陰!學起來!很划算!
以上!
Enjoy!