dockerfile指定多from意義

2021-10-08 02:28:38 字數 3672 閱讀 6193

老版本docker中為什麼不支援多個 from 指令

docker 17.05版本以後,新增了dockerfile多階段構建。所謂多階段構建,實際上是允許乙個dockerfile **現多個 from 指令。這樣做有什麼意義呢?

老版本docker中為什麼不支援多個 from 指令

在17.05版本之前的docker,只允許dockerfile**現乙個from指令,這得從映象的本質說起。

在《docker概念簡介》 中我們提到,你可以簡單理解docker的映象是乙個壓縮檔案,其中包含了你需要的程式和乙個檔案系統。其實這樣說是不嚴謹的,docker映象並非只是乙個檔案,而是由一堆檔案組成,最主要的檔案是 層。

dockerfile 中,大多數指令會生成乙個層,比如下方的兩個例子:

from ubuntu:

16.04

run apt-get update \

&&apt-get

install -y --no-install-recommends git \

&&apt-get clean \

&&rm -rf /var/lib/apt/lists/*

from foo
run apt-get update \

&&apt-get

install -y --no-install-recommends nginx \

&&apt-get clean \

&&rm -rf /var/lib/apt/lists/*

假設基礎映象ubuntu:16.04已經存在5層,使用第乙個dockerfile打包成映象foo,則foo6層,又使用第二個dockerfile打包成映象bar,則bar中有7層。

如果 ubuntu:16.04 等其他映象不算,如果系統中只存在 foo 和 bar 兩個映象,那麼系統中一共儲存了多少層呢?

是7層,並非13層,這是因為,foo和bar共享了6層。層的共享機制可以節約大量的磁碟空間和傳輸頻寬,比如你本地已經有了foo映象,又從映象倉庫中拉取bar映象時,只拉取本地所沒有的最後一層就可以了,不需要把整個bar映象連根拉一遍。但是層共享是怎樣實現的呢?

原來,docker映象的每一層只記錄檔案變更,在容器啟動時,docker會將映象的各個層進行計算,最後生成乙個檔案系統,這個被稱為 聯合掛載。對此感興趣的話可以進入了解一下 aufs。

docker的各個層是有相關性的,在聯合掛載的過程中,系統需要知道在什麼樣的基礎上再增加新的檔案。那麼這就要求乙個docker映象只能有乙個起始層,只能有乙個根。所以,dockerfile中,就只允許乙個 from指令。因為多個 from 指令會造成多根,則是無法實現的。但為什麼 docker 17.05 版本以後允許 dockerfile支援多個 from 指令了呢,莫非已經支援了多根?

多個 from 指令的意義

多個 from 指令並不是為了生成多根的層關係,最後生成的映象,仍以最後一條 from 為準,之前的 from 會被拋棄,那麼之前的from 又有什麼意義呢?

每一條 from 指令都是乙個構建階段,多條 from 就是多階段構建,雖然最後生成的映象只能是最後乙個階段的結果,但是,能夠將前置階段中的檔案拷貝到後邊的階段中,這就是多階段構建的最大意義。

最大的使用場景是將編譯環境和執行環境分離,比如,之前我們需要構建乙個go語言程式,那麼就需要用到go命令等編譯環境,我們的dockerfile可能是這樣的:

from golang:1.10.3
copy server.go /build/
workdir /build
run cgo_enabled=0 goos=linux goarch=amd64 goarm=6 go build -ldflags '-w -s' -o server
entrypoint [

"/build/server"

]

基礎映象 golang:1.10.3 是非常龐大的,因為其中包含了所有的go語言編譯工具和庫,而執行時候我們僅僅需要編譯後的 server 程式就行了,不需要編譯時的編譯工具,最後生成的大體積映象就是一種浪費。

使用脈衝雲的解決辦法是將程式編譯和映象打包分開,使用脈衝雲的編譯構建服務,選擇增加構go語言構建工具,然後在構建步驟中編譯。

最後將編譯介面拷貝到映象中就行了,那麼dockerfile的基礎映象並不需要包含go編譯環境:

from scratch
copy server /server
entrypoint [

"/server"

]

在 docker 17.05版本以後,就有了新的解決方案,直接乙個dockerfile就可以解決:

from golang:1.10.3

copy server.go /build/

workdir /build

run cgo_enabled=0 goos=linux goarch=amd64 goarm=6 go build -ldflags '-w -s' -o server

from scratch

copy --from=0 /build/server /

entrypoint [

"/server"

]

這個 dockerfile 的玄妙之處就在於 copy 指令的 --from=0 引數,從前邊的階段中拷貝檔案到當前階段中,多個from語句時,0代表第乙個階段。除了使用數字,我們還可以給階段命名,比如:

from golang:1.10.3 as builder
from scratch
copy --from=builder /build/server /
更為強大的是,copy --from 不但可以從前置階段中拷貝,還可以直接從乙個已經存在的映象中拷貝。比如,

from ubuntu:16.04

copy --from=quay.io/coreos/etcd:v3.3.9 /usr/local/bin/etcd /usr/local/bin/

我們直接將etcd映象中的程式拷貝到了我們的映象中,這樣,在生成我們的程式映象時,就不需要原始碼編譯etcd了,直接將官方編譯好的程式檔案拿過來就行了。

有些程式要麼沒有apt源,要麼apt源中的版本太老,要麼乾脆只提供原始碼需要自己編譯,使用這些程式時,我們可以方便地使用已經存在的docker映象作為我們的基礎映象。但是我們的軟體有時候可能需要依賴多個這種檔案,我們並不能同時將 nginx 和 etcd 的映象同時作為我們的基礎映象(不支援多根),這種情況下,使用 copy --from 就非常方便實用了。參考資料:脈衝雲dockerfile多階段構建

Dockerfile檔案 FROM指定基礎映象

第二次docker images 檢視映象id,寫到from後,例如 from b7dff675qwe as build from或 from from指定乙個基礎映象,一般情況下乙個可用的dockerfile一定是from為第乙個指令。至於image則可以是任何合理存在的image映象。from一...

多網絡卡指定網絡卡傳送資料

環境 2個網絡卡,分別在不同網路環境。區域網一 閘道器 10.9.16.254 區域網二 閘道器 10.9.24.1 電腦位址2 10.9.24.88 如下 include stdafx.h include include pragma comment lib,ws2 32.lib includev...

多網絡卡指定網絡卡傳送資料

環境 2個網絡卡,分別在不同網路環境。區域網一 閘道器 10.9.16.254 區域網二 閘道器 10.9.24.1 電腦位址2 10.9.24.88 如下 include stdafx.h include include pragma comment lib,ws2 32.lib include ...