優化加速 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 吧!

本文環境

什麼是 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

yarnnpm 同樣都能夠設定 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!

References

Docker docs - Mounts

對抗久坐職業傷害

研究指出每天增加 2 小時坐著的時間,會增加大腸癌、心臟疾病、肺癌的風險,也造成肩頸、腰背疼痛等常見問題。

然而對抗這些問題,卻只需要工作時定期休息跟伸展身體即可!

你想輕鬆改變現狀嗎?試試看我們的 PomodoRoll 番茄鐘吧! PomodoRoll 番茄鐘會根據你所設定的專注時間,定期建議你 1 項辦公族適用的伸展運動,幫助你打敗久坐所帶來的傷害!

贊助我們的創作

看完這篇文章了嗎? 休息一下,喝杯咖啡吧!

如果你覺得 MyApollo 有讓你獲得實用的資訊,希望能看到更多的技術分享,邀請你贊助我們一杯咖啡,讓我們有更多的動力與精力繼續提供高品質的文章,感謝你的支持!