この記事では、書籍「モノリスからマイクロサービスへ」の要約、および本ブログ筆者の考えを一部記載していきます。
- マイクロサービス、モノリスとは何かや、それらの違いを理解したい。
- モノリスからマイクロサービスに移行する際の注意点を理解したい。
1章 必要十分なマイクロサービス
マイクロサービスとは
マイクロサービスとは、ビジネスドメインに基づいてモデル化された、独立してデプロイ可能なサービスのこと。サービス同士はネットワークを介して相互に通信してシステムを形成する。
マイクロサービスを技術的な観点から見ると、カプセル化されたビジネス機能が1つ以上のネットワークエンドポイントを介して公開するもの。各サービスの境界は、あるサービスの変更が他のサービスに影響が出ないこと。
マイクロサービスがもたらす利点
本書を読んで、マイクロサービスの利点は何より「柔軟性」にあると思いました。
例えば、作業の観点でいうとより多くの開発者がお互いの邪魔をすることなく問題に取り組めることが利点となる。
また、プロセスの観点では、プロセスの分離によって多様な技術の選択が可能になることも利点の一つ。
ただし、マイクロサービスの目先の利点に囚われず、マイクロサービスから何を得ようとしているかを理解することが重要である。
マイクロサービスにおける技術とサイズの話
マイクロサービスだからと言って、何も新しい技術を利用しないといけないわけではない。新しい技術を取り入れるのにはコストもかかる。そのため、マイクロサービスに移行し始めるのであれば、むしろ慣れ親しんだ技術で問題に対処できる方が有益となる。
マイクロサービスの各サービスのサイズを決める上で、気にするべきなのは以下2点。
- 自分たちが扱えるサイズかどうか。サービスが増えれば増えるほど管理が大変になる。
- サービスの境界をどう定義すれば、ぐちゃぐちゃに結び付かずマイクロサービスを最大限活用できるか。
モノリスとは
全てのコードが単一のプロセスとしてデプロイされているシステムのこと。
また、似たものにモジュラーモノリスと分散モノリスがある。
モジュラーモノリスとは、単一のプロセスがモジュールで構成され、それぞれ独立して作業できるものの、デプロイのために結合する必要があるシステム。
分散モノリスとは、複数のサービスで構成されているものの、何らかの理由でシステム全体が一緒話にデプロイされなければならないシステム。
モノリスの課題
単一プロセスのモノリスであれば分散モノリスであれ、モノリスは結合、特に実装時とデプロイ時の結合の危険に対してより脆弱なことが多い。
同じ場所で作業する人が増えれば増えるほど、お互いの邪魔になる。
モノリスの利点
モノリスにも利点はある。
マイクロサービスに比べてはるかにシンプルなデプロイ方式によって、マイクロサービスのような分散システムにおける多くの落とし穴を回避できる。例えば、監視・トラブルシューティング・エンドツーエンドテストなどの作業もずっと簡単になる。
人々は「モノリス」=「問題がある」「レガシー」と見るようになりましたが、これは問題なこと。モノリスは、あくまでアーキテクチャの選択肢の一つであり、マイクロサービスとトレードオフにあるものである。
2章 移行を計画する
目的を明確にする
マイクロサービスは目的ではない。マイクロサービスアーキテクチャへの移行は、既存のシステムアーキテクチャのままでは達成できないことを実現するために考えるべきである。
「マイクロサービスがNetflixにとって良いものなら、自分たちにとっても良いもののはずだ!」と思い込むのは危険性がある。
目的を明確化する上で、以下の3つの質問が重要である。
- 達成したいことは何か?
- マイクロサービスの他に代替手段はなかったか?
- どうすれば移行がうまくいっているか分かるだろうか?
マイクロサービスを選択する理由
マイクロサービスを世界中の企業が選択する理由として、よく挙げられるものは以下である。
- チームの自律性を高める
- 市場投入までの時間を減らす
- 負荷への費用対効果が高いスケーリング
- 堅牢性を改善する(一つの機能停止がシステム全体の停止に繋がらない)
- 開発者の数を増やす(デリバリ衝突が減らせる)
- 新しい技術を受け入れる(サービスごとにOSやプログラミング言語を変更可能)
段階的に移行していくことの重要性
既存のモノリシックシステムを分解することが適切だと判断した場合には、そのシステムを少しずつ解体していくことを強くお勧めする。
モノリスを大理石の塊だと考えると、それを丸ごと吹き飛ばすこともできるが、それでうまくいくことはほとんどない。
マイクロサービスが良いアイデアだと思うのであれば、どこか小さなところから始めてみる。昨日の1つか2つの分野を選び、それをマイクロサービスとして実装し、本番環境にデプロイしてみて、それがうまくいったかどうかを振り返ってみると良い。
機能を新しいサービスへ抽出する作業は、本番環境で使われるまで完了とは言えない。最初のいくつかのサービスを実際に使ってみるというプロセスから、多くのことを学べる。初期の段階では、そこに焦点を当てる必要がある。
3章 モノリスを分割する
切り貼りか再実装か?
モノリスからマイクロサービスへ移行する際、既存のコードをどうするかは必ずしも明確ではない。
既存のモノリシックナコードが十分に整理されているなら、コード自体を移動することで大幅な時間を節約できるかもしれない。しかし、この段階ではモノリスからコードをコピーしても、モノリス自体からこの機能を削除しないことがとても重要だ。理由は、
- 機能を一定期間モノリスに残すことで、より多くの選択肢が得られる。
- そうすることで、ロールバックできるポイントや両方の実装を同時実行する機会が得られる。
そうして、移行がうまくいった後に、モノリスからその機能を削除する。
移行パターン
モノリスからマイクロサービスへの移行パターンを紹介し、それぞれどこで役立つのか、どのように実装できるのかを説明する。
パターン①:ストラングラーアプリケーション
このパターンは、古いシステムと新しいシステムを共存させ、新しいシステムが成長する時間を与え、古いシステムを完全に置き換えていくというもの。
- 新システムへの段階的な移行をサポートしている。
- 段階的に提供された新システムを引き続き利用しながらも、移行を一時的に中断したり、完全に中止(ロールバック)したりすることも可能にする。
- 段階移行中に、移行対象のモノリス側の機能を変更していた場合、単純にロールバックできない。
- 移行に時間がかかればかかるほど、機能の凍結期間を長くしなければならない。
1. 既存のシステムから移行したい部分を特定する。
2. 次に、この機能をマイクロサービスに実装する。
3. 新しい実装の準備ができたら、モノリスからの呼び出しを新しいマイクロサービスに切り替えられるようにする。
パターン②:抽象化によるブランチ
抽象化によるブランチは、ソースコードのブランチに頼らずに、モノリスに少しずつ変更を加えることを可能にするパターン。ソースコードの別のブランチで作業を行う代わりに、抽象化によるブランチを利用して既存のコードベースに変更を加えることで、混乱を抑えつつも同じバージョンのコード内で実装を安全に共存させられる。
- コードベースに少しずつ変更を加えつつも、同じコードベースの他の箇所で作業している開発者の混乱を最小に抑えることができる。
1. 置き換える機能の抽象を作る。
2. 作成した抽象を使用するように既存機能のクライアントを変更する。
3. 機能を改良した抽象の実装を新たに作る。この新しい実装がマイクロサービスを呼び出すことになる。
4. 新しい実装を使用するように抽象を切り替える。
5. 抽象を後始末し、古い実装を削除する。
パターン③:同時実行
同時実行では、古い実装と新しい実装のどちらかではなく、両方の実装を呼び出して結果を比較することで、それらが同等かを確認する。
- 古いモノリスの実装と新しいマイクロサービスの実装を同時に実行するので、機能的同等性のみでなく非機能的同等性も確認できる。
パターン④:デコレーティングコラボレーター
デコレーターパターンを使うと、基礎となるものが何かを知ることなしに新しい機能をそこに追加できる。デコレーターを使うことで、基礎となるモノリスを変更しなくても、モノリスが直接サービスを呼び出しているように見せかけられる。
- このパターンは、受信リクエストやモノリスからのレスポンスから必要な情報を抽出できる場合には最適。
- 新しいサービスに対して適切な呼び出しを行うためにより多くの情報が必要な場合、この実装はより複雑で絡み合ったものになる。
パターン⑤:変更データキャプチャ
変更データキャプチャは、モノリスに行われた呼び出しを傍受して動作させようとするのではなく、データストアで行われた変更に反応するパターン。
変更データのキャプチャ方法には、以下のような手法がある。
- RDBでデータが変更されたときにカスタム動作をトリガーする、「データベーストリガー」。
- データベースのトランザクションログをトリガーとする、「トランザクションログ監視」。
- 定期的にデータベースをスキャンしてデータの変更をスキャンしそれをトリガーとする、「バッチによる差分コピー」。
- このパターンは、特にデータを複製する必要がある場合に便利なパターン。
- 変更データキャプチャが動作するには、基礎となるキャプチャシステムをモノリスのデータストアに結合する必要がある。
- 既存のコードベースを段階的に分解し、少しずつマイクロサービス化するテクニックが膨大に存在する。
- それらの膨大なテクニックを組み合わせて使う。
- そして、どのテクニックが最適なのかを見極める必要がある。
4章 データベースを分割する
マイクロサービスは、情報隠蔽を実践しているときに最もうまくいく。そのため、データストレージや取得のメカニズムを完全にカプセル化するマイクロサービスを構築しようとする。そのことから、マイクロサービスへ移行する場合に、その移行の力を最大限引き出したいのなら、モノリスのデータベースを分割する必要があるという結論が導かれる。
しかし、データベースを分割するには、以下のような課題に対応する必要がある。
- 移行中のデータ同期
- 論理スキーマと物理スキーマの分解
- トランザクションの整合性
- テーブルの結合
- レイテンシ
パターン:共有データベース
1つのデータベースを複数のサービスで共有することは、多くの課題がある。何よりも大きな課題は、何を共有して何を隠すのかを決める機会を自ら捨てるということだ。それは、情報隠蔽へと向かう動きと真っ向から反する。
他には、誰がデータを管理するのかが不明瞭になるという問題がある。
対処パターンは、各マイクロサービスが独自に所有できるようにデータベースを分割すること。
- 読み取り専用の静的参照データを考える場合
国の通貨コード情報や郵便番号の参照テーブルなどを保持しているスキーマのデータ構造は安定している。 - 複数のサービスが同じデータベースに直接アクセスするのが適切な状況
パターン:データベースビュー
ビューを使うと、サービスは元のスキーマの限定的な射影をスキーマとして提示できる。この射影によって、サービスに公開するデータを制限でき、サービスがアクセスすべきでない情報を隠蔽できる。
- 既存のモノリシックスキーマを分解することが現実的ではない場合