このページの目次
サービスディスカバリ
動的にスケールアップ・ダウンするクラスタにおいて、呼び出し元はどの IP に接続すべきかどうやって知るのか?サービスディスカバリは、「登録→ヘルスチェック→クエリ」を標準プロセスとして整備する。consul/etcd は登録情報を保持し、DNS-SD とクライアント側ロードバランシングは異なるクエリ経路であり、ヘルスチェックが「このインスタンスがトラフィックを受けられるか」を決定する。
課題
サービスインスタンスの IP:port は動的に変化する(スケールアップ/ダウン、ローリングアップデート、フェイルオーバー)。呼び出し元は「現在利用可能なインスタンスはどれか」をどうやって知るのか?
DNS-SD (DNS によるサービスディスカバリ)
成熟した DNS インフラストラクチャを活用してサービスディスカバリを行う:
サービス: api-service → DNS SRV レコード:
_api._tcp.example.com SRV 10 60 8080 instance-1.example.com
_api._tcp.example.com SRV 10 40 8080 instance-2.example.com
クライアント: dig SRV _api._tcp.example.com → [instance-1:8080, instance-2:8080]
利点:追加のコンポーネントが不要。制約:DNS TTL が変更の遅延を決定する(通常 30〜60 秒)。ネイティブなヘルスチェックは存在しない。
Consul/etcd へのサービス登録
サービスインスタンス起動 → Consul/etcd へ登録(ヘルスチェック URL 付き)→ クライアントがクエリ → 正常なインスタンスの一覧を返す:
サポート機能:HTTP API、DNS インターフェース(DNS-SD と互換)、ロングポーリングウォッチ(変更のプッシュ)。
クライアント側 vs サーバー側ロードバランシング
クライアント側:
[クライアント] → サービスディスカバリをクエリ → [I1, I2, I3] を取得 → 自ら選択 → 直接接続
例: gRPC (Consul/etcd リゾルバー使用), Finagle, netflix-eureka
サーバー側:
[クライアント] → LB (固定アドレス) → LB → [I1, I2, I3]
例: kube-proxy, nginx upstream, HAProxy
比較:クライアント側はホップが1回減る(レイテンシ↓)が、クライアントがサービスディスカバリロジックを維持する必要がある。サーバー側ではクライアントはバックエンドの変化を認識する必要がないが、LB がボトルネックおよび単一障害点となる可能性がある。
ヘルスチェック
- パッシブ: 実際のリクエストの失敗(HTTP 5xx, TCP RST)から判断する。追加のリクエストは不要だが、一時的なエラーによる誤判定を受ける可能性がある
- アクティブ: 定期的に GET /health を実行し、200 応答を期待する。信頼性が高いが負荷が増加する
- TTL ベース (Consul): Agent が定期的に Consul へ健康状態を報告する。タイムアウト内にレポートを受信できない場合 → unhealthy とマーク → ディスカバリから削除
参考
- Consul: consul.io/docs/discovery
- etcd: etcd.io/docs
- gRPC name resolution: github.com/grpc/grpc/blob/master/doc/naming.md
キーワード: サービスディスカバリ, DNS-SD, Consul, etcd, ヘルスチェック, クライアント側 LB, サーバー側 LB