本番運用を考慮したgit-flowブランチモデルの拡張とJenkinsジョブ構築

お久しぶりです。アルゴリズムの探求者です。
オブジェクト指向の時代は終わった」とか「nullとExceptionは悪だ」とか言ってたら、僧職系エンジニアマサカリ王子って言われました。

さて、今回はgitのお話です。Speeeでは全てのプロダクトで、gitを使ってソースコード管理をしています。
イエウール開発でもgitを利用していますが、ブランチルールを少し工夫してみました。そこで今回は、どのような工夫をしたか、その紹介をしようと思います。一言で言うと、git-flowブランチモデルをベースに、Jenkinsを使った本番運用を考慮して、一部拡張したものです。

今回解決しようとした課題

想定するアプリケーション

  • Servletコンテナ上で動作するJavaアプリケーション
  • アプリケーションの挙動を設定ファイルで制御
  • 設定ファイルは動的に読み込む仕組みを構築し、アプリケーション再起動なしで反映

今回のトピック

設定ファイルってどうやって管理するべき?
アプリケーションのソースコードは、gitで管理するいくつかの標準的なブランチモデルがメジャーになっており、それらに従うことで概ね十分に管理することができると思います。しかし、設定ファイルはどうでしょう?今回はこの設定ファイルの管理に目を向けます。
設定ファイル管理のゴールは以下のようなものでしょうか。

  • いつどのような設定だったかあとから確認できるようにしたい
  • 開発環境やテスト環境でテストする際、本番環境で稼働中の設定を半自動的に取り込んで、設定ファイルの差異による無効なテストを無くしたい
  • 本番環境への設定ファイルリリース時に、例えば一部の設定が巻き戻ってしまうなどの事故を防ぎたい
たかが設定ファイルと侮ってはいけません。もし間違った設定を本番稼働させて挙動が意図と違ったとしたら、それはやはりバグです。当然エンジニアとして責任を持つ必要があります。そのためには、本番環境での設定の履歴が管理されているのはもちろんですが、加えて、設定ファイル開発・運用のワークフローもより安全に構築する必要があります。

構築したブランチモデル

ブランチ運用ルール

ポイントを簡単にまとめます。

  • 通常のソースコードを管理するmaster、developを中心としたメインストリームとは別に、設定ファイル管理用のストリームを設ける
  • マージは常に設定ファイルストリームからソースコードストリームの方向に行う
  • ソースコードストリームでは設定ファイルに触らない(開発環境用の設定をするだけ)
  • 本番設定の変更や、機能追加に伴う設定項目追加等は設定ファイルストリームで行う
  • 本番環境への設定ファイルデプロイは、設定ファイルストリームから行う
このルールを適用すると、先に述べた課題をうまく解決することができます。

  • いつどのような設定だったかあとから確認できるようにしたい

    config-masterブランチが本番環境での設定の履歴そのものです。

  • 開発環境やテスト環境でテストする際、本番環境で稼働中の設定を半自動的に取り込んで、設定ファイルの差異による無効なテストを無くしたい

    設定ファイルストリームからソースコードストリームへマージするだけで本番環境の設定を取り込むことができます。

  • 本番環境へのconfファイルリリース時に、例えば一部の設定が巻き戻ってしまうなどの事故を防ぎたい

    設定ファイルストリームが最上流にあるものとし、「全ての変更は、まずここに適用する」というルールを徹底します。変更は、設定ファイルストリームから本番サーバや開発環境に一方向に流れていく形にすることで、意図しない変更が紛れ込む可能性が小さくなります。

Jenkinsリリースジョブ

以下の様な観点で、Jenkinsにリリースジョブを構築します。

  • 操作ミスのないように(ブランチが複雑になっているので特に注意)
  • アプリケーションをデプロイしたら必ず設定ファイルも更新されるように
  • 逆に、運用を考慮して、設定ファイルだけを更新することもできるように
できあがったものがこちら。


  1. アプリケーションビルド

    masterブランチのソースコードからwarをビルドします。

  2. アプリケーションデプロイ

    1.でビルドしたwarを本番サーバにデプロイします。

  3. 設定ファイルデプロイ

    config-masterブランチの設定ファイルを本番サーバにデプロイします。

  4. アプリケーション再起動

    warの変更を反映するためにプロセス再起動します。このジョブは実行/無視をパラメータで制御できるようにしておきます。

アプリケーションの新バージョンをリリースする際には、ジョブ1を実行します。すべてのジョブは、成功したら次のジョブを実行するように設定されているため、設定ファイルデプロイジョブも必ず実行されます。
一方、設定ファイルのみをデプロイするときは、ジョブ3を実行します。このときジョブ3のあとジョブ4を実行するときには「無視」パラメータを引き渡すようにし、プロセス再起動は行わないようにしておきます。

このブランチ運用をプロダクトに導入してみて

まだイエウールでこのブランチ運用を始めて半年ちょっとしか経っていませんが、非常にうまく機能しているという印象です。

この仕組みを導入してから、設定ファイルの安全性により自信を持てるようになっています。Jenkinsによる自動デプロイとセットにすることで、事前に作成し、テストされた設定ファイルそのままで稼働することが保証されます。また、仮に設定ファイルに変更がなかったとしても、アプリケーションリリースの度にconfig-masterブランチから設定ファイルがデプロイされるため、「本当に本番環境もこの設定ファイルで動いているかわからない」という不安も解消されます。

また、設定ファイル変更がgitに乗ることによるメリットもありました。

設定ファイルもソースコードと同じようなワークフローに乗せることができるため、設定ファイル変更のみをしたい場合でも、Pull Requestを用いたレビューを導入することができます。また、設定ファイル変更はソースコードと比べて「なぜその変更をしたのか」の部分が後から確認する際に重要になることが多いですが、設定ファイルのみの変更でコミットされるので、コミットログに変更の意図を残しやすくなります。

一方、デメリットとしては、やはりブランチが複雑になってしまうことが挙げられます。

gitに慣れていないメンバーにとっては、git-flowモデルでも複雑なのに、更に複雑にしたブランチモデルをいきなり把握するのは簡単ではないかもしれません。

イエウールでは、まずはconfig系ブランチには触らないようにしてもらい、git-flowモデルの部分に慣れてもらってから、徐々にconfig-developブランチへのPull Requestも出してもらうようにしました。もちろん、gitに手慣れているメンバーであれば、このモデルはあくまでgit-flowモデルの拡張なので、比較的スムーズに把握できるのではないかと思います。

付録

■ 空のconfig-masterブランチを作成する
$ cd /path/to/repo
$ git symbolic-ref HEAD refs/heads/config-master
$ rm .git/index
$ git clean -fdx
$ vi .gitignore # 何かファイルを作る。何でもいいけどgitignoreが無難。
$ git add .
$ git commit
$ git push origin config-master