Drone 工作流编写
也许 Jenkins 庞大的体积不适合小型服务器,Drone + Gitea 才是适合个人和小型团队的方案吧?Drone 的官方文档太过于简陋,是时候写一个记录了
Preface
Drone + Gitea 配合的 CI/CD,Drone 的 工作流编写是一个很大的坑,文档少,不知道社区文档在哪里,官方文档又太简陋,只能在 Google
上一顿乱找,相信我吧,Baidu
是个废物。现在重新拾起这个 Gitea + Drone 自然有许多感慨。Gitea 的 隐私保护还是不够,token
在前端只是一个 type="password"
这样一个约束,所以实际上还是可以看到的,倒不如 GitHub 的不予显示,或者干脆不让看。
Start
这里我选用的 Drone Runner 是 Docker Runner,对于非 Docker Runner,仅供参考
Drone + Drone Runner 的 docker-compose.yml 文件示例
version: "3"
services:
drone-server:
container_name: drone
# 启动容器所使用的镜像
image: drone/drone:latest
# 映射容器内80端口到宿主机的3008端口
ports:
- 3008:80
# 映射容器内/data目录到宿主机的目录
volumes:
- ./data/apps/drone:/data
# 容器随 docker 自动启动
restart: always
# 是否特权启动
privileged: false
networks:
- drone-network
environment:
# Gitea 服务器地址
- DRONE_GITEA_SERVER=https://git.xxx.com
# Gitea OAuth2客户端ID
- DRONE_GITEA_CLIENT_ID=
# Gitea OAuth2客户端密钥
- DRONE_GITEA_CLIENT_SECRET=
# drone的共享密钥
- DRONE_RPC_SECRET=311137bbbd11b205737ecac6cceab823
# drone的主机名
- DRONE_SERVER_HOST=ci.xxx.com
# 外部协议方案
- DRONE_SERVER_PROTO=https
# 创建管理员账户,这里对应为gitea的用户名
- DRONE_USER_CREATE=username:admin,admin:true
docker-runner:
container_name: drone-runner
image: drone/drone-runner-docker:latest
restart: always
privileged: true
networks:
- drone-network
depends_on:
- drone-server
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
# 用于连接到 Drone 服务器的协议。该值必须是 http 或 https 。
- DRONE_RPC_PROTO=http
# 用于连接到 Drone 服务器的主机名,例如 10.0.0.1:80
- DRONE_RPC_HOST=drone:80
# Drone 服务器进行身份验证的共享密钥,和上面设置一样
- DRONE_RPC_SECRET=311137bbbd11b205737ecac6cceab823
# 限制运行程序可以执行的并发管道数。运行程序默认情况下执行 2 个并发管道。
- DRONE_RUNNER_CAPACITY=2
# docker runner 名称
- DRONE_RUNNER_NAME=docker-runner
# docker runner node 名称,单节点可以注释掉
#- DRONE_RUNNER_LABELS=node-slave:runner-slave
networks:
drone-network:
driver: bridge
单 Drone-Runner 的 docker-compose.yml 文件示例
version: "3"
services:
docker-runner:
container_name: drone-runner
image: drone/drone-runner-docker:latest
restart: always
privileged: true
networks:
- drone-network
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
# 用于连接到 Drone 服务器的协议。该值必须是 http 或 https 。
- DRONE_RPC_PROTO=http
# 用于连接到 Drone 服务器的主机名,例如 10.0.0.1:80
- DRONE_RPC_HOST=10.0.0.1:80
# Drone 服务器进行身份验证的共享密钥,和 Drone Server(Drone) 一致
- DRONE_RPC_SECRET=311137bbbd11b205737ecac6cceab823
# 限制运行程序可以执行的并发管道数。运行程序默认情况下执行 2 个并发管道。
- DRONE_RUNNER_CAPACITY=2
# docker runner 名称
- DRONE_RUNNER_NAME=docker-runner
# docker runner node 名称,单节点可以注释掉
- DRONE_RUNNER_LABELS=node-slave:runner-slave
networks:
drone-network:
driver: bridge
Base
首先你要知道的是
- Drone 每一个工作流的起点都是
clone
(克隆仓库),在一些情况下,即便是这个工作流不满足触发条件,依旧会触发clone
.drone.yml
(一般是)文件使用的是 yaml 语法,请注意缩进问题,可以使用在线的 yaml 语法检测器
这里我选择 Node.js
作为简单的示例
example_one
kind: pipeline
type: docker
name: test & deploy
kine
字段定义了这个工作流,固定写法
type
字段定义了工作流使用的 Runner 类型,示例是 Docker Runner
name
字段定义了工作流的名字,可以任意写
example_two
kind: pipeline
type: docker
name: test & deploy
steps:
- name: init
image: node:16-alpine
commands:
- 'npm config set registry https://registry.npmmirror.com/'
- 'npm i -g pnpm'
- 'pnpm i'
when:
event: [push]
- name: build-test
image: node:16-alpine
commands:
- 'npm run build'
when:
event: [push]
depends_on:
- init
这个工作流就稍微复杂了,逐步拆解
steps
字段定义了这个工作流的每一个步骤
name
字段定义了这个步骤的名字
image
字段是选用的 Docker image ,可以来自 docker.io
, ghcr.io
只要你的 CI 节点能用这些你定义的镜像就可以
commands
字段定义了生成指定的 Docker 容器后执行的命令
when
字段是控制这个步骤的如何触发,一般和 image
字段对齐,当然你可以和其他字段对其,控制其中更细粒度的步骤,这个字段下面一般常用的是两个字段,其中一个是 branch
控制哪个分支触发 Drone 工作流,另外一个是 event
控制着什么行为触发工作流
depends_on
字段控制着工作流的依赖关系,一般工作流是按照文本从上到下顺序执行,如果使用该字段控制,可以乱序编写(不建议)
相信你也了解了基本的工作流,接下来我们进行一个完整的工作流编写
Advanced
example_three
上示例
---
kind: pipeline
name: test & build
# 分发的 Runner 的平台,一般是 amd64,除非你跑 arm 架构的 CI/CD
platform:
os: linux
arch: amd64
steps:
- name: init
image: node:16-alpine
commands:
- 'npm config set registry https://registry.npmmirror.com/'
- 'npm i -g pnpm'
- 'pnpm i'
when:
event: [push]
- name: build-test
image: node:16-alpine
commands:
- 'npm run build'
when:
event: [push]
depends_on:
- init
- name: docker-build
image: plugins/docker
settings:
# push 到目标仓库
repo: ccr.ccs.tencentyun.com/timochan/kami
# push 的地址
registry: ccr.ccs.tencentyun.com
# pull 使用的地址
mirror: https://mirror.ccs.tencentyun.com
# 使用 build cache ,Boolean 类型
use_cache: true
# 自动给 Docker image 打 tag ,Boolean 类型
auto_tag: true
username:
from_secret: username
password:
from_secret: password
when:
event: [tag]
- name: deploy-app
image: appleboy/drone-ssh
settings:
host:
from_secret: HOST_HOST
username:
from_secret: HOST_USERNAME
key:
from_secret: HOST_KEY
port:
from_secret: HOST_PROT
# command 执行错误直接跳过,Boolean 类型
script_stop: true
script:
- bash ./rebuild.sh
when:
event: [tag]
depends_on:
- docker-build
- name: notify
image: drillster/drone-email
settings:
from:
from_secret: SMTP_FROM
host:
from_secret: SMTP_HOST
port:
from_secret: SMTP_PORT
username:
from_secret: SMTP_USERNAME
password:
from_secret: SMTP_PASSWORD
recipients:
- xxx@example.com
when:
event: [push,tag]
depends_on:
- build-test
- deploy-app
node:
node-slave: runner-slave
这个工作流中有两个分支,第一个分支的步骤是 init
,build-test
和 notify
第二个分支的步骤是 docker-build
, deploy-app
和 notify
你也许会好奇 notify
为啥两个分支都有,因为单独把分支拎出来,写一个工作流,存在的问题就是,每一个工作流即便是啥都不干,都会执行 clone
关键是,clone
步骤还不能控制,所以干脆一个工作流写两个分支(其实我懒),你可能想知道在 notify
这个步骤中 recipients
字段是否是必须的;我明确地说这个字段不是必须的,默认会给 commit 提交者的邮箱发送邮件,当然这个是该镜像的文档表示该字段非必须值。
这个工作流出现一个全新的字段
from_secret
字段,从 Drone Server(Drone) ORGANIZATION SECRET
取 secret 内容
node:
node-slave: runner-slave
这个字段表示,该工作流在你自己 Runner 哪个节点执行,如果是单节点,这个可以缺省;如果是多节点,必须指明,否则可能会出现工作流分配 BUG ,当然你可以随时随地让一个服务器加入 Drone,只要该 Runner 可以与 Drone Server(Drone) 通信即可,具体见我文章开头示例的 docker-compose.yml
你也许会好奇,我的 docker-build 和 deploy-app 工作流中的 image 从哪里找的,这个是 Drone 插件社区 找到的,具体可以参照这些 steps 的使用文档
当然如果你不想定义每一个步骤的触发条件,可以这样写
trigger:
branch:
include:
- master
event:
include:
- push
单个工作流编写完毕,定义多个工作流就很简单了
---
kind: pipeline
name: test
platform:
os: linux
arch: amd64
steps:
- name : init
xxxxx
---
kind: pipeline
name: build
platform:
os: linux
arch: amd64
steps:
- name : build
xxxxxx
当然如果想本次提交跳过 CI ,可以在 message 上带有 [skip ci]
/ [ci skip]
等指示跳过 CI 的字样。例如
fix : pm2 [skip ci]
End
看看效果吧?
分支一
分支二
PS
假设我想 build 完成后,把构建产物部署到目标服务器呢,而不是打包成 docker image 再部署到目标服务器呢?
这里给一份示例,自行揣摩吧
kind: pipeline
type: docker
name: test & deploy
steps:
- name: install & build
image: node:16-alpine
commands:
- 'npm config set registry https://registry.npmmirror.com/'
- 'npm i -g pnpm'
- 'pnpm config set registry https://registry.npmmirror.com/'
- 'pnpm i'
- 'pnpm build'
- name: deploy
image: appleboy/drone-scp
settings:
debug: false
source:
- dist/*
host:
from_secret: HOST_HOST
username:
from_secret: HOST_USERNAME
port:
from_secret: HOST_PROT
key:
from_secret: HOST_KEY
target:
from_secret: HOST_TARGET
depends_on:
- install & build
- name: notify
image: drillster/drone-email
settings:
from:
from_secret: SMTP_FROM
host:
from_secret: SMTP_HOST
port:
from_secret: SMTP_PORT
username:
from_secret: SMTP_USERNAME
password:
from_secret: SMTP_PASSWORD
recipients:
- xxx@xxx.com # 可以不指明
depends_on:
- deploy
trigger:
branch:
include:
- master
event:
include:
- push
node:
node-master: runner-master