「マイクロサービス」という言葉は、KubernetesやDockerといった実行基盤の話とセットで語られることが多く、技術スタックの名前だと誤解されがちです。実際にはマイクロサービスはアプリケーションをどの単位で分けて動かすかという設計の考え方で、それをどの基盤で動かすか(Kubernetesなど)は別のレイヤーです。
この記事では、マイクロサービスの概要を、対になる構成であるモノリスとの違いを入口に整理します。まず両者の違いで全体像をつかみ、そのうえでマイクロサービスを特徴づける要素(分割・通信・DB分離)と、それによって増える複雑性までを一続きで押さえます。
- モノリスと対比したときのマイクロサービスの位置づけ
- マイクロサービスを特徴づける要素(サービス分割・API通信・DB分離)
- マイクロサービスにすることで増える複雑性
モノリスとマイクロサービスの違い
モノリス(monolith、一枚岩)は、アプリケーション全体を1つのプログラム・1つのデプロイ単位として動かす構成です。ユーザー管理も注文処理も決済も、同じコードベースに含まれ、まとめてビルドしてまとめてデプロイします。
マイクロサービスは、この1つのアプリケーションを、独立してデプロイできる小さなサービスの集まりに分ける構成です。ユーザーサービス、注文サービス、決済サービスがそれぞれ別のプログラムとして動き、ネットワーク越しに連携します。
両者の違いを整理すると以下です。
| 観点 | モノリス | マイクロサービス |
|---|---|---|
| デプロイ単位 | アプリ全体で1つ | サービスごとに独立 |
| サービス間の呼び出し | 関数呼び出し(同一プロセス内) | ネットワーク越しの通信 |
| データベース | 1つを共有することが多い | サービスごとに分けるのが基本 |
| 障害の波及 | 全体に及びやすい | サービス単位に閉じ込めやすい |
| 技術選定 | 全体で揃える | サービスごとに変えられる |
モノリスが悪いわけではありません。規模が小さいうちは、1つにまとまっている方が開発もデプロイも単純です。マイクロサービスは、チームやサービスが増えて「全体を一度にデプロイすること」がボトルネックになってきたときに効いてきます。
この表の各行は、マイクロサービスにすると具体的に何が変わるのかという話につながっています。以降で、その中心となる要素を順に見ていきます。
マイクロサービスを特徴づける要素
マイクロサービスが「独立してデプロイできるサービスの集まり」であることは、次の3つの要素に具体化されます。この3つがそろって初めて、サービスは独立したと言えます。
サービスをドメイン単位で分割する
出発点は、アプリケーションを意味のある単位に分けることです。分ける基準は技術的なレイヤー(画面・ロジック・DBで割る)ではなく、業務上の関心事(ドメイン)で割るのが基本です。
たとえばECサイトなら、次のような単位が考えられます。
- ユーザーサービス
- 会員登録、ログイン、プロフィール管理
- 商品サービス
- 商品カタログ、在庫
- 注文サービス
- カート、注文確定、注文履歴
- 決済サービス
- 支払い処理、返金
分割の狙いは、それぞれのサービスが自分の責任範囲だけを持ち、他サービスの都合に引きずられずに変更・デプロイできる状態にすることです。逆に、分割の粒度が細かすぎると、あとで述べる通信や運用のコストばかりが増えます。
サービス間はネットワーク越しに通信する
サービスを分けると、モノリスなら関数呼び出しで済んでいた処理が、ネットワーク越しの通信になります。通信の方法は大きく同期と非同期の2つに分かれ、用途で使い分けます。
同期API通信
呼び出した側が、相手の応答を待ってから次に進む方式です。REST APIやgRPCが代表例です。「注文画面を出すためにユーザー情報と商品情報を取得する」のように、結果がその場で必要な処理に向きます。
同期通信は分かりやすい一方、相手サービスが遅い・落ちていると、呼び出した側もその場で待たされたり失敗したりします。呼び出しの連鎖が深くなるほど、この影響は広がります。
非同期通信でサービスを疎結合にする
呼び出した側が応答を待たず、メッセージを送ったら次に進む方式です。メッセージキュー(送信内容を一旦ためておく仕組み)を挟み、受け取る側は自分のペースで処理します。「注文確定を伝えたら、在庫の引き当てとメール送信は各サービスが後から拾う」といった処理に向きます。
非同期にすると、受け取る側が一時的に落ちていてもメッセージは失われず、送る側は待たされません。サービス同士の結びつき(結合)を弱められるのが利点です。GCPでの非同期メッセージングの基本は『Pub/Subとは何か: Publisher・Topic・Subscription・Subscriberの基本』で扱っています。
同期と非同期は、どちらか一方ではなく、処理の性質で使い分けます。結果がその場で要るなら同期、後続処理を切り離したいなら非同期、という判断です。
データベースをサービスごとに分ける
マイクロサービスでは、データベースもサービスごとに分けるのが基本です。ユーザーサービスはユーザーDB、注文サービスは注文DBを持ち、他サービスのDBを直接読み書きしません。
1つのDBを全サービスで共有すると、次の問題が起きます。
- スキーマ変更が全サービスに影響する
- あるサービスの都合でテーブルを変えると、同じテーブルを見ている他サービスが壊れる
- 独立してデプロイできなくなる
- DBを共有している時点で、サービスは疎結合になっていない
DBを分けると、他サービスのデータが必要なときは、DBを覗くのではなくAPIやメッセージ経由で取得します。どのデータをどのサービスが持つかという役割分担が、そのままサービスの境界になります。DBそのものの選び方は『GCPのストレージ・データベースはオブジェクトストレージ・アプリケーションDB・DWHの3分類で選ぶ』で扱っています。
分割で増える複雑性
ここまでの要素はマイクロサービスの利点につながりますが、分けたことで新しく発生する問題もあります。マイクロサービスは複雑性も増やします。
- ネットワーク越しの通信が失敗する
- 関数呼び出しなら起きなかった「相手が応答しない」「遅い」が日常的に起きる。タイムアウトやリトライの設計が要る
- 複数サービスにまたがる処理の一貫性が難しい
- 「注文を確定して在庫を減らす」が別サービスに分かれると、片方だけ成功した状態をどう扱うかを考える必要がある
- 全体像の把握と調査が難しくなる
- 1つのリクエストが複数サービスを横断するため、どこで失敗したかを追う仕組み(分散トレーシングなど)が要る
- 運用対象が増える
- デプロイ・監視・ログの対象がサービスの数だけ増える
- サービスやチームがまだ小さく、全体を一度にデプロイしても困っていない
- ドメインの境界が固まっておらず、どこで分けるべきか見えていない
- ネットワーク越しの通信や運用のコストを払う体制がない
これらのコストを払ってでも、独立したデプロイやスケールが必要になったときに、マイクロサービスは効いてきます。小さく始めてモノリスで進め、必要になった部分から切り出す、という進め方もよく採られます。
まとめ
マイクロサービスは実行基盤の名前ではなく、アプリケーションをどの単位で分けて動かすかという設計の考え方です。モノリスとの違いから整理すると、要点は以下です。
- モノリスはアプリ全体を1つのデプロイ単位とし、マイクロサービスは独立してデプロイできるサービスの集まりに分ける
- マイクロサービスを特徴づけるのは、ドメイン単位の分割・ネットワーク越しの通信・サービスごとのDB分離の3要素
- サービス間通信は、結果がその場で要る同期APIと、後続処理を切り離す非同期メッセージングを使い分ける
- 分割は通信の失敗・一貫性・運用対象の増加といった複雑性を増やす
- 小さいうちはモノリスで十分で、独立性が必要になった部分から切り出す判断が現実的