Docker多階段構建鏡像

一、環境說明

1.ubuntu: 18.04.3 LTS

2.docker: 19.03.5

二、多階段構建(multi-stage builds)

1、原理

多階段構建通過在Dockerfile中使用多個 FROM指令實現。每一條 FROM 指令都是一個構建階段,多個 FROM指令就是多階段構建。多階段構建的意義在於:在構建的過程中,可以選擇性的將前面階段中必要的文件複製到後面的階段中,並拋棄不需要的文件。這樣,最後的鏡像中只保留需要的文件。

2、示例

目錄結構如下:
– Dockerfile
– app.go

(1)app.go

package main

import "fmt"

func main() {
	fmt.Println("Hello, 世界")
}

(2)Dockerfile

FROM golang:1.13.5 
WORKDIR /go/src/github.com/helloworld/
# RUN go get -d -v golang.org/x/net/html  
RUN go get -d -v github.com/go-sql-driver/mysql
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/helloworld/app .
CMD ["./app"]  

說明:

a.第一個FROM: 以1.13.5標籤的go爲基礎鏡像。

b.第一個WORKDIR: 在從go鏡像創建容器時,在容器內部設置工作目錄爲/go/src/github.com/helloworld/目錄, 後續的 RUN go get -d -v github.com/go-sql-driver/mysql等指令會在該目錄下執行。

官方文檔示例用的是 RUN go get -d -v golang.org/x/net/html,因爲網絡原因,修改爲RUN go get -d -v github.com/go-sql-driver/mysql演示。

c.go get:從github上下載mysql driver源碼並編譯生成二進制文件。

d.COPY: 把第2(1)的app.go文件複製到容器的/go/src/github.com/helloworld/目錄。

e.第二個FROM: 這次構建以latest標籤的alpine爲基礎鏡像。

f.第二個WORKDIR:設置工作目錄爲/root/目錄。後續的指令在該目錄下執行。

g.第二個COPY: 把前構建階段(–from=0)的/go/src/github.com/helloworld/app文件複製到 .(/root/)目錄。

(3)build

$ docker build -t sweeneys/helloworld:v1 .

如上所示,通過多個FROM指令,就可以實現多階段構建。每個 FROM指令指定基礎鏡像,結合COPY指令保留前面構建階段生成的必要文件,即可創建新的鏡像。

3、AS:爲某一階段命名

默認情況下,每一個構建階段是沒有命名的,通過其整數編號進行引用(如 --from=2),第一個 FROM指令對應的編號爲0。我們也可以通過 AS <NAME>FROM 指令中爲每個階段命名。

(1)示例

Dockerfile:

FROM golang:1.13.5 AS builder
WORKDIR /go/src/github.com/helloworld/
# RUN go get -d -v golang.org/x/net/html  
RUN go get -d -v github.com/go-sql-driver/mysql
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/helloworld/app .
CMD ["./app"]  

如上所示,第一行我們使用 AS將該階段命名爲 builder, 然後在 COPY命令中通過該名字--from=builder 即可引用。

4、–target: 只構建指定階段鏡像

可以使用 --target構建指定階段的鏡像,該階段鏡像構建完即停止。

(1)示例

該示例使用3(1)的Dockerfile,但是這次只構建第一個FROM指令指定的階段。

$ docker build  --target builder -t sweeneys/helloworld:targetbuilder .
Sending build context to Docker daemon  3.072kB
Step 1/5 : FROM golang:1.13.5 AS builder
 ---> ed081345a3da
Step 2/5 : WORKDIR /go/src/github.com/helloworld/
 ---> Running in 31a2f401fc93
Removing intermediate container 31a2f401fc93
 ---> 449948e706cf
Step 3/5 : RUN go get -d -v github.com/go-sql-driver/mysql
 ---> Running in 285dd69a987f
github.com/go-sql-driver/mysql (download)
Removing intermediate container 285dd69a987f
 ---> 762cee703e08
Step 4/5 : COPY app.go    .
 ---> 64d818cfdb58
Step 5/5 : RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
 ---> Running in d3a40f14a509
Removing intermediate container d3a40f14a509
 ---> 7e49d73e0ff2
Successfully built 7e49d73e0ff2
Successfully tagged sweeneys/helloworld:targetbuilder
SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.

如上所示,只構建builder這個階段,第二個FROM指令指定的階段沒有進行構建,最終生成一個新的鏡像:

$ docker images
REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
sweeneys/helloworld   targetbuilder       7e49d73e0ff2        10 minutes ago      816MB
golang                1.13.5              ed081345a3da        2 days ago          803MB
alpine                latest              cc0abc535e36        7 days ago          5.59MB

5、COPY --from:構建時從其他鏡像複製文件

可以使用COPY --from指令從其他鏡像(如前面階段構建的鏡像,本地鏡像,Docker Hub上的鏡像)

(1)示例

Dockerfile:

FROM golang:1.13.5 AS builder
WORKDIR /go/src/github.com/helloworld/
# RUN go get -d -v golang.org/x/net/html  
RUN go get -d -v github.com/go-sql-driver/mysql
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/helloworld/app .
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
CMD ["./app"]  

如上所示,COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf實現在構建鏡像時複製nginx:latest鏡像的/etc/nginx/nginx.conf文件到容器的根目錄(即 /nginx.conf)。

三、應用場景

(1)編譯環境和應用環境分離。

四、參考資料

[1]docker官方文檔,多階段構建:https://docs.docker.com/develop/develop-images/multistage-build/

發佈了138 篇原創文章 · 獲贊 47 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章