多階段構建是一個新特性,需要 Docker 17.05 或更高版本的守護行程和客戶端,對于那些努力優化 Dockerfiles 并使其易于閱讀和維護的人來說,多階段構建非常有用,
在多階段構建之前
構建鏡像時最具挑戰性的事情之一就是縮小鏡像大小,Dockerfile 中的每一條指令都會在鏡像中添加一個層,在進入下一層之前,您需要記住清除所有不需要的工件,要撰寫一個真正高效的 Dockerfile,傳統上需要使用 shell 技巧和其他邏輯來保持層盡可能小,并確保每一層都有它需要的來自前一層的工件,而沒有其他東西,
實際上,有一個 Dockerfile 用于開發環境(包含構建應用程式所需的所有內容),同時有一個精簡的 Dockerfile 用于生產環境(僅包含應用程式和運行應用程式所需的內容)是非常常見的,這被稱為“建造者模式”,維護兩個 Dockerfiles 并不理想,
這里有一個例子 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 .
請注意,此示例還使用 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 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 工件,
多階段構建極大地簡化了這種情況!
使用多階段構建
對于多階段構建,可以在 Dockerfile 中使用多個 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 開始,但是,您可以通過添加一個 AS <NAME> 到 FROM 指令來命名階段,下面示例通過命名階段并在 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"]
在特定的構建階段停止
在構建映像時,不必構建包括每個階段的整個 Dockerfile,你可以指定目標構建階段,以下命令假設你正在使用之前的 Dockerfile,但是在名為 builder 的階段停止:
$ docker build --target builder -t alexellis2/href-counter:latest .
這可能非常強有力的幾個場景是:
- 除錯一個特定的構建階段
- 使用一個啟用了所有除錯符號或工具的
除錯(debug)階段和一個精益的生產(production)階段 - 使用一個
測驗(testing)階段,在這個階段你的應用會被測驗資料填充,但是在構建產品時,使用一個使用真實資料的不同階段,

使用外部鏡像作為“階段”
當使用多階段構建時,您不受限于從 Dockerfile 中先前創建的階段進行復制,您可以使用 COPY --from 指令從單獨的鏡像中進行復制,可以使用本地鏡像名稱、本地或 Docker 注冊表上可用的標簽或標簽 ID,Docker 客戶端會在必要時拉取鏡像并從中復制工件,語法是:
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
把以前的階段作為新的階段
在使用 FROM 指令時,您可以參考前一階段的內容,例如:
FROM alpine:latest as builder
RUN apk --no-cache add build-base
FROM builder as build1
COPY source1.cpp source.cpp
RUN g++ -o /binary source.cpp
FROM builder as build2
COPY source2.cpp source.cpp
RUN g++ -o /binary source.cpp
作者 : Docker 官網
譯者 : 技術譯民
出品 : 技術譯站
鏈接 : 英文原文
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/537.html
標籤:其他
