多階段構(gòu)建是 Docker 17.05 及更高版本提供的新功能叁怪。這對致力于優(yōu)化 Dockerfile 的人來說族展,使得 Dockerfile 易于閱讀和維護森缠。
在多階段構(gòu)建之前
關(guān)于構(gòu)建鏡像最具挑戰(zhàn)性的事情之一是保持鏡像體積小巧。 Dockerfile 中的每條指令都會在鏡像中增加一層仪缸,并且在移動到下一層之前贵涵,需要記住清除不需要的構(gòu)件。要編寫一個非常高效的 Dockerfile恰画,你通常需要使用 shell 技巧和其它方式來盡可能地減少層數(shù)宾茂,并確保每一層都具有上一層所需的構(gòu)件,而其它任何東西都不需要拴还。
實際上最常見的是刻炒,有一個 Dockerfile 用于開發(fā)(其中包含構(gòu)建應(yīng)用程序所需的所有內(nèi)容),而另一個裁剪過的用于生產(chǎn)環(huán)境自沧,它只包含您的應(yīng)用程序以及運行它所需的內(nèi)容。這被稱為“構(gòu)建器模式”树瞭。但是維護兩個 Dockerfile 并不理想拇厢。
下面分別是一個 Dockerfile.build 和遵循上面的構(gòu)建器模式的 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 .
注意這個例子還使用 Bash 的 && 運算符人為地將兩個 RUN 命令壓縮在一起,以避免在鏡像中創(chuàng)建額外的層晒喷。這很容易失敗孝偎,難以維護。例如凉敲,插入另一個命令時衣盾,很容易忘記繼續(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)您運行 build.sh 腳本時爷抓,它會構(gòu)建第一個鏡像势决,從中創(chuàng)建一個容器,以便將該構(gòu)件復(fù)制出來蓝撇,然后構(gòu)建第二個鏡像果复。 這兩個鏡像會占用您的系統(tǒng)的空間。
使用多階段構(gòu)建
在多階段構(gòu)建中渤昌,您需要在 Dockerfile 中多次使用 FROM 聲明虽抄。每次 FROM 指令可以使用不同的基礎(chǔ)鏡像,并且每次 FROM 指令都會開始新階段的構(gòu)建独柑。您可以選擇將構(gòu)件從一個階段復(fù)制到另一個階段迈窟,在最終鏡像中,不會留下您不需要的所有內(nèi)容忌栅。為了演示這是如何工作的车酣,讓我們調(diào)整前一節(jié)中的 Dockerfile 以使用多階段構(gòu)建。
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。 不需要另外的構(gòu)建腳本骇径。只需運行 docker build 即可躯肌。
$ docker build -t alexellis2/href-counter:latest .
最終的結(jié)果是和以前體積一樣小的生產(chǎn)鏡像,復(fù)雜性顯著降低破衔。您不需要創(chuàng)建任何中間鏡像清女,也不需要將任何構(gòu)件提取到本地系統(tǒng)。
它是如何工作的呢晰筛?第二條 FROM 指令以 alpine:latest 鏡像作為基礎(chǔ)開始新的建造階段嫡丙。COPY --from=0 這一行將剛才前一個階段產(chǎn)生的構(gòu)件復(fù)制到這個新階段。Go SDK和任何中間構(gòu)件都被留在那里读第,而不會保存到最終的鏡像中曙博。
命名您的構(gòu)建階段
默認情況下,這些階段沒有命名怜瞒,您可以通過它們的整數(shù)來引用它們父泳,從第一個 FROM 指令的 0 開始。但是吴汪,你可以通過在 FROM 指令中使用 as來為階段命名惠窄。以下示例通過命名階段并在 COPY 指令中使用名稱來改進前一個示例。這意味著漾橙,即使您的 Dockerfile 中的指令稍后重新排序杆融,COPY 也不會出問題。
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"]