Recruit Data Blog

  • はてなブックマーク

目次

はじめに

こんにちは!若月と申します。

今回は自分が開発メンバーとして携わってきたCroisというプロダクトでのデプロイフローについて書きます。

全体の流れ

背景

Croisは内製のワークフローエンジンです。 特定の形式に従ってコンテナイメージを作成し、YAMLで定義を記述することによって様々な機能を持ったワークフローを実行できます。 Croisに関するより詳しい説明は こちらの記事 にあるので、興味がある方はぜひご覧ください。

今回のテーマはCrois自体についてではなく、Croisの開発で行っているデプロイフローに関してです。

Croisのコードは複数のリポジトリで管理されています。例えば、APIサーバー、Web UI、Terraformファイル、AWS Lambdaのコード、タスク間の仲介のためにコンテナ内で実行されるコードなどからなり、それらを別々のリポジトリで管理しています。

このように、複数コンポーネントからなっている場合のリリース手順について考えてみます。
production環境へのリリースはダウンタイムなしで行わなければなりません。 変更の間に依存関係がある場合、リリースの手順に気をつける必要があります。

簡単な例として、APIサーバーに新たな環境変数を追加したいという場合を考えます。コンテナに渡す環境変数はインフラのリポジトリで管理されているため、インフラ、APIサーバーの順にデプロイを行う必要があります。
もし逆に、APIサーバー、インフラの順にデプロイした場合、APIサーバーが更新されてからインフラが更新されるまでの間はAPIサーバーから環境変数が参照できなくなってしまいます。

挙げたのは簡単な例ですが、1回のリリースで依存関係が複数生じたり、複数人で同時に作業していると途端にややこしくなってしまいます。

この記事で紹介するのは、このように複数リポジトリで管理しているCroisのデプロイフローをチーム内(主担当は若月&宇野)で改善した話です。

改善前のデプロイフロー

以前のデプロイフローを具体的に説明します。

  1. 各リポジトリにおいて、featureブランチからmasterブランチに向けてプルリクエスト(PR)を作成すると、development環境にデプロイされる。
  2. 各リポジトリにおいて、featureブランチをmasterブランチにマージすると、staging環境にデプロイされる。
  3. 各リポジトリにおいて、masterブランチからreleaseブランチにPRを出してマージすると、production環境にデプロイされる。
変更前のデプロイフロー

先程述べた通り、変更の内容によっては異なるリポジトリのPR間に依存関係が存在します。その場合、開発者はデプロイ順序に気をつけなければなりませんでした。

つまり、staging環境に変更を反映する際には、依存先のPRが既にマージされているかということと、既にマージされたがproduction環境に未リリースのPRの依存関係と問題ないか(後述)を確認する必要がありました。production環境に変更を反映する際には、反映対象の全てのPRの依存関係をチェックし、依存先のないリポジトリから順にリリースしていかなければなりませんでした。

また、masterブランチにマージした段階で依存関係を解消できない状態にしてしまった場合、そのままproduction環境に反映できなくなってしまうということが何度かありました。

例えば、下の図のように、リポジトリ間で依存関係が逆の2組のPRが存在し、それをmasterブランチにマージしてしまった状態を考えます。
この場合は片方のリポジトリの内容をproduction環境にリリースした段階でダウンタイムが発生してしまいます。つまり、PRの依存関係を有向グラフと見たとき、masterブランチにマージした段階で閉路ができてはいけないという制約がありました。この場合はアドホックな対応などが必要でした。

リポジトリAにPR1とPR2があり、リポジトリBにPR3とPR4があり、PR1はPR3に依存し、PR4はPR2に依存している。それらの変更が各リポジトリのmasterブランチに取り込まれた状況。

このように、以前のデプロイフローでは毎回変更を反映する順番に気をつける必要がありました。

改善後のデプロイフロー

手動でデプロイ順に気をつけるのではなく、自動的にPR間の依存関係を解消してデプロイするようにしました。 そのような構成にするため、次の2つのリポジトリを追加しました。

  • リリースリポジトリ
    • Crois自体(全リポジトリのセット)にバージョンを振り、バージョン情報をファイルとして管理するリポジトリ
    • バージョンファイルには各リポジトリのマージコミットハッシュとデプロイの順序が記述される
    • 各PR間の依存関係を調べてバージョンファイルを生成するスクリプトが管理されている
  • デプロイスクリプトリポジトリ
    • リリースリポジトリで管理されているバージョンファイルを読み取り、指定したバージョンまで各リポジトリの内容をデプロイするスクリプトが管理されている

PRの依存関係は、決められたフォーマットでPRのコメントに記述します。例えば次のように書きます。

# dependency
- pull: crois/repo-1#123
  is_compatible: true

この例では、このPRがrepo-1の#123のPRに依存していることを意味します。is_compatibleは連続してリリースしてよいかのフラグで、is_compatibleがfalseの場合は2つのPRは別々のリリースに分けられます。
例えば、AWS Batchのコンピューティング環境を変更する際、ジョブが残っていては更新できないので、1つ目のPRで新しいコンピューティング環境を作り、古いコンピューティング環境でのジョブが全て完了してから2つ目のPRで古いコンピューティング環境を削除しなければならないという場合に使います。

リリースリポジトリにあるスクリプトは直近にマージされたPRのこのコメントを読み取り、依存関係を解決してバージョンファイルを生成します。依存関係が解決できない場合はこの段階でエラーとなり気づくことができます。 例えば下図のような依存関係からは、次のようなバージョンファイルが生成されます。

依存関係の例。repo-2のfb97d23はrepo-1の29d4d4dに依存している。他も同様。
repositories:
  repo-1: 29d4d4d49fe2cecee9dd2cfda11c6004375bcd39
  repo-2: fb97d23abf156cd4d93fe9ebb0b52438d07e112f
  repo-3: bb99716cd9111d702dbb2105a83e30917f7eef1a
  repo-4: fde111e4be40f3293e5544757148fcb15abcc5b0
steps:
- repository: repo-1
  commit: 29d4d4d49fe2cecee9dd2cfda11c6004375bcd39
- repository: repo-2
  commit: fb97d23abf156cd4d93fe9ebb0b52438d07e112f
- repository: repo-3
  commit: bb99716cd9111d702dbb2105a83e30917f7eef1a
- repository: repo-4
  repo-4: fde111e4be40f3293e5544757148fcb15abcc5b0

repositoriesにはこのバージョンの各リポジトリのコミットハッシュが書かれています。また、stepsにはこの順にデプロイすれば問題ないというリポジトリ名とコミットハッシュが書かれています。

デプロイスクリプトは、リリースリポジトリにあるバージョンファイルを読み込んでstepsの順番にデプロイしていきます。 各コンポーネントのデプロイはAWS CodeBuildのAPIを叩くことで行われます。

デプロイフローは以下のようになりました。

  1. 各リポジトリにおいて、featureブランチからmasterブランチに向けてPRを作成すると、development環境にデプロイされる。依存先のPRがあればコメントに記述する。
  2. featureブランチをmasterブランチにマージする。このときは何も起きない。
  3. ローカルでリリースリポジトリのコードを実行すると、直近マージされたPRに基づいてバージョンファイルが作成される。それをリリースリポジトリのstagingブランチにpushすると、各リポジトリの内容がstaging環境にデプロイされる。
  4. リリースリポジトリのstagingブランチからproductionブランチに向けてPRを出してマージすると、各リポジトリの内容がproduction環境にデプロイされる。
変更後のデプロイフロー

まとめ

以前はPR間の依存関係に注意してリリースしていく必要がありましたが、リポジトリのコミットの組に対してバージョンを振ることで自動的にリリースしていくことができるようになりました。

また、開発チーム内のデプロイフローが自動化された他にも恩恵があります。
バージョン情報をリポジトリで管理するようにしたことによって、Croisを別のAWSアカウントにホスティングし、好きなタイミングで好きなバージョンに無停止でリリースできるような仕組みができました。これにより他の事業領域でも社内OSSのように独自でホスティングされるCroisの利用がされるようになりました。

最後に

ここまで読んでいただいてありがとうございます。 弊社では、様々な職種のエンジニアを募集しています。興味のある方は、以下の採用ページをご覧ください。

===========================

リクルート新卒キャリアサイト

===========================

若月良平

最近は人材領域でのデータ周りの分析などを担当

若月良平

リクルートグループ新卒入社4年。プログラミングのコンテストが好き。スプラトゥーン2のプレイ時間は3000時間超え。