將使用 docker + docker compose + drone + git 實現(xiàn)項目自動部署
使用docker可以讓drone實現(xiàn)任意語言程序的構建與部署
分工
-
docker
: 整潔迅速的部署方案 -
docker-compose
: 組網(wǎng)和簡單容器編排 -
drone
: go語言寫的基于docker的CI框架
流程預覽
- 代碼提交到git
- git 通知drone應用
- 觸發(fā)drone服務器運行項目下的.drone.yml
- 如果沒有配置clone步驟, drone會運行默認的clone步驟, 并clone到
workspace
設置下的path - 依次運行pipeline下的步驟, 通常是 test -> build -> publish -> ssh連接到運行服務容器的服務器 并重啟容器, 下面會依次說明pipeline各步驟.
- 如果沒有配置clone步驟, drone會運行默認的clone步驟, 并clone到
安裝Drone
此文基于Drone0.8版本安裝, 已經(jīng)過時, 請多多參考Drone官方安裝文檔.
官方使用docker-compose安裝, 編寫docker-compose.yml如下
docker-compose.yml
version: '2'
services:
drone-server:
image: drone/drone:latest
ports:
- 8000
- 9000
volumes:
- /workspace/docker/volumes/drone:/var/lib/drone/
restart: always
environment:
- DRONE_OPEN=true
- DRONE_HOST=${DRONE_HOST}
- DRONE_CODING=true
- DRONE_CODING_CLIENT=${DRONE_CODING_CLIENT}
- DRONE_CODING_URL=https://coding.net
- DRONE_CODING_SECRET=${DRONE_CODING_SECRET}
- DRONE_CODING_GIT_MACHINE=e.coding.net
- DRONE_CODING_SCOPE=user,project,project:depot
- DRONE_GITHUB_SKIP_VERIFY=false
- DRONE_SECRET=${DRONE_SECRET}
- DRONE_ADMIN=bysir
- DRONE_CODING_GIT_USERNAME=${DRONE_CODING_GIT_USERNAME}
- DRONE_CODING_GIT_PASSWORD=${DRONE_CODING_GIT_PASSWORD}
drone-agent:
image: drone/agent:latest
command: agent
restart: always
depends_on:
- drone-server
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- DRONE_SERVER=drone-server:9000
- DRONE_SECRET=${DRONE_SECRET}
- DRONE_MAX_PROCS=5
備注
字段 | 注釋 |
---|---|
DRONE_ADMIN | 管理員, 名字是你版本控制的賬號名, 添加了管理員的才可以管理用戶 |
DRONE_OPEN | 是否開啟新用戶注冊 |
DRONE_GITHUB | 使用github作為版本控制 |
在上面你會看到其中有${DRONE_HOST}
的變量,這是docker-compose支持的語法, 文檔
我們需要在同級目錄下編寫一個.env文件來寫入這些變量
.env
# drone的安裝服務器, 不需要端口
DRONE_HOST=http://40.90.200.130
# DRONE需要GITHUB作為應用授權, 這里填寫授權賬號密碼, 詳情看[官方文檔](http://docs.drone.io/install-for-github/)
DRONE_GITHUB_CLIENT=xxxx
DRONE_GITHUB_SECRET=xxxx
# 用于drone服務器和agent之間的密鑰, 隨意填寫
DRONE_SECRET=123456
現(xiàn)在運行docker-compose up
就能運行drone了, 如果有報錯就按報錯提示來解決.
訪問服務器 8000 端口就能看到drone的web頁面了, 使用GITHUB的賬號登陸即可, 登陸后drone會自動拉取項目目錄, 點擊項目就能開啟對這個項目的監(jiān)管.
編寫項目
需要單獨為每一個項目編寫.drone.yml
文件 用來表述在代碼提交后需要執(zhí)行哪些操作.
用一個簡單的go項目說明
.drone.yml
workspace:
base: /go
path: src/github.com/bysir-zl/gokit_start
pipeline:
build:
image: golang:1.9
commands:
- pwd
- go env
- go version
- go build
publish:
image: plugins/docker
registry: https://index.docker.io/v1/ # 倉庫
repo: bysir/golang_base # docker倉庫地址
# mirror: https://docker.mirrors.ustc.edu.cn
# 需要使用drone cli添加secrets: http://docs.drone.io/manage-secrets/
secrets: [ docker_username, docker_password ]
tags:
- latest
deploy:
image: appleboy/drone-ssh
host: 47.94.204.137
username: root
port: 22
secrets: [ ssh_key ]
script:
- cd /root/app
- docker-compose pull bysir/golang_base
- docker-compose up
workspace
其中workspace指定pipeline的工作目錄, 上例中我們會在build
中pwd
看到當前目錄是/go/src/github.com/bysir-zl/gokit_start
, 為什么我們需要指定到/go目錄下, 因為在golang:1.9
的鏡像中, go_path
就是/go
, 我們要go build當然要在go_path下執(zhí)行.
build
build
步驟很簡單只是go build
, 你好奇為什么沒有go get
, 因為我將vendor
目錄也一起提交了, 推薦使用go官方依賴管理工具dep
docker在構建的時候都是以一個空白鏡像golang:1.9
作為基礎的, 如果不提交vendor
就需要每次構建都go get, 十分耗時. 當然還有辦法就是提交一個已經(jīng)按照好go包的基礎鏡像到registry里, 在build
中的image
就換成你提交的鏡像. 相比之下更簡單的方法就是提交vendor
目錄.
publish
publish
步驟就有點復雜了, 使用到了plugins/docker
插件, 這個插件是drone寫的, 用于發(fā)布docker鏡像. 它的作用就是構建一個鏡像, 并push到registry.
我們需要配置的值有
-
registry
: 倉庫registry, 如hub.docker.com的registry地址是https://index.docker.io/v1/
-
repo
: 在docker倉庫下的項目名稱 -
secrets
: drone用于傳遞密鑰的實現(xiàn)方式, 下面會詳細介紹
在plugins/docker
插件中, 構建項目鏡像是通過Dockerfile來的, 所以我們還需要在項目根編寫一個Dockerfile
Dockerfile
FROM alpine:latest
COPY gokit_start /
WORKDIR /
ENTRYPOINT ["./gokit_start"]
其中ENTRYPOINT是容器啟動后的運行入口, "./gokit_start"是示例項目build后的二進制文件
deply
發(fā)布流程就是通過SSH登陸上要部署程序的服務器pull
下剛剛publish
的鏡像并啟動.
登陸SSH就需要配置ssh_key
或者ssh_password
, 更多詳情看appleboy/ssh
這個插件的文檔, 這里推薦使用ssh_key
, 我們需要在drone的secrets添加一項ssh_key
值為私鑰
, 然后我們將與之匹配的公鑰
放在服務器上.ssh/authorized_keys
里, 這樣就能使用ssh_key
登陸上服務器并執(zhí)行script
.
測試
現(xiàn)在你的項目機構應該類似于
現(xiàn)在你可以將你寫的代碼和配置文件一起提交到git, 然后你就能看到這個項目正在構建
你可以點擊查看構建步驟
ps
終于跑起來了
2018/04/27 更新
最近需要使用coding作為版本控制軟件
文檔說drone-server在0.8版本之后是支持coding的, 但實際不然. 實際運行起來會報錯:version control system not configured
找了很久的原因, 翻了很久源代碼發(fā)現(xiàn)并沒有問題, 直到發(fā)現(xiàn)這個Issue
I encounter this issue too.
When use drone from latest docker image, setting DRONE_CODING=true has no effect, result: "version control system not configured".
When build drone from git source code, It's works.
I found
git clone git@github.com:drone/drone-enterprise.git extras
go build -ldflags '-extldflags "-static" -X github.com/drone/drone/version.VersionDev=build.'${DRONE_BUILD_NUMBER} -o release/drone-server github.com/drone/drone/extras/cmd/drone-server
at .drone.sh,
You use drone-enterprise.git for extras to build the docker image, not the open source code.
Is that the problem?
所以我嘗試使用git上源碼重新編譯一個drone-server使用, 修改倉庫下.drone.sh
如下:
#!/bin/sh
# clone the extras project.
set -e
set -x
# 由于要運行在alpine, 所以需要加GOOS=linux GOARCH=arm64 CGO_ENABLED=0
# 由于drone-server使用到了sqlite3, sqlite3使用到了gcc, 所以需要-extldflags "-static"
# -ldflags "-extldflags -static" at the end makes sure C code is statically linked so resulting binary truly has no dependencies even for C code.
go build -ldflags '-extldflags "-static" -X github.com/drone/drone/version.VersionDev=build.'0.01 -o release/drone-server github.com/drone/drone/cmd/drone-server
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags '-X github.com/drone/drone/version.VersionDev=build.'0.01 -o release/drone-agent github.com/drone/drone/cmd/drone-agent
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -ldflags '-X github.com/drone/drone/version.VersionDev=build.'0.01 -o release/linux/arm64/drone-agent github.com/drone/drone/cmd/drone-agent
GOOS=linux GOARCH=arm CGO_ENABLED=0 GOARM=7 go build -ldflags '-X github.com/drone/drone/version.VersionDev=build.'0.01 -o release/linux/arm/drone-agent github.com/drone/drone/cmd/drone-agent
重新編譯, 打包鏡像.
為了方便, 我將做好的鏡像放在了共有倉庫下:
bysir/drone-server
(對于官方0.8.5的版本)
可以這樣使用:
version: '2'
services:
drone-server:
image: bysir/drone-server
ports:
- 8082:8000
- 9000
volumes:
- /workspace/docker/volumes/drone:/var/lib/drone/
restart: always
environment:
- DRONE_OPEN=true
- DRONE_HOST=${DRONE_HOST}
- DRONE_CODING=true
- DRONE_CODING_CLIENT=${DRONE_CODING_CLIENT}
- DRONE_CODING_URL=https://zhuzi.coding.net
- DRONE_CODING_SECRET=${DRONE_CODING_SECRET}
- DRONE_CODING_GIT_MACHINE=e.coding.net
- DRONE_CODING_SCOPE=user,project,project:depot
- DRONE_GITHUB_SKIP_VERIFY=false
- DRONE_SECRET=${DRONE_SECRET}
- DRONE_ADMIN=bysir
drone-agent:
image: drone/agent
command: agent
restart: always
depends_on:
- drone-server
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- DRONE_SERVER=drone-server:9000
- DRONE_SECRET=${DRONE_SECRET}
ps: 真是折騰... 不過終于好了
歡迎看我接下來的折騰:
擴展閱讀: 私鑰登錄原理
為什么要生成一對秘鑰將秘鑰放在drone的ssh_key
:
DroneService(A)想要登錄Service(B)并執(zhí)行命令, 在SSH登錄認證的時候需要A用私鑰簽名一段信息發(fā)送到B, B收到請求后會使用.ssh/authorized_keys
中的公鑰依次驗簽, 如果驗簽成功則登錄成功.
ps: 私鑰的格式是-----BEGIN RSA PRIVATE KEY-----xxxxxxxxx-----END RSA PRIVATE KEY-----
全部放在web頁面就行了, 如下
添加secrets
在publish
步驟有secrets
選項, 其中docker_username和docker_password是由drone注入的, 所以我們需要在drone右上角添加secrets:
添加之后如下:
secrets實現(xiàn)原理:
drone有一個專門存儲secrets的地方, 當pipeline中step需要secrets的時候, 會得到其值并設置為容器的環(huán)境變量
上例中drone會從數(shù)據(jù)庫拿到docker_username
和docker_password
并賦值給環(huán)境變量DOCKER_USERNAME
和DOCKER_PASSWORD
, 在plugins/docker
中會使用這兩個環(huán)境變量去登陸你設置的registry
: docker login -p $DOCKER_PASWORD -u $DOCKER_USERNAME $DOCKER_REGISTRY
擴展閱讀:安裝drone cli
可以使用drone的cli來添加secrets: manage-secrets
安裝方法: CLI Installation
curl -L https://github.com/drone/drone-cli/releases/download/v0.8.3/drone_linux_amd64.tar.gz | tar zx
sudo install -t /usr/local/bin drone
CLI需要通過server的授權: CLI Authentication
export DRONE_SERVER=http://47.94.204.137:8080
export DRONE_TOKEN=eyJh33OiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXh0IjoiYnlzaXItemwiLCJ0eXBlIjoidXNlciJ9.jLsJvi4PafTi-ffefe5-bYTD2N_xmIi9fg
之后就可以使用cli執(zhí)行命令
添加secrets
drone secret add -repository bysir-zl/gokit_start -image plugins/docker -name docker_username -value bysir
drone secret add -repository bysir-zl/gokit_start -image plugins/docker -name docker_password -value 123456
刪除secret
drone secret rm --repository bysir-zl/gokit_start --name docker_username
2020/5/14 更新
Coding.net改了API 導致Drone0.8無法獲取到倉庫的.drone.yml 文件, 相關補丁已發(fā)布到此倉庫中https://github.com/zbysir/drone/tree/0.8.10-coding.
如果需要編譯, 為了方便, 應該在linux上編譯, 因為drone依賴了sqlite3, sqlite3依賴的cgg. 另外drone中的vendor缺失了一些依賴包, 你可以試著運行./drone.sh并且下載所缺失的包, 總之 折騰起來有些麻煩, 耐心.
或者 你可以直接使用我構建好的鏡像來一鍵修復此問題: https://hub.docker.com/repository/docker/bysir/drone-server