マイクロサービス
Microservices ・ まいくろさーびす
業務機能ごとに独立してデプロイできるサービスへ分割する構成。チームの自律性と引き換えに分散の複雑さを負う。
✦ 30秒で試す — 航海分けて身軽にしたはずが、全部そろって直す羽目になる注文1件が失敗する確率は?予想してみる概要
マイクロサービスとは、システムを「注文」「在庫」「決済」「通知」のような業務機能単位の小さなサービス群に分割し、それぞれを独立したアプリケーションとして開発・デプロイ・運用するアーキテクチャです。各サービスは自分専用のコードベースとデータベースを持ち、他のサービスとはネットワーク越しの API(多くは REST API や gRPC)やメッセージキューを通じてのみ会話します。
全機能をひとつにまとめるモノリスの対極にある構成で、Netflix、Amazon、Uber などの大規模サービスが実践例として知られています。「マイクロ」という名前ですが、重要なのはサービスの小ささそのものではなく、独立性です。あるサービスの変更が、他のサービスの再デプロイを必要としないこと——これがこのアーキテクチャの核心です。
そしてこの独立性は、技術の話であると同時に組織の話です。サービスごとに担当チームを置き、各チームが自分のサービスを自分のペースでリリースできるようにする。マイクロサービスは「大人数の開発組織を、互いを待たずに並行して動けるようにするための構成」として理解するのが実態に近いです。その代わり、関数呼び出しだったものがすべてネットワーク越しになり、システムは分散システムとしての難しさを背負います。
なぜ生まれたか
2000年代、急成長したWebサービス企業は共通の壁に突き当たりました。数百人の開発者がひとつのモノリスを触ると、リリースは全チームの変更を束ねた一大イベントになり、誰かのバグが全員のデプロイを巻き戻す。障害はシステム全体を道連れにし、一部の機能だけ負荷が高くても全体をスケーリングするしかない。技術スタックも全体でひとつに縛られる。つまり、システムの成長速度が組織の成長に追いつかなくなったのです。
Amazon が2000年代前半に打ち出したと伝えられる「チーム間のやり取りは API だけで行え。データベースの直接参照も共有メモリも禁止」という社内方針や、2009年頃からの Netflix のクラウド移行での実践が、この問題への答えを示しました。境界を業務機能で切り、チームとサービスを一対一で対応させれば、コミュニケーションのコストとデプロイの調整が劇的に減る。「システムの構造は組織のコミュニケーション構造を写す」というコンウェイの法則(1968年)を逆手に取り、望む組織の形に合わせてシステムを分割する——この発想が2014年に James Lewis と Martin Fowler の記事で「マイクロサービス」として定式化され、コンテナやクラウドコンピューティングの成熟がそれを技術的に支えました。
詳細
分割の単位とデータの所有
設計の出発点は「どこで切るか」です。定石は、技術の層(画面・ロジック・データ)ではなく業務能力(ビジネスケイパビリティ)で切ること。ドメイン駆動設計の「境界づけられたコンテキスト」——同じ言葉が同じ意味で通じる範囲——が分割線の指針としてよく使われます。もう一つの実用的な物差しは呼び出しの頻度です。1回の処理で何度も呼び合う「おしゃべりな」組を別のサービスに分けると、その往復がそのまま遅延と失敗の源になります。
そして各サービスは自分のデータベースを排他的に所有し、他サービスのデータを直接読むことは禁じられます。データが欲しければ API を呼ぶ。この規律を破って複数サービスがひとつの DB を共有すると、テーブルの形を1つ変えるだけで全サービスを同時に直してデプロイし直すことになり、デプロイの独立性が失われます。サービスは分かれているのに一緒にしか動かせない——これが「分散したモノリス」と呼ばれる最悪の形で、モノリスの不自由さと分散の税金を両方払うことになります。
同期と非同期、二つの会話のしかた
サービス間の通信には二つの様式があります。同期呼び出しは HTTP ベースの REST API や gRPC で相手の応答を待つ方式で、単純ですが、呼び出しが連鎖すると一つのサービスの遅延・障害が呼び出し元へ波及します。非同期はメッセージキューやイベントストリームに「起きたこと」を流し、関心のあるサービスが各自のペースで処理する方式で、疎結合になる代わりに「結果がすぐには反映されない」結果整合性を受け入れることになります。実務では、ユーザーを待たせる経路は同期、後続処理は非同期、と使い分けるのが典型です。
ユーザーを待たせる決済は同期で確実に、後続の在庫引当や通知はイベント経由で非同期に。注文サービスはキューの先に誰がいるかを知らなくてよいため、後から購読者を増やしても注文サービスの変更は不要です。
分散システムの税金
マイクロサービスの代償は、モノリスでは存在しなかった問題群です。プロセス内の関数呼び出しだったものがネットワーク通信になるため、1回あたり数ミリ秒の遅延と、部分的な失敗が常態になります。1回の失敗率が小さくても、1件の処理で呼び出しが7回あれば、どれか1つが落ちる確率は7倍近くに膨らみます。そのため、待ち続けないためのタイムアウト、一時的な失敗をやり直すリトライと指数バックオフ、障害の連鎖を断つサーキットブレーカーといった防御が全通信に必要です。
複数サービスにまたがる更新はひとつのトランザクションで守れないため、ACID の保証を諦め、サーガ(ローカルトランザクションをつなぎ、失敗したら補償処理で打ち消す)のような手法で整合性を作り込みます。障害調査も一変します。一つのリクエストが何個ものサービスを渡り歩くため、リクエストごとの ID でログとスパンを紐づける分散トレーシングと、充実した監視が事実上の必須要件になります。
この税金は、境界の引き方で大きく変わります。下の部品で、モノリスの中の点線をなぞって切り、サービスに分けてみてください。切るたびに注文が1件流れ、境界をまたいだ呼び出しがネットワーク越し(青の破線)に変わります。どこを切ると呼び出しの回数と応答時間が跳ね上がるか、DB を共有したまま切ると何が起きるかを確かめてください。
モノリスの中を、ブロックの間の点線(切り取り線)を横切るように指でなぞって切ってください。切ると注文が1件流れ、境界をまたぐ呼び出しがネットワーク越しに変わります。
それを支える運用基盤
数十のサービスを人手でデプロイするのは不可能なので、CI/CD パイプラインの自動化が前提です。実行基盤としては、サービスをコンテナにパッケージし Kubernetes で配置・スケーリング・自己修復を任せる構成が事実上の標準になりました。サービス間の認証や暗号化、リトライやタイムアウトといったトラフィック制御を通信層で一括して面倒を見るサービスメッシュ(Istio など)や、外部からのリクエストの入口を束ねる APIゲートウェイも、このアーキテクチャに付随して発達した道具です。逆に言えば、これらの基盤を整備・運用できる組織体力がないなら、マイクロサービスはまだ早いというサインです。
採用判断の現実
マイクロサービスで得られる最大のものは、技術的性能ではなく組織のスケーラビリティです。したがって判断基準も組織側にあります。チームが数個以下で、デプロイの衝突に苦しんでいないなら、分散の税金を払う理由はほぼありません。まずモノリスで始めて、境界が業務として確かめられ、組織が大きくなって痛みが出た箇所から切り出す——これが失敗事例の蓄積から得られた現代の定石です。「マイクロサービスを採用する資格は、モノリスをきちんと設計できることだ」と言われるのは、分割の成否が結局のところ境界設計の腕にかかっているからです。
試問
会員・注文・決済・商品・在庫・通知の6つの機能が、1つのプログラム(モノリス)に入っています。矢印は1件の注文で起きる呼び出しで、在庫は商品を3回呼びます。ブロックの間の点線をなぞる(または押す)と切れてサービスに分かれ、境界をまたぐ呼び出しはネットワーク越しになります。下の DB を押すと、サービスごとに分けられます。
モノリスの中を、ブロックの間の点線(切り取り線)を横切るように指でなぞって切ってください。切ると注文が1件流れ、境界をまたぐ呼び出しがネットワーク越しに変わります。
