Concourseでマイクロサービスを継続的インテグレーション

これは[Fujitsu Advent Calendar 2016](http://qiita.com/advent-calendar/2016/fujitsu)の21日目の記事です。

※タイトルについて、申し訳ないですがハウツーではないです。

## はじめに
先日、[Cloud Foundry Days in Tokyo](https://cfdays.connpass.com/event/41201/)というコミュニティイベントに参加してきました。
資料は[connpass](https://cfdays.connpass.com/event/41201/presentation/)に公開されていますが、ConcourseというCI/CDツールが度々登場しました。
プロペラマークが目印です。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">concourseはよいものですよ <a href="https://twitter.com/hashtag/cfdtokyo?src=hash">#cfdtokyo</a></p>&mdash; Ken Ojiri (@kenojiri) <a href="https://twitter.com/kenojiri/status/796952855592566784">2016年11月11日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Cloud FoundryとConcourse CI使っています <a href="https://twitter.com/hashtag/cfdtokyo?src=hash">#cfdtokyo</a> <a href="https://t.co/IVR5xjcKcu">pic.twitter.com/IVR5xjcKcu</a></p>&mdash; Toshiaki Maki (@making) <a href="https://twitter.com/making/status/796995948874342401">2016年11月11日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-lang="ja"><p lang="en" dir="ltr">Circle of code <a href="https://twitter.com/hashtag/cfdtokyo?src=hash">#cfdtokyo</a> <a href="https://t.co/GfSnJHROWI">pic.twitter.com/GfSnJHROWI</a></p>&mdash; Toshiaki Maki (@making) <a href="https://twitter.com/making/status/796953543538155520">2016年11月11日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

今回のイベントで頻出したのはConcourseがCloudFoundryのために作られたCI/CDツールだからという理由もあるでしょう。
ただCI/CDツールと言えばJenkinsというイメージだった私は焦りを覚えてConcourseを触って見ることにしました。

## Concourseをとりあえず動かす
Concourseは、ビルド等のタスクの集まりをパイプラインで記述することでビルドパイプラインを実行、可視化してくれるCI/CDツールです。
また、各タスクはDockerコンテナ上で実行されるので再現性の高いビルドが可能となっています。
日本語の資料は少ないですが、以下のサイトがよくまとまっているので、とりあえず動かすのは難しくなかったです（今回はdocker-compose版を使用）。

* [パイプラインベースのCI/CDツール Concourse CI入門 - BLOG.IK.AM](https://blog.ik.am/entries/379)
* [はじめてのConcourse CI - BLOG.IK.AM](https://blog.ik.am/entries/380)

これで今どきのマイクロサービスを継続的インテグレーションできる気がしてきました。

## サービスのテスト

[Cinema 3](https://github.com/umermansoor/microservices)というgithubで見つけた超シンプルなマイクロサービスっぽいものを使わせていただきます。
これは映画予約を想定した4つのマイクロサービスから構成されます。(bookings, movies, showtimes, user)
今回は元のコードから以下の点を変更することにしました。

* 言語：Python2→Python3
* DB：JSONファイル→MongoDB (herokuのMongoDBアドオンを想定)
* リポジトリ管理：
    * 1つのリポジトリのサブディレクトリにサービス→１リポジトリ１サービスに分ける。([『マイクロサービスアーキテクチャ』の6章.デプロイ](https://www.oreilly.co.jp/books/9784873117607/)→[まとめ記事](http://qiita.com/crossroad0201/items/23864b3b177fb1fce966#%EF%BC%96%E7%AB%A0%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4)）
    * 新規→リポジトリにciディレクトリを作り、Dockerfile, pipeline.ymlなどのCI資材を管理する。([8 Pro Tips for Using Concourse CI with Cloud Foundry](https://blog.altoros.com/concourse-ci-architecture-features-and-usage.html)のPro tipsその１)

ということで作ったmoviesサービスがこちら(<https://github.com/matsulib/cinema3-movies>)です。

以下のコマンドを実行してみると…

```shell-session
$ fly -t lite set-pipeline -p movies -c cinema3-movies/ci/pipelines/pipeline-movies.yml
```

成功しました。

![m1.PNG](https://qiita-image-store.s3.amazonaws.com/0/141719/8a8d4167-3995-7ead-9e3c-6bd5bf1ab63c.png)

標準出力、標準エラー出力からテスト結果を確認することができます。

![m2.PNG](https://qiita-image-store.s3.amazonaws.com/0/141719/aae485ca-610e-0004-aea9-1eb973bbe0d2.png)

Jenkinsには、xUnit形式で出力したXMLファイルのテスト結果をレポートとして表示できる機能がありますが、Concourseにはないようです。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="en" dir="ltr"><a href="https://twitter.com/DougStephenJr">@DougStephenJr</a> Nope, just stdout/stderr for now. Not sure yet whether we want to integrate with test frameworks vs. delegating to resources.</p>&mdash; Concourse CI (@concourseci) <a href="https://twitter.com/concourseci/status/715219875761795072">2016年3月30日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

どうしてもレポートを見たい人はタスクの成果物としてレポートをコンテナの外にファイル出力する必要があります。

## 社内で使う場合
タスクはDockerコンテナ上で実行されますが、タスクが終了するとコンテナは消えてしまいます。
毎回タスクが走るたびに何も入っていないベースイメージからビルド環境を構築するのは無駄なので、必要なものが入ったイメージを予め作っておき、そのイメージキャッシュを利用した方が良いです。（[これだけはやっておきたい〜マイクロサービスのデプロイメント - クラウドワークス エンジニアブログ](http://engineer.crowdworks.jp/entry/microservices-architecture-and-deployment#コンテナイメージ)）
例えば上記のmovies-unittestというタスクを実行するコンテナは、今回私が作ったlibmatsu/mongo-python3というDockerイメージから作られています。

```:movies_unittest.yml
---
platform: linux

image_resource:
  type: docker-image
  source:
    repository: libmatsu/mongo-python3

inputs:
  - name: cinema3-movies

run:
  path: cinema3-movies/ci/scripts/test_runner.sh
```

このDockerイメージはDockerHubに公開されていますが、そんなとこに公開できないよという環境の人は多いと思います。
そのような場合はPrivate Docker Registryが使えます。その他、社内環境でのConcourseの利用例は以下のリンク先に紹介されています。

* [Running Concourse CI on enterprise private cloud【日本語版】 ](http://sssslide.com/speakerdeck.com/ozzozz/running-concourse-ci-on-enterprise-private-cloud-ri-ben-yu-ban)
* [Nexus RepositoryのDocker Registryを使ってオフラインでConcourse CIを使う - BLOG.IK.AM](https://blog.ik.am/entries/400)

## リポジトリ管理再考
あとはmoviesサービス以外のサービスも作ってインテグレーションテストしてPaaSにデプロイするだけ、と思ってこんな感じのパイプラインを作ったら問題が発生しました。
![i2.PNG](https://qiita-image-store.s3.amazonaws.com/0/141719/39f31e9e-c616-b79e-287f-717f3357b98e.png)

エラーが出ているのは無視してください。ところでこのpipeline.ymlはどのリポジトリに管理されているのでしょうか？
例えばmoviesリポジトリのciディレクトリにはmovies用のpipeline.ymlしか含まれていません。
全体用のpipeline.ymlを管理するリポジトリを新たに作るのか、もしくは元のCinema3のように１つのリポジトリにしてサブディレクトリごとにビルドするのか。。

後者の方がすっきりすると思うし、確かにサービスごとにリポジトリを分ける必要はないという意見もあります。

>Aを更新したらBとの結合テストをしてからAを更新、Bを更新したらAとの結合テストをしてからBを更新、またはSlackからbotに依頼したらAかBを更新…やりたかったことはこれではないでしょうか。その観点からすると、複数GitレポジトリにそれぞれCIするとか、分散ロックなどはただの手段です。やりたいことをもっと直接的に表現できるツールはないでしょうか。
[これだけはやっておきたい〜マイクロサービスのデプロイメント - クラウドワークス エンジニアブログ](http://engineer.crowdworks.jp/entry/microservices-architecture-and-deployment#ビルドパイプライン)

ただリポジトリ１つの場合、githubの変更をトリガーにジョブが走る設定だと、あるサービスを変更すると全てのユニットテストが走ってしまってあんまり独立してないなあと若干感じます。

そもそも誰がインテグレーションテストを書くのかという問題もあります。
１つのチームが１つのサービスを作る分には問題ないが、複数のチームが関わると生じる問題と解決案は[『マイクロサービスアーキテクチャ』](https://www.oreilly.co.jp/books/9784873117607/)でも繰り返し述べられています。
でも結局は組織論ってお話になりそうです。

## おわりに
Concourseを使ってみました。導入は難しくないのでCI/CDツールの選択肢としてはありだと思います。
CI/CDの知識も含めて運用上のベストプラクティスがまとまっていれば良いなと思いました。