drone.ioでdrone-volume-cacheを使ってビルドを高速化する

## 概要
drone.ioでは、毎回コンテナが生成され、終わると破棄されるため、
bundler、composer、npmなどを使っていると、
毎回インストールが発生するためビルドに時間がかかってしまいます。

そこで、 `drillster/drone-volume-cache` というpluginを使い、
そのような毎回インストールが発生するパッケージをキャッシュとして保存し、
ビルドに時間がかからないようにします。

- [[Github] drillster/drone-volume-cache](https://github.com/Drillster/drone-volume-cache)
- [[DockerHub] drillster/drone-volume-cache](https://hub.docker.com/r/drillster/drone-volume-cache/)

## 環境
- drone 0.7.3
- docker-compose 1.8.1

## まずはじめに
drone-volume-cacheを使うためには、
drone.ioの画面の各レポジトリの [Settings]で 、[Trusted]を[on]にする必要があります。
OFFのままビルドをしようとすると、
`ERROR: Insufficient privileges to use volumes` 
というエラーが出ます。

そしてまた、[Trusted]を[on]にするためには、ADMINユーザーである必要があります。

ネットワークによっては下記のようなことが起きるかも。。
参考：[Trustedオプションの設定](http://qiita.com/mechamogera/items/f3166d643f5fb0436f02#trusted%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E8%A8%AD%E5%AE%9A)

> Drone.ioのSETTINGSの変更
> 
> 現象：Drone.ioで該当リポジトリのSETTINGSを変更しても反映されない。
> 原因：SETTINGSの変更はPATCHメソッドで実行されていたがプロキシが対応しておらず400でエラーになっていた(ブラウザのネットワークの通信を見てわかった)。
> 対応：一時的に外部からのアクセスを許可してプロキシを利用しないで設定変更、、、

まず、ADMINユーザーを設定するため、
drone.ioのホストOS上のdocker-compose.ymlで、 `DRONE_ADMIN` に、ADMINにするユーザーを指定します。
GITのユーザー名をカンマ区切りで複数指定することができます。
参考：http://readme.drone.io/0.5/install/authorization/

```yaml:docker-compose.yml
version: '2'

services:
  drone-server:
    image: drone/drone:0.7.3
    ports:
      - 80:8000
    volumes:
      - ./drone:/var/lib/drone/
    restart: always
    # カンマ区切りで複数指定可能
    DRONE_ADMIN=GITのユーザー名, GITのユーザー名

# *snip*
```

次に、drone.ioの画面で、対象のレポジトリの[Settings]で[Trusted]を[on]にします。

<img width="469" alt="drone_trusted.png" src="https://qiita-image-store.s3.amazonaws.com/0/105936/735cbe46-d48d-2a39-2242-e18b1cd5830a.png">

ADMINユーザー以外で操作をすると、
`Error updating repository settings`
というエラーが表示され、 [Trusted] の設定を変更することができません。

## .drone.ymlの作成
各レポジトリの.drone.ymlに、`restore-cache` と `rebuild-cache`を追記します。

```yaml:.drone.yml
pipeline:
  restore-cache:
    image: drillster/drone-volume-cache
    restore: true
    mount:
      - ./vendor
    volumes:
      - /tmp/cache:/cache
    ttl: 7
    when:
      branch: [ master, staging ]
      event: push

  build:
    image: ruby:2.3.1
    commands:
      - git clone https://github.com/user/appname.git /tmp/appname
      - bundle install --path vendor/bundle
      - cp .env.drone .env
      - bundle exec rake db:migrate
      - bundle exec rspec spec
    when:
      branch: [ master, staging ]
      event: push

  rebuild-cache:
    image: drillster/drone-volume-cache
    rebuild: true
    mount:
      - ./vendor
    volumes:
      - /tmp/cache:/cache
    ttl: 7
    when:
      branch: [ master, staging ]
      event: push

services:
  drone_postgresql:
    image: postgres:9.5
    environment:
      - POSTGRES_DB=appname_test
      - POSTGRES_USER=postgres
```

`restore-cache` で、ホストOSからビルドコンテナへファイルをコピーします。
`rebuild-cache` で、ビルドコンテナからホストOSへファイルをコピーします。

build前に、キャッシュされているファイルを展開し、
build後に、ファイルをキャッシュし直します。
こうすることで、buildによって差分が生まれたファイルもキャッシュされることになります。

その他の設定については以下の通りです。

| name | desc |
| --- | --- |
| mount | キャッシュする各レポジトリのディレクトリを指定します。配列で複数指定も可能です。 |
| volumes | ホストOSの/tmp/cacheとビルドコンテナの/cacheを同期します。 |
| ttl | キャッシュファイルを何日残すか、日数を指定します。 |

これで、droneのホストマシンの/tmp/cacheの下に以下のようなキャッシュファイルが作成され、ビルドする際に参照されるようになります。
`/tmp/cache/username/appname/1/vendor/`

参考: http://plugins.drone.io/drillster/drone-volume-cache/

## キャッシュクリア方法、キャッシュを利用しない方法
GITのコミットメッセージに以下を含めると、それぞれ特定の挙動をします。

| message | desc |
| --- | --- |
| [CLEAR CACHE] | キャッシュをクリアしてからビルドする |
| [NO CACHE] | キャッシュを使用せずにビルドする(rebuildもされなくなる) |

参考：https://github.com/Drillster/drone-volume-cache/blob/master/DOCS.md


## 課題
初回実行時のみ、
`restore-cache` で、以下のエラーが発生します。

```
find: ‘/cache/username/appname/1’: No such file or directory
```

初回は、ホストOS上に、`/cache/username/appname/1` が作成されていないことが原因のようです。
`rebuild-cache` が実行されたタイミングでディレクトリが作成されるようです。
手動で、ディレクトリを作成しておけばエラーにはなりませんが、
今のところ良い解決方法はまだ見つかっていません。

### 暫定対応案
とりあえず、下記のいずれかで対応する。

- 手動でdroneのホストOSに `mkdir /cache/username/appname/1` をする
- `appleboy/drone-ssh` のpluginを使って、droneサーバーにsshで入り、 `/cache/username/appname/1`がなければ作成する。（一番イケてない）

## 追記 - 解決 2017/07/12
drone-volume-cacheのプラグインに問題があることが発覚しました。
最初に、古いキャッシュを消そうとする処理があるのですが、キャッシュが存在していなくても、削除処理を走らせようとしていて、そこでエラーになっていました。

問題の箇所はこちらです。
https://github.com/Drillster/drone-volume-cache/blob/master/cacher.sh#L43
43 - 44行目

```bash
42            echo "Removing files and (empty) folders older than $PLUGIN_TTL days..."
43            find "/cache/$DRONE_REPO_OWNER/$DRONE_REPO_NAME/$DRONE_JOB_NUMBER" -type f -ctime +$PLUGIN_TTL -delete
44            find "/cache/$DRONE_REPO_OWNER/$DRONE_REPO_NAME/$DRONE_JOB_NUMBER" -type d -ctime +$PLUGIN_TTL -empty -delete
```

なので、 該当箇所を下記のように修正し、自作イメージ化して使うことにしました。

```bash
42            if [ -d "/cache/$DRONE_REPO_OWNER/$DRONE_REPO_NAME/$DRONE_JOB_NUMBER" ]; then
43              echo "Removing files and (empty) folders older than $PLUGIN_TTL days..."
44              find "/cache/$DRONE_REPO_OWNER/$DRONE_REPO_NAME/$DRONE_JOB_NUMBER" -type f -ctime +$PLUGIN_TTL -delete
45              find "/cache/$DRONE_REPO_OWNER/$DRONE_REPO_NAME/$DRONE_JOB_NUMBER" -type d -ctime +$PLUGIN_TTL -empty -delete
46            fi
```

修正したものを、DockerHubに公開しました！
~~https://hub.docker.com/r/esfahan/drone-volume-cache/~~
[追記 2017/07/13] オリジナルのものが修正されたのでprivateにしました。

今回私は、DockerHubにイメージ公開しましたが、
公開しなくても、droneのホストOSにイメージが存在していればOKです。
参考：[DockerHubに自作イメージを公開する](http://qiita.com/Esfahan/items/a5d13f2987a5b69a63b8)

オリジナルのプラグインには、現在プルリクを出しています。
https://github.com/Drillster/drone-volume-cache/pull/9


## 追記 2017/07/13
> オリジナルのプラグインには、現在プルリクを出しています。
> https://github.com/Drillster/drone-volume-cache/pull/9

こちら、マージされました！
動作も問題ありませんでした。
https://hub.docker.com/r/drillster/drone-volume-cache/

以上

## 関連
- [Droneのキャッシュを使ってビルドを高速化](qiita.com/naokazuterada/items/2f1e6473476a0b85acad)
- [docker(docker-engine), docker-composeのインストール](http://qiita.com/Esfahan/items/aa5802548ee42d8a98de)
- [docker-compose on CentOS7構築手順メモ](http://qiita.com/Esfahan/items/284d49e20648fde92639#_reference-d38112c6975680e294f9)
- [OSSのCI、Droneを導入する (Ver0.7)](http://qiita.com/kurkuru/items/585ca5c83c48664273b2)
- [DockerHubに自作イメージを公開する](http://qiita.com/Esfahan/items/a5d13f2987a5b69a63b8)