timochan

timochan

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:
       - [email protected]
    when:
      event: [push,tag]
    depends_on:
      - build-test
      - deploy-app
node:
  node-slave: runner-slave

这个工作流中有两个分支,第一个分支的步骤是 init ,build-testnotify

第二个分支的步骤是 docker-build , deploy-appnotify

你也许会好奇 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#

看看效果吧?

分支一

image

分支二

image

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:
       - [email protected] # 可以不指明
  depends_on:
     - deploy

trigger:
  branch:
    include:
      - master
  event:
    include:
      - push
node:
  node-master: runner-master
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.