Docker 映象多階段構建
阿新 • • 發佈:2022-04-29
本文內容來自我參與維護的 《Docker 從入門到實踐》 專案。
之前的做法
在 Docker 17.05 版本之前,我們構建 Docker 映象時,通常會採用兩種方式:
全部放入一個 Dockerfile
一種方式是將所有的構建過程編包含在一個 Dockerfile
中,包括專案及其依賴庫的編譯、測試、打包等流程,這裡可能會帶來的一些問題:
-
Dockerfile
特別長,可維護性降低 - 映象層次多,映象體積較大,部署時間變長
- 原始碼存在洩露的風險
例如
編寫 app.go
檔案,該程式輸出 Hello World!
package main import "fmt" func main(){ fmt.Printf("Hello World!"); }
編寫 Dockerfile.one
檔案
FROM golang:1.9-alpine RUN apk --no-cache add git ca-certificates WORKDIR /go/src/github.com/go/helloworld/ COPY app.go . RUN go get -d -v github.com/go-sql-driver/mysql && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . && cp /go/src/github.com/go/helloworld/app /root WORKDIR /root/ CMD ["./app"]
構建映象
$ docker build -t go/helloworld:1 -f Dockerfile.one .
分散到多個 Dockerfile
另一種方式,就是我們事先在一個 Dockerfile
將專案及其依賴庫編譯測試打包好後,再將其拷貝到執行環境中,這種方式需要我們編寫兩個 Dockerfile
和一些編譯指令碼才能將其兩個階段自動整合起來,這種方式雖然可以很好地規避第一種方式存在的風險,但明顯部署過程較複雜。
例如
編寫 Dockerfile.build
檔案
FROM golang:1.9-alpine RUN apk --no-cache add git WORKDIR /go/src/github.com/go/helloworld COPY app.go . RUN go get -d -v github.com/go-sql-driver/mysql && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
編寫 Dockerfile.copy
檔案
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app .
CMD ["./app"]
新建 build.sh
#!/bin/sh
echo Building go/helloworld:build
docker build -t go/helloworld:build . -f Dockerfile.build
docker create --name extract go/helloworld:build
docker cp extract:/go/src/github.com/go/helloworld/app ./app
docker rm -f extract
echo Building go/helloworld:2
docker build --no-cache -t go/helloworld:2 . -f Dockerfile.copy
rm ./app
現在執行指令碼即可構建映象
$ chmod +x build.sh
$ ./build.sh
對比兩種方式生成的映象大小
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
go/helloworld 2 f7cf3465432c 22 seconds ago 6.47MB
go/helloworld 1 f55d3e16affc 2 minutes ago 295MB
使用多階段構建
為解決以上問題,Docker v17.05 開始支援多階段構建 (multistage builds
)。使用多階段構建我們就可以很容易解決前面提到的問題,並且只需要編寫一個 Dockerfile
:
例如
編寫 Dockerfile
檔案
FROM golang:1.9-alpine
RUN apk --no-cache add git
WORKDIR /go/src/github.com/go/helloworld/
RUN go get -d -v github.com/go-sql-driver/mysql
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/go/helloworld/app .
CMD ["./app"]
構建映象
$ docker build -t go/helloworld:3 .
對比三個映象大小
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
go/helloworld 3 d6911ed9c846 7 seconds ago 6.47MB
go/helloworld 2 f7cf3465432c 22 seconds ago 6.47MB
go/helloworld 1 f55d3e16affc 2 minutes ago 295MB
很明顯使用多階段構建的映象體積小,同時也完美解決了上邊提到的問題。