← 一覧へ
インフラ・クラウド
#서비스 디스커버리#서비스 레지스트리#마이크로서비스#헬스체크#클라이언트/서버 사이드
最終更新 · 2026-10-12

サービスディスカバリ(Service Discovery)と動的なサービス位置探索

1. 概要

A. 定義

サービスディスカバリ(Service Discovery)とは、マイクロサービス・分散環境で頻繁に変化するサービスインスタンスのネットワーク位置(IP・ポート)を、中央レジストリや照会メカニズムを通じて自動的に登録・探索・更新し、呼び出し側がハードコードされたアドレスなしでも正常なインスタンスへ要求を送れるようにするメカニズムである。

サービスディスカバリは「誰がどこにいるか」という問いをランタイムに自動的に解決する仕組みである。 単一サーバで運用されていた過去には、アプリケーションがデータベースや他サービスのアドレスを設定ファイルに固定しても問題はなかった。 しかし数十・数百のサービスがコンテナ上で生成と消滅を繰り返す現代の環境では、アドレスが分単位で変わるため、位置を人が管理する方式はもはや有効でない。 サービスディスカバリはこの変化をシステム自身に追跡させ、呼び出し側と被呼び出し側の結合度を下げる。

ここで重要な区別は、ディスカバリが単なる「アドレス帳」ではなく、状態を反映する動的なアドレス帳である点である。 レジストリはインスタンスの存在だけでなく、そのインスタンスが今要求を受けられる状態か(ヘルス)も併せて管理する。 技術士はディスカバリを説明する際、登録・照会・ヘルスチェック・伝播という四つの軸を併せて扱い、各軸で生じる一貫性と可用性のトレードオフを提示すべきである。

B. 登場背景と必要性

伝統的なモノリシックアーキテクチャでは、構成要素が一つのプロセス内にあるため関数呼び出しで互いを見つけた。 3層構造に分離されても、ウェブ・アプリケーション・DB層のアドレスは比較的固定的であり、ロードバランサ(VIP)とDNSレコードを手動で管理するだけで十分だった。 しかしマイクロサービスへ移行すると、サービス数が急増し、各サービスが独立に配備・拡張され、オートスケーリングとコンテナオーケストレーションがインスタンスを頻繁に入れ替える。 たとえばKubernetesではPodがリスタートするとほぼ必ず新しいIPを受け取るため、昨日のアドレスが今日はまったく別のサービスを指すことがある。

手動管理に固執すると二つの問題が生じる。 第一に、運用者がアドレス変更に追いつけず設定ドリフトが蓄積し、死んだインスタンスへトラフィックが流れて障害が伝播する。 第二に、配備のたびに呼び出し側の設定も変えねばならないため、サービス間に配備結合が生じ、独立配備というマイクロサービスの本質的な利点が失われる。 サービスディスカバリは位置情報をランタイムの真実の源(Source of Truth)として中央化し、変更を自動伝播してこの二つの問題を同時に解消する。

実際の事例として、Netflixは数千のインスタンスが常時入れ替わる環境で自社レジストリであるEurekaを導入し、クライアントが利用可能なインスタンス一覧を定期的に受け取ってローカルで選択するよう設計した。 これは中央の照会地点の障害が全呼び出しを止めないよう可用性を優先した選択であり、ディスカバリ設計が障害隔離戦略に直結することを示している。

C. 中核的特性

第一に、動的性である。インスタンスの生成・消滅・アドレス変更がほぼリアルタイムでレジストリに反映されねばならない。 第二に、ヘルス認識である。単に登録されたインスタンスではなく、現在要求を処理できるインスタンスのみが照会結果に含まれねばならない。 第三に、位置透過性である。呼び出し側は物理アドレスではなく論理的なサービス名で相手を指し、実際のアドレス決定をディスカバリ層に委ねる。 第四に、拡張性と障害隔離である。レジストリ自体がボトルネックや単一障害点にならないよう、複製・キャッシュ・縮退(graceful degradation)を備えねばならない。

2. 全体構造と動作モデル

サービスディスカバリは、サービス提供者(Provider)、サービスレジストリ(Registry)、サービス消費者(Consumer)という三つの役割と、これらを結ぶ登録・照会・ヘルスチェックの流れで構成される。 提供者は起動時に自身の位置とメタデータをレジストリに登録し、消費者はレジストリに問い合わせて呼び出し対象を得る。 レジストリは登録されたインスタンスの生存を継続的に確認し、応答しないインスタンスを一覧から除去する。

graph TD
  subgraph Providers["サービス提供者インスタンス"]
    P1["注文サービス #1 (10.0.1.5:8080)"]
    P2["注文サービス #2 (10.0.1.7:8080)"]
    P3["決済サービス #1 (10.0.2.3:9090)"]
  end
  REG["サービスレジストリ<br/>(登録情報 + ヘルス状態)"]
  subgraph Consumers["サービス消費者"]
    C1["APIゲートウェイ"]
    C2["フロントエンドBFF"]
  end
  P1 -->|"① 登録/ハートビート"| REG
  P2 -->|"① 登録/ハートビート"| REG
  P3 -->|"① 登録/ハートビート"| REG
  REG -->|"② ヘルスチェック"| P1
  REG -->|"② ヘルスチェック"| P2
  C1 -->|"③ 照会(サービス名)"| REG
  C2 -->|"③ 照会(サービス名)"| REG
  C1 -->|"④ 呼び出し(正常インスタンス)"| P1
  C2 -->|"④ 呼び出し(正常インスタンス)"| P3

上記の構造で核心は、①〜④が疎に分離され各自の周期で動作する点である。 登録とハートビートは提供者が主導し、ヘルスチェックはレジストリが主導し、照会は消費者が必要なときに行う。 この分離のおかげで一つの軸の遅延が他の軸を即座に止めないが、同時に各軸間に時差が生じて一時的に古い(stale)情報が照会されうる。 したがって消費者側には、照会結果を盲信せず呼び出し失敗時に別のインスタンスへ再試行する安全装置(再試行・サーキットブレーカ)が併せて必要である。

レジストリに保存される情報は単なるIP・ポートにとどまらない。 サービス名、バージョン、可用ゾーン(AZ)・リージョン、重み、プロトコル、状態(UP/DOWN/STARTING/OUT_OF_SERVICE)といったメタデータが併せて保存され、消費者が地域近接性やカナリア比率に応じてインスタンスを選べるようにする。 たとえば同一AZ内のインスタンスを優先選択すればAZ間トラフィックのコストと遅延を減らせるが、こうしたポリシーを実装するにはレジストリが位置メタデータを保持せねばならない。

3. ディスカバリ方式: クライアントサイド vs サーバサイド

ディスカバリは「どこでインスタンスを選択するか」によってクライアントサイドとサーバサイドに分かれる。 この区別はロードバランシングの責任の所在に直結するため、両方式の違いを理由とともに理解することが重要である。

クライアントサイドディスカバリでは、消費者がレジストリからインスタンスの一覧全体を受け取り、ローカルでロードバランシングアルゴリズム(ラウンドロビン・重み付け・最小接続など)を適用して直接呼び出す。 中間ホップがなく遅延が低く、消費者が細かな選択ポリシーを持てる利点がある。 一方、あらゆる言語・ランタイムのクライアントにディスカバリロジックを埋め込まねばならないため、言語依存とクライアントライブラリのバージョン管理の負担が生じる。 Netflix Eureka + Ribbonの組み合わせが代表的であり、消費者が定期的にレジストリをポーリングしてローカルキャッシュを維持する。

サーバサイドディスカバリでは、消費者が固定のルータ(ロードバランサ・APIゲートウェイ・KubernetesのService)にのみ要求を送り、ルータがレジストリを照会して正常なインスタンスへ転送する。 消費者はディスカバリをまったく知らなくてよいためクライアントが単純になり言語中立である利点が大きい。 代わりにルータというホップが追加され、遅延と可用性管理の負担がルータに集中する。 KubernetesのService(仮想IP) + kube-proxyやAWS ELBがこの方式の典型である。

sequenceDiagram
  participant C as 消費者(クライアント)
  participant R as レジストリ
  participant LB as ルータ/ロードバランサ
  participant S as サービスインスタンス
  Note over C,S: クライアントサイド
  C->>R: サービス名でインスタンス一覧を照会
  R-->>C: 正常インスタンス一覧を返却
  C->>C: ローカルロードバランシングで対象選択
  C->>S: 選択したインスタンスを直接呼び出し
  Note over C,S: サーバサイド
  C->>LB: サービス名/仮想IPで要求
  LB->>R: インスタンス照会(またはキャッシュ)
  R-->>LB: 正常インスタンス一覧
  LB->>S: ロードバランシング後に転送
  S-->>LB: 応答
  LB-->>C: 応答を返却

両方式の選択は組織の状況によって異なる。 単一言語・少数チームならクライアントサイドがホップ削減と細かな制御の面で有利だが、多数の言語と外部連携が多いプラットフォームならサーバサイド(またはサイドカーベース)が運用一貫性の面でよい。 近年は両方式の折衷として、ディスカバリとロードバランシングをアプリケーション外のサイドカープロキシ(サービスメッシュのデータプレーン)へ移し、クライアントはローカルホストへのみ呼び出してプロキシがディスカバリを行うパターンが広がっている。 これはクライアントサイドの性能とサーバサイドの言語中立性を同時に取ろうとする試みである。

4. サービス登録パターンとヘルスチェック

A. 登録パターン: セルフ登録 vs 第三者登録

登録方式は誰がレジストリにインスタンスを記録するかによって分かれる。 セルフ登録(Self-registration)は、サービスインスタンスが自ら起動時にレジストリに登録し終了時に解除し、定期的なハートビートで生存を知らせる。 実装が単純でインスタンスが自身の状態を最もよく知るが、ディスカバリロジックがアプリケーションコードに侵入し、異常終了時に登録が残る問題がある。

第三者登録(Third-party registration)は、別の登録管理者(Registrar)がオーケストレータの配備イベントを観察してインスタンスを代わりに登録・解除する。 アプリケーションコードからディスカバリの関心事を分離できる利点が大きく、Kubernetesがこの方式の代表である。 Kubernetesでは開発者がディスカバリコードを書かなくても、コントロールプレーンがPodのライフサイクルを観察してEndpoints/EndpointSliceを自動的に更新する。

B. ヘルスチェックの設計

ヘルスチェックはディスカバリの信頼性を左右する核心である。 最も単純な方式はハートビート(heartbeat)で、インスタンスが一定周期でレジストリに信号を送り、一定時間信号がなければレジストリが当該インスタンスを失効させる。 この方式はネットワーク分断時に正常なインスタンスを誤って失効させる危険があり、Netflix Eurekaは一定割合以上のハートビートが途絶えると登録解除を保留する自己保存(self-preservation)モードを置き、ネットワーク問題をインスタンス障害と誤認しないようにする。

より能動的な方式は、レジストリやルータがインスタンスのヘルスエンドポイント(例: /health)を定期的に呼び出して応答を確認することである。 ここでライブネス(liveness)とレディネス(readiness)を区別することが重要である。 ライブネスはプロセスが生きているかを、レディネスは今トラフィックを受ける準備ができたかを意味する。 起動直後にキャッシュウォーミングやコネクションプール初期化が終わっていないインスタンスは、生きていてもレディネスが偽であるべきであり、このときディスカバリは当該インスタンスを照会結果から除外して初期エラーを防ぐ。 KubernetesはlivenessProbe・readinessProbe・startupProbeを分離してこの意味の違いを明示的に扱う。

5. 主要な実装技術の比較

サービスディスカバリのソリューションは内部で分散合意や複製メカニズムを用い、その選択がCAP上の性格(一貫性優先CP vs 可用性優先AP)を決める。 下表は代表的な技術を比較するが、数値より設計思想の違いに着目すべきである。

技術 合意/複製 CAP性格 照会インターフェース 特徴
Netflix Eureka ピア間の非同期複製 AP(可用性優先) REST(クライアントキャッシュ) 自己保存モード、クライアントサイド前提
HashiCorp Consul Raft CP(既定) DNS + HTTP マルチDC、豊富なヘルスチェック、KVストア
etcd Raft CP gRPC/HTTP(watch) Kubernetesバックエンド、強い一貫性
Apache ZooKeeper ZAB CP ネイティブクライアント(watch) 成熟だが運用が複雑、セッションベース
Kubernetes(CoreDNS+Service) etcdベース CP(保存) + キャッシュ DNS + 仮想IP プラットフォーム内蔵、第三者登録の自動化

CP系(Consul・etcd・ZooKeeper)はネットワーク分断時に一貫性を守るため、少数派パーティションで書き込みや照会を拒否することがある。 逆にAP系(Eureka)は分断中も各ノードが持つ(古いかもしれない)一覧で照会に応答し可用性を維持する。 ディスカバリの本質は「少し古い一覧でも呼び出しを続けられること」が「正確だが照会が止まること」より優る場合が多い点であり、このためディスカバリレジストリにはAP性向が適するという見解が広く受け入れられている。 ただしKubernetesのように強い一貫性のetcdを保存庫に用いても、実際の照会経路(CoreDNS・kube-proxyキャッシュ)はキャッシュと非同期反映で可用性を確保するため、保存庫のCAP性格とデータプレーンの体感性格は区別して理解せねばならない。

たとえばマルチデータセンターを運用する金融サービスがConsulを選べば、DCごとのサーバ集合がRaftで一貫性を保ちつつWANゲートウェイでDC間の問い合わせを中継し、地域障害隔離とDC間照会を併せて得られる。 逆に一つの巨大なグローバルサービスプールを運用するなら、Eureka系の可用性優先設計が運用安定性の面で有利でありうる。

6. 適用事例と設計例

第一に、Kubernetesネイティブディスカバリである。 Podが生成されるとkubeletがレディネスプローブを通過したPodのIPをEndpoints/EndpointSliceに反映し、Serviceは安定したClusterIP(仮想IP)とsvc.namespace.svc.cluster.localというDNS名を提供する。 消費者はhttp://order-serviceのようにサービス名のみ呼び出せば、CoreDNSが名前を解決しkube-proxyが実際のPodへ分配する。 開発者がディスカバリコードをまったく書かなくてよい第三者登録の典型である。

第二に、Spring Cloudベースのクライアントサイドディスカバリである。 サービスがEureka Clientで自身を登録し、消費者はEurekaから受け取った一覧をSpring Cloud LoadBalancerで選択して直接呼び出す。 社内の多数のサービスが同一のJVMスタックのとき、ホップを減らし細かな選択ポリシー(地域選好・重み付け)を実装するのに向く。

第三に、サービスメッシュ連携設計である。 Istio・Linkerdのようなメッシュはプラットフォームのディスカバリ(KubernetesのEndpoints)を読み、コントロールプレーンが各サイドカー(Envoy)にエンドポイントを配布する。 アプリケーションはローカルホストのプロキシへのみ呼び出し、プロキシがディスカバリ・ロードバランシング・mTLS・再試行を担う。 こうすれば数百の多言語サービスが共通のディスカバリ・セキュリティポリシーをコード変更なしで適用される。

7. 深化: 最新動向と拡張設計

近年のディスカバリは三つの方向へ進化している。 第一に、ディスカバリのサイドカー移管である。サービスメッシュの拡散により、ディスカバリ・ロードバランシング・障害処理がアプリケーション外のデータプレーンへ移り、言語中立性とポリシー一貫性を同時に達成する流れが明確である。 第二に、マルチクラスタ・マルチクラウドディスカバリである。単一クラスタを超えて複数のKubernetesクラスタのサービスを束ねる標準化の取り組み(例: マルチクラスタService API)とグローバルロードバランシングが結合し、地域障害時に別クラスタへのフェイルオーバをディスカバリ層で処理する。 第三に、ヘルス信号の精緻化である。単純な生死判定を超え、応答遅延・エラー率といった品質信号を反映してdegraded(性能低下)状態を区別し、重みを動的に下げる外れ値検出(outlier detection)と結合する。

拡張設計の観点で、レジストリは照会QPSがサービス・消費者数の積で増えるためキャッシュが必須である。 消費者側ローカルキャッシュ(一定TTL)とサーバ側応答キャッシュを組み合わせ、変更をポーリングではなくwatch/ストリーミングで伝播すれば、照会負荷と反映遅延を同時に下げられる。 また大規模環境では全エンドポイントを全プロキシへ配布するとメモリ・演算が爆発するため、消費者が実際に呼び出すサービスのエンドポイントのみ選択的に下ろす範囲限定(scoping)が拡張性の鍵となる。

8. 考慮事項および示唆点

第一に、一貫性と可用性の明示的な選択である。ディスカバリデータは多少古くても可用性を守る方が有利な場合が多いため、レジストリのCAP性格とキャッシュTTLをサービスの障害許容水準に合わせて設計せねばならない。長すぎるTTLは死んだインスタンスへの呼び出しを、短すぎるTTLはレジストリ過負荷を招く。

第二に、ヘルスチェックの意味分離と安全装置である。ライブネス・レディネス・スタートアップを区別し、ネットワーク分断をインスタンス障害と誤認しないよう自己保存・誤検知緩衝を置く。ディスカバリのみを信じず、消費者側の再試行・タイムアウト・サーキットブレーカを併せて設計し、古い一覧による失敗を吸収せねばならない。

第三に、セキュリティと信頼境界である。レジストリは全サービストポロジを含む機微資産であるため、登録・照会に対する認証・認可(ACL)と伝送暗号化(mTLS)が必要である。悪意あるインスタンスが任意に登録してトラフィックを奪う脅威(ディスカバリスプーフィング)を防ぐには登録主体の身元検証が核心であり、ゼロトラストの観点でサービスアイデンティティベースの認可へ拡張する趨勢である。

第四に、運用観測性とベンダーロックインである。登録遅延・照会失敗・失効率・一覧鮮度を指標として収集し、ディスカバリ自体の健全性を追跡せねばならない。また特定のレジストリ製品にディスカバリロジックが深く結合すると移行コストが増すため、アプリケーションはディスカバリ抽象化層(またはプラットフォーム・メッシュ委任)を通じて実装依存を下げるのが長期的に有利である。

第五に、漸進的な導入戦略である。モノリシックからマイクロサービスへ移行する際、最初から完全なメッシュを導入するより、プラットフォーム内蔵ディスカバリ(Kubernetes DNS)で始め、トラフィック制御・セキュリティ要求が高まったときにサイドカー・メッシュへ段階的に拡張する接近が、複雑度と効用の均衡の面で望ましい。

参考資料


一言まとめ: サービスディスカバリは頻繁に変わるインスタンスの位置をレジストリに中央化し、ヘルス状態とともに自動伝播して、呼び出し側が論理名のみで正常なインスタンスへ到達できるようにする動的位置探索メカニズムである。