Linux中國

Docker:使用多階段構建鏡像

多階段構建是 Docker 17.05 及更高版本提供的新功能。這對致力於優化 Dockerfile 的人來說,使得 Dockerfile 易於閱讀和維護。

致謝: 特別感謝 Alex Ellis 授權使用他的關於 Docker 多階段構建的博客文章 Builder pattern vs. Multi-stage builds in Docker 作為以下示例的基礎。

在多階段構建之前

關於構建鏡像最具挑戰性的事情之一是保持鏡像體積小巧。 Dockerfile 中的每條指令都會在鏡像中增加一層,並且在移動到下一層之前,需要記住清除不需要的構件。要編寫一個非常高效的 Dockerfile,你通常需要使用 shell 技巧和其它方式來儘可能地減少層數,並確保每一層都具有上一層所需的構件,而其它任何東西都不需要。

實際上最常見的是,有一個 Dockerfile 用於開發(其中包含構建應用程序所需的所有內容),而另一個裁剪過的用於生產環境,它只包含您的應用程序以及運行它所需的內容。這被稱為「構建器模式」。但是維護兩個 Dockerfile 並不理想。

下面分別是一個 Dockerfile.build 和遵循上面的構建器模式的 Dockerfile 的例子:

Dockerfile.build

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 go get -d -v golang.org/x/net/html 
  && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

注意這個例子還使用 Bash 的 && 運算符人為地將兩個 RUN 命令壓縮在一起,以避免在鏡像中創建額外的層。這很容易失敗,難以維護。例如,插入另一個命令時,很容易忘記繼續使用 `` 字元。

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

當您運行 build.sh 腳本時,它會構建第一個鏡像,從中創建一個容器,以便將該構件複製出來,然後構建第二個鏡像。 這兩個鏡像會佔用您的系統的空間,而你仍然會一個 app 構件存放在你的本地磁碟上。

多階段構建大大簡化了這種情況!

使用多階段構建

在多階段構建中,您需要在 Dockerfile 中多次使用 FROM 聲明。每次 FROM 指令可以使用不同的基礎鏡像,並且每次 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 .

最終的結果是和以前體積一樣小的生產鏡像,複雜性顯著降低。您不需要創建任何中間鏡像,也不需要將任何構件提取到本地系統。

它是如何工作的呢?第二條 FROM 指令以 alpine:latest 鏡像作為基礎開始新的建造階段。COPY --from=0 這一行將剛才前一個階段產生的構件複製到這個新階段。Go SDK 和任何中間構件都被留在那裡,而不會保存到最終的鏡像中。

命名您的構建階段

默認情況下,這些階段沒有命名,您可以通過它們的整數來引用它們,從第一個 FROM 指令的 0 開始。但是,你可以通過在 FROM 指令中使用 as <NAME> 來為階段命名。以下示例通過命名階段並在 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"]

via: https://docs.docker.com/engine/userguide/eng-image/multistage-build/

作者:docker 譯者:iron0x 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國