雑に描いた餅

餅は餅屋、わたしは技術屋、と言いたかった。

マイクロサービスとは何であり何でないか?を知る、実践的な教科書:「モノリスからマイクロサービスへ ―モノリスを進化させる実践移行ガイド」を読んだ

はじめに

オライリー・ジャパンから出版されている、「モノリスからマイクロサービスへ ―モノリスを進化させる実践移行ガイド」を読みました。

最近で読んだ技術書の中でもなるほど、と思う部分が多く、概要に触れながら感想を書き残していこうと思います。

概要

この本の著者Sam Newmanは以前にBuilding Microservices(邦題:「マイクロサービスアーキテクチャ」)を著し、本書のことを冒頭でも「既存のシステムをマイクロサービスアーキテクチャに分解することをどう考え、実行するかを深く掘り下げた内容になっている」と紹介しています。

その紹介の通り、この書籍は確かに既存のシステムからマイクロサービスへの段階的な移行について、アプリケーションやデータベースの分割に当たって具体的な方法を示しています。

一方で、その移行前後の存在である「既存のシステム(しばしばモノリスとして形容される)」と「マイクロサービス」が何であり、何でないか、を詳細に紹介してくれています。

その為、マイクロサービスへの移行を志すエンジニアは勿論ですが、マイクロサービスを採用している現場で働くエンジニアにとっては何故今のようなシステムアーキテクチャが採用されているかの理解を助ける一冊となりますし、今後自分がシステムを設計していく際の引き出しを増やしてくれるような存在にもなっていると思います。

以下、オライリー・ジャパンの公式サイトから本書の内容の紹介を引用します。

本書は、モノリスからマイクロサービスアーキテクチャへと移行するための実践的なガイドです。マイクロサービスが自分たちのシステムに適しているかを判断するところから、ビジネスを維持しながらモノリシックなシステムを少しずつマイクロサービスに切り替えていく方法、さらには、マイクロサービスアーキテクチャが成長するにつれて起こる課題への対処の仕方まで、豊富な例やシナリオを用いて解説します。また、モノリスやデータベースを分解していくのに役立つ様々なパターンやテクニックも扱います。 システムのアーキテクチャ移行について具体的な方法を解説する本書は、エンジニア必携の一冊です。

O'Reilly Japan - モノリスからマイクロサービスへ

各章のまとめ

以下では、印象に残った箇所を引用しつつ、各章の内容の一部を簡単に紹介します。

1章 必要十分なマイクロサービス

この章では、以降で触れていくマイクロサービス、及びそれの対になるモノリスについて紹介しています。

まずマイクロサービスですが、本書内では以下のような言葉で説明されています。

マイクロサービスとは、ビジネスドメインに基づいてモデル化された、独立してデプロイ可能なサービスだ。サービス同士はネットワークを介して相互に通信してシステムを形成する。(p.1)

技術的な観点から見ると、マイクロサービスとは、カプセル化されたビジネス機能を1つ以上のネットワークエンドポイントを介して公開するものだ。それらはネットワークを介して相互に通信しあい、分散システムを形成する。(p.1)

ここにマイクロサービスを構成する要素である、以下のような3つの要素が見られます。

  • ビジネスドメインに基づいてモデル化・カプセル化されたサービスである
  • 独立してデプロイ可能なサービスである
  • ネットワークを介して相互に通信することでシステムを形成するサービスである

システムを実際に設計する上では、特に最初の2点が重要になります。1点目は、「どのような境界で、どのような方法でサービスを分割するか」「どのような情報を公開・隠蔽するか」という指針に、2点目は「独立してデプロイ可能にするために、どのような方法でサービスを分割するか」「分割されたサービスを独立してデプロイできるパイプラインをどのように設計するか」という指針に繋がります。

一方で筆者は、本書のテーマはモノリスからマイクロサービスへの移行である、と掲げています。その為この章の中で、モノリスがどのようなものであるかについても説明しています。本書の中では単一プロセスとしてデプロイされているシステムを一般的にモノリスとして紹介していますが、この章では、単一プロセスを複数モジュールで構成するモジュラーモノリス、複数サービスで高壊死されているもののシステム全体の独立デプロイ性を欠いている分散モノリスサードパーティ製のブラックボックスシステムを他の類型として紹介しています。

筆者はモノリスそれ自体にも利点があることを認めた上で、具体的に移行を行うにはどのようにしていくかを後の章で紹介しています。

2章 移行を計画する

この章では、実際に移行を計画する上で留意する点を説明しています。

章の冒頭で筆者は以下のように切り出しています。

マイクロサービスは目的ではない。マイクロサービスを手にすることが「勝利」ではないのだ。マイクロサービスアーキテクチャを採用することは、合理的な意思決定に基づいた意識的な判断であるべきだ。マイクロサービスアーキテクチャへの移行は、既存のシステムアーキテクチャのままでは達成できないことを実現するために考えるべきだ。(p.37)

この文章が本書の本質だと思います。執筆当初、マイクロサービス、が一種のバズワードと化していたように思います(今でもそうかもしれません)が、マイクロサービスはあくまでシステムを設計する上での合理的な手段の一つであり、目的とはならないということを留意するべきだと思います。

筆者はさらに、「3つの重要な質問(p.37)」として、マイクロサービスアーキテクチャ化を検討すべきか組織が見極める際、筆者が実際に行っている質問を紹介しています。

  • 達成したいことはなにか?

  • マイクロサービスの他に代替案はなかったか?

  • どうすれば移行がうまくいってるか分かるだろうか?

以降で筆者は、マイクロサービスを採用する際によく挙げられる理由(利益)を例として取り上げつつ、その利益がマイクロサービス以外でも享受できることを説明しています。例えば「チームの自律性向上」に対しては、コードベースの所有権の変更による自律性の付与、「リリースまでのリードタイム短縮」に対しては、デリバリーに関わるステップの可視化、ボトルネックの特定、といったように。

自分は特に2つ目の「リードタイム短縮」についての記述が参考になりました。一般的にアプリケーションのサイズ(コードの記述量、関連するドメインの規模、機能数)が小さくなるほど、リリースまでの時間は短縮される傾向にあると思います。一方でそれだけに着目して「マイクロサービスアーキテクチャを採用して、リードタイムを短縮するぞ!」と意気込んでも、実際にはサービス間のオーケストレーションであったり、分割に伴う組織設計であったり、それに付随するコストを見落としてしまい、結果的にリードタイムの短縮に繋がらない、という結果にもなり得るかもしれないなと思いました。

そのような場面でこそ、一見華やかな解決策に飛びつくのではなく、愚直に自分達のプロセスを可視化し、ボトルネックを特定、対処した方が、問題の解決に繋がるのではないかと感じました。

3章 モノリスを分割する

4章 データベースを分割する

この2章では、実際のモノリスとなっているアプリケーションや、共有してしまっているデータベースを分割するプラクティスを紹介しています。

詳細な説明は実際に本書を読んでいただくとして、自分が参考になった部分をいくつか抜粋します。

ストラングラーパターン(3.3)

既存のアプリケーションから新しいマイクロサービスへと機能を移行する際のパターンの一つです。このパターンでは、以下のようなステップで移行を進めます。

  1. 既存のシステム内で移行対象と、どのような呼び出しが行われるかを特定する
  2. 既存のシステムへの呼び出しを傍受する
  3. 移行対象の機能を新しいマイクロサービスへ移行する
  4. 移行が完了したタイミングで、2.で傍受していた呼び出しを新しいマイクロサービスへ切り替える

このパターンの長所として、呼び出しを傍受していることから、機能のリリース/ロールバックの切り替えを容易に行えることが挙げられます。既存のシステムを活かしつつ開発を進めた上で、最後に呼び出しを切り替えることで簡単にリリースができますし、万が一問題があったときも元の呼び出しにロールバックすれば良いだけです。

具体的な実装例としては、Nginxのようなリバースプロキシを前段に用意する、等が挙げられています。初めは新しいサービスへの呼び出しは 501 Not Implementedのレスポンスを返すようにして、最後に呼び出しを有効にするよう設定を行うことで簡単にストラングラーパターンを実現できます。

機能を移行しながら振る舞いを変える(3.4)

これは読んで字のごとく、機能を移行するタイミングと同時に、既存のシステムから振る舞いを変えてしまう、というものです。これはパターンと言うよりも、移行の開発中に既存システムに手を加える必要が出てきた場合にどうするか?という問に対する答えのようなものだと解釈しています。

筆者はこの場合に、以下のように述べています。

この問題を解決するのは簡単ではない。移行中の機能を途中で変更することを許容する場合には、ロールバックが困難になることを受け入れなければいけない。(p.99)

例えば既存システムに対してバグが見つかった場合、既存システムと新しいサービスの両方に不具合修正を施さなければ、ロールバックした際に振る舞いが変わってしまうリスクがあります。このようなリスクを避けるため、既存システムへは緊急のものを除いて手を加えない、逆に割り切って必ずどちらのバージョンにも不具合修正を加える、という意思決定をする必要が出てきます。

筆者はこの他にも変更データキャプチャパターン(3.9), サーガパターン(4.16)等の様々なパターンを紹介していますが、最後に3章の冒頭に載っている以下の文章を紹介します。

それぞれのパターンの長所と短所を必ず理解すること。これらは一概に「正しい」方法とは言えない。(p.80)

あくまで本書で紹介されている「パターン」は長所と短所を伴った解決策の一つであり、絶対的に正しい解決策ではない、ということを著者は最初に触れています。方法論が多く記述されているからこそ、それをそのまま模倣するのではなく、どのような問題意識に対して、どのように短所を割り切った上で、長所をもって問題を解決しようとしたかを検討するべき、という初心に戻らせてくれます。

5章 成長の痛み

筆者は本書の最後の章で、マイクロサービスへの移行に際して直面しうる問題について更に掘り下げて述べています。いつ、どのような問題が発生しやすいか、いつこうした問題に対処すべきなのか、どのように対処すべきか、という点を紹介してくれているこの章からも印象に残った箇所を抜粋します。

全体最適と局所最適の問題は)ランチの時に偶然耳にした一言がきっかけで気付くことが多い。コミュニティオブプラクティス(実践コミュニティ)のような、ある種のチーム横断的な技術グループがあれば、これらの問題をはるかに早く発見できる。(p.231)

マイクロサービスへの移行は全体最適と局所最適のバランスが必要な局面を生み出します。マイクロサービスとして分割されたサービスにチームが責任を持つ場合、意思決定はそのサービスに対する局所化されたものになりやすく、全体最適的な目線が失われるおそれがあります。そのような兆候に気付くという点で、カジュアルな他チームとの横断的なやり取りが役に立つのではないか、と著者は述べています。

昨今のコロナ禍によって、リモートワークへの移行が加速していきました。この傾向は、こうしたカジュアルな他チームとのやり取りの機械の喪失に繋がると日々体感しています。ゆえに、意図的にチームを横断するようなコミュニケーションをどのように設計するか、という点は今後のチーム開発において今まで以上に重要になってくるのではないかと感じています。

自分たちの意思決定が不可逆的なものと可逆的なもののどちらに傾いているかを、チームの人々が認識できるようにすることが秘訣だ。意思決定が不可逆的な方向に傾いているほど、チームの境界外の人を意思決定に参加させることが重要になる。

上記のようなチームを横断する話と類似して、意思決定の影響範囲について著者が指針を出しています。なんとなくこの決定は重要そうだから他のチームも巻き込んでおこう、という曖昧な基準ではなく、「この決定は不可逆性が高いので、隣のチームも巻き込んだ上で意思決定しよう」と能動的に選択していくことが重要だと感じました。

さいごに

自分は普段マイクロサービスアーキテクチャを採用している組織でソフトウェアエンジニアとして開発を行っています。そして自分がプライベートで開発を行う際も、規模の小さなアプリケーションをいくつか設計することが多いです。

そんな中本書を読んで、「マイクロサービスアーキテクチャを採用する」とは単に「Webとバックエンドを分割する」という部分にとどまらない意思決定を伴うものであること、そしてレガシーなアプリケーションからマイクロサービスに分割していく手法は、規模の大小を変えてアプリケーションの設計の様々な場面において役に立つことを学びました。

マイクロサービスという単語を知っている、普段マイクロサービスアーキテクチャを採用しているエンジニアにこそ、この本を読んでもらいたいなと感じました。