皆さんお元気ですか? LINEのMessaging Serverを開発しているNaoです。
2014年にLINE Serverの開発とリリースプロセスについて紹介しました。それから時間が経ち、開発環境やコードも変化してきました。今回はその後、改善した点について紹介したいと思います。
LINEのMessagingプラットフォームは多くのServer Applicationから構成されています。そのうちの1つであるMessaging Serverは、Messaging機能を担うPlatformであり、他のチームの開発者と協力して複数のプロジェクトを並行して進めています。
Messaging Serverの旧開発プロセス
複数のプロジェクトの作業を同時に進行すると、それぞれの作業による混乱や現在どのような内容が進められているのかに関して曖昧な状況になりやすくなります。このような問題を少しでも改善するため、共同開発の進行の手間を最小限にするルールやポリシーを開発者同士が試行錯誤してきました。その結果として作られてきたものが、次に紹介するLINEのMessaging Serverの旧開発プロセスです。
旧開発プロセスの特徴は、Feature開発はdevelopブランチ上でFeatureブランチを作って行い、リリースはmasterブランチからReleaseブランチを作ってcherry-pickを利用して反映し、その後masterブランチに適用する点です。
- 開発者は基本的にはUnit Testも必ずセットで実装します。
- developブランチににマージした後、Beta環境で他のコンポーネントとのテストを行います。
- Integration Testもあり、毎時Betaサーバに対してテストが自動で実行されます。開発者はFeature開発と共にIntegration Testも追加します。Integration Testのおかげで他のコンポーネントの変更や意図しない変更が入った場合にRegressionに気づきやすいです。
- QAチームによって、LINEクライアントを使ったRegression Testが定期的に行われています。
- Feature Testが完了したら、変更をmasterブランチにマージし、RC環境で同時にリリースされる他の変更と一緒にRC QAを行います。
- Release環境にデプロイする場合はcanary groupのサーバーに先にリリースし、その後、全体に反映させます。
Background
Messaging Serverのコードは、LINEの中では長い歴史を持っており、時間が経つにつれて改善したい点も出てきます。マイクロサービス化に伴い、多くのコードが他のサーバーに分離されました。それにより、同時開発の作業が減ったため、今までは実行が難しかった改善も試すことができるようになりました。
以下のケースを見てみます。
developブランチ上ではFeature Aが先に適用され、その後Feature Bが適応されました。しかしFeature Bを先にリリースする事になりました。その時にcherry-pickでconflictが起こり、conflictを解決してからリリースしなければならない場合があります。この場合はReleaseブランチ上でテストを実施します。
ここで問題となる部分があります。
- cherry-pickにより発生したconflictを解決した部分はdevelopブランチとの差分になる可能性があり、一部のコードでdevelop/masterブランチ間で差が出る可能性があります
- リリースされていないコード上で開発してるいるため、リリースされていないFeatureを含んだコードをベースに開発/テストすることになります
developブランチとmasterブランチの差分をなくすため、定期的にdevelopブランチをmasterから作り直す"Branch Reset"を行っていますが、もう少し可視化/自動化して手軽にテストできるように改善を試みました。
Mission
- 開発の段階から他のリリースされていないコードから隔離してテストを実行する
- developブランチからリリースされたFeatureと、されていないFeatureを把握できる様にし、開発の進捗を可視化する
Goal
上記のミッションを達成するために次のような改善を行いました。
- Featureブランチはmasterブランチから作成
- masterブランチとdevelopブランチ間でgitのhistoryをsyncするためにcherry-pick-basedフローからmerge-basedフローへ変更
- Container技術などを活用した隔離されたテスト環境の整備
最終的なイメージはこのようになります。各々のFeatureブランチはmasterブランチから作り、隔離された環境でテストしてリリースをすることができます。
旧開発プロセスではLocal環境でのテストはある程度可能でしたが、マイクロサービス化によってLocal環境でのテストはより難しくなります。
Local環境でのテストはMessaging ServerをLocal環境で立ち上げ、他コンポーネントに関しては社内ネットワーク上のalpha環境やbeta環境に接続して、それらの環境と組み合わせてテストを行います。Integration TestもLocalモードで実行し、テストできます。
Messaging Serverの新開発プロセス
新開発プロセスの紹介をします。
その前によく知られてるフローのGitflowとGitHub flowも一緒に比較してみます。
よく知られているGitflowではdevelopブランチからreleaseブランチを作り、releaseブランチをmasterブランチにマージすることでリリースします。GitHub flowではdevelopブランチは無く、masterブランチだけで管理する簡単なフローになります。社内ではGitHub flowで開発されているプロジェクトも少なくありません。
Messaging Serverでは多くの独立したFeature開発が進められています。他のコンポーネントと連携したテストを行うため、Messaging ServerはBeta環境を用意しています。このBeta環境のためにdevelopブランチが存在します。Messaging Serverは毎日のようにリリースがあり、リリースサイクルが早いため、developブランチをそのままmasterブランチにマージしてリリースする事はできません。そのため、Gitflowは私たちのリリースサイクルには適していません。developブランチを残しつつ、その上でGitHub Flowのようにmasterブランチ上で開発する事で他のFeatureと独立した開発・テスト・リリースを可能にします。
基本的なコンセプトは以下の図のようになります。開発とテストはmasterブランチ上で行い、以前と同様にBETA QA, RC QAを行います。リリースの時にはFeatureブランチから直接masterブランチにマージします。ここでBack Mergeと呼んでるものがありますが、後ほど説明します。
複雑な例を一つ紹介します。Featureブランチをdevelopブランチにマージする時に、conflictが発生したケースです。このケースではConflict-resolutionブランチと呼ばれるものをdevelopブランチから作り、FeatureブランチをConflict-resolutionブランチにマージしてconflictを解消します。そして、このConflict-resolutionブランチをdevelopブランチにマージします。リリースは基本的なフローと変わらず、Featureブランチからmasterブランチにマージをします。
このフローでの利点
- git commandを利用して色々なことが自動化・可視化しやすくなります。
- developブランチ上のどのFeatureブランチがリリースされたか、されてないか開発の進捗の可視化ができます。
- Featureブランチ間の依存性も可視化できます。
- リリースされていないコード上でテストすると自分のFeatureを先行してリリースする場合、テストした時に通ったコードパスが変わります。そのため、Release環境のコード上でテストすることで開発段階のテストReliabilityが上がります。
難しかった点
新しいフローに移る過渡期には新しいフローを関連する様々なチームのメンバー全員に対して説明し、理解してもらうのが難しく、以前のフローで開発されてしまうことがありました。
ある程度マイクロサービス化されて、複数のサーバーコンポーネントに分けたとはいえ、多くの開発者が携わっているためconflictが起きやすい状態でした。そのため、Conflict-resolutionブランチの作成コストがPain pointとなりました。
このようなミスオペレーションを予防したり、conflictケースを減らすためにテスト環境改善やツールが必要になりメンテナンスコストが発生しました。
新開発プロセスをサポートするためのツールの紹介
様々なツールがありますが、その中でPR Checker、Back Merge、Weekly Reporterを紹介します。
PR Checker
Pull Requestで間違ったフローでブランチが作られるのを防ぐためのツールです。このツールによって間違って作られたブランチがdevelopブランチにマージされる事がなくなりました。
Feature Branch
Featureブランチがmasterブランチから生成されたかを確認します。
Conflict-resolution Branch
developブランチから新しいブランチを作成し、そこにFeatureブランチをマージしたケースは、Conflict-resolutionブランチとして検知されます。
Wrong Branch
developブランチ上で開発してしまうと警告を表示します。
Back Merge
Back Mergeについて説明します。
Back Mergeが必要になる理由を理解するためにはcommitタイプの理解が必要です。
Commitタイプにはmerge commitとnon-merge commitの2種類があります。merge commitとnon-merge commitはparent commitの数で決まります。
例えば以下の図でc1, c2, c3, f1, f2はparent commitが1個のみあるnon-merge commitです。m1は複数のブランチをマージする時に作られるcommitで2個以上のparent commitを持つmerge commitです。
masterブランチにリリースするときに作られるmerge commitもdevelopにsyncする必要があります。
masterブランチにmergeする時にdevelopブランチに差分のないmerge commitをdevelopブランチにマージする操作がBack Mergeです。
Weekly Reporter
Weekly Reporterはdevelopブランチの開発の進捗を提供します。
当初はconflictを減らす目的として開発されました。conflictの原因としては、developブランチマージされたけど、長くリリースされていないブランチが考えられます。長くリリースされない理由としてはリリース予定がなく、Local環境でテストできないケースが考えられます。
開発の進捗とconflictの原因を可視化するために、GitHub pagesを使い、以下の機能を提供します。
- リリースされてないブランチリスト
- 間違ったフローで作られたブランチリスト
- Conflit-resolutionブランチリスト
- Conflict-resolutionブランチでconflictの起因となったブランチリスト
Unreleased feature branches
developブランチにマージされたけれどリリースされてないFeatureブランチを古い順で表示します。このWeekly Reporterの機能はmasterブランチから新しくdevelopブランチを作り直すBranch resetterにも利用されます。作り直す場合、古いFeatureから順にマージを試し、conflictなしでマージ可能なのかも表示してくれます。conflictがある場合は依存性をブランチも表示します。Branch resetterはauto-mergeできるものは自動でマージするため、今までマニュアルで行っていたBranch resetも、ある程度自動化が可能になりました。
Branches created by an incorrect flow
developブランチにマージされたブランチのうち、間違って作られたブランチを表示します。間違って作られたブランチが見つかったら、Revertしなければなりません。現在ではPR Checkerのおかげで間違ったブランチは作られなくなりました。
Conflict-resolution branches
Conflictがどの位の頻度で、どういう場合に起きているのか確認できます。
Branches caused conflicts of conflict-resolution branches
Conflict-resolutionブランチを作らないといけなくなった原因のブランチを影響度が高い順で表示します。なぜリリースできてないのか確認し改善を模索できます。
導入してから
最初は慣れてないフローであったので学習コストが高くなりました。
現在はリリースされずに忘れられたブランチなどの進捗が見えるようになりました。conflictが起きた場合は開発者の中で次のような対応をすることがあります。
- コードがconflictした人の間でいつリリースするのか、どうやってテストするのか予め相談し決めておく傾向があります。これはある意味ではリリースサイクルを早めます。
- Feature flagを利用してFeatureをdisable状態で徐々に事前リリースをします。
- Conflictが起きづらいような構造にコードをリファクタリングする。
その他、conflictが多かったため更なるテスト環境改善が必要になりました。Local環境では環境のスペックなど様々な制約によりテストできないケースもあります。そのため、Beta環境でテストしないといけません。Beta環境にマージせずにBetaテストができるようにするため、色んなプロジェクトからの開発者の協力でTask Forceを立ち上げてテスト環境の改善をすることができました。その方法としてContainer技術を利用し、簡易に作成されたコンテナ内に、Betaと同等のserverを立てます。これを我々はBeta Containerと呼んでいます。
Beta containerの構築に関してはまた他の記事で紹介したいと思います。
最後に
LINEには多くのServer Applicationがありブランチ管理方法はプロジェクトごとの目的に合わせて異なります。
今回はMessaging Serverの開発フローのブランチ管理方法の改善について紹介しました。その他にもまだ、課題は残されています。
developブランチのmerge commitをmasterブランチにsyncする必要があります。そのため、developブランチのmerge commitをmasterにsyncするFoward-mergeと言う概念の導入や、定期的に全てauto-mergeableな場合、自動でBranch resetterを実行するなどの様々な方法を検討しています。
社内には複数のベータサーバーが稼働しています。社内の他のチームのServer Applicationからのcallbackは、その中の予め指定したサーバーを固定で呼び出す場あります。呼び出すサーバーを動的に変更可能にしテストの幅を広げるなどの改善を持続的に進めています。
開発プロセスは常に改善を続けています。この場を借りて関わった全てのチームに感謝の言葉を伝えたいと思います。ありがとうございました。
Links
- LINE Serverの開発とリリースプロセス: https://engineering.linecorp.com/ja/blog/line-server-dev-and-release-process/
- Gitflow: http://nvie.com/posts/a-successful-git-branching-model/
- GitHub flow: http://scottchacon.com/2011/08/31/github-flow.html