GitLab CIとJenkinsの違いについて

社内で Gitlab(8.6系) + Gitlab Plugin(1.1.32) + Jenkins(1.651) の構成でCIを行っていますが、このところ Gitlab Plugin がバグでたまに動かないことがあったり、Jenkins のジョブが多くなってごちゃごちゃしてきて困ることもでてきています。

これに対して、Gitlab にそもそも用意されている Gitlab CI というCIツールのことを聞いたので、試しにどういうものかを調べてみました。


紹介
====

Gitlab CIは、Gitlabと連携することを前提に作られた専用のCIツールです。
主な機能はGitlab本体に内在されて、プロジェクトの"Runner"だとか"Build"だとかのタブ項目などがCIに関連した画面になります。
（Gitlabが7系以前は、Gitlab本体と別のツールだったようですが、8系になって本体と統合されたようです。）

公式の説明としては、以下になります。

  * GitLab Continuous Integration | GitLab
    https://about.gitlab.com/gitlab-ci/
  * GitLab Documentation
    http://doc.gitlab.com/ce/ci/quick_start/README.html


Gitlab CIは、Gitlab本体とは別にジョブを実行するRunnerというサーバーを起動し、本体と連携させて利用します。このRunnerは、Jenkinsでいうスレーブノードのようなものになります。

  * GitLab.org / gitlab-ci-multi-runner · GitLab
    https://gitlab.com/gitlab-org/gitlab-ci-multi-runner

インストールの方法は、上記のページのドキュメントを読めばだいたいわかると思います。
このRunnerはジョブを処理する方法が何種類かあり、ジョブをそのサーバー内で直接処理するモードや、Dockerコンテンナを起動してその中で処理するモードがあります。Runnerはプロジェクト間で共有して利用するのが通常の使い方ですが、個々のプロジェクト専用で登録することもできます。あるジョブを特定のRunnerでのみ実行したい場合は、Runnerにタグを設定してジョブ側で実行対象を指定することができます。


CIを利用する側としては、他のCIサービスの Travis CI や Circle CI と似た方式になっています。実行させたい処理をプロジェクトのルートディレクトリに ".gitlab-ci.yml" という名前のファイルで定義します。このファイルでは、Runnerサーバーで処理させたいことをシェルスクリプトの形式で記述します。

公文の詳細な定義は以下を参照してください。

  * GitLab Documentation
    http://doc.gitlab.com/ee/ci/yaml/README.html



Gitlab CIとJenkinsの比較
====

Gitlab CIを試しで動かした上でのJenkinsとの差異を以下に列挙していきます。

### ジョブの定義場所

Jenkinsはサーバー側にGUIで設定を登録します。そのため、全てのブランチを同じ設定で処理ことしかできません。そのため、 ブランチの改修内容によってテスト方法の変更が必要になったときに対応しづらいという問題があります。

なお Pipeline Plugin というプラグインを使うことでジョブの内容をリポジトリ内のファイルで定義する方法があります。ですが、このプラグインはジョブ定義ファイルを読み込む対象のブランチを１つに固定する方法でしか設定ができないので、結局は同じ問題が起きてしまいます。

一方、Gitlab CIではリポジトリ内にジョブ定義ファイルを作成します。
ブランチごとに定義ファイルを別々に持っているので、それぞれでジョブ内容に差異があっても問題ありません。反面、全ブランチで共通な部分で設定を変えたいときに、全ブランチを修正しないといけないという欠点もあります。

また、定義をファイルで記述するという利点として、設定変更をGitなどで管理できるので修正箇所や意図を記録に残すことができます。また、各設定項目に対してコメントを記述できるので、ジョブ処理の内容に説明を残しておくこともできるのも大きな利点だと思います。


### ジョブフローの作り方

Jenkinsでは、基本的に１つのジョブ設定が１つの仕事をするように定義し、複数のジョブ設定の前後関係を設定することでフローを定義します。
そのため、ジョブ設定が増えてわかりにくくなりがちで、フローを見やすくするプラグインを導入するなどして対応します。
また、フロー中のジョブで共有の設定を変えたい場合に、一括で変更する方法が無いのでかなり面倒なことになります。


Gitlab CIでは、１つのジョブ定義ファイル( .gitlab-ci.yml )にて複数のジョブとそれらの依存関係を定義することができます。一連の処理が１ファイルにまとまっていますし、共通部分を再利用もできるのでフローを作成、編集するのはかなり楽だと思います。


### ジョブの起動トリガー

Jenkinsでは、定期的なジョブの実行やGitへのコミットを監視してジョブを起動することができます。これ以外にも、GitLab Plugin でGitlabのMerge Requestに連動するようなこともできます。

一方で、Gitlab CIは git-pushされるとそのブランチの最終コミットに対してジョブが起動されます。このとき、ジョブ定義ファイルにて対象とするブランチ名を正規表現で絞ることができます。gitのtagを対象にジョブを実行することもでき、これは
productionのリリース処理などに利用することことが想定されているようです。
Gitの内容変更にのみを対象とするので、MergeRequestのみを対象に実行するという機能は今のところは無いようです。
また、手動で実行することが基本的にはできないので、バッチ処理の実行に利用するようなことには向いていません。ですが、GitlabにTriggerというジョブを起動させるためのWebAPIが用意されているので、それを使うことで代用することはできるかもしれません。


### プラグインによる拡張

Jenkinsでは、プラグインによって機能拡張ができるのが特徴です。
特にJUnitやカバレッジ結果を見るようなビジュアライズに関する機能が便利です。

Gitlab CIはプラグイン機能はありません。全てをスクリプトで処理するのでスクリプトで実現できることを各開発者が各自で実装するということになります。
また、ビジュアライズを行う方法が無いので、結果を見やすく整形したいといったことの実現には工夫が必要になります。

### ジョブの時系列的な集計 (追記: 2016/04/18 14:30)

書き忘れていたので追記です。

Jenkinsでは、１つのジョブの実行結果を何世代か保存し、それらの時系列変化をグラフで表示する機能があります。これによって総テスト数やテスト失敗数のトレンドをみることができます。

一方、Gitlab CIでは個々のビルドは独立しています。そのため、時系列での変化を集計することはできません。そもそも基本的に標準出力のログファイルしか保存していませんので、テスト数などの情報がありません。

ですが、CIツールの使い方によりますが、時系列の変化トレンドグラフがどれだけ意味があるかどうかという点が疑問でもあります。１つのブランチのみを処理対象としているならともかく、幾つものfeatureブランチを１つのジョブで実行した場合、ジョブの実行順序が時系列になっているとは限りません。feature/hogeの実行後に、feature/fugaを実行したときに、この２個を時系列として考える意味はあまり無いように思います。


まとめ
-----

試しに動かしてみた感じでは、Gitlab CIは結構いいものだと思います。
簡単にセットアップすることができますし、Gitlabと連携して結果画面の見た目もよいです。
何より、ジョブの処理をファイルで管理できるのはとても便利だと思います。

もし既にTravis CIやCircle CIなどの利用になれている方であれば、
Jenkinsよりも使いやすいと思います。

ですが、ジョブの処理結果は標準出力とエラー出力の内容のみとなるため、ログの出力方法に工夫が必要そうだと感じました。

課題としては、ジョブが失敗したときに事後処理を行えなかったり、失敗したテスト結果をファイルで保存しておくようなことができなかったりするため、もう少し機能の充実を期待したい面があります。
(追記: 2016/05/01
事後処理の機能がGitLab Runner v1.2で追加されたようです。 (http://doc.gitlab.com/ee/ci/yaml/README.html#after_script))

テストの処理について、基本的に成否のみがわかればよいのであればこれで十分だと思いますが、Jenkinsのようなビジュアライズされた結果をみたいのであればジョブの作り方を工夫する必要がありそうです。