寫在前面
在Docker Engine 17.05 中引入了多階段構建,以此降低構建複雜度,同時使縮小映象尺寸更為簡單。這篇小作文我們來學習一下如何編寫實現多階段構建的Dockerfile
關於dockerfile基礎編寫可參考之前docker容器dockerfile詳解
一 、不使用多階段構建
我們知道在Dockerfile中每新增一個指令都會在映象中生產新的層,一個高效的Dockerfile應該在繼續下一層之前清除之前所有不需要的資源。
不使用多階段構建時,我們通常會建立兩dockerfile檔案,一個用於開發及編譯應用,另一個用於構建精簡的生產映象。這樣能比較大限度的減小生產映象的大小。
我們以一個go應用來看看。我首先會建立一個dockerfile,構建這個映象的主要目的就是編譯我們的應用。
FROM golang:1.16
WORKDIR /go/src
COPY app.go ./
#go編譯
RUN go build -o myapp app.go
構建映象
[root@localhost dockerfiles]# docker build -t builder_app:v1 .
Sending build context to Docker daemon 3.072kB
Step 1/4 : FROM golang:1.16
---> 019c7b2e3cb8
Step 2/4 : WORKDIR /go/src
---> Using cache
---> 15362720e897
Step 3/4 : COPY app.go ./
---> Using cache
---> 8f14ac97a68a
Step 4/4 : RUN go build -o myapp app.go
---> Running in 4368cc4617a7
Removing intermediate container 4368cc4617a7
---> 631f67587803
Successfully built 631f67587803
Successfully tagged builder_app:v1
這樣在這個映象中就包含了我們編譯後的應用myapp,現在我們可以建立容器將myapp拷貝到宿主機等待後續使用。
# docker create --name builder builder_app:v1
fafc1cf7ffa42e06d19430b807d24eafe0bf731fc45ff0ecf31ada5a6075f1d5
# docker cp builder:/go/src/myapp ./
我們有了應用,下一步就是構建生產映象
FROM scratch
WORKDIR /server
COPY myapp ./
CMD ["./myapp"]
由於此時我們不需要其他依賴環境,所以我們採用scratch這個空映象,不僅可以減小容器尺寸,還可以提高安全性。
構建映象
#docker build --no-cache -t server_app:v1 .
我們看一次構建的兩個映象大小
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
server_app v1 6ebc0833cad0 6 minutes ago 1.94MB
builder_app v1 801f0b615004 23 minutes ago 921MB
顯然在不使用多階段構建時,我們也可以構建出生產映象,但是我們需要維護兩個dockerfile,需要將app遺留到本地,並且帶來了更多儲存空間開銷。在使用多階段構建時能比較好的解決以上問題。
二、使用多階段構建
在一個Dockerfile中使用多個FROM
指令,每個FROM
都可以使用不同的基映象,並且每條指令都將開始新階段構建。在多階段構建中,我們可以將資源從一個階段複製到另一個階段,在最終映象中只保留我們所需要的內容。
我們將上面例項的兩個Dockerfile合併為如下:
#階段1
FROM golang:1.16
WORKDIR /go/src
COPY app.go ./
RUN go build app.go -o myapp
#階段2
FROM scratch
WORKDIR /server
COPY --from=0 /go/src/myapp ./
CMD ["./myapp"]
構建映象
# docker build --no-cache -t server_app:v2 .
檢視構建好的映象
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
server_app v2 20225cb1ea6b 12 seconds ago 1.94MB
這樣我們無需建立額外映象,以更簡單的方式構建出了同樣微小的目標映象。可以看到在多階段構建dockerfile中最關鍵的是COPY --from=0 /go/src/myapp ./
通過 --from=0
指定我們資源來源,這裡的0即是指第一階段。
命令構建階段
預設情況下構建階段沒有名稱,我們可以通過整數0~N來引用,即第一個from從0開始。其實我們還可以在FROM
指令中新增AS <NAME>
來命名構建階段,接著在COPY指令中通過<NAME>
引用。我們對上面dockerfile修改如下:
#階段1命名為builder
FROM golang:1.16 as builder
WORKDIR /go/src
COPY app.go ./
RUN go build app.go -o myapp
#階段2
FROM scratch
WORKDIR /server
#通過名稱引用
COPY --from=builder /go/src/myapp ./
CMD ["./myapp"]
只構建某個階段
構建映象時,您不一定需要構建整個 Dockerfile,我們可以通過--target
引數指定某個目標階段構建,比如我們開發階段我們只構建builder階段進行測試。
#docker build --target builder -t builder_app:v2 .
使用外部映象
使用多階段構建時,我們侷限於從之前在 Dockerfile 中建立的階段進行復制。還可以使用COPY --from
指令從單獨的映象複製,如本地映象名稱、本地或 Dockerhub上可用的標籤或標籤 ID。Docker 客戶端在必要時會拉取需要的映象到本地。
COPY --from httpd:latest /usr/local/apache2/conf/httpd.conf ./httpd.conf
從上一階段建立新的階段
我們可以通過FROM指令來引用上一階段作為新階段的開始
#階段1命名為builder
FROM golang:1.16 as builder
WORKDIR /go/src
COPY app.go ./
RUN go build app.go -o myapp
#階段2
FROM builder as builder_ex
ADD dest.tar ./
...
通過上面我們對dockerfile多階段構建有了一個整體的瞭解。
NEXT
- Dockerfile 與Docker容器安全實踐
希望小作文對你有些許幫助,如果內容有誤請指正。
您可以隨意轉載、修改、釋出本文章,無需經過本人同意。 個人blog:iqsing.github.io