R's Workshop

Use Multi-stage Builds to Generate Smaller Docker Images

縮減 image size 除了使用之前提到的 docker export/import 方法外, 另一個作法就是使用 docker 在 v17.05 開始提供的 multi-stage builds 來建制 docker image.

History

在 multi-stage builds 推出以前的作法是使用兩個 Dockerfile: 一個負責產生編譯 binary 的 builder image, 另一個產生實際執行的 main image builder image 產生的 binary 可以透過 host 的 disk 空間傳遞, 或是透過 pipe 將 binary 與 main image 的 Dockerfile 傳給 docker build 執行. 如此, 編譯 binary 產生的中繼檔就只會存在 builder image 中. 詳細作法可以參考這篇文章.

Multi-stage Builds

Multi-stage builds 簡單來說就是把上述兩個 Dockerfile 合併成一個, 並在 Dockerfile 中利用 COPY 指令從 builder image 複製 binary 到 main image 中.

一個簡易的 multiple-stage build 的 Dockerfile 範例如下:

# Builder Stage

FROM ubuntu:20.04 AS common_base_image

RUN apt update; \
    apt install -y git python3; \
    update-alternatives --install /usr/bin/python python /usr/bin/python3 1; \
    rm -rf /var/lib/apt/lists/* && apt clean && apt autoclean; 

# Builder Stage

FROM common_base_image AS repo_downloader
ARG BUILD_ID
LABEL stage=builder
LABEL build=$BUILD_ID

RUN cd /root/; git clone https://gerrit.googlesource.com/git-repo; \
    cd /root/git-repo; git checkout v2.19;

# Main Stage

FROM common_base_image

COPY --from=repo_downloader /root/git-repo/repo /usr/bin/repo

執行 multi-stage build 就下:

$ docker build . -t IMAGE:TAG

Remove Builder Image

Multi-stage 編譯產生的 builder images 會被保留下來, 需要手動刪除. 若要一行指令搞定的話, 可以利用 Dockerfile 裡的 ARGLABEL 命令.

以上面的範例來說, 在要刪除 repo_downloader image 的 build step 加入下面這三行

ARG BUILD_ID
LABEL stage=builder
LABEL build=$BUILD_ID

然後在編譯時, 改成用以下指令

Stop Build at Specific Stage

若只是要 debug 某個 build stage, 可以在 docker build 指定中止在哪個 stage

$ docker build --target STAGE_ALIAS_NAME . -t IMAGE:TAG

Reference

Docker container