Docker多階段構建最佳實踐——Doker官方指南

預計閱讀全文時間: 6分鐘

多階段構建是一項新功能,需要在守護程序和客戶端上使用Docker 17.05或更高版本。 多階段構建對於任何努力優化Dockerfiles的人都是有用的,同時使它們易於閱讀和維護。


Docker多階段構建最佳實踐——Doker官方指南


致謝 :特別感謝Alex Ellis允許使用他的博客文章Builder模式與Docker中的多階段構建作為以下示例的基礎。

在多階段構建之前

關於構建鏡像,最具挑戰性的事情之一是保持鏡像尺寸變小。 Dockerfile中的每條指令都會在鏡像上添加一層,您需要記住在移至下一層之前清除不需要的任何組件。 為了編寫一個真正有效的Dockerfile,傳統上,您需要使用Shell技巧和其他邏輯來使各層儘可能小,並確保每一層都具有上一層所需的組件,而沒有其他任何東西。

實際上,通常有一個Dockerfile用於開發(包含構建應用程序所需的一切),而一個精簡的Dockerfile用於生產時,僅包含您的應用程序以及運行它所需的內容。 這被稱為“構建者模式”。 維護兩個Dockerfile是不理想的。

這是遵循上述構建器模式的Dockerfile.build和Dockerfile的示例:

Dockerfile.build

<code>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 .//<code>

請注意,此示例還使用Bash &&運算符將兩個RUN命令人工壓縮在一起,以避免在鏡像中創建額外的鏡像層。 這是容易失敗的並且難以維護。 例如,插入另一個命令很容易,而忘記使用\\字符繼續該行。

Dockerfile

<code>FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app .
CMD ["./app"] /<code>

build.sh

<code>#!/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/<code>

運行build.sh腳本時,它需要構建第一個鏡像,從中創建一個容器以複製組件,然後構建第二個鏡像。 這兩個鏡像都佔用了系統空間,並且本地磁盤上仍然有app構件。

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

使用多階段構建

通過多階段構建,您可以在Dockerfile中使用多個FROM語句。 每個FROM指令可以使用不同的基礎,並且每個都開始構建的新階段。 您可以有選擇地將組件從一個階段複製到另一個階段,從而在最終鏡像中留下不需要的所有內容。 為了展示它是如何工作的,讓我們改編上一部分中的Dockerfile以使用多階段構建。

Dockerfile

<code>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"]  /<code>

您只需要單個Dockerfile。 您也不需要單獨的構建腳本。 只需運行docker build 。

<code>$ docker build -t alexellis2/href-counter:latest .//<code>

最終結果是與以前相同的微小生產鏡像,並大大降低了複雜性。 您無需創建任何中間鏡像,也不需要將任何組件提取到本地系統。

它是如何工作的? 第二條FROM指令以alpine:latest鏡像為基礎開始新的構建階段。 COPY --from=0行僅將之前階段的構建組件複製到此新階段。 Go SDK和任何中間組件都被保留了下來,沒有保存在最終鏡像中。

命名您的構建階段

默認情況下,未命名階段,您可以通過它們的整數來引用它們,對於第一個FROM指令,它們以0開頭。 但是,可以通過在FROM指令中添加AS <name>來命名階段。 本示例通過命名階段並使用COPY指令中的名稱來改進前一個示例。 這意味著,即使稍後對Dockerfile中的指令進行了重新排序, COPY也不會中斷。/<name>

<code>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"]  /<code>

在特定的構建階段停止

構建鏡像時,不一定需要構建包括每個階段的整個Dockerfile。 您可以指定目標構建階段。 以下命令假定您正在使用以前的Dockerfile但在名為builder的階段停止:

<code>$ docker build --target builder -t alexellis2/href-counter:latest .//<code> 

一些可能非常強大的方案是:

  • 調試特定的構建階段
  • 使用啟用了所有調試符號或工具的debug階段以及精益production階段
  • 使用testing階段,在該階段中,您的應用將填充測試數據,但使用另一個使用真實數據的階段進行生產構建

將外部鏡像用作“階段”

使用多階段構建時,您不僅限於從之前在Dockerfile中創建的階段進行復制。 您可以使用COPY --from指令從單獨的鏡像進行復制,方法是使用本地鏡像名稱,本地或Docker註冊表中可用的標籤或標籤ID。 Docker客戶端在必要時提取鏡像並從那裡複製組件。 語法為:

<code>COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf/<code>

將上一個階段用作新階段

在使用FROM指令時,可以通過引用前一個階段結束的地方來進行選擇。 例如:

<code>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/<code>
Docker多階段構建最佳實踐——Doker官方指南


譯文鏈接:https://docs.docker.com/develop/develop-images/multistage-build/


分享到:


相關文章: