Docker 的口號是 Build, Ship, and Run Any App, Anywhere.可是咱們在應用過程當中會遇到一個問題,咱們在 build 的時候,把源碼也 build 進去了。而後就繼續把源碼 Ship 出去嗎?這可不行。全部的編譯型語言都面臨這個困擾。 即便是腳本型語言,build 的時候也會使用不少上線時用不到的構建工具,而咱們但願減少生產鏡像的體積,這樣咱們的小鯨魚才能多拉一點集裝箱嘛。git
咱們最終的目的是要將編譯好的可執行文件複製到 alpine
這樣的迷你鏡像裏,那麼該怎麼弄到編譯好的文件呢?基於 Docker 的思想,咱們確定須要在一個標準容器中編譯,這樣這個過程纔是標準化的,再說,你在 Ubuntu 編譯出一個二進制文件在 alpine 也運行不了。github
因而咱們先須要準備一個編譯用的自定義鏡像。通常是用相應語言的 alpine 基礎鏡像,把編譯項目額外須要的各類工具打包進去,好比 golang 目前沒有官方的包管理,你就須要把你用的包管理工具裝進去。golang
而後咱們須要在運行 container 時把主機的一個目錄經過 -v 掛載到 container上,讓它把編譯的結果輸出到這個掛載的目錄,這樣咱們就在主機上拿到這個文件了。docker
最後,咱們用一個最小的 alpine
鏡像,把二進制文件複製進去。可能你還須要設置一下時區之類的。安全
上面的流程,在用持續集成工具時又變成了一個問題。你會發現每一家 CI 提供商都不太同樣。你未必有權限控制 CI 時的宿主機。bash
好比 Docker Cloud
,你須要定義 pre-build 的 hook 去完成這個工做,在 SEMAPHORE
,你發現你有了一臺宿主機,這下和咱們在本地的作法能夠同樣了。 在更多的提供商,你會發現他們只是能根據 git 倉庫和 Dockerfile 構建鏡像,你用他們的系統甚至沒辦法作出一個最小鏡像……中國的 DaoCloud
其實挺先進的,很早就推出了安全鏡像的概念,讓你的構建經過兩步完成。可是,那個配置的內容太多讓不太懂的人看了直接暈掉。app
在2017年5月3日即將發行的 Docker 17.05.0-ce
中,Docker 官方提供了簡便的多階段構建(multi-stage build) 方案。我用例子爲你們介紹下:ide
FROM muninn/glide:alpine AS build-env
ADD . /go/src/app WORKDIR /go/src/app RUN glide install RUN go build -v -o /go/src/app/app-server
FROM alpine
RUN apk add -U tzdata RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime COPY --from=build-env /go/src/app/app-server /usr/local/bin/app-server EXPOSE 80
CMD ["app-server"]複製代碼
首先,第一個 FROM
後邊多了個 AS
關鍵字,能夠給這個階段起個名字。我舉例子這個鏡像是官方 golang:alpine 加上構建工具 glide ,咱們照舊安裝依賴, build 出一個二進制程序。工具
而後,第二部分用了官方的 alpine
鏡像,改變時區到中國,新特性體如今 COPY
關鍵字,它如今能夠接受 --from=
這樣的參數,從上個咱們起名字的階段複製文件過來。ui
就這麼簡單,如今你只須要一個 Dockerfile 就什麼都搞定了。
因而如今你能夠把好幾個項目的二進制文件構建在一個迷你鏡像中發佈了,繼續舉個栗子:
from debian as build-essential
arg APT_MIRROR
run apt-get update run apt-get install -y make gcc workdir /src
from build-essential as foo
copy src1 . run make
from build-essential as bar
copy src2 . run make
from alpine
copy --from=foo bin1 . copy --from=bar bin2 . cmd ...複製代碼
這個就是把兩個項目編譯出來的文件最終合併到了一個鏡像裏。
好了,祝賀那些不支持多段構建的 CI 服務,Docker 幫大家追平了競爭對手。我有機會會寫一個支持 Docker 的 CI 的主觀評論,也歡迎你們吐槽各路 CI 給我提供素材。