大概是一年前發(fā)現(xiàn)了這樣一個(gè)叫做 Drone 的開源 ci,在逐漸的嘗試過(guò)程中發(fā)現(xiàn)它的功能非常的強(qiáng)大,其 pipeline as code + docker + backing service 支持的體系基本和我理想中的 ci 一模一樣...這里就介紹一下我看到的 drone 的一些非常出彩的地方以及日常使用時(shí)一些非常有用的使用方式。
本地安裝

可以看到 drone 的界面非常的簡(jiǎn)潔,和其他 ci 一樣它通過(guò)和 github gitlab 或者是 gogs 這樣的 git repository 鏈接并綁定 web hook 在用戶提交新的 commit 的時(shí)候出發(fā) ci 的執(zhí)行。drone 作為一個(gè)開源的 ci 其支持 docker 方式的安裝,非常的簡(jiǎn)單:
version: '2'
services:
drone-server:
image: drone/drone:0.7
ports:
- 80:8000
volumes:
- /var/lib/drone:/var/lib/drone/
restart: always
environment:
- DRONE_OPEN=true
- DRONE_HOST=${DRONE_HOST}
- DRONE_GITHUB=true
- DRONE_GITHUB_CLIENT=${DRONE_GITHUB_CLIENT}
- DRONE_GITHUB_SECRET=${DRONE_GITHUB_SECRET}
- DRONE_SECRET=${DRONE_SECRET}
drone-agent:
image: drone/drone:0.7
command: agent
restart: always
depends_on:
- drone-server
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- DRONE_SERVER=ws://drone-server:8000/ws/broker
- DRONE_SECRET=${DRONE_SECRET}
通過(guò)這樣的 docker-compose 文件就可以在本地啟動(dòng)一個(gè) droner server 和一個(gè) drone-agent(其概念和 gocd 類似)。
drone 的亮點(diǎn)
pipeline as code
首先 drone 支持 pipeline as code 其通過(guò)一個(gè)簡(jiǎn)單的 yaml 文件就包含了一個(gè)項(xiàng)目 ci 的所有內(nèi)容了(當(dāng)然,到底簡(jiǎn)不簡(jiǎn)單要看你構(gòu)建流程的復(fù)雜程度以及你對(duì)項(xiàng)目的封裝程度)。舉一個(gè)例子:
pipeline:
build:
image: node:6.10.2-alpine
commands:
- yarn install
- yarn run build
publish:
image: plugins/docker
repo: eisenxu/realtopper-app
secrets: [ docker_username, docker_password ]
tags:
- latest
- ${DRONE_COMMIT_SHA:0:8}
這是一個(gè)采用 create-react-app 創(chuàng)建的一個(gè)前端單頁(yè)應(yīng)用項(xiàng)目。這里定義了 build 和 publish 兩個(gè)階段。第一個(gè)階段 yarn install 和 yarn run build 分別下載依賴和編譯但也應(yīng)用。第二階段 publish 采用項(xiàng)目中的 Dockerfile 構(gòu)建一個(gè) docker image 并發(fā)布到 hub.docker.com,具體兩個(gè)步驟如何進(jìn)行的細(xì)節(jié)我們?cè)诤竺鏁?huì)慢慢介紹。
原生支持 docker
上面的 yaml 中每個(gè)階段都有一個(gè) image 的字段,這個(gè) image 就是指一個(gè) docker image 也就是說(shuō) drone 下每一個(gè)階段都是在一個(gè)你所指定的 docker container 下執(zhí)行的。這樣當(dāng)然就?集成了 docker 所引入的一系列的好處:環(huán)境隔離、標(biāo)準(zhǔn)化鏡像。?并且,它是后面插件擴(kuò)展以及 backing service 可以被輕而易舉的實(shí)現(xiàn)的基礎(chǔ)。
簡(jiǎn)單易用的插件擴(kuò)展
還是在看上面的那個(gè)例子,?第一個(gè)階段我們?cè)?node:6.10.2-alpine 下構(gòu)建了一個(gè)單頁(yè)應(yīng)用。然后,我們采用了 plugins/docker 鏡像構(gòu)建并發(fā)布了我們的鏡像到 hub.docker.com。而這里的 plugins/docker 就是 drone 為我們提供的一個(gè)插件了。
雖說(shuō)是一個(gè)插件,但實(shí)際上用起來(lái)就和其他的? image 一樣,這個(gè)插件的功能就是幫我們利用項(xiàng)目中的 Dockerfile 構(gòu)建一個(gè)新的 docker image 并提交。除此之外還有一些其他的官方插件可供使用,詳情在這里。
當(dāng)然,自己做一個(gè)插件也是非常簡(jiǎn)單的,在插件被執(zhí)行的時(shí)候,當(dāng)前目錄就是項(xiàng)目的根目錄,然后 drone 會(huì)暴露一系列的環(huán)境變量給用戶使用,我們可以采用之前的步驟所產(chǎn)生的數(shù)據(jù)或者環(huán)境變量中的內(nèi)容實(shí)現(xiàn)一個(gè)特定功能的插件。
支持 backing service
我們跑 ci 的時(shí)候難免會(huì)有一些外部的依賴,比如跑單元測(cè)試的時(shí)候可能會(huì)用到外部的數(shù)據(jù)庫(kù)。比如跑前端界面測(cè)試的時(shí)候我們會(huì)需要 selenium。drone 對(duì)這種場(chǎng)景提供了支持。
pipeline:
test:
image: golang
commands:
- go get
- go test
services:
database:
image: postgres
environment:
- POSTGRES_USER=postgres
- POSTGRES_DB=test
這是官方所提供的一個(gè)數(shù)據(jù)庫(kù)的例子。通過(guò)定義一個(gè) services 字段,我們可以提供一個(gè)或者多個(gè)外部服務(wù)。
最佳實(shí)踐
對(duì) docker image 的驗(yàn)收測(cè)試
我們采用 docker 容器進(jìn)行部署的時(shí)候通常是在 docker 容器中跑單元測(cè)試然后打包鏡像并推到 registry。但是這樣的流程并不能保證我們打包好的鏡像是工作的,可能我們的 Dockerfile 寫的有問(wèn)題導(dǎo)致服務(wù)沒(méi)辦法被使用。所以,其實(shí)還可以最這種最終構(gòu)件好的鏡像做一個(gè)驗(yàn)收測(cè)試。
backing service 的機(jī)制可以讓我們這么做:
pipeline:
build:
image: node:6.10.2-alpine
commands:
- npm build
publish_for_test:
image: plugins/docker
repo: test/bar
tags: [ 1.0.0, 1.0, latest ]
run_server:
image: test/bar:latest
detach: true
verify:
image: blueimp/chromedriver
environment:
- VNC_ENABLED=true
- EXPOSE_X11=true
commands:
- nightwatch
publish:
image: plugins/docker
repo: production/bar
tags: [ 1.0.0, 1.0, latest ]
- 首先,我們?cè)?
publish_for_test中構(gòu)建一個(gè)test/bar:latest的鏡像。 - 然后我們采用
detach的字段表明我們?cè)谶@里把我們剛剛創(chuàng)建好的鏡像運(yùn)行起來(lái)。 - 在
verify階段,我們采用nightwatch對(duì)已經(jīng)運(yùn)行起來(lái)的test/bar:latest服務(wù)執(zhí)行驗(yàn)收測(cè)試,也就是說(shuō),這時(shí)候我們把剛剛創(chuàng)建的應(yīng)用當(dāng)做我們的backing service。 - 如果測(cè)試通過(guò)了,我們?cè)贅?gòu)建一個(gè)新的鏡像并 push 到生產(chǎn)環(huán)境 registry
當(dāng)然,這里的 publish_for_test 其實(shí)最好的辦法是只構(gòu)建鏡像而不提交鏡像,然后在本地啟動(dòng)這個(gè)鏡像。不過(guò)這種使用本地鏡像的方式并沒(méi)有使用過(guò),而且也沒(méi)有那種只構(gòu)建不提交或者只提交已經(jīng)存在的鏡像的插件,以后可以自己進(jìn)一步做一些優(yōu)化。
用 drone 部署多個(gè)階段的環(huán)境
雖然 drone 沒(méi)有 GoCD 里的 deployment pipeline 的概念,但是它可以通過(guò)指定特殊的 deployment 的事件實(shí)現(xiàn)手動(dòng)激活的多環(huán)境部署。
pipeline:
build:
image: golang
commands:
- go build
- go test
publish:
image: plugins/docker
registry: registry.heroku.com
repo: registry.heroku.com/my-staging-app/web
when:
+ event: deployment
+ environment: staging
publish_to_prod:
image: plugins/docker
registry: registry.heroku.com
repo: registry.heroku.com/my-production-app/web
when:
+ event: deployment
+ environment: production
可以看到通過(guò)指定 event 和 environment 可以指定兩個(gè)不同的環(huán)境:staging 和 production。然后,通過(guò) drone 所提供的命令行可以實(shí)現(xiàn)手工部署到不同的環(huán)境。
drone deploy octocat/hello-world 24 staging
這里是將構(gòu)建 24 號(hào)部署到 staging 環(huán)境。
使用插件為構(gòu)建增加緩存
每次構(gòu)建都從遠(yuǎn)端獲取依賴真是非常費(fèi)流量費(fèi)時(shí)間,最好可以不要重復(fù)下載。drone 就以插件的方式支持了這樣的功能。
pipeline:
restore-cache:
image: drillster/drone-volume-cache
restore: true
mount:
- ./node_modules
volumes:
- /tmp/cache:/cache
build:
image: node
commands:
- npm install
rebuild-cache:
image: drillster/drone-volume-cache
rebuild: true
mount:
- ./node_modules
volumes:
- /tmp/cache:/cache
第一階段 restore-cache 將 /tmp/cache 下該項(xiàng)目的緩存拷貝到 ./node_modules。第三階段將 ./node_modules 的內(nèi)容拷貝會(huì) /tmp/cache 詳細(xì)的內(nèi)容見緩存。
當(dāng)前的狀態(tài)
目前 drone 剛剛開始了商業(yè)化之路,并且在瘋狂的更新中,整體社區(qū)非常的活躍 star 也已經(jīng)過(guò)萬(wàn)了,非常期待它未來(lái)的發(fā)展。