🦔

8個のマイクロサービスを一つのモノリスへ還元した話

2024/12/08に公開

この記事は、「エンジニアと人生アドベントカレンダー2024」8日目の記事です!

https://adventar.org/calendars/10296

昨日は Hiromu Tsurutaさんのスリランカ・マレーシア旅行記でした!


急に寒い季節になってきました、皆さんはいかがお過ごしでしょうか?毎年恒例の、一年の総まとめ「エンジニアと人生」アドベントカレンダーの記事です。

ISUCON14の準備しながら書いています

毎年人生成分多めのふりかえり記事を書くのですが、実は明日はISUCON 14の本番で、そのリハーサルで脳みそが技術色全開なタイミングでこの記事を書いています。

そんなこともあり、また人生でまだ棚卸しをするには早いと思えることもあり、今年は多少テクニカルな記事で一年を締めくくりたいと思います。

2024年元旦の記事にも書きましたが、今年はフリーランスエンジニアに専念した年でした。

https://zenn.dev/kenzan100/articles/2be3b0273df87d

今までも副業をスポットでやることはありましたが、正社員としてどこかに属さずに、一年フリーランスエンジニアとして過ごしたのは初めてです。

マイクロサービス乱立から、モノリスへ還元した話

本日は、稼働先の一つで行った技術負債解消の話をしたいと思います。

稼働してすぐに手をつけた二つのRailsサービスの統合がスムーズに進んだので、それを他のサービス群にも当てはめ、6ヶ月ほどで当初は8個ほどあったマイクロサービスを1つのRailsモノリスに統合したプロジェクトを(話せる範囲で)ご紹介します。

なぜそもそもマイクロサービスが乱立する自体になったのか

歴史的経緯から、開発チームは一人の正社員エンジニアと、彼をサポートする数人の業務委託エンジニアという体制が続いていました。


(Photo by olia danilevich: https://www.pexels.com/photo/man-using-3-computers-4974914/)

サービスを伸ばすための機能開発の話が上がったときに、そのときどきの業務委託エンジニアが「ベスト」だと思える実行環境と開発言語・ライブラリを選んだのだと思います。

その選択は、開発開始からリリースまでのスピードをあげることに寄与したのしょう。このサービスインまでのスピードだけに注力した結果、だんだんとサービス群が増えてきました。

エンジニアの数よりも多いマイクロサービスができあがることで、新規ではない機能改善のときに、サービス境界やデータ境界をまたぐ開発の複雑度があがり、そこでスピードが一気におちたり、リグレッションが起きる可能性が増していました。

これは、今後入るエンジニアにとっても前向きになれる状況ではなく、一刻も早く改善すべき技術的課題を位置付け、「技術と実行環境の一本化」と名付けてプロジェクトを立ち上げました。

一本化対象の選定方法

ターゲット選定として、レポジトリ一覧をNotion DBに転記し、

  1. デプロイ先(AWS, Vercel, ...)
  2. 使用言語(TS, Ruby, Golang, ...)
  3. 関係者にヒアリングした「相対的なヤバさ」

などを総合してリタイアすべきサービスの優先順位をつけました。

相対的やヤバさとは

ヤバさを

(知っている人の少なさ)* (複雑性)* (独立している理由の少なさ)

と定義し、その観点で事情をしっている方々にヒアリングを進めました。

結果、独立した実行環境化で動いているバックエンドの多くが

  • 前述のRailsモノリスと同じDBを使用している
  • 実行環境と言語がそのときに開発した技術者の志向に基づくものだった
  • スケールやパフォーマンス要件も、Railsに移行できるもの

だということがわかりました。

実際にやったこと

マイグレーション中に許容できるリスクの判断


(Photo by Drew Rae: https://www.pexels.com/photo/person-playing-poker-1871508/)

一本化は、いわば開発組織(これから入ってくるひと含む)をスタート地点に立たせるためのPJです。

スピード感をもって取り組み、機能開発に耐えうる技術負債のレベルにはやくしておくことが大事なので、かなりアクセルを踏んで一本化を進めていました。

  • 移行をしたときのリグレッションパターンでどんなものが起こり得るのか
  • その影響を受ける範囲がどこまでなのか

シビアに判断し、本来であればより慎重に進めるものでも、ある程度の障害を許容してもらうことができるか、ビジネス側とコミュニケーションをとりながら進めました。

許容できないバグ、できるバグ

  • 起こりうるバグが永続化のレイヤーにまで波及するようなもの
  • そのボリュームが大きかったり、検知できない形で起こりうるもの
  • 影響範囲が外部を巻き込むもの

などは、後述する「デュアルライト」というアプローチを使って、スピードを落とさずにデータのQA精度を高める方法で進めました。

一方で、

  • バグが起こりうる範囲が社内のユーザーに閉じており
  • 起こりうるバグの種類が、ホットフィックスを適用すればその後余波がないもの

などは、言い方は悪いかもしれませんが、エイヤで進めさせていただいた部分もありました。 その代わり、コミュニケーションは全てオープンに迅速に行い、

  • リリースした内容はなんなのか(とその意図)
  • マイグレーション先が動かなかったときのワークアラウンド方法
  • 不具合の報告方法

を明確にし、信頼関係を落とさずに進められるよう心がけました。

データのデュアルライト(Dual Write)

レガシーな移行元アプリケーションと、統合したい移行先アプリケーションを「データの書き込み」を含めて並行稼働させ、その書き込まれたデータの内容をQAするアプローチのことです。


(Photo by Mike Bird: https://www.pexels.com/ja-jp/photo/350626/)

従来は、https://en.wikipedia.org/wiki/Strangler_fig_pattern のように、アプリケーションレベルで新システムと旧システムを並行稼働させるときのやり方の一部として語られることが多いと思います。

今回は、多数のマイクロサービスをなるべく早くモノリスに吸収させるにあたって、アプリケーションレベルの複雑性はそのままであることを許容しました。なので、自分たちの関心ごとはおもにデータの書き込みの正確さでした。

具体的には、

  1. 移行対象のサービスにおいて、データの書き込みのエントリーポイントを特定する
  2. そのポイントで、データを従来通り書き込むコードに加えて、新サービスに書き込む処理を追加する
  3. 新サービス側のデータはただのストックにしておき、適宜新旧のデータフィールドやボリュームを比較して、マイグレーションの精度を高める
  4. 十分に新サービスのデータの精度が正しいと判断できた時点で、旧サービスへのデータ書き込むを止める

という順番で動かしました。

実際にやってみて

実際にデュアルライトで移行させてみて、多くの場合はQAがうまく進んだのですが、たまにデータの書き込みやそのあとの別の外部サービスからのさらなる書き込みが想定されておらず、QAしているデータの不一致が続くことがありました。

データの書き込み場所が統一されていないことそのものが負債であること=移行を早めた方がよいことになると思いますが、こういった 「誰も予期していなかったデータの書き込み」は、ビジネス側のステークホルダーを巻き込むことで「本当はいらない書き込みなのでは?」という議論のとっかかり がつくれます。

実際に、そのうちのいくつかは本来であれば不要なデータの書き込みであり、移行先では気にしなくて良いものだと判明しました。データの不一致が、そもそもの複雑性を下げる気づきにもつながりました。

まとめ

上であげた方法を愚直に繰り返し、一本化プロジェクトを立ち上げてから数ヶ月で、8割のサービスをモノリスに吸収させることができました。

データの書き込み発生頻度が(外部サービスの制約で)遅いものなどは時間がかかりましたが、開発者が知るべきものがRailsモノリスの中にひとまず収まる、という状況はスピーディーにつくれたと思います。

冒頭にも書きましたが、これはあくまでスタート地点につくための準備です。


(Photo by cottonbro studio: https://www.pexels.com/photo/a-woman-getting-ready-to-run-5310773/)

その後、モノリスに移行されたコードを丁寧にながめ、データ境界を適切に定義していくことで、サービスがスケールしてきたあとでも耐えられる疎結合なシステムをつくっていく、というステージが控えています。

サービスが順調に伸びていることもあり、その作業を開発チーム全体でできる段階になったことに、とてもワクワクしています。

もし似たような状況にあるサービス群に立ち向かっている方がいらっしゃったら、その一助になれば幸いです。

Discussion