摘要:版本以后,新增了多階段構建。所謂多階段構建,實際上是允許一個中出現多個指令。因為多個指令會造成多根,則是無法實現的。會使用一個完全干凈的文件系統,不包含任何文件??梢允沟米詈笊傻溺R像最小化,其中只包含了程序。
Docker 17.05版本以后,新增了Dockerfile多階段構建。所謂多階段構建,實際上是允許一個Dockerfile 中出現多個 FROM 指令。這樣做有什么意義呢?
老版本Docker中為什么不支持多個 FROM 指令在17.05版本之前的Docker,只允許Dockerfile中出現一個FROM指令,這得從鏡像的本質說起。
在《Docker概念簡介》 中我們提到,你可以簡單理解Docker的鏡像是一個壓縮文件,其中包含了你需要的程序和一個文件系統。其實這樣說是不嚴謹的,Docker鏡像并非只是一個文件,而是由一堆文件組成,最主要的文件是 層。
Dockerfile 中,大多數指令會生成一個層,比如下方的兩個例子:
# 示例一,foo 鏡像的Dockerfile # 基礎鏡像中已經存在若干個層了 FROM ubuntu:16.04 # RUN指令會增加一層,在這一層中,安裝了 git 軟件 RUN apt-get update && apt-get install -y --no-install-recommends git && apt-get clean && rm -rf /var/lib/apt/lists/* # 示例二,bar 鏡像的Dockerfile FROM foo # RUN指令會增加一層,在這一層中,安裝了 nginx 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,則foo有6層,又使用第二個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可能是這樣的:
# Go語言環境基礎鏡像 FROM golang:1.10.3 # 將源碼拷貝到鏡像中 COPY server.go /build/ # 指定工作目錄 WORKDIR /build # 編譯鏡像時,運行 go build 編譯生成 server 程序 RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOARM=6 go build -ldflags "-w -s" -o server # 指定容器運行時入口程序 server ENTRYPOINT ["/build/server"]
基礎鏡像golang:1.10.3是非常龐大的,因為其中包含了所有的Go語言編譯工具和庫,而運行時候我們僅僅需要編譯后的server程序就行了,不需要編譯時的編譯工具,最后生成的大體積鏡像就是一種浪費。
使用脈沖云的解決辦法是將程序編譯和鏡像打包分開,使用脈沖云的編譯構建服務,選擇增加構Go語言構建工具,然后在構建步驟中編譯。
最后將編譯接口拷貝到鏡像中就行了,那么Dockerfile的基礎鏡像并不需要包含Go編譯環境:
# 不需要Go語言編譯環境 FROM scratch # 將編譯結果拷貝到容器中 COPY server /server # 指定容器運行時入口程序 server ENTRYPOINT ["/server"]
提示:scratch 是內置關鍵詞,并不是一個真實存在的鏡像。 FROM scratch 會使用一個完全干凈的文件系統,不包含任何文件。 因為Go語言編譯后不需要運行時,也就不需要安裝任何的運行庫。FROM scratch可以使得最后生成的鏡像最小化,其中只包含了 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代表第一個階段。除了使用數字,我們還可以給階段命名,比如:
# 編譯階段 命名為 builder 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 就非常方便實用了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/97016.html
摘要:容器作為一類操作系統層面的虛擬化技術,其目標是在單一主機交付多套隔離性環境,容器共享同一套主機操作系統內核。與其它容器平臺不同,引入了一整套與容器管理相關的生態系統。每個容器都是相互隔離的保證安全的平臺。 導讀:本文章對Docker技術進行了介紹,闡述了Docker的技術發展歷程、容器與虛擬機的差異、Docker原理、特點、Docker三組件和Docker帶來的影響,為我們進一步理解D...
摘要:導讀要從容器化開始,而容器又需要從開始,本文將介紹如何寫出一個優雅的文件。只要記住以上三點就能寫出不錯的。執行完成項目的構建。 導讀 Kubernetes要從容器化開始,而容器又需要從Dockerfile開始,本文將介紹如何寫出一個優雅的Dockerfile文件。 文章主要內容包括: Docker容器 Dockerfile 使用多階構建 感謝公司提供大量機器資源及時間讓我們可以實踐...
摘要:本文將介紹精簡容器鏡像的必要性并以基于的應用為例描述最小化容器鏡像的常用技巧。經過這一優化,最終鏡像的大小為。 背景 隨著容器技術的普及,越來越多的應用被容器化。人們使用容器的頻率越來越高,但常常忽略一個基本但又非常重要的問題 - 容器鏡像的體積。本文將介紹精簡容器鏡像的必要性并以基于 spring boot 的 java 應用為例描述最小化容器鏡像的常用技巧。 精簡容器鏡像的必要性 ...
摘要:無論這個連接是外部主動建立的,還是內部建立的。協議有表示層數據的表示安全壓縮。在整個發展過程中的所有思想和著重點都以一種稱為的文檔格式存在。 部署基礎知識url:協議://網站地址:端口(/)路徑地址?參數eg: http://www.baidu.com:80/abc/dd/ www.baidu.com找服務器 80端口:找服務器上提供服務的應用 nginx uri:/ab...
摘要:無論這個連接是外部主動建立的,還是內部建立的。協議有表示層數據的表示安全壓縮。在整個發展過程中的所有思想和著重點都以一種稱為的文檔格式存在。 部署基礎知識url:協議://網站地址:端口(/)路徑地址?參數eg: http://www.baidu.com:80/abc/dd/ www.baidu.com找服務器 80端口:找服務器上提供服務的應用 nginx uri:/ab...
閱讀 2645·2021-09-13 10:26
閱讀 1907·2021-09-03 10:28
閱讀 1977·2019-08-30 15:44
閱讀 794·2019-08-29 14:07
閱讀 386·2019-08-29 13:12
閱讀 2144·2019-08-26 11:44
閱讀 2336·2019-08-26 11:36
閱讀 2003·2019-08-26 10:19