多階段構建是一項新功能,需要守護程序和客戶端上使用Docker 17.05或更高版本。多級構建對於在優化Dockerfile的同時使其易於閱讀和維護的任何人都非常有用。
在進行多階段構建之前
關於構建鏡像,最具挑戰性的事情之一是保持鏡像尺寸變小。Dockerfile中的每條指令都會在映像上添加一層,您需要記住在移至下一層之前清除不需要的任何工件。為了編寫一個真正有效的Dockerfile,傳統上,您需要使用Shell技巧和其他邏輯來使各層儘可能小,並確保每一層都具有上一層所需的工件,而沒有其他任何東西。
實際上,通常只有一個Dockerfile用於開發(包含構建應用程式所需的一切),而精簡的Dockerfile用於生產時,僅包含您的應用程式以及運行該應用程式所需的內容。這被稱為「構建器模式」。維護兩個Dockerfile是不理想的。
這是一個Dockerfile.build和Dockerfile的例子,它遵循上面的模式:
Dockerfile.build:
FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
COPY app.go .
RUN go get -d -v golang.org/x/net/html \
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
請注意,此示例還RUN使用Bash&&運算符將兩個命令人工壓縮在一起,以避免在鏡像中創建額外的圖層。這是容易失敗的並且難以維護。
Dockerfile:
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app .
CMD ["./app"]
build.sh:
#!/bin/sh
echo Building alexellis2/href-counter:build
docker build --build-arg https_proxy=$https_proxy --build-arg http_proxy=$http_proxy \
-t alexellis2/href-counter:build . -f Dockerfile.build
docker container create --name extract alexellis2/href-counter:build
docker container cp extract:/go/src/github.com/alexellis/href-counter/app ./app
docker container rm -f extract
echo Building alexellis2/href-counter:latest
docker build --no-cache -t alexellis2/href-counter:latest .
rm ./app
運行build.sh腳本時,它需要構建第一個鏡像,從中創建一個容器以複製工件,然後構建第二個鏡像。這兩個映像都佔用了系統空間,並且app 本地磁碟上也仍然有工件。
多階段構建極大地簡化了這種情況!
使用多階段構建
通過多階段構建,您可以FROM在Dockerfile中使用多個語句。每個FROM指令可以使用不同的基礎,並且每個指令都
開始構建的新階段。您可以有選擇地將工件從一個階段複製到另一個階段,從而在最終圖像中留下不需要的所有內
容。為了展示它是如何工作的,讓我們改編上一部分中的Dockerfile以使用多階段構建。
Dockerfile:
FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
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/alexellis/href-counter/app .
CMD ["./app"]
您只需要單個Dockerfile。您也不需要單獨的構建腳本。只需運行docker build:
$ docker build -t alexellis2/href-counter:latest .
最終結果是與之前的鏡像大小相同,並大大降低了複雜性。您無需創建任何中間映像,也不需要將任何工件提取到本地系統。