オープンソース版Droneで--depthオプション付きでgit cloneを行うようにしました

オープンソース版Droneでのビルド時に、git cloneで時間がかからないよう、--depthオプション付きでgit cloneを実施するようにしました。

デフォルトでは、--depth=50でgit cloneが実行されます。(デフォルト値はTravis CIを参考にしました。)

f:id:kysd:20140213160208p:plain

以下のように、.drone.ymlでgit: depth:に値を指定すると、--depthにその値が設定されてgit cloneが実行されます。

image: go1.2
env:
  - GOPATH=/var/cache/drone
services:
  - mongodb
script:
  - go get labix.org/v2/mgo
  - go get labix.org/v2/mgo/bson
  - go build
  - go test -cover -v
git:
  depth: 1

f:id:kysd:20140213160601p:plain

オープンソース版Droneでビルドの開始・終了をHipChatへ通知する方法

オープンソース版Droneでビルドの開始・終了をHipChatへ通知する方法を記載します。

ビルド対象リポジトリの.drone.ymlに以下を記載します。

notify:
  hipchat:
    room: xxxx
    token: xxxxxxxxxxxxxxxx
    on_started: true
    on_success: true
    on_failure: true

roomにはHipChatのRoom名もしくはRoomのAPI IDを指定します。tokenにはHipChatのAPI Auth Tokenを指定します。

on_startedにtrueを指定すると、ビルド開始時に以下のような通知メッセージが送信されます。

f:id:kysd:20140212232651p:plain

on_successにtrueを指定すると、ビルド成功時に以下のような通知メッセージが送信されます。

f:id:kysd:20140212232657p:plain

on_failureにtrueを指定すると、ビルド失敗時に以下のような通知メッセージが送信されます。

f:id:kysd:20140212232641p:plain

オープンソース版Droneをソースコードから起動する方法

オープンソース版Droneをソースコードから起動する方法は以下の通りです。

1. ソースコードの取得

以下のコマンドを実行してソースコードを取得します。

$ go get github.com/drone/drone/...

2. サービスの起動

以下のコマンドを実行してサービスを起動します。

$ go run $GOPATH/src/github.com/drone/drone/cmd/droned/drone.go

3. サービスの起動確認

Webブラウザでhttp://localhost:8080/installへアクセスし、管理者アカウント登録画面が表示されることを確認します。

f:id:kysd:20140211234127p:plain

オープンソース版Droneで、自作のDocker imageを使ってビルドを行う方法

オープンソース版Droneで、自作のDocker imageを使ってビルドを行ってみましたので、その方法・手順を共有します。

今回は、Go 1.2とMongoDB 2.4がインストールされたDocker imageを作成し、DroneではそのDocker imageを使ってビルドを行います。

なお、Droneのservicesという機能を使うことでビルド時にデータベースを使用できるため、通常は、今回のようなMongoDBがインストールされたDocker imageを使う必要はありません。今回は練習としてこのようなDocker imageを作成しています。ただし、Droneのservicesでは、現在、データベースのバージョンを指定することができませんので、もしデータベースの特定のバージョンでビルドを行いたい場合は、今回のように、ビルド時に使用するデータベースをインストールしたDocker imageを使う必要があります。

Docker imageの作成

以下の内容が記載されたDockerfileを作成します。

# Go 1.2 and MongoDB 2.4
#
# VERSION 1.0

# Use the ubuntu base image provided by dotColud.
FROM ubuntu

MAINTAINER Keiji Yoshida, yoshida.keiji.84@gmail.com

# Update the package lists.
RUN apt-get update

# Install curl and git.
RUN apt-get install -y curl git

# Install Go 1.2.
RUN curl -o /usr/local/go1.2.linux-amd64.tar.gz https://go.googlecode.com/files/go1.2.linux-amd64.tar.gz
RUN tar -C /usr/local -xzf /usr/local/go1.2.linux-amd64.tar.gz
RUN rm /usr/local/go1.2.linux-amd64.tar.gz
ENV PATH $PATH:/usr/local/go/bin

# Install MongoDB 2.4.9.
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10
RUN echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | tee /etc/apt/sources.list.d/mongodb.list
RUN apt-get update
RUN apt-get install -y mongodb-10gen=2.4.9

# Install Bazaar.
RUN echo "deb http://ppa.launchpad.net/bzr/ppa/ubuntu precise main" >> /etc/apt/sources.list
RUN echo "deb-src http://ppa.launchpad.net/bzr/ppa/ubuntu precise main" >> /etc/apt/sources.list
RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys D702BF6B8C6C1EFD
RUN apt-get update
RUN apt-get install -y bzr

※このDockerfileはこちらのGitHubリポジトリより取得頂けます。

以下のコマンドを実行し、作成したDockerfileをもとにDocker imageを作成します。

$ docker build .
Uploading context 40.78 MB
Uploading context
Step 0 : FROM ubuntu
(中略)
Successfully built 36a40c0fe330

以下のコマンドを実行し、作成したDocker imageにタグ付けを行います。今回は、作成したDocker imageにyosssi/go-mongoというリポジトリ名を設定し、1.2-2.4というタグを設定します。

$ docker tag 36a40c0fe330 yosssi/go-mongo:1.2-2.4

以下のコマンドを実行し、作成したDocker imageをDocker公式レジストリへアップロードします。

$ docker push yosssi/go-mongo

Docker Indexの自分のアカウントページで、アップロードしたDocker imageが表示されていることを確認します。

f:id:kysd:20140210024058p:plain

※今回私が作成したDocker imageは、こちらのDocker Indexのページにて内容をご確認頂けます。

プロジェクトの作成

ビルドを行うプロジェクトを作成します。今回は、MongoDBを使用する以下のプロジェクトを用意しました。

yosssi/drone-test-custom-docker-image

mongo.go

package mongo

import (
    "labix.org/v2/mgo"
    "labix.org/v2/mgo/bson"
)

type User struct {
    Name string `bson:"name"`
    Age  int    `bson:"age"`
}

func Find(name string) (*User, error) {
    session, err := mgo.Dial("localhost")
    if err != nil {
        return nil, err
    }
    defer session.Close()
    session.SetMode(mgo.Monotonic, true)
    c := session.DB("droneTest").C("users")
    var user User
    err = c.Find(bson.M{"name": name}).One(&user)
    if err != nil {
        return nil, err
    }
    return &user, nil
}

mongo_test.go

package mongo

import (
    "fmt"
    "labix.org/v2/mgo"
    "testing"
)

var (
    taro   = &User{Name: "Taro", Age: 20}
    hanako = &User{Name: "Hanako", Age: 22}
)

// テストデータを投入
func init() {
    session, err := mgo.Dial("localhost")
    if err != nil {
        panic(err)
    }
    defer session.Close()
    session.SetMode(mgo.Monotonic, true)
    c := session.DB("droneTest").C("users")
    c.DropCollection()
    err = c.Insert(taro)
    if err != nil {
        panic(err)
    }
    err = c.Insert(hanako)
    if err != nil {
        panic(err)
    }
}

// Find関数をテスト
func TestFind(t *testing.T) {
    // User(Nmae="Taro")を抽出
    user, err := Find(taro.Name)
    if err != nil {
        t.Error(err)
    }
    if user.Name != taro.Name || user.Age != taro.Age {
        t.Error(fmt.Sprintf("検索結果が不正です。[期待値: %+v][実際: %+v]", taro, user))
    }

    // 存在しないUserを抽出
    user, err = Find("X")
    if err == nil || err.Error() != "not found" {
        t.Error("検索結果が不正です。検索結果が存在します。")
    }
}

Droneへのリポジトリの登録

画面右上のNew Repositoryボタンをクリックし、今回ビルドを行うGitHubリポジトリをDroneへ登録します。

f:id:kysd:20140210025358p:plain

.drone.ymlの作成

以下の.drone.ymlを作成し、GitHubリポジトリへプッシュします。

image: yosssi/go-mongo:1.2-2.4
env:
  - GOPATH=/var/cache/drone
script:
  - LC_ALL=C mongod --dbpath /var/lib/mongodb --logpath /var/log/mongodb/mongo.log &
  - sleep 10
  - go get labix.org/v2/mgo
  - go get labix.org/v2/mgo/bson
  - go build
  - go test -cover -v

imageに、今回作成したDocker imageのリポジトリ名:タグを記載します。scriptの先頭行でMongoDBを起動しています。

ビルド結果の確認

Droneのビルド結果画面を開き、ビルドが正常終了していることを確認します。

f:id:kysd:20140210025631p:plain

所感

自作Docker imageを使用することで、個々のプロダクトに合わせたビルド環境を柔軟かつ簡単に構築することができると思います。使い捨ての環境を簡単に構築できるというDockerの強み・特徴を十二分に活かした素晴らしい機能だと思います。

オープンソース版Droneのビルドでデータベースを使用する方法

オープンソース版Droneのビルドでデータベースを使用してみました。そのための設定手順を以下に記載します。

サンプルプロジェクト

検証用に、MongoDBを使用する以下のプロジェクトを用意しました。

yosssi/drone-test

mongo.go

package mongo

import (
    "labix.org/v2/mgo"
    "labix.org/v2/mgo/bson"
)

type User struct {
    Name string `bson:"name"`
    Age  int    `bson:"age"`
}

func Find(name string) (*User, error) {
    session, err := mgo.Dial("localhost")
    if err != nil {
        return nil, err
    }
    defer session.Close()
    session.SetMode(mgo.Monotonic, true)
    c := session.DB("droneTest").C("users")
    var user User
    err = c.Find(bson.M{"name": name}).One(&user)
    if err != nil {
        return nil, err
    }
    return &user, nil
}

mongo_test.go

package mongo

import (
    "fmt"
    "labix.org/v2/mgo"
    "testing"
)

var (
    taro   = &User{Name: "Taro", Age: 20}
    hanako = &User{Name: "Hanako", Age: 22}
)

// テストデータを投入
func init() {
    session, err := mgo.Dial("localhost")
    if err != nil {
        panic(err)
    }
    defer session.Close()
    session.SetMode(mgo.Monotonic, true)
    c := session.DB("droneTest").C("users")
    c.DropCollection()
    err = c.Insert(taro)
    if err != nil {
        panic(err)
    }
    err = c.Insert(hanako)
    if err != nil {
        panic(err)
    }
}

// Find関数をテスト
func TestFind(t *testing.T) {
    // User(Nmae="Taro")を抽出
    user, err := Find(taro.Name)
    if err != nil {
        t.Error(err)
    }
    if user.Name != taro.Name || user.Age != taro.Age {
        t.Error(fmt.Sprintf("検索結果が不正です。[期待値: %+v][実際: %+v]", taro, user))
    }

    // 存在しないUserを抽出
    user, err = Find("X")
    if err == nil || err.Error() != "not found" {
        t.Error("検索結果が不正です。検索結果が存在します。")
    }
}

Droneへのリポジトリの登録

画面右上のNew Repositoryボタンをクリックし、今回ビルドを行うGitHubリポジトリをDroneへ登録します。

f:id:kysd:20140209014947p:plain

.drone.ymlの作成

以下の.drone.ymlを作成し、GitHubリポジトリへプッシュします。

image: go1.2
env:
  - GOPATH=/var/cache/drone
services:
  - mongodb
script:
  - go get labix.org/v2/mgo
  - go get labix.org/v2/mgo/bson
  - go build
  - go test -cover -v

.drone.ymlにservicesを記載することで、ビルド時に起動するデータベースコンテナを指定することができます。設定できるデータベースは以下の通りです。

services:
  - cassandra
  - couchdb
  - elasticsearch
  - neo4j
  - mongodb
  - mysql
  - postgres
  - rabbitmq
  - redis
  - riak
  - zookeeper

ビルド結果の確認

Droneのビルド結果画面を開き、ビルドが正常終了していることを確認します。

f:id:kysd:20140209020042p:plain

Droneのオープンソース版を試してみました。

Droneのオープンソース版が公開されたということで、早速こちらを試してみました。

GitHub: https://github.com/drone/drone
デモビデオ: https://docs.google.com/file/d/0By8deR1ROz8memUxV0lTSGZPQUk

GitHubREADME.mdによると、Droneは現在以下のバージョンのUbuntuで動作検証が実施されているとのことでしたので、今回は前述のデモビデオでの手順通り、DigitalOceanでDocker 0.8 Ubuntu 13.04 x64のDropletを作成・起動し、その上でDroneをインストールすることにしました。

  • Ubuntu Precise 12.04 (LTS) (64-bit)
  • Ubuntu Raring 13.04 (64 bit)

f:id:kysd:20140208130000p:plain

インストール

$ wget http://downloads.drone.io/latest/drone.deb
$ sudo dpkg -i drone.deb
$ sudo start drone

以上の作業により、ポート番号80番でDroneが起動します。http://DRONE-HOSTNAME/installへアクセスすると、以下のサインアップ画面が表示されます。

f:id:kysd:20140208131019p:plain

この画面で名前、Eメールアドレス、パスワードを入力し、Create Adminボタンをクリックするとアカウントが作成され、Sysadmin画面が表示されます。

f:id:kysd:20140208131211p:plain

設定

GitHubのAccount settings -> ApplicationsにてRegister new applicationボタンをクリックします。

f:id:kysd:20140208124613p:plain

Homepage URLにhttp://DRONE-HOSTNAME/を、Authorization callback URLにhttp://DRONE-HOSTNAME/auth/login/githubをそれぞれ入力し、GitHub Applicationを登録します。

f:id:kysd:20140208132243p:plain

登録したGitHub ApplicationのClient ID、Client Secretを、Drone Sysadmin画面のGitHub Client ID and Secret:へ入力し、Saveボタンをクリックします。

f:id:kysd:20140208131922p:plain

Droneの画面右上のNew Repositoryボタンをクリックし、リポジトリの登録作業に移ります。

f:id:kysd:20140208132548p:plain

Link Your GitHub AccountのLink NowボタンをクリックしてGitHubアカウントを連携させた後、Repository Setup画面のGitHub Owner、Repository Nameに、DroneでビルドしたいGitHubリポジトリ名を入力し、Addボタンをクリックします。(今回はyosssi/gonhkというリポジトリを登録します。)

f:id:kysd:20140208135018p:plain

.drone.ymlファイルの作成

Droneでビルドしたいリポジトリのルートディレクトリに、以下のような.drone.ymlファイルを作成します。

image: mischief/docker-golang
env:
  - GOPATH=/var/cache/drone
script:
  - go build
  - go test -cover -v

Droneに登録したリポジトリへモジュールをプッシュすると、.drone.ymlファイルの定義情報をもとにビルドが自動的に実行されます。

f:id:kysd:20140208141634p:plain

f:id:kysd:20140208141649p:plain

Docker image

Droneでは、.drone.ymlのimageで設定されたDocker imageをベースにしたコンテナ上でビルドが実行されます。前述の手順ではmischief/docker-golangというDocker imageが使用されています。

リポジトリは、Dockerコンテナ上の以下パスにクローンされます。

/var/cache/drone/src/github.com/$owner/$name

Drone公式のDocker image

Drone公式のDocker imagesとして、以下のものがあります。

# these are the base images for all Drone containers.
# these are BIG (~3GB) so make sure you have a FAST internet connection
docker pull bradrydzewski/ubuntu
docker pull bradrydzewski/base

# clojure images
docker pull bradrydzewski/lein             # image: lein

# dart images
docker pull bradrydzewski/dart:stable      # image: dart

# erlang images
docker pull bradrydzewski/erlang:R16B      # image: erlangR16B
docker pull bradrydzewski/erlang:R16B02    # image: erlangR16B02
docker pull bradrydzewski/erlang:R16B01    # image: erlangR16B01

# gcc images (c/c++)
docker pull bradrydzewski/gcc:4.6          # image: gcc4.6
docker pull bradrydzewski/gcc:4.8          # image: gcc4.8

# go images
docker pull bradrydzewski/go:1.0           # image: go1
docker pull bradrydzewski/go:1.1           # image: go1.1
docker pull bradrydzewski/go:1.2           # image: go1.2

# haskell images
docker pull bradrydzewski/haskell:7.4      # image: haskell

# java and jdk images
docker pull bradrydzewski/java:openjdk6    # image: openjdk6
docker pull bradrydzewski/java:openjdk7    # image: openjdk7
docker pull bradrydzewski/java:oraclejdk7  # image: oraclejdk7
docker pull bradrydzewski/java:oraclejdk8  # image: oraclejdk8

# node images
docker pull bradrydzewski/node:0.10        # image node0.10
docker pull bradrydzewski/node:0.8         # image node0.8

# php images
docker pull bradrydzewski/php:5.5          # image: php5.5
docker pull bradrydzewski/php:5.4          # image: php5.4

# python images
docker pull bradrydzewski/python:2.7       # image: python2.7
docker pull bradrydzewski/python:3.2       # image: python3.2
docker pull bradrydzewski/python:3.3       # image: python3.3
docker pull bradrydzewski/python:pypy      # image: pypy

# ruby images
docker pull bradrydzewski/ruby:2.0.0       # image: ruby2.0.0
docker pull bradrydzewski/ruby:1.9.3       # image: ruby1.9.3

# scala images
docker pull bradrydzewski/scala:2.10.3     # image: scala2.10.3
docker pull bradrydzewski/scala:2.9.3      # image: scala2.9.3

データベース

Droneでは、ビルド時に以下のデータベースコンテナを起動することができます。

service:
  - cassandra
  - couchdb
  - elasticsearch
  - neo4j
  - mongodb
  - mysql
  - postgres
  - rabbitmq
  - redis
  - riak
  - zookeeper

デプロイ

Droneではビルド成功時にデプロイを自動的に行うことができます。

deploy:
  heroku:
    app: safe-island-6261

publish:
  s3:
    acl: public-read
    region: us-east-1
    bucket: downloads.drone.io
    access_key: C24526974F365C3B
    secret_key: 2263c9751ed084a68df28fd2f658b127
    source: /tmp/drone.deb
    target: latest/

通知

Droneではビルド完了時にEメール、HipChat、Webフックでの通知を自動的に行うことができます。

notify:
  email:
    recipients:
      - brad@drone.io
      - burke@drone.io

  urls:
    - http://my-deploy-hook.com

  hipchat:
    room: support
    token: 3028700e5466d375

Eメール通知の設定を行ってみます。今回はAmazon SESを使ってメールを送信することにします。

SysadminのSMTP Server Settings.の各欄へAmazon SESの情報を入力し、Saveをクリックします。

f:id:kysd:20140208152600p:plain

.drone.ymlのnotify.email.recipientsに通知先のメールアドレスを指定します。

image: go1.2
env:
  - GOPATH=/var/cache/drone
script:
  - go build
  - go test -cover -v
notify:
  email:
    recipients:
      - yoshida.keiji.84@gmail.com
      - yoshida.keiji@economica.co.jp

Droneでのビルドが完了すると、.drone.ymlで指定したメールアドレスへ以下のような通知メールが送信されます。

f:id:kysd:20140208153055p:plain

非公開の環境変数の登録

APIキー、パスワードなどの、.drone.ymlファイルに記載したくないパラメータは、リポジトリ名 ->Settings -> Paramsにて設定することができます。

f:id:kysd:20140208142200p:plain

Droneビルドバッヂの取得

リポジトリ名 -> Settings -> BadgesにてDroneビルドバッヂのMarkdown、HTMLコードを取得することができます。

登録可能なリポジトリ

現時点ではGitHub上のリポジトリのみ登録可能でした。Bitbucketはcoming soonとのことです。

所感

「使い捨ての環境を簡単に作り出せる」というDockerの特徴を十分に活かした素晴らしいサービスだと思います。やはりDockerとCIツールの相性は抜群ですね。自作のDocker imageを使ってビルドすることができるため、自分・自社のプロダクト・プロジェクトにあわせた独自のCIツールを簡単に構築することができると思います。