?
This document uses PHP Chinese website manual Release
多階段構(gòu)建是Docker 17.05中的一項(xiàng)新功能,對(duì)于那些努力優(yōu)化Docker文件的人來(lái)說(shuō),他們會(huì)很激動(dòng),同時(shí)讓他們易于閱讀和維護(hù)。
致謝:特別感謝Alex Ellis授予他使用他的博客文章構(gòu)建器模式與Docker中的多階段構(gòu)建的權(quán)限,作為以下示例的基礎(chǔ)。
關(guān)于構(gòu)建圖像最具挑戰(zhàn)性的事情之一是保持圖像的大小。Dockerfile中的每條指令都會(huì)為圖像添加一個(gè)圖層,并且您需要記住在移動(dòng)到下一圖層之前清理不需要的任何工件。為了編寫(xiě)一個(gè)非常高效的Dockerfile,傳統(tǒng)上需要使用shell技巧和其他邏輯來(lái)盡可能地減小圖層,并確保每個(gè)圖層都具有它從上一圖層需要的構(gòu)件,而不是其他任何東西。
實(shí)際上,有一個(gè)Dockerfile用于開(kāi)發(fā)(其中包含構(gòu)建應(yīng)用程序所需的所有內(nèi)容)以及一個(gè)用于生產(chǎn)的瘦客戶(hù)端,它只包含您的應(yīng)用程序以及運(yùn)行它所需的內(nèi)容。這被稱(chēng)為“建造者模式”。維護(hù)兩個(gè)Dockerfiles并不理想。
這里有一個(gè)例子Dockerfile.build
和Dockerfile
它遵守上面建造者模式:
Dockerfile.build
*
FROM golang:1.7.3WORKDIR /go/src/github.com/alexellis/href-counter/RUN go get -d -v golang.org/x/net/html 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 .
請(qǐng)注意,此示例還RUN
使用Bash &&
運(yùn)算符人為地壓縮兩個(gè)命令,以避免在圖像中創(chuàng)建額外的圖層。這很容易失敗并且很難維護(hù)。例如,插入另一個(gè)命令并忘記繼續(xù)使用該\
字符行很容易。
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 create --name extract alexellis2/href-counter:build docker cp extract:/go/src/github.com/alexellis/href-counter/app ./app docker rm -f extract echo Building alexellis2/href-counter:latest docker build --no-cache -t alexellis2/href-counter:latest .rm ./app
當(dāng)你運(yùn)行這個(gè)build.sh
腳本時(shí),它需要構(gòu)建第一個(gè)圖像,從中創(chuàng)建一個(gè)容器以便將該構(gòu)件復(fù)制出來(lái),然后構(gòu)建第二個(gè)圖像。這兩個(gè)圖像都占用了系統(tǒng)空間,并且您app
的本地磁盤(pán)上仍然存在工件。
多階段構(gòu)建大大簡(jiǎn)化了這種情況!
使用多階段構(gòu)建,您可以在Dockerfile中使用多個(gè)FROM
語(yǔ)句。每條FROM
指令都可以使用不同的基礎(chǔ),并且每條指令都開(kāi)始構(gòu)建的新階段。您可以選擇性地將工件從一個(gè)階段復(fù)制到另一個(gè)階段,在最終圖像中留下不需要的所有內(nèi)容。為了演示這是如何工作的,讓我們修改上一節(jié)中的Dockerfile以使用多階段構(gòu)建。
Dockerfile
*
FROM golang:1.7.3WORKDIR /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"]
你只需要單個(gè)Dockerfile。您也不需要單獨(dú)的構(gòu)建腳本。就跑吧docker build
。
$ docker build -t alexellis2/href-counter:latest .
最終的結(jié)果是與以前相同的小型生產(chǎn)映像,并顯著降低了復(fù)雜性。您不需要?jiǎng)?chuàng)建任何中間圖像,也不需要將任何工件提取到本地系統(tǒng)。
它是如何工作的?第二FROM
條指令以alpine:latest
圖像為基礎(chǔ)開(kāi)始新的構(gòu)建階段。該COPY --from=0
行只將前一階段構(gòu)建的工件復(fù)制到這個(gè)新階段。Go SDK和任何中間工件都被留下,并未保存在最終圖像中。
默認(rèn)情況下,這些階段未被命名,并且您可以用它們的整數(shù)來(lái)引用它們,從第一FROM
條指令的第一個(gè)0開(kāi)始。但是,您可以通過(guò)as <NAME>
在FROM
指令中添加一個(gè)名稱(chēng)來(lái)命名您的階段。此示例通過(guò)命名階段并在COPY
指令中使用名稱(chēng)來(lái)改進(jìn)前一個(gè)示例。這意味著即使Dockerfile中的指令稍后重新排序,COPY
也不會(huì)中斷。
FROM golang:1.7.3 as builder 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=builder /go/src/github.com/alexellis/href-counter/app .CMD ["./app"]
查看博客文章Builder模式與Docker中的多階段構(gòu)建,了解完整的源代碼以及這些示例的演練。