簡單來說,多階段。
多階段容許在建立Dockerfile時使用多個from,它很是有用,由於它使咱們可以使用全部必需的工具構建應用程序。舉個例子,首先咱們使用Golang的基礎鏡像,而後在第二階段的時候使用構建好的鏡像的二進制文件,最後階段構建出來的鏡像用於發佈到咱們本身的倉庫或者是用於上線發佈。
在上述的案例中,咱們總共有三個階段:html
在build階段主要是編譯咱們的應用程序,證書認證階段將會安裝咱們所須要的CA證書,最後的生產發佈階段會將咱們構建好的鏡像推到鏡像倉庫中。並且發佈階段將會使用build階段編譯完畢的二進制文件和certs階段安裝的證書。linux
項目發佈的多個build階段git
對於這個方法,咱們將使用一個很是簡單的項目。它只是一個運行在8080端口的HTTP服務,而且返回結果爲傳遞過去的URL的內容結果。github
GET http://localhost:8080?url=https://google.com 返回結果爲goole頁面內容展現。
你也能夠在這裏找到代碼倉庫。
在master分支上只包含了應用程序,final分支上還包含本篇教程中使用的Dockerfile文件
若是你想跟着本教程來作,只須要拉下master上的代碼而且跟着我來建立Dockerfile。golang
第一階段主要是使用Golang基礎鏡像來將咱們的應用程序打包爲二進制文件。這個基礎鏡像包含了將咱們的應用程序編譯成可執行二進制文件的全部工具。
下面是咱們最原始的Dockerfile:spring
1 # 2 # BUILD 階段 3 # 4 FROM golang:1.10 AS build 5 6 # 設置咱們應用程序的工做目錄 7 WORKDIR /go/src/github.com/scboffspring/blog-multistage-go 8 9 # 添加全部須要編譯的應用代碼 10 ADD . . 11 12 # 編譯一個靜態的go應用(在二進制構建中包含C語言依賴庫) 13 RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo . 14 15 # 設置咱們應用程序的啓動命令 16 CMD ["./blog-multistage-go"]
如今咱們進行編譯並使用Docker容器,咱們的應用程序如咱們預期正常運行:docker
docker build -t scboffspring/blog-multistage-go . docker run --rm -ti -p 8080:8080 \ scboffspring/blog-multistage-go
咱們可使用curl命令來請求,而且它會返回http://google.com頁面內容。
在終端運行curl localhost:8080
。json
1 <html itemscope="" itemtype="http://schema.org/WebPage" lang="de-CH"> 2 <head> 3 <meta content="text/html; charset=UTF-8" 4 http-equiv="Content-Type"> 5 <meta content="/images/branding/googleg/1x/googleg_standard_color_128dp.png" 6 itemprop="image"><title>Google</title> 7 ....
讓咱們使用docker images
,來看看鏡像的大小:服務器
REPOSITORY ... SIZE scboffspring/blog-multistage-go ... 818MB
荒唐,太荒唐了,一個這麼小的應用竟然佔了磁盤818M內存空間。
推送到鏡像倉庫後,鏡像大小被壓縮到309M。app
docker hub 佔用309M
接下來咱們來改善這種狀況,把鏡像的大小下降到10M!
上面提供的鏡像是徹底能夠進行部署使用的,可是它真的是太大了。每次在Kubernetes上啓動你的容器時須要拉取309M的鏡像?真的是太浪費時間和帶寬。
讓咱們來爲咱們的鏡像構建一個生產階段,正如上面解釋的,這個階段只是從build階段拷貝二進制文件到容器中。
咱們新的Dockerfile將會以下所示:
1 # 2 # BUILD 階段 3 # 4 FROM golang:1.10 AS build 5 6 # 設置咱們應用程序的工做目錄 7 WORKDIR /go/src/github.com/scboffspring/blog-multistage-go 8 9 # 添加全部須要編譯的應用代碼 10 ADD . . 11 12 # 編譯一個靜態的go應用(在二進制構建中包含C語言依賴庫) 13 RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo . 14 15 # 設置咱們應用程序的啓動命令 16 CMD ["./blog-multistage-go"] 17 18 19 20 # 21 # 生產階段 22 # 23 FROM scratch AS prod 24 25 # 從buil階段拷貝二進制文件 26 COPY --from=build /go/src/github.com/scboffspring/blog-multistage-go/blog-multistage-go . 27 CMD ["./blog-multistage-go"]
如你所見,同一個Dockerfile文件中咱們添加了第二個FROM語句。此次,咱們直接拉取二進制文件,不須要添加任何其餘依賴。
/go/src/github.com/scboffspring/blog-multistage-go/blog-multistage-go
拷貝build階段編譯的文件
簡單吧。
讓咱們像以前同樣編譯並使用Docker容器:
docker build -t scboffspring/blog-multistage-go . docker run --rm -ti -p 8080:8080 \ scboffspring/blog-multistage-go
咱們能夠看到服務正常啓動,也就是意味着它正確的啓動了!咱們完成了!
讓咱們使用docker images
,來看看鏡像的大小:
REPOSITORY ... SIZE scboffspring/blog-multistage-go ... 6.65MB
如咱們以前所說,鏡像的大小變爲10MB如下。並且鏡像被推送到鏡像倉庫後,它只有2MB。當你啓動容器時,只需下載2MB便可,相比於以前節省了大量的時間和帶寬呢。
使用prod階段編譯的容器僅2MB
可是,它在咱們的例子中不起做用。 若是運行curl localhost:8080
,你看到的返回的結果爲500。
curl localhost:8080 500 - Something bad happened
若是你查看容器的日誌,你能夠找到以下錯誤:
發生了一個錯誤:Get http://google.com:X509:加載系統根目錄失敗而且沒有根目錄可使用。
咱們嘗試使用https來鏈接Goole服務器,可是咱們沒有用於驗證Google的SSL證書的CA(證書頒發機構)證書或是其餘網站的CA證書。若是你的應用不須要使用SSL的話,能夠選擇跳到下一節,不然,讓咱們來改善咱們的軟件使得其能夠進行訪問。
1 # 2 # BUILD 階段 3 # 4 FROM golang:1.10 AS build 5 6 # 設置咱們應用程序的工做目錄 7 WORKDIR /go/src/github.com/scboffspring/blog-multistage-go 8 9 # 添加全部須要編譯的應用代碼 10 ADD . . 11 12 # 編譯一個靜態的go應用(在二進制構建中包含C語言依賴庫) 13 RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo . 14 15 # 設置咱們應用程序的啓動命令 16 CMD ["./blog-multistage-go"] 17 18 19 # 20 # CERTS Stage 21 # 22 FROM alpine:latest as certs 23 24 # Install the CA certificates 25 RUN apk --update add ca-certificates 26 27 # 28 # PRODUCTION STAGE 29 # 30 FROM scratch AS prod 31 32 # 從certs階段拷貝CA證書 33 COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt 34 # 從buil階段拷貝二進制文件 35 COPY --from=build /go/src/github.com/scboffspring/blog-multistage-go/blog-multistage-go . 36 CMD ["./blog-multistage-go"]
/etc/ssl/certs/ca-certificates.crt
讓咱們再次編譯並使用Docker容器:
docker build -t scboffspring/blog-multistage-go . docker run --rm -ti -p 8080:8080 \ scboffspring/blog-multistage-go
如今,curl localhost:8080
將會返回真實的頁面!它真的奏效了!
使用docker images
查看,鏡像依然仍是很是小的:
REPOSITORY ... SIZE scboffspring/blog-multistage-go ... 6.89MB
有時候咱們可能會在各個階段爲鏡像建立一個tag,在咱們的示例中,咱們可能也會將build階段產生的結果發佈到Docker,由於它對開發真的十分有用。
要想這樣作的話,只須要在build鏡像的時候簡單的使用--target=NAMEOFTHESTAGE
。
舉個例子:
docker build -t scboffspring/blog-multistage-go:build . --target=build
如今你已經可以爲你的Golang應用程序建立一個很是輕量級的應用程序。階段構建的概念對其餘許多案例也是很是有用的。
我在NodeJS世界中的一個用法是第一階段編譯TypeScript項目。而後第一個階段編譯以便使得該鏡像能夠運行測試。此鏡像也可以用於開發環境,由於它包含了全部開發環境所需的依賴。
當第一階段測試經過後,第二階段只是簡單的安裝項目中的package.json
中的依賴(並非測試環境依賴)。它只將編譯和縮小的代碼複製到鏡像中,而後將該鏡像推送並部署到生產中。