Setting up drone.io CI

By: on May 8, 2018

Drone.io for go

I was recently asked to set up a CI server for one of our go projects and decided to try out drone.io 0.8.

From my point of view, the two most attractive features of drone.io are that:

  • the build is defined through a single .drone.yml file in the root directory of the project
  • all build steps are executed in docker containers

Drone is primarily offered as software-as-a-service, but you can host and run your own instances via docker-compose.

Drone integrates with a number of source code repository providers and relies on their authentication / user management. Builds for all branches are triggered via webhooks.

Drone’s web UI is slick and minimal. A command line interface is also provided.

Documentation is clear and to the point. A number of useful plugins exist, for example for posting build results on a Slack channel.

Some details

To give you an impression of a simple drone setup, we’ll have a look at some key files.

Our project uses make and vgo. Our .drone.yml looks roughly like this:

workspace:
  base: /go
  path: slick_project

pipeline:
  build:
    image: base:1
    commands:
      - set -ex
      - mkdir -p /go/src/Go_external
      - export GOPATH=/go/src/Go_external:/go/slick_project/Go
      - go get -u github.com/kevinburke/go-bindata/...
      - go get -u golang.org/x/vgo
      - export PATH=$PATH:/go/src/Go_external/bin:/opt/wkhtmltopdf/wkhtmltox/bin/
      - (cd Go/src/ed4/ && go get ./...)
      - make platform
  test:
    image: base:1
    commands:
      - set -ex
      - export GOPATH=/go/src/Go_external:/go/slick_project/Go
      - export PATH=$PATH:/go/src/Go_external/bin:/opt/wkhtmltopdf/wkhtmltox/bin/
      - make test
  slack:
    image: plugins/slack
    webhook: https://hooks.slack.com/services/XYZ
    channel: slick_channel
    when:
      status: [ success, failure ]
    template: >
      {{#success build.status}}
        Success! Build {{build.number}} {{build.link}} ({{build.commit}} on {{build.branch}} by {{build.author}})
      {{else}}
        Fail. Build {{build.number}} {{build.link}} ({{build.commit}} on {{build.branch}} by {{build.author}})
      {{/success}}

The workspace section defines a docker volume that will be shared by all docker containers in the build pipeline.

The build pipeline comprises three steps, which are executed sequentially and in individual containers. For the build and test steps, we use a custom local docker image that adds external dependencies (such as wkhtmltopdf and various libraries) on top of the official golang 1.10.x image. The command section for each step is translated into a bash script, which is then executed in the respective container. In our case, the scripts fetch go dependencies, set up appropriate paths and finally call make. Make’s exit code determines success/failure of the step. The third step demonstrates how you could post the build outcome on a slack channel. As you can see, drone provides various pieces of build information via variables, as well as conditional execution of build steps.

We host our internal drone instance on a server with a public DNS entry so that it is visible to our source code repository (bitbucket cloud). The server needs docker and docker-compose installed. The docker-compose.yml for our setup looks like this:

version: '2'

services:
  drone-server:
    image: drone/drone:0.8
    ports:
      - 80:80
      - 443:443
      - 9000
    volumes:
      - /var/lib/drone:/var/lib/drone/
    restart: always
    environment:
      - DRONE_OPEN=false
      - DRONE_ADMIN=superstar,unicorn,patrick
      - DRONE_BITBUCKET=true
      - DRONE_BITBUCKET_CLIENT=some_key
      - DRONE_BITBUCKET_SECRET=some_secret
      - DRONE_HOST=https://drone.xyz.com
      - DRONE_SECRET=drone_secret
      - DRONE_LETS_ENCRYPT=true

  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

In this configuration, there is no public access to the web UI, and drone’s built-in support for Let’s Encrypt is used to automatically obtain certificates. A single agent for executing build pipelines is run on the same server.

We run drone’s docker-compose.yml via systemd; the drone.service file looks as follows:

[Unit]
Description=drone service with docker-compose
Requires=docker.service
After=docker.service

[Service]
Restart=always
WorkingDirectory=/drone
User=drone
ExecStartPre=/usr/local/bin/docker-compose down -v
ExecStartPre=/usr/local/bin/docker-compose rm -fv
ExecStart=/usr/local/bin/docker-compose up
ExecStop=/usr/local/bin/docker-compose down -v

[Install]
WantedBy=multi-user.target

The drone user needs to be part of the docker group and is set up in a standard fashion.

Conclusion

It always takes a little while to set up a new piece of infrastructure. In the case of drone.io, team consensus is that it has been worth the effort.

Share

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*