Drone CI:搭建自己CI/CD(二)

CI 进阶篇:

上一篇里介绍了如何使用 drone ci从零部署一套自己的 CI,并简单地介绍了如何配置一个自动进行单元测试和编译的示例,这一篇则更加详细地介绍Drone 是如何满足我们在 CI 上的各种需求的。本篇所有内容都基于上篇介绍的.drone.yaml示例,在这里再熟悉一下,不熟悉的同学可以找到上一篇介绍如何部署和配置的文章看一下。

kind: pipeline
name: api
steps:
  # api unit test
  - name: api-test
    image: node:11
    depends_on: [clone]
    commands:
      - cd ./api/
      - npm install --registry https://registry.npm.taobao.org
      - cd ./dgc_sdk && npm install --production
      - cd .. && npm run test
  # web lint
  - name: web-lint
    image: node:10.15.1
    depends_on: [clone]
    commands:
      - cd ./web
      - npm install --registry https://registry.npm.taobao.org
      - npm run lint
trigger:
  branch:
    - master
  event:
    - pull_request
    - push

一、自定义clone 步骤

有一些时候,我们可能需要对 CI 上的 clone 步骤进行一些自定义的操作,比如跳过 clone,比如 clone 的时候也要拉取 tag

  1. 跳过 clone 步骤
    有时候我们可能需要跳过默认的 clone 步骤(虽然这种需求不太常见,但也不是没有,比如我就用到了,哈哈)。可以先像下面这样定义clone 步骤,将 clone 步骤的 disable 设置为 false 就可以啦
# 这是一个空的 pipeline
# 当 pull_request事件被触发,并且不是提忘 master 分支时
# 或者出去 tag 的其他的事件时会触发
# 触发后不进行任何操作
# 这个 pipeline 的主要目的是,我们在 gitlba 中定义了如果 CI 不成功,pr 不允许合入分支
# 所以当我们往非 master提交 pr 时,不想让 CI 执行任何步骤,但也不能不触发 CI,因为不触发 CI 那上面的规则就会限制 pr 无法合入
kind: pipeline
type: docker
name: pipeline

clone:
  disable: true

trigger:
  event:
    - pull_request
  branch:
    exclude:
      - master
  ref:
    exclude:
      - refs/tags/*
  1. clone时也拉取 tag
    当我们用 tag 事件触发 CI 时,默认的配置下可能存在一种很令人迷惑的情况:我在镜像中访问这个 git 仓库并尝试切换到这 tag,你会发现竟然找不到这个 tag。因为 drone 默认的 clone 步骤不会 fetch仓库的 tags,这个时候我们需要对默认的 clone 步骤进行如下更改:
kind: pipeline
type: docker
name: build

# 自定义 clone 的 git 操作
# image 指定使用 drone 提供的 git 插件,当然我们也可以使用我们自定义的或者其他的 git 镜像
# tags: true 指定在 clone 代码时也将仓库的 tag同步到本地
clone:
  git:
    image: plugins/git
    network_mode: host
    tags: true

# 下面可以定义你的编译步骤和升级操作

二、并行执行steps

当我们的 CI 步骤比较长的时候,我们希望这些步骤不是串行的,并行执行能给我们节省很多时间,同时呢可能多个步骤之间还存在着依赖关系,比如某一个模块的镜像编译与推送可能依赖于这个模块本身的 build(比如 npm build,mvn clean package等等)。使用 drone 提供的 depends_on可以轻松完成以上任务:depends_on 需要设置为一个数组形式,如果数组中存在多个值,本步骤会等到这多个步骤都执行完成后再执行。
首先明确的是,drone 中所有的 steps默认都是并行执行的,所以如果我们定义如下,便可以实现我们想要的功能

kind: pipeline
type: docker
name: build

steps:
  # get tag
  - name: tag
    image: bashell/alpine-bash:latest
    network_mode: host
    depends_on: [clone]
    commands:
        # 当tag 事件触发后,drone 会默认将 tag 名字写入到环境变量DRONE_TAG中,更多默认的环境变量请查看文档:https://docs.drone.io/pipeline/environment/reference/
        # 将当前的 tag 名称输出到.tags文件中,如果tag 名不存在那么输出latest 到 .tags文件中
      - /bin/bash -c '[ -z $DRONE_TAG ] && echo "latest" > .tags || echo $DRONE_TAG > .tags'

  # build api
  - name: api-build
    image: node:12.18.1
    network_mode: host
    depends_on: [tag]
    volumes:
      - name: cache_api_node
        path: /drone/src/api/node_modules
    commands:
      - cd ./api/
      - npm install --registry http://mirrors.cloud.tencent.com/npm
      - npm run build

  # build web
  - name: web-build
    image: node:12.18.1
    network_mode: host
    depends_on: [tag]
    volumes:
      - name: cache_web_app_node
        path: /drone/src/web/app/node_modules
    commands:
      - cd ./web/app
      - npm install --registry http://mirrors.cloud.tencent.com/npm
      - npm run build

上面定义的 steps 将会按照下面这种并行方式执行,上下排布并使用箭头连接的步骤是串行执行的,并排并且没有箭头连接的步骤是串行执行的:

Drone CI:搭建自己CI/CD(二)_第1张图片

三、提供文件挂载和环境变量
有些时候我们需要将宿主机的文件或文件夹挂载到 CI 步骤当中去,比如缓存文件,比如外部不适合放在代码仓库中但CI 步骤需要的一些文件。
有些时候我们需要添加或者变更一下 CI 步骤中运行的 docker 容器的环境变量,比如当我将外部缓存挂载到容器里之后,更改 node 或者 golang 的默认包路径。
下面这个示例将演示如何使用挂载和环境变量:
需要注意的是,想要使用挂载功能,需要将仓库在 Drone CI 中的 trusted 选项打开。这个操作可以参考下面的第六步。

kind: pipeline
type: docker
name: unit test


# 将宿主机的路径挂载到 pipeline 中
# 将宿主机的路径/drone/cache/star/golang-app/go挂载到 pipeline 中,并命名为 cache_golang_app_go,用作 golang 程序编译时的缓存文件夹
# 将宿主机的路径/drone/cache/star/api/node_modules挂载到 pipeline 中,并命名为 cache_api_node,用作 node 程序编译时的缓存文件夹
# 将宿主机的路径 /var/run/docker.sock挂载到 pipeline 中,并命名为 docker,让 pipeline 中 docker 镜像使用宿主机的 docker socket
# 将宿主机的路径 /etc/docker/daemon.json挂载到 pipeline 中,并命名为 docker-daemon,让 pipeline 中 docker 镜像使用宿主机的 docker配置
volumes:
 - name: cache_golang_app_go
   host:
     path: /drone/cache/star/golang-app/go
 - name: cache_api_node
   host:
     path: /drone/cache/star/api/node_modules
 - name: docker
   host:
     path: /var/run/docker.sock
 - name: docker-daemon
   host:
     path: /etc/docker/daemon.json

# master pull requset
steps:
 - name: clean
   image: docker:19.03.1
   network_mode: host
   # 将宿主机的 docker和配置挂载到运行的 docker 容器中,那么在容器中运行 docker 命令时,等同于在宿主机中运行该docker 命令
   volumes:
     - name: docker
       path: /var/run/docker.sock
     - name: images
       path: /images
     - name: docker-daemon
       path: /etc/docker/daemon.json
   commands:
       # 清理 docker 服务中不再使用的镜像、挂载、网络等资源
     - docker system prune --force --volumes || true


 # api unit test
 - name: api-test
   image: node:12.18.1
   depends_on: [clean]
   network_mode: bridge
   # 将宿主机中文件夹挂载到容器中,宿主机文件夹中的文件会被容器访问并修改,起到使用缓存的作用,避免每次运行都要重现下载依赖,提高运行速度
   volumes:
     - name: cache_api_node
       path: /drone/src/api/node_modules
   commands:
     - cd ./api/
     - npm install --registry http://mirrors.cloud.tencent.com/npm
     - npm run test

   when:
     branch:
       - master

 # golang-app test
 - name: golang-app-test
   image: library/golang:1.13.3-alpine3.10
   network_mode: host
   depends_on: [clean]
   # 将宿主机中文件夹挂载到容器中,宿主机文件夹中的文件会被容器访问并修改,起到使用缓存的作用,避免每次运行都要重现下载依赖,提高运行速度
   volumes:
     - name: cache_golang_app_go
       path: /drone/src/golang-app/go
   # 由于 golang 是通过GOPATH寻找本地包路径的,因此除了将依赖文件挂载到容器中之外,还要指定GOPATH到这个目录下
   # 设置GOPROXY为腾讯元镜像,提高 golang 依赖下载速度
   environment:
     GOPATH: /drone/src/golang-app/go
     GOPROXY: http://mirrors.cloud.tencent.com/go/
   commands:
     - cd ./golang
     - CGO_ENABLED=0 go test -v
   when:
     branch:
       - master

trigger:
 branch:
   - master
 event:
   - pull_request

四、拉取私有镜像

在生产环境中,很可能我们需要运行或者推送的镜像并不是开放在 dockerhub 中的,我们会使用私有镜像,或者私有镜像仓库,这个时候我们需要在drone 中指定image_pull_secrets来配置私有镜像或者私有仓库。这个时候我们还需要上一篇文章提到的 Secret 来保证这个私有配置是被加密的。

  1. 在 Drone 的页面中配置仓库的 Secret
    使用浏览器访问 drone 页面(默认使用 80 端口,比如本地启动的访问路径是http://127.0.0.1),点击你要操作的仓库后选择 SETTINGS tab 页,会看到如下配置内容:
    Drone CI:搭建自己CI/CD(二)_第2张图片

上图中的Secrets 项中可以添加自定义的内容,比如我这里就设置了三个 secret,docker_password,docker_username 和 dockerconfigjson。拉取私有镜像的配置便是用到了这个dockerconfigjson,另外两个 Secret 是我用在了其他地方(请查看第五步docker 镜像的编译与推送)。在 Secret Name中填写dockerconfigjson(当然也可以是其他的名字),然后在 Secret Value 填写 docker 的配置内容。该内容可以通过以下方式获取到(ubuntu):
* 运行如下命令,使用用户名登录到指定的镜像仓库中,输入密码后确认登录成功
docker login --username=[username] ccr.ccs.tencentyun.com
* 查看并复制/etc/docker/key.json文件内容,其大致格式如下

{
   "auths": {
       "ccr.ccs.tencentyun.com": {
       "auth": ""
       },
       "hub.tencentyun.com": {
         "auth": ""
       }
   }
}
  • 在刚才的 Drone 配置页面将复制的内容填入Secret Value即可,可以选择开启 Allow Pull Request 选项使得该Secret 也可以在 pull_request 中被访问到,不开启的话pull_request触发的 ci 是无法使用这个 Secret 的哦
  1. 在 pipeline 文件中指定image_pull_secrets使用配置好的Secret
kind: pipeline
type: docker
name: build

# 这里使用image_pull_secrets指定拉取docker镜像时的 secret
image_pull_secrets:
  - dockerconfigjson

clone:
  git:
    image: plugins/git
    network_mode: host
    tags: true

steps:
  # build api
  - name: api-build
    image: my-private-node:12.18.1
    network_mode: host
    depends_on: [tag]
    volumes:
      - name: cache_api_node
        path: /drone/src/api/node_modules
    commands:
      - cd ./api/
      - npm install --registry http://mirrors.cloud.tencent.com/npm
      - npm run build

五、docker 镜像编译与推送

当我们使用 docker 作为服务部署方式的时候,很自然地想要在 CI 中每次将新的docker 镜像编译完成并推送到镜像仓库中去。
这个时候我们可以使用以下两种方式

  1. drone 提供的docker 插件: drone 提供的插件(plugin)功能:http://plugins.drone.io/
steps:

  # push docker-image
  - name: docker-push
    # docker 插件会根据配置将指定的镜像编译完成,并推送到给定的仓库中去
    image: plugins/docker:18.09.0
    depends_on: [your-app-build]
    settings:
      # 配置推送镜像时连接镜像仓库的用户名
      username:
        # 从配置的 Secret 中获取
        from_secret: docker_username
      # 配置推送镜像时连接镜像仓库的密码
      password:
        # 从配置的 Secret 中获取
        from_secret: docker_password
      # 配置私有镜像仓库地址
      repo: hub.tencentyun.com/demo/your-image-name
      # 指定 context,默认git 仓库根路径
      context: ./
      # 指定 dockerfile
      dockerfile: ./Dockerfile
     
      # 配置编译的镜像的 tag,可以指定多个
      # 也可以在其他步骤(如your-app-build)中,将想要使用的 tag 名写入 .tags 文件中
      # 如: echo ‘1.0.01’ > .tags
      # 那个下面的 tags 项可以不写,默认会使用.tags 文件中的内容
      tags:
        - latest
        - ‘1.0.0'

具体配置项可以看这个文档:http://plugins.drone.io/drone-plugins/drone-docker/

  1. 手动指定镜像编译和推送过程

手动的方式配置起来比上面的插件方式要复杂,但上面的 plugin 很愚蠢的一件事情是,会在启动的 docker 容器中再启动docker 命令去编译和推送,相当于 docker 容器中再运行一个 docker 容器,这样的逻辑在并行执行多个时总是会出现异常情况,比如会报错说docker 引擎没有运行。所以使用手动的方式虽然复杂但运行效率高,因为通过挂载的方式使编译过程使用宿主机的 docker 引擎。

steps:

# 这里用到了上面说的挂载,忘记了可以看一下本篇文章上面的挂载与环境变量的内容
# 将宿主机的路径挂载到 pipeline 中
# 将宿主机的路径 /var/run/docker.sock挂载到 pipeline 中,并命名为 docker,让 pipeline 中 docker 镜像使用宿主机的 docker socket
# 将宿主机的路径 /etc/docker/daemon.json挂载到 pipeline 中,并命名为 docker-daemon,让 pipeline 中 docker 镜像使用宿主机的 docker配置
volumes:
  - name: docker
    host:
      path: /var/run/docker.sock
  - name: docker-daemon
    host:
      path: /etc/docker/daemon.json

  # push docker-image
  - name: docker-push
    image: docker:19.03.1
    depends_on: [your-app-build]
    volumes:
      - name: docker
        path: /var/run/docker.sock
      - name: docker-daemon
        path: /etc/docker/daemon.json
    environment:
      IMAGE: hub.tencentyun.com/demo/your-image-name
    commands:
      - export DRONE_TAG=$(cat .tags)
      - docker build -f ./Dockerfile -t $IMAGE:$DRONE_TAG ./
      - docker push $IMAGE:$DRONE_TAG

六、drone 打开 trusted 选项

想在pipeline 中使用挂载功能首先要保证该仓库的 Trusted 选项已经打开,打开方式如下:

  1. 在启动 drone 服务的 docker 命令或者 docker-compose文件中添加如下环境变量,DRONE_ADMIN_USER换成你登录 drone(或者登录gitlab跳转过来) 后的昵称,并重启drone 服务:
    - DRONE_USER_CREATE=username:DRONE_ADMIN_USER,admin:true
  2. drone 页面打开对应仓库的 Settings 页面,当你使用上面配置好的用户登录之后会看到Trusted这样一个配置项,勾选并保存即可:
    Drone CI:搭建自己CI/CD(二)_第3张图片

好啦,对于 Drone CI 的介绍和我的一些使用经验就分享这么多了,大家感兴趣的话可以一试,详细的内容可以查看它的官方网站进行更多的了解哦~!

参考资料:Drone CI - Automate Software Testing and Delivery

你可能感兴趣的:(CI/CD)