Concourse CIでRailsアプリケーションをデプロイする

## 概要
Concourse CIを使ってRailsアプリケーションをデプロイするデモです。
情報が少なく、色々と自分なりに工夫しました。
これがベストプラクティスなのかどうかは分かりませんが、ひとまずこれでやりたいことは実現できました。

## 環境
- CentOS 7.2
- kernel 4.12.4-1.el7.elrepo.x86_64
- fly 3.3.4
- docker 17.05.0-ce
- docker-compose version 1.15.0
- ruby2.4.1

## 前提条件
以下の条件を満たすようなConcourse CIを作ります。

### 1. ビルドキャッシュを使う
bundle installを毎回1からやると時間がかかってしまうので、キャッシュする

### 2. ブランチによってpipelineを分ける
stagingブランチにマージされたらstaging環境へ、productionブランチにマージされたらproduction環境にデプロイする

### 3. pipelineは独立させる
pipelineのファイルは、railsアプリケーションのレポジトリに入れるのではなく、独立したレポジトリで管理し、concourse-ciのサーバーに設置する

### 4. ビルドログの永続化（未実施）
`docker-compose down` すると、ビルドの履歴や、登録したpipelineなどがすべて消えてしまう

## 運用フロー
簡単にですが、運用フローは以下のようになります。

1. [Rails Application](https://github.com/Esfahan/rails-sample) レポジトリのstaging、productionブランチにマージ
1. Concourse CIが動く
    - Railsのrspecが実行される
    - Capistranoが実行されてデプロイ

## サーバー構成
vagrantを使って以下の環境を構築します。
参考：[Vagrantのインストール、設定手順](https://qiita.com/Esfahan/items/41c4469f28f6011ab33b)

それぞれのサーバーには、`services`の列に書かれているソースを設置します。

| hostname | ipaddr | services |
| --- | --- | --- |
| concourse-ci | 192.168.33.10 | <ul><li>[Concourse CI本体](https://github.com/Esfahan/concourse-ci)</li><li>[pipelines](https://github.com/Esfahan/concourse-ci-pipelines)</li></ul> |
| staging | 192.168.33.11 | [Rails Application](https://github.com/Esfahan/rails-sample) |
| production | 192.168.33.12 | [Rails Application](https://github.com/Esfahan/rails-sample) |

## サンプル用ソース
各種アプリケーションのソースは、githubに上げてある以下の4つのレポジトリのものを使用します。
Capistranoは、Concourse CIのdockerコンテナ内で実行するので、ホストOS上には設置しません。

| GitHub repository | 用途 | 設置するサーバー |
| --- | --- | --- |
| [concourse-ci](https://github.com/Esfahan/concourse-ci) | Concourse CI本体 | 192.168.33.10 |
| [concourse-ci-pipelines](https://github.com/Esfahan/concourse-ci-pipelines) | Railsをデプロイするためのpipeline | 192.168.33.10 |
| [rails-sample](https://github.com/Esfahan/rails-sample) | テスト用Rails Application | <ul><li>192.168.33.11</li><li>192.168.33.12</li></ul> |
| [capistrano-sample](https://github.com/Esfahan/capistrano-sample) | テスト用Capistrano | - |


# 環境構築手順
## concourse-ci(192.168.33.10)の環境構築

### カーネルのバージョンアップ
Concourse CIを使うためには、kernelのバージョンは、v3.19以上である必要があります。
`uname -r` コマンドで、kernelのバージョンを確認して、バージョンが低い場合はアップデートをしてください。

以下に、kernelをupdateするためのシェルスクリプトを作成しました。
こちらを実行すれば、カーネルがアップデートされます。
CentOS7.2でしか動作検証していません。
https://github.com/Esfahan/kernel-updater

もし、このシェルスクリプトでアップデートできない場合は、以下を参考にしてアップデートしてください。
[VagrantのCentOS7のカーネルを更新(yum)](http://qiita.com/reflet/items/b1d9f169dfdad69c4d35)

### docker-composeのインストール
こちらを参考に、docker-composeをインストールしてください。
[docker(docker-engine), docker-composeのインストール](http://qiita.com/Esfahan/items/aa5802548ee42d8a98de)

## staging(192.168.33.11), production(192.168.33.12)の環境構築
[こちら](https://qiita.com/Esfahan/items/1a497fe75405561ef433)を参考にしてruby2.4.1をインストールしてください。
また、`rails-app`という名前のlinuxユーザーを作成してください。

# Concourse CIの設置、設定
## concourse-ciの設置
### ソースの取得
Concourse CI本体のソースを取得します。

```
$ git clone https://github.com/Esfahan/concourse-ci
```

### webとworkerのためのキーを作成
docker-compose.ymlがある階層で実行します。

```
$ mkdir -p keys/web keys/worker
$ ssh-keygen -t rsa -f ./keys/web/tsa_host_key -N ''
$ ssh-keygen -t rsa -f ./keys/web/session_signing_key -N ''
$ ssh-keygen -t rsa -f ./keys/worker/worker_key -N ''
$ cp ./keys/worker/worker_key.pub ./keys/web/authorized_worker_keys
$ cp ./keys/web/tsa_host_key.pub ./keys/worker
```

### .envの作成
内容は適宜変更してください。

```
$ mv .env.example .env

CONCOURSE_BASIC_AUTH_USERNAME=管理画面にログインするためのユーザー名
CONCOURSE_BASIC_AUTH_PASSWORD=管理画面にログインするためのパスワード
CONCOURSE_EXTERNAL_URL=http://192.168.33.10:8080（管理画面のURL:8080）
CONCOURSE_POSTGRES_USER=postgresのユーザー名
CONCOURSE_POSTGRES_PASSWORD=postgresのパスワード
```

### Concourse CIを起動

```
$ docker-compose up -d --build
```

以下のURLにアクセスすると、画面が見れるようになっているはずです。
.envでセットしたアカウントでログインしてみてください。
http://192.168.33.10:8080

### CLIのインストール
Concourse CIをCLIで実行するためにflyを使います。
以下の記事を参考にしてインストールしてください。

[CLIのインストール](https://qiita.com/Esfahan/items/0bb62b76d999c30b87e3#cli%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)

## pipelinesの設置
### ソースの取得
Railsをデプロイするためのpipelineのソースを取得します。

```
$ git clone https://github.com/Esfahan/concourse-ci-pipelines
```

### 秘密鍵を定数にセット
pipelineでgitを扱うためと、capistranoでデプロイする際に使う秘密鍵を、以下のようにして定数にセットします。
どちらも同一の鍵を使用するので、定義する定数は1つです。

```yaml:~/.concourse/credentials.yml
---
github-private-key: |
  -----BEGIN RSA PRIVATE KEY-----
  **snip**
  -----END RSA PRIVATE KEY-----
```

### Concoures CIにログイン
Concoures CIにflyコマンドでログインします。
`-t`のあとの名前は、任意のものを付けてください。

```
$ fly -t concourse-sample login -c http://192.168.33.10:8080
```

ログイン成功すると、以下のコマンドでターゲットの一覧を確認できます。

```
$ fly targets
name          url                        team  expiry
concourse-sample  http://192.168.33.14:8080  main  Sat, 30 Sep 2017 07:33:20 UTC
```

### pipelineの書式
長くなるので、別記事にしました。
[Concourse CIのpipelineの書き方](https://qiita.com/Esfahan/items/81eb53686506ab6bd898)

こちらのソースの詳細な説明となります。
https://github.com/Esfahan/concourse-ci-pipelines

pipelineの書き方を知りたい場合に参考にしてください。

### pipelineをConcourse CIに登録
Concourse CIは、以下の流れでデプロイを実行します。

- pipelineをConcourse CIに登録
- Concourse CIが定期的にレポジトリをチェックし、前回デプロイ時とソースに差分があると自動でデプロイをする
    - pause中はデプロイしないが、unpuaseにした時点で差分を検知すればデプロイが始まる（pauseについては[後述](https://qiita.com/Esfahan/items/af6f5b092fbb2a406210#%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E5%BE%85%E6%A9%9F%E7%8A%B6%E6%85%8B%E3%81%AB%E3%81%99%E3%82%8B)）

以下のコマンドでpipelineを登録します。

```
$ fly -t [target name] set-pipeline -p [pipelineの名前] -c concourse-ci-pipelines/rails-sample/pipelines/pipeline.yml -l ~/.concourse/credentials.yml
```

`target name`は、login時に付けた名前、`pipelineの名前`は、何でもよいのですが、アプリケーションの名前を付けるとわかりやすいと思います。今回は、以下の内容で登録をします。

```
$ fly -t concourse-sample set-pipeline -p rails-sample -c concourse-ci-pipelines/rails-sample/pipelines/pipeline.yml -l ~/.concourse/credentials.yml

apply configuration? [yN]: y
pipeline created!
you can view your pipeline here: http://192.168.33.10:8080/teams/main/pipelines/rails-sample

the pipeline is currently paused. to unpause, either:
  - run the unpause-pipeline command
  - click play next to the pipeline in the web ui
```

### pipelineの図を確認
登録が完了すると、web画面で以下のようなpipelineの図が見れるようになっているはずです。
http://192.168.33.10:8080

![f76a136b0286f1eb060547191c61c10b.png](https://qiita-image-store.s3.amazonaws.com/0/105936/77323fb0-594d-22d2-7a6d-29d56a881a7f.png)

黒い四角はgitレポジトリ、灰色はdockerコンテナで、
実線は、そのgitレポジトリのソースに変更があったらCIが稼働するという意味で、点線は変更があってもCI稼働のトリガーとならないものです。

### デプロイ待機状態にする
このままではまだデプロイされません。
web画面の左メニューを見ると、`pipelineの名前`の横に、再生ボタンがあると思います。
このボタンをクリックするか、以下のコマンドを実行することで、デプロイ待機状態となり、
gitのソースに差分があることを検知すると、自動でデプロイが実行されるようになります。

コマンドでunpauseしてみます。

```
$ fly -t concourse-sample unpause-pipeline -p rails-sample
```

すると、以下のように、`rails-job-staging`と`rails-job-production`が光りだし、デプロイが始まります。

![e41b0d578360a4388a890ce0df34a41f.png](https://qiita-image-store.s3.amazonaws.com/0/105936/2821179d-7463-1265-b64c-4ccd72e60821.png)

*`capistrano-sample`がオレンジ色になっているのは、エラーが起きているからです。
存在しないブランチ名を定義してしまっていたことが原因でした、気にしないでください。
このように、エラーが起きた場合はオレンジ色になります。*

### 実行内容を確認
rails-job-stagingをクリックすると、以下のように、実行内容を確認できます。
以下の画像は、実行が正常終了したあとのものですが、実行中は黄色、エラー終了の場合はオレンジになります。

![9200b3e9a692d20152bdf0e7d8d149e2.png](https://qiita-image-store.s3.amazonaws.com/0/105936/bae4309d-980f-4c7f-f7dd-2690289fce5c.png)

全て正常終了すると、緑になります。
これでデプロイ完了です！

![5f3d2b786717cba9a3538c9f3ccf3c88.png](https://qiita-image-store.s3.amazonaws.com/0/105936/c9407134-6749-fa24-6dd5-f449761705b8.png)

コマンドでも実行させることもできます。

```
$ fly -t concourse-sample trigger-job -j rails-sample/rails-job-staging
```

### 各種ステータス
TOPページの左下にあるように、各種ステータスの説明があります。

![a56ff9225fae8ba4e809092f17db6b12.png](https://qiita-image-store.s3.amazonaws.com/0/105936/562bcd29-0add-f61b-9ae6-66afe54e5670.png)

### 手動実行
実行内容詳細の画面の右上の`+`ボタンで、手動実行させることも可能です。

![2adb06bc36bd1f13b1f14e2c67bc02e8.png](https://qiita-image-store.s3.amazonaws.com/0/105936/c5e77e41-e63b-472b-2122-97cc8498b766.png)


## ビルドキャッシュについて
前提条件のところに書いたとおり、ビルドキャッシュを有効にしています。
その証拠に、`bundle install`が実行される、`rails-job-staging`と`rails-job-production`ともに、1回目より2回目以降のほうが、実行時間が短くなっています。

durationが、初回は2分51秒だったのが、2回目以降は、9秒に短縮されています。
ただし、キャッシュは、キャッシュ用コンテナで管理されているので、`docker-compose down`をすると、キャッシュクリアされます。

![c08d9d73a7ab7f1c44a4222a58d8606a.png](https://qiita-image-store.s3.amazonaws.com/0/105936/53b95554-684e-9716-23b4-4a3699004bb2.png)

![4cf8108cc4107f1a0fd77bb1cb3258cd.png](https://qiita-image-store.s3.amazonaws.com/0/105936/25b8fc70-61ee-dff4-a948-21fe43f88902.png)


## 同時実行数
pipelineに、`max_in_flight`というものを定義すると、同時実行数がセットできるみたいです。
デフォルトは`1`みたいです。
詳しくは以下で。
https://concourse.ci/configuring-jobs.html#max_in_flight

以上

## 関連記事
- [Concourse CIの環境構築手順](https://qiita.com/Esfahan/items/0bb62b76d999c30b87e3)
- [Concourse CIのpipelineの書き方](https://qiita.com/Esfahan/items/81eb53686506ab6bd898)

## 参考
- [Documentation](https://concourse.ci/introduction.html)
- [パイプラインベースのCI/CDツール Concourse CI入門](https://blog.ik.am/entries/379)
- https://concourse.ci/running-tasks.html#caches
- [Caching directories between runs of a task #230](https://github.com/concourse/concourse/issues/230)