はじめまして!

この度、2023年1月4日~1月31日の一ヶ月間「CA Tech Job」でインターンシップ生として働かせていただきました仲川宙舞(@s0ran)と申します。

インターンシップ生で本ブログを書かせていただくのは久しぶりだそうで、嬉しさのあまり二つ返事で承諾してしまったものの少しプレッシャーを感じています。

私が配属されたチームは「ABEMA」のプロダクト基盤のバックエンドチームです!
本ブログでは自身の一ヶ月間のインターンシップでの体験談を踏まえ、自身が取り組んだ最も大きなタスクにまつわる

「モノリポ」
について、徹底的に書かせていただきます!

 

背景

インターンシップ概要

私が参加したプログラムはこちらの「CA Tech Job」というものです。社員の方の手厚いバックアップとフィードバックを受け、業務のみならず、自身の進路や目的のために、現場で働く方との面談やランチのセッティングも手伝っていただけます。

ほぼ、全出社日ランチへ連れて行っていただき、自身の携わるプロダクトへの思いやキャリア、技術に対する向き合い方などを聞かせていただきました。

CA Tech JOBの画像

週3回フルタイムで働かせていただき、自身の大学の都合等に合わせて柔軟に就業時間や就業日を調整していただきました。

大規模開発を体験したいという思いから、希望を聞いていただき、「ABEMA」のプロダクト基盤チームにてバックエンドエンジニアとしてインターンシップを体験させていただくことになりました。

自身のこれまでの経験では、一人での開発や短期の小さな開発が多く、大規模に動いているプロダクトに関わるのは初めてでした。マイクロサービスの開発ももちろんやったことはないですし、Go言語も夏のインターンシップでちょっと触った程度、PRを出してレビューをもらう一連のフローにも不安があるような状態での参加でした。

本ブログを通して、「CA Tech Job」でどういったことが体験できるのか、いかに成長できるのかも含めて、技術に関する話を通して感じ取っていただければと考えています。

 

モノリポによる複数サービス管理

モノリポって?

普段ガッツリ開発している方でも意外と聞き馴染みがないという人は多いのかなと思います。モノリポとは言葉通り、「Mono Repository」の略で単一のリポジトリの意味です。
他の部署が採用していることを聞き、「ABEMA」のプロダクト、バックエンドチームでも採用することになったようです。

私が初めてタスクをいただき、概要を聞いた時はこの単語の意味はわかったとしても、驚きが大きすぎて、何度も聞き返した記憶があります。

「ABEMAってマイクロサービス?モノリポジトリ?」

と理解が追いつかなく、思考停止した記憶を鮮明に覚えています。

モノリポとはつまり、マイクロサービスをサービスごとにリポジトリを分離して管理するのではなく、一つのリポジトリ上でマイクロサービスを実現することを指すそうです。

 

マイクロサービスのモノリポ化

「ABEMA」では、もともと複数のサービスを複数のリポジトリにて開発を行っていました。それら複数あるサービスを一つずつ巨大なリポジトリに移行し、モノリポ管理を実現するというタスクが動いていました。現時点では、大半の移行が終わっており、まだ移行のできていないあるサービスの移行をタスクとして担当しました。

私が担当したサービスはmiscと呼ばれるサービスで、他のマイクロサービスに割り振ることのできないその他サービスの集合体のようなサービスです。

 

モノリポのメリット/デメリット

マイクロサービスをモノリポで管理することによって何が嬉しいのでしょう。
まず、マイクロサービス導入の最大の足枷は何だと思いますか?

 

構成/管理の複雑化
といわれています。

モノリポはこの管理の複雑性を解消する戦略の一つです。

具体的な利点としては

  • lint、ビルド、テスト、デプロイ、リリースのプロセスの統一
  • 複数サービスにまたがるサードパーティライブラリの依存関係の更新が容易
  • 共通内製ライブラリの更新による全サービスの動作保証
  • コードの可視性の向上
  • リファクタリングが容易
  • 他チームとのコラボレーションの加速

特に上3つの項目における恩恵は大きいです。

 

例えば、複数サービスにまたがるサードパーティライブラリのバージョンアップに伴い、ほぼ全てのサービスの更新が必要になった際に、サービスごとにリポジトリを変え、テストをし、デプロイ、リリースをそれぞれに定義されているプロセスでの実行が必要になります。

また、テスト、デプロイ、リリースの手順を変更したい際にも、全サービスにて統一するために全てのリポジトリへの行き来が発生します。非常に骨の折れる作業です。

モノリポではそのマイクロサービス全体で使用するように作成している内製ライブラリに関しても、変更を加える際に、依存するサービス全てでの動作確認をワンライナーで行えてしまいます。

もし複数リポジトリにより管理しているのであれば、内製ライブラリの更新により依存するサービス全てで問題がないのかリポジトリを切り替えテストをしていく必要があります。

上記のように、モノリポはマイクロサービスにおける最大の欠点、管理の複雑化を大きく改善する戦略なのです。

 

ただし、モノリポ化によって失われるポリリポ(マルチリポ)の利点も存在します。

  • アクセス制限
  • ビルド、テストに時間を要する
  • コードオーナが不明
  • 競合の発生率が高くなる

などが挙げられます。

 

解決が難しい問題はアクセス制限です。全ユーザがリポジトリ内の全てのサービスにアクセスできてしまいます。しかし他の問題は運用手法、ツールにより簡単に解決することができます。

「ABEMA」ではキャッシュを活かすことでビルド、テストの時間を短縮し、コードオーナも各マイクロサービスごとに担当のチームに割り振られるようになっています。

チーム人数が多くなると、競合リスクの上昇は致命的な問題らしく、トランクベース開発を採用することで競合リスクを減らす手段もあるようです。ABEMAではトランクベース開発を採用するチームもあるようですが、私が所属したチームでは採用しておらず、チーム体制、PRの粒度、レビューへの意識により競合リスクを減らすことで特に問題なく機能しているようです。

 

モノリポ移行

モノリポ移行に必要な作業

モノリポのメリットは分かったものの、モノレボに移行するとなるとそれほど容易ではないのでは?と思われる方も多いと思います。

実際、私は一ヶ月間一つのサービスのモノリポ化にほとんどの時間を要しました。

「ABEMA」では現在、大半のサービスはモノリポで稼働しており、現在移行が完了していないものは比較的メンテナンスの頻度が低いサービスばかりで、移行開始時に比べ、モノリポ上での開発も進んでいるため、移行の難易度は客観的に見ても高かったそうです。

私の直面した問題を通してモノリポ移行の参考として役立てていただければと思います。

私のタスクでは統合先の巨大リポジトリ上ですでにサービスが活発に開発されており、移行手順はある程度確立され、標準化されていました。実際に生じた作業としては以下のようになっています。

 

  1. git subtreeを用いてディレクトリの統合、不要ファイルを削除し、ディレクトリ構造を整える。
  2. 依存パッケージ管理を統一する。ex)go mod tidy
  3. モノリポのビルドツール用の設定を行う。(bazelというツールを用いてます。)

a.ビルドファイルの作成(gazelleというツールを用いています。)

 b.追加したサービスのビルドができているか確認する。

 4.テストの設定を確認する。ex)必要なエミュレータの作成

 5.新たなlintツールの指摘を解消する。

 6.CI/CDの設定を確認する。

 a.他のサービスで使用している自動化が新サービスでも動くようにする。

 

私が直面した問題は大きく二つです。

一つはモノリポと取り入れられる側のリポジトリが用いている内製ライブラリの差です。取り入れるマイクロサービスが依存しているライブラリは統合先のリポジトリでは使用ができませんでした。ABEMAでは古い内製ライブラリから新しい内製ライブラリへの置き換えも同時に進んでいたので、内製ライブラリの置き換えが必要でした。

二つ目はモノリポ上でのテストが通らないことでした。統合先と統合元のリポジトリでは実行環境が異なり、元のリポジトリでは通っていたテストが統合されたリポジトリではテストが通りませんでした。

 

内製ライブラリの置き換え

まず、一つ目の問題は依存パッケージを統一するときに発生しました。今回の対象リポジトリをモノリポジトリの指定されたディレクトリへ移動させたのちに、go mod tidyにより依存モジュールをモノリポジトリ全体で単一のファイル群で管理します。

このとき、問題になるのが依存パッケージのバージョン不一致です。今回のタスクでは置き換え途中の古いライブラリの一部で使っているモジュールのバージョンとモノリポジトリで用いている内製ライブラリの依存するモジュールのバージョンが異なることが原因だったようです。

これまで「ABEMA」では古い内製ライブラリを新バージョンに置き換えることでこの問題に対応していたようなので私も同じ方法で取り組みました。トレーナーの大芽さんの助言も受け、元の移行前リポジトリ上でリリースまで行い、動作保証の確認が取れたのちにモノリポ移行タスクに戻る方向での実装を行うことにしました。

 

初のリリース

さて、ライブラリを無事置き換え、テストで動作も確認し終えたので、リリースを行います。自身初のリリース体験です。「ABEMA」ではmainブランチにマージすると自動的にdev環境へデプロイされ、リリースファイルを編集することで本番環境へのリリースが始まります。

dev環境へのデプロイ後、慎重にモニタリングを行い、エラーがないこと、レイテンシが増えていないこと、怪しいログ(エラー)がないことを確認し、いざ本番環境へのデプロイを行います。

「ABEMA」の主要サービス(バッチ処理系のサービス以外)では、Canary releaseを採用しています。今回は詳細は控えますが、PipeCDを用いて一部のPodをデプロイしモニタリングしたのちに全てのPodを置き換えます。

リリースファイルを編集し、モニタリングをしたのちに、PipeCDにて全Podのロールアウトを完了させ、無事リリースを終えました。

心配の余り、ボタン一つ押すのに何度も躊躇し、しつこくトレーナーに確認していました。

 

テストの置き換え

「ABEMA」では、モノリポジトリのビルドとテストにBazelというツールを用いています。このツールにより、変更のないビルドまたはテストをキャッシュし、モノリポジトリの「ビルドに時間がかかる」という問題を軽減します。

二つ目の問題はこのBazelを使用したテストがタイムアウトしてしまうという問題でした。この原因は移行するマイクロサービスのテストが外部通信(GCS上のバケット所得)をしていることにありました。

モノリポジトリ上のBazelでは通信先のアドレス(環境変数)を上書きする設定になっていたためでした。(localhost上にエミュレータをたて、そこにアクセスがいくように。)

今回選択した解決策はテストのコード内で上書きされているアドレス(localhost)上にこれまでテストで用いていたダミーデータをアップロードし、これまでアクセスしていたサーバと同じ環境をローカルエミュレータとして用意するという策です。

他の選択肢としては、モノレポ移行の問題と分離するためにとりあえずアドレスの上書きをしないように変え元のテストを維持する策、ダミデータのアップロードをテストのコードでするのではなくコンテナのvolumeで行うなどがありました。それぞれ問題があり、断念しました。

ここでは「テストのあり方」について学びました。

私の疑問は、これまで実際にサーバにアクセスし、疎通の保証をしていたテストをlocalhostのエミュレータに変えることで実際にサーバにアクセスができるというインフラ上の保証が失われてしまうのではという疑問です。

この件に対してトレーナーからテストコードでサーバの存在やインフラまで意識する必要はないと意見をいただきました。

つまり、今回書き換えた部分のテストコードはGCSのプロトコルに則りデータの読み取りができるのかを確かめるテストだったのでこの保証は上記の書き換えにて失われないという回答をいただきました。

テストが何を保証しているものなのか、何を保証するべきなのか、学生ではなかなかテストを書く機会が少なかったので、大変勉強になりました。

 

PRの粒度

モノリポ移行ではモノリポ管理による問題の一つである競合の問題が顕著に感じ取れるタスクでした。

モノリポ移行はそもそも別リポジトリで管理していたサービスをそのまま持ってくるため、プルリクエストの差分は尋常じゃない量になります。もちろんテストが通るまではマージすることもできないので、長い時間ドラフトのままブランチをキープすることになります。

「ABEMA」ではこれまでに何度もモノリポ移行を実施していたので、ある程度モノリポ移行の標準化が行われていました。今回は、標準化された作業手順でカバーできていなかった二点の問題により約3週間ブランチを生存させていました。

上記二点の問題のうち、内製ライブラリの置き換えをトレーナーに助言をいただき、元ブランチ上で作業をすることに決めましたが、当初はモノリポ移行ブランチ上で行おうとしていました。作業後半では地獄のようにコンフリクトに遭遇し、あのまま移行ブランチ上で作業を行っていると間違いなく、インターンシップ期間中に作業を終えることはできなかっただろうなと感じています。

PRを分割することにより、同時に起こっていた二つの問題を分離して扱うことができ、問題の動作保証も独立に確認でき、想定以上の恩恵に、驚きを隠せませんでした。

普段、問題をスタートとゴールを見据えて雑に解いている自分を認識し、自身のエンジニアリングの新たな改善点を自覚することができました。

自身にとって初めての大規模開発ということで「PRの粒度は細かくするべき」という教訓を実際に身をもって実感することができ、非常に貴重な学びになりました。

 

まとめ

さて、ここまでモノリポという考え方について、インターンシップ生目線でのサービスのモノリポ移行の苦労を綴ってきました。

マイクロサービス、モノリポともにとにかく導入しろというものではなく、それぞれのフェーズに向き、不向きがあると思います。

ちなみに「ABEMA」では、ほぼ満場一致で現フェーズでのモノリポ移行の恩恵を実感しており、私自身も「ABEMA」の指数関数状に向上する開発速度、モノリポの恩恵を短期間で実感していました。

「ABEMA」ではポリリポによるマイクロサービスアーキテクチャの採用→モノリポ管理によるマイクロサービスアーキテクチャという順序での採用をたどりました。モノリポへの移行は非常に骨の折れるタスクだと思います。

ただし、モノリポは間違いなくマイクロサービスアーキテクチャ採用のハードルを下げてくれるものだと思います。本ブログがモノリポ移行の、あるいはマイクロサービスアーキテクチャ採用の手助けになると幸いです。

一ヶ月間、技術面のみの成長だけでなく、キャリア形成にも助力していただきました。自身が行き詰まっている際に近くのエンジニアの方が「どうしたの?」と声をかけてくれたり、原因のわからない問題にともに向き合ってくれたり、優秀なエンジニアに囲まれて仕事をする環境が何より楽しかったです。

手を挙げると仕事もどんどん任せていただき、毎日ミーティングにて進捗にも気を払っていただけるため、のびのびと仕事をすることができました。

 

また、社員の方々は皆さん視座が高く、周囲の人々の成長や目標を積極的にサポートする環境がとても魅力的でした。社員の方とランチへいく機会にも、無意識的に「あの人のここがすごいよね」や「最近あの人とても頑張っているね」という話を聞く機会が多く、チームの挑戦をサポートし合える体制が今の「ABEMA」の急成長に貢献しているのだと感じました。

トレーナーの大芽さん、人事担当の峰岸さん、エンジニアの方々、そのほか関わった皆様充実した一ヶ月間をありがとうございました!