一、環境說明
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/