容器云时代的持续发布工具 Drone 入门

前言

对于现代化的新型应用运行和部署架构而言,传统的 CD 工具虽说不能算力不从心,但至少已经失去了大量优势。从步骤化、流程化和管道化的现代工具变成了“脚本触发器”。原因在于传统的 CI/CD 并不适应对容器化应用的运行和部署过程(无法提供直接性的帮助),同时因为 Docker 自身工具链的强大而变相的削弱了 CD 平台的“力量”。

而本文想介绍的就是一个为 Docker 应用构建和发布而生的工具,利用它能很方便的搭建出适应容器化时代的 CI/CD 平台。

传统的持续发布

如果提到持续集成/发布方案,最典型的最大众的大概就是 Jenkins 了。Jenkins 很强大,但是同时它又很普通,不过很多时候我们缺乏的是一个可靠的能实际解决问题的方案,哪怕这个方案不够优雅,Jenkins 便是这样一个选择。

借用 Société Générale(一家法国银行)公司曾经在 DockerCon 上的演讲内容的一张图:

level-1

这张图充分表示出了一个 Java 应用如何利用 Jenkins 实现持续集成和应对容器化环境的持续发布过程。

在这整套流程中,Jenkins 集成了 Github、Nexus、Trusted Registry 等远程服务。从一个 Trigger 事件(这里图中没有描述)开始调度 Slave 进程,对源代码和项目依赖进行拉取,然后经过单元测试和对应用镜像的构建,最终推送到 Registry 中。

其中主要有几个方面值得思考:

  1. 首先是一系列的小问题合集:

    • 如何准确判断容器镜像在构建过程中的失败状态?
    • 在单元测试阶段如何解决外部服务的依赖问题?例如:测试代码中包含对数据库的访问
    • 如何并行构建多个不互相依赖的模块?
    • 如何进行多版本环境下的集中测试?
  2. 宿主机环境的隔离性问题以及分布式构建架构是否多余?

    • 通常 Jenkins 是直接运行在宿主机上的,为了实现对多种应用环境的支持,宿主机系统上会安装各种编译器、运行时等集成环境以及存在大量的应用依赖,这些东西如何保证隔离性以及便捷的升级维护?并且,这种对系统的强侵入导致换掉 CI 服务器或者多部署几台 CI 服务器都变得复杂和麻烦。

    • Jenkins 的 Master/Slave 架构在传统的应用部署上是有优势的(面对众多的需要分发的应用运行节点),但是在容器化部署方式面前就有些多余了。因为存在一个统一的 DTR 这样的集中式的应用获取渠道,再加上容器的核心概念即环境的封装,对于应用的分发而言已经不需要构建和测试过程。在 Docker 工具链的强大支持上(例如 Docker Compose),升级应用版本相当于仅仅就是传递一个新的标签变量而已,那么多的 Slave 节点不是多余吗?

上面提到的一些实际上可以用一些小技巧解决,但是这些小技巧并不“优雅”,并且会带来重复的样板代码。并且,最重要的,它们是“不受信任的”。如果集中在 Jenkins 上的 Job 仍然需要的大量 Shell 来完成构建过程,那么对于 Jenkins 而言本就是很讽刺的一件事,并且刚才提到过,因为这种方式相比插件而言不够安全,所以是“不受信任的”。

当然,我并不是完全不建议使用 Jenkins,其实我以前也用 Jenkins 并且有大量的方案和经验。但是当你接触过 Travis 或者 Gitlab CI 并且习惯它们以后,你才会发现 Jenkins 的真正不足的点在哪里。所以我说了,它强大但是普通。

Drone

Drone is a Continuous Delivery platform built on Docker, written in Go

这是官方仓库上的描述,这一句话的意思指出 Drone 三个重点:

  1. 持续交付平台
  2. 建立在 Docker 之上
  3. 使用 Go 语言编写

Drone 是一个使用 Go 语言编写的持续交付平台(工具),这一句很好理解,但是为什么说建立在 Docker 之上呢?当我们经过下面的尝试便知这句话的含义了!

安装 Drone

虽然 Drone 官方提供有完全托管的云服务,但没有免费选项(每个月 129 美元),并且也不适合内部团队使用。幸运的是 Drone 同时提供了开源版本,所以我们可以自行部署内部的 Drone 服务,也就是得经历本章节的安装配置过程。

Drone 可以直接放在本地执行,当然并不建议这么做,因为彻底的 built on docker 可以方便以后对环境的迁移(保证了平台和系统之间的隔离性)。

Drone 自身主要由 drone-server 和 drone-agent 两个“组件”组成,它们都提供 Image 可以直接拉取下来使用,所以我们创建 docker-compose.yaml:

version: '2'

services:
  drone-server:
    image: drone/drone:0.8

    ports:
      - 80:8000
      - 9000
    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/agent:0.8

    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:有关 Docker Compose 的用法本文不作介绍,这个应该是必备的技能。

我们可以发现这个文件中有很多的变量,其实这些就是所需的全部配置选项,完成这些变量的赋值即等于配置完成。我们可以直接填入的部分有 DRONE_HOST、DRONE_SECRET,前者表示绑定 Drone 服务的域名(包含协议),后面的 DRONE_SECRET 是通信认证的密钥,agent 和 server 要保持一致,使用随机字符串即可。

接下来我们得对 Github 进行配置才能填写后续变量。访问 https://github.com/settings/applications 在 Deleloper settings 中创建一个 OAuth App,其中 Homepage URL 要使用 DRONE_HOST 变量的值,也就是你访问(绑定) Drone 服务的地址。Authorization callback URL 则是在 Homepage URL 的基础上加上 /authorize。创建完成后会生成 Client ID 和 Client Secret,分别对应 docker-componse.yml 中的 DRONE_GITHUB_CLIENT 和 DRONE_GITHUB_SECRET 变量,至此所有的值已经确定好了。

我的例子:

version: '2'

services:
  drone-server:
    image: drone/drone:0.8
    ports:
      - 8000:8000
      - 9000
    volumes:
      - /var/lib/drone:/var/lib/drone/
    restart: always
    environment:
      - DRONE_OPEN=false
      - DRONE_HOST=https://drone.bluerain.io
#      - DRONE_ORGS=Hentioe
      - DRONE_ADMIN=Hentioe
      - DRONE_GITHUB=true
      - DRONE_GITHUB_CLIENT=****
      - DRONE_GITHUB_SECRET=****
      - DRONE_SECRET=****

  drone-agent:
    image: drone/agent:0.8

    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=****

如果你还没明白 Github 上的 OAuth App 的 Authorization callback URL 该怎么填写的话,我将我的的贴出来就应该明白了:

https://drone.bluerain.io/authorize

排除星号替代的敏感数据,会发现我的配置中多了 DRONE_ADMIN 以及将 DRONE_OPEN 的值改为了 false。其中 DRONE_ADMIN 的值是你的 Github 账号的 Username,经过这样的配置你自己部署的 Drone 服务只会允许你自己的 Github 账号登录后使用。
如果要授权给整个组织账号的使用权限,则配置 DRONE_ORGS 为 Organization 的名称即可,也就是我注释掉的部分。

配置前端代理(Nginx):

server {
    listen       80;
    server_name  <YOUR_DRONE_HOST>;

    location / {
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
        proxy_pass http://0.0.0.0:8000;
        proxy_redirect off;
        proxy_http_version 1.1;
        proxy_buffering off;
        chunked_transfer_encoding off;
    }
}

现在只需要手动访问自己部署的私有 Drone 服务,完成授权登录便可以使用了。当然,Drone 并不是只支持 Github,毕竟企业项目通常是存放在内部仓库中,Drone 同时支持 Gitlab 和 Bitbucket 还有国内的 Coding 等公共或私有平台:)集成其它平台的大致过程是一样的,可以访问官方文档:http://docs.drone.io/install-for-gitlab

入门 Drone

待写