透過 Multi-Stage Builds 改善持續交付流程
學習利用 Docker multi-stage builds
文章目錄
製作容器映像檔時有個常見的原則: 映像檔越小越好
但針對編譯式語言 (e.g. go, java),由於映像檔本身包含編譯環境、相依套件導致最終的映像檔十分肥大。在文章「打造最小 Go Docker Image」,我們必須先編譯好所需的 artifacts 再放進 scratch
裡面。可以想見對於一個建構持續交付的流水線 (Continuous Delivery Pipeline) 的開發人員而言,上述步驟會變得過於臃腫與繁雜。
為解決此問題在 Docker 17.05+ 以後提供名為「多階段構建 (multi-stage builds)
」的新功能,透過將原先流水線中的多個映像檔整合進同個 Dockerfile 內,而後續的映像檔可透過指令取得中間映像檔 (intermediate image) 所產生的檔案 (artifacts),如此便能讓整個過程更為簡單,也確保流水線簡潔易懂及提高維護性。
多階段構建 (multi-stage builds) 範例 dockerfile
FROM alpine AS base
RUN apk add --no-cache curl wget
FROM golang:1.9.2 AS go-builder
WORKDIR /go
COPY *.go /go/
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o main .
FROM base
COPY --from=go-builder /go/main /main
CMD ["/main"]
我們用示意圖解釋整個建構流程中間到底發生什麼事
FROM alpine AS base
RUN apk add --no-cache curl wget
- 利用
as <NAME>
將 stage 0 命名為base
- 以
alpine
為 parent image 建立中間映像檔,並在裡面安裝所需的套件 (curl, wget)
FROM golang:1.9.2 AS go-builder
WORKDIR /go
COPY *.go /go/
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o main .
- 將 stage 1 命名為
go-builder
- 編譯 packages & dependencies
FROM base
COPY --from=go-builder /go/main /main
CMD ["/main"]
- 利用 stage 0 (stage base) 建立好的中間映像檔作為 parent image
- 利用
COPY
取得剛剛在 stage 1 (stage go-builder) 產生的 artifactsCOPY --from=1
COPY --from=<intermediate_image_name>
FROM <base_image> as <stage_name>
- 記得為每個 stage 命名,提高整體可讀性
- 未來若有更多 stage 加入時,可以不需跟著修改現存的
COPY
指令
COPY --from=<stage_name>
- 多利用 stage 名字取代數字以提高可讀性及維護性
構建映像檔
$ docker build -t multi_stage_minimal_go_docker_img .
可以看到類似下面的 log outputs
Sending build context to Docker daemon 3.072kB
Step 1/9 : FROM alpine AS base
---> 3fd9065eaf02
Step 2/9 : RUN apk add --no-cache curl wget
---> Running in c1c75ffbfef4
fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/community/x86_64/APKINDEX.tar.gz
(1/5) Installing ca-certificates (20171114-r0)
(2/5) Installing libssh2 (1.8.0-r2)
(3/5) Installing libcurl (7.59.0-r0)
(4/5) Installing curl (7.59.0-r0)
(5/5) Installing wget (1.19.2-r1)
Executing busybox-1.27.2-r7.trigger
Executing ca-certificates-20171114-r0.trigger
OK: 6 MiB in 16 packages
Removing intermediate container c1c75ffbfef4
---> 6061a54c31c9
Step 3/9 : FROM golang:1.9.2 AS go-builder
---> 138bd936fa29
Step 4/9 : WORKDIR /go
Removing intermediate container e7a1f8df0451
---> 8f440d314727
Step 5/9 : COPY *.go /go/
---> 95f1803ce6b3
Step 6/9 : RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o main .
---> Running in 72cba3033d5c
Removing intermediate container 72cba3033d5c
---> 9a8875b6eec1
Step 7/9 : FROM base
---> 6061a54c31c9
Step 8/9 : COPY --from=go-builder /go/main /main
---> 99e564fa87b8
Step 9/9 : CMD ["/main"]
---> Running in 7751604e5d0c
Removing intermediate container 7751604e5d0c
---> 50aa7d144269
Successfully built 50aa7d144269
Successfully tagged multi_stage_minimal_go_docker_img:latest
透過整合多個 stage 來解決原本繁複的步驟,是不是讓建構持續交付流水線時的負擔降低不少了呢?(笑)
- https://blog.alexellis.io/mutli-stage-docker-builds/
- https://docs.docker.com/develop/develop-images/multistage-build/
相關文章
- Adopting Container and Kubernetes in Production
- Fission 1.4 更新重點聚光燈
- 如何直接在 Minikube 內構建容器映像檔
- Fission x Istio 迸出新滋味
- Kubernetes Service 深度剖析 - 標籤對於 Service 的影響
文章內容的轉載、重製、發佈,請註明出處: https://tachingchen.com/tw/
Twitter
Google+
Facebook
Reddit
LinkedIn
StumbleUpon
Pinterest
Email