
Docker 的口號(hào)是 Build, Ship, and Run Any App, Anywhere.
但是我們?cè)趹?yīng)用過程中會(huì)遇到一個(gè)問題,我們?cè)?build 的時(shí)候,把源碼也 build 進(jìn)去了。
然后就繼續(xù)把源碼 Ship 出去嗎?這可不行。所有的編譯型語言都面臨這個(gè)困擾。
即使是腳本型語言,build 的時(shí)候也會(huì)使用很多上線時(shí)用不到的構(gòu)建工具,
而我們希望減小生產(chǎn)鏡像的體積,這樣我們的小鯨魚才能多拉一點(diǎn)集裝箱嘛。
傳統(tǒng)做法
我們最終的目的是要將編譯好的可執(zhí)行文件復(fù)制到 alpine 這樣的迷你鏡像里,
那么該怎么弄到編譯好的文件呢?基于 Docker 的思想,我們肯定需要在一個(gè)標(biāo)準(zhǔn)容器中編譯,
這樣這個(gè)過程才是標(biāo)準(zhǔn)化的,再說,你在 Ubuntu 編譯出一個(gè)二進(jìn)制文件在 alpine 也運(yùn)行不了。
于是我們先需要準(zhǔn)備一個(gè)編譯用的自定義鏡像。一般是用相應(yīng)語言的 alpine 基礎(chǔ)鏡像,
把編譯項(xiàng)目額外需要的各種工具打包進(jìn)去,比如 golang 目前沒有官方的包管理,
你就需要把你用的包管理工具裝進(jìn)去。
然后我們需要在運(yùn)行 container 時(shí)把主機(jī)的一個(gè)目錄通過 -v 掛載到 container上,
讓它把編譯的結(jié)果輸出到這個(gè)掛載的目錄,這樣我們就在主機(jī)上拿到這個(gè)文件了。
最后,我們用一個(gè)最小的 alpine 鏡像,把二進(jìn)制文件復(fù)制進(jìn)去。
可能你還需要設(shè)置一下時(shí)區(qū)之類的。
持續(xù)集成
上面的流程,在用持續(xù)集成工具時(shí)又變成了一個(gè)問題。你會(huì)發(fā)現(xiàn)每一家 CI 提供商都不太一樣。
你未必有權(quán)限控制 CI 時(shí)的宿主機(jī)。
比如 Docker Cloud,你需要定義 pre-build 的 hook 去完成這個(gè)工作,
在 SEMAPHORE,你發(fā)現(xiàn)你有了一臺(tái)宿主機(jī),這下和我們?cè)诒镜氐淖龇梢砸粯恿恕?br>
在更多的提供商,你會(huì)發(fā)現(xiàn)他們只是能根據(jù) git 倉庫和 Dockerfile 構(gòu)建鏡像,
你用他們的系統(tǒng)甚至沒辦法做出一個(gè)最小鏡像……
中國的 DaoCloud 其實(shí)挺先進(jìn)的,很早就推出了安全鏡像的概念,讓你的構(gòu)建通過兩步完成。
但是,那個(gè)配置的內(nèi)容太多讓不太懂的人看了直接暈掉。
官方方案
在2017年5月3日即將發(fā)行的 Docker 17.05.0-ce 中,Docker 官方提供了簡便的多階段構(gòu)建
(multi-stage build) 方案。我用例子為大家介紹下:
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"]
首先,第一個(gè) FROM 后邊多了個(gè) AS 關(guān)鍵字,可以給這個(gè)階段起個(gè)名字。
我舉例子這個(gè)鏡像是官方
golang:alpine 加上構(gòu)建工具 glide ,我們照舊安裝依賴, build 出一個(gè)二進(jìn)制程序。
然后,第二部分用了官方的 alpine 鏡像,改變時(shí)區(qū)到中國,新特性體現(xiàn)在 COPY 關(guān)鍵字,
它現(xiàn)在可以接受 --from= 這樣的參數(shù),從上個(gè)我們起名字的階段復(fù)制文件過來。
就這么簡單,現(xiàn)在你只需要一個(gè) Dockerfile 就什么都搞定了。
多項(xiàng)目構(gòu)建
于是現(xiàn)在你可以把好幾個(gè)項(xiàng)目的二進(jìn)制文件構(gòu)建在一個(gè)迷你鏡像中發(fā)布了,繼續(xù)舉個(gè)栗子:
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 ...
這個(gè)就是把兩個(gè)項(xiàng)目編譯出來的文件最終合并到了一個(gè)鏡像里。
好了,祝賀那些不支持多段構(gòu)建的 CI 服務(wù),Docker 幫你們追平了競(jìng)爭對(duì)手。
我有機(jī)會(huì)會(huì)寫一個(gè)支持 Docker 的 CI 的主觀評(píng)論,也歡迎大家吐槽各路 CI 給我提供素材。