Docker 重要更新: 原生支持多階段構建(multi-stage build)

Docker 的口號是 Build, Ship, and Run Any App, Anywhere. 但是我們在應用過程中會遇到一個問題,我們在 build 的時候,把源碼也 build 進去了。 然後就繼續把源碼 Ship 出去嗎?這可不行。所有的編譯型語言都面臨這個困擾。 即使是腳本型語言,build 的時候也會使用很多上線時用不到的構建工具, 而我們希望減小生產鏡像的體積,這樣我們的小鯨魚才能多拉一點集裝箱嘛。

傳統做法

我們最終的目的是要將編譯好的可執行文件複製到 alpine 這樣的迷你鏡像里, 那麼該怎麼弄到編譯好的文件呢?基於 Docker 的思想,我們肯定需要在一個標準容器中編譯, 這樣這個過程才是標準化的,再說,你在 Ubuntu 編譯出一個二進位文件在 alpine 也運行不了。

於是我們先需要準備一個編譯用的自定義鏡像。一般是用相應語言的 alpine 基礎鏡像, 把編譯項目額外需要的各種工具打包進去,比如 golang 目前沒有官方的包管理, 你就需要把你用的包管理工具裝進去。

然後我們需要在運行 container 時把主機的一個目錄通過 -v 掛載到 container上, 讓它把編譯的結果輸出到這個掛載的目錄,這樣我們就在主機上拿到這個文件了。

最後,我們用一個最小的 alpine 鏡像,把二進位文件複製進去。 可能你還需要設置一下時區之類的。

持續集成

上面的流程,在用持續集成工具時又變成了一個問題。你會發現每一家 CI 提供商都不太一樣。 你未必有許可權控制 CI 時的宿主機。

比如 Docker Cloud,你需要定義 pre-build 的 hook 去完成這個工作, 在 SEMAPHORE,你發現你有了一台宿主機,這下和我們在本地的做法可以一樣了。 在更多的提供商,你會發現他們只是能根據 git 倉庫和 Dockerfile 構建鏡像, 你用他們的系統甚至沒辦法做出一個最小鏡像…… 中國的 DaoCloud 其實挺先進的,很早就推出了安全鏡像的概念,讓你的構建通過兩步完成。 但是,那個配置的內容太多讓不太懂的人看了直接暈掉。

官方方案

在2017年5月3日即將發行的 Docker 17.05.0-ce 中,Docker 官方提供了簡便的多階段構建 (multi-stage build) 方案。我用例子為大家介紹下:

FROM muninn/glide:alpine AS build-envnADD . /go/src/appnWORKDIR /go/src/appnRUN glide installnRUN go build -v -o /go/src/app/app-servernnFROM alpinenRUN apk add -U tzdatanRUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtimenCOPY --from=build-env /go/src/app/app-server /usr/local/bin/app-servernEXPOSE 80nCMD ["app-server"]n

首先,第一個 FROM 後邊多了個 AS 關鍵字,可以給這個階段起個名字。 我舉例子這個鏡像是官方 golang:alpine 加上構建工具 glide ,我們照舊安裝依賴, build 出一個二進位程序。

然後,第二部分用了官方的 alpine 鏡像,改變時區到中國,新特性體現在 COPY 關鍵字, 它現在可以接受 --from= 這樣的參數,從上個我們起名字的階段複製文件過來。

就這麼簡單,現在你只需要一個 Dockerfile 就什麼都搞定了。

多項目構建

於是現在你可以把好幾個項目的二進位文件構建在一個迷你鏡像中發布了,繼續舉個栗子:

from debian as build-essentialnarg APT_MIRRORnrun apt-get updatenrun apt-get install -y make gccnworkdir /srcnnfrom build-essential as fooncopy src1 .nrun makennfrom build-essential as barncopy src2 .nrun makennfrom alpinencopy --from=foo bin1 .ncopy --from=bar bin2 .ncmd ...n

這個就是把兩個項目編譯出來的文件最終合併到了一個鏡像里。

好了,祝賀那些不支持多段構建的 CI 服務,Docker 幫你們追平了競爭對手。 我有機會會寫一個支持 Docker 的 CI 的主觀評論,也歡迎大家吐槽各路 CI 給我提供素材。

推薦閱讀:

Docker Remote API 開發(一)
docker的幾點疑問?
什麼是docker鏡像?
docker怎麼修改拉取源從指定的國內倉庫拉取鏡像?
如何基於Docker進行開發?

TAG:Docker | Go语言 | 微服务架构 |