← Back to list
Infrastructure & Cloud
#서비스 디스커버리#서비스 레지스트리#마이크로서비스#헬스체크#클라이언트/서버 사이드
Last updated · 2026-10-12

Service Discovery and Dynamic Service Location

1. Overview

A. Definition

Service Discovery is a mechanism that automatically registers, locates, and refreshes the network locations (IP and port) of frequently changing service instances in microservice and distributed environments through a central registry or lookup mechanism, so that callers can reach a healthy instance without hardcoded addresses.

Service discovery is the machinery that answers the question "who is where" automatically at runtime. In the past, when systems ran on a single server, an application could pin the address of a database or another service in a configuration file without any problem. But in a modern environment where tens or hundreds of services are repeatedly created and destroyed on containers, addresses change by the minute, so managing locations by hand is no longer viable. Service discovery lets the system track these changes on its own, reducing the coupling between caller and callee.

An important distinction here is that discovery is not a simple "address book" but a dynamic address book that reflects state. The registry manages not only the existence of an instance but also whether that instance is currently able to accept requests (its health). When explaining discovery, a professional engineer should address the four axes of registration, lookup, health check, and propagation together, and present the consistency-versus-availability trade-offs that arise on each axis.

B. Background and Necessity

In a traditional monolithic architecture, components live in a single process and find one another through function calls. Even when split into a 3-tier structure, the addresses of the web, application, and DB tiers were relatively fixed, so manually managing a load balancer (VIP) and DNS records was sufficient. Once you move to microservices, however, the number of services grows sharply, each service is deployed and scaled independently, and autoscaling and container orchestration replace instances constantly. For example, in Kubernetes a Pod almost always receives a new IP when it restarts, so yesterday's address may point to an entirely different service today.

Insisting on manual management creates two problems. First, operators cannot keep up with address changes, so configuration drift accumulates and traffic flows to dead instances, propagating failures. Second, because caller configuration must be changed on every deployment, deployment coupling arises between services, erasing the essential microservice benefit of independent deployment. Service discovery centralizes location information as the runtime Source of Truth and propagates changes automatically, resolving both problems at once.

As a real case, Netflix introduced its own registry, Eureka, in an environment where thousands of instances are constantly replaced, designing clients to periodically fetch the list of available instances and choose among them locally. This was a choice that prioritized availability so that a failure of the central lookup point would not block all calls, showing that discovery design is directly tied to failure-isolation strategy.

C. Core Characteristics

First, dynamism. The creation, destruction, and address change of instances must be reflected in the registry in near real time. Second, health awareness. The lookup result must contain only instances that can currently handle requests, not merely registered ones. Third, location transparency. Callers refer to a counterpart by a logical service name rather than a physical address, delegating actual address resolution to the discovery layer. Fourth, scalability and failure isolation. The registry itself must be equipped with replication, caching, and graceful degradation so it does not become a bottleneck or single point of failure.

2. Overall Structure and Operating Model

Service discovery consists of three roles — service Provider, service Registry, and service Consumer — and the registration, lookup, and health-check flows that link them. A provider registers its location and metadata with the registry at startup, and a consumer queries the registry to obtain a call target. The registry continuously verifies the liveness of registered instances and removes those that fail to respond from the list.

graph TD
  subgraph Providers["Service provider instances"]
    P1["Order service #1 (10.0.1.5:8080)"]
    P2["Order service #2 (10.0.1.7:8080)"]
    P3["Payment service #1 (10.0.2.3:9090)"]
  end
  REG["Service registry<br/>(registration info + health state)"]
  subgraph Consumers["Service consumers"]
    C1["API gateway"]
    C2["Frontend BFF"]
  end
  P1 -->|"① register/heartbeat"| REG
  P2 -->|"① register/heartbeat"| REG
  P3 -->|"① register/heartbeat"| REG
  REG -->|"② health check"| P1
  REG -->|"② health check"| P2
  C1 -->|"③ lookup(service name)"| REG
  C2 -->|"③ lookup(service name)"| REG
  C1 -->|"④ call(healthy instance)"| P1
  C2 -->|"④ call(healthy instance)"| P3

The key in this structure is that ①–④ are loosely decoupled and each operates on its own cadence. Registration and heartbeats are driven by the provider, health checks are driven by the registry, and lookups are performed by the consumer when needed. Thanks to this decoupling, a delay on one axis does not immediately block another, but at the same time a lag develops between axes so that stale information may be returned temporarily. Therefore the consumer side needs a safety net (retry and circuit breaker) that does not blindly trust lookup results and retries on another instance when a call fails.

The information stored in the registry is not limited to IP and port. Metadata such as service name, version, availability zone (AZ)/region, weight, protocol, and state (UP/DOWN/STARTING/OUT_OF_SERVICE) is stored together so that a consumer can choose an instance based on regional proximity or a canary ratio. For instance, preferring an instance within the same AZ can reduce cross-AZ traffic cost and latency, and implementing such a policy requires the registry to hold location metadata.

3. Discovery Modes: Client-Side vs Server-Side

Discovery is divided into client-side and server-side depending on "where the instance is selected." Because this distinction is directly tied to the location of load-balancing responsibility, it is important to understand the difference between the two modes together with the reasons.

In client-side discovery, the consumer receives the entire list of instances from the registry and applies a load-balancing algorithm (round robin, weighted, least connections, etc.) locally to call directly. It has the advantage of low latency because there is no intermediate hop, and the consumer can hold a fine-grained selection policy. On the other hand, discovery logic must be embedded in clients of every language and runtime, creating language dependency and the burden of client-library version management. The Netflix Eureka + Ribbon combination is representative, where the consumer periodically polls the registry to maintain a local cache.

In server-side discovery, the consumer sends requests only to a fixed router (load balancer, API gateway, Kubernetes Service), and the router queries the registry and forwards to a healthy instance. Since the consumer need not know anything about discovery, the client becomes simple and language-neutral, which is a large advantage. Instead, an extra hop called the router is added, concentrating the burden of latency and availability management on the router. Kubernetes' Service (virtual IP) + kube-proxy and AWS ELB are typical of this mode.

sequenceDiagram
  participant C as Consumer(client)
  participant R as Registry
  participant LB as Router/load balancer
  participant S as Service instance
  Note over C,S: Client-side
  C->>R: Look up instance list by service name
  R-->>C: Return healthy instance list
  C->>C: Select target by local load balancing
  C->>S: Call the chosen instance directly
  Note over C,S: Server-side
  C->>LB: Request by service name/virtual IP
  LB->>R: Query instances (or cache)
  R-->>LB: Healthy instance list
  LB->>S: Forward after load balancing
  S-->>LB: Response
  LB-->>C: Return response

The choice between the two modes depends on organizational context. For a single-language, few-team setting, client-side is advantageous in reducing hops and offering fine-grained control, but for a platform with many languages and much external integration, server-side (or sidecar-based) is better for operational consistency. Recently, as a compromise between the two, the pattern of moving discovery and load balancing to a sidecar proxy outside the application (the data plane of a service mesh) — so the client calls only localhost and the proxy performs discovery — has been spreading. This is an attempt to take the performance of client-side and the language neutrality of server-side at the same time.

4. Service Registration Patterns and Health Checks

A. Registration Patterns: Self-Registration vs Third-Party Registration

The registration method is divided according to who records the instance in the registry. In self-registration, a service instance registers itself with the registry at startup, deregisters at shutdown, and announces its liveness through periodic heartbeats. It is simple to implement and the instance knows its own state best, but discovery logic intrudes into application code and registrations can linger after an abnormal termination.

In third-party registration, a separate Registrar observes the orchestrator's deployment events and registers/deregisters instances on their behalf. This has the large advantage of separating the discovery concern from application code, with Kubernetes being the prime example. In Kubernetes, even if developers write no discovery code, the control plane observes the Pod lifecycle and automatically updates Endpoints/EndpointSlice.

B. Designing Health Checks

Health checks are the core that determines the reliability of discovery. The simplest method is the heartbeat, where an instance sends a signal to the registry at a fixed interval, and if no signal arrives for a certain time the registry expires that instance. This method risks wrongly expiring healthy instances during a network partition, so Netflix Eureka has a self-preservation mode that suspends deregistration when more than a certain proportion of heartbeats are lost, so it does not mistake a network problem for an instance failure.

A more active method is for the registry or router to periodically call an instance's health endpoint (e.g., /health) and verify the response. Here it is important to distinguish liveness from readiness. Liveness means whether the process is alive, and readiness means whether it is ready to take traffic right now. An instance that has just started and has not finished cache warming or connection-pool initialization should have readiness false even though it is alive, and in that case discovery excludes the instance from lookup results to prevent early errors. Kubernetes separates livenessProbe, readinessProbe, and startupProbe to handle this difference in meaning explicitly.

5. Comparison of Major Implementation Technologies

Service discovery solutions internally use distributed consensus or replication mechanisms, and that choice determines their CAP character (consistency-first CP vs availability-first AP). The table below compares representative technologies, but one should focus on the difference in design philosophy rather than on numbers.

Technology Consensus/replication CAP character Lookup interface Characteristics
Netflix Eureka Async peer replication AP (availability-first) REST (client cache) Self-preservation mode, assumes client-side
HashiCorp Consul Raft CP (default) DNS + HTTP Multi-DC, rich health checks, KV store
etcd Raft CP gRPC/HTTP (watch) Kubernetes backend, strong consistency
Apache ZooKeeper ZAB CP Native client (watch) Mature but operationally complex, session-based
Kubernetes (CoreDNS+Service) etcd-backed CP (store) + cache DNS + virtual IP Platform built-in, automated third-party registration

CP-family solutions (Consul, etcd, ZooKeeper) may reject writes or lookups in a minority partition during a network split to preserve consistency. Conversely, the AP family (Eureka) responds to lookups even during a split with the (possibly stale) list each node holds, maintaining availability. The essence of discovery is that "being able to keep calling with a slightly stale list" is often better than "being accurate but having lookups halt," which is why the view that an AP tendency suits a discovery registry is widely accepted. That said, even when strongly consistent etcd is used as a store as in Kubernetes, the actual lookup path (CoreDNS, kube-proxy cache) secures availability through caching and asynchronous reflection, so one must distinguish the CAP character of the store from the perceived character of the data plane.

For example, if a financial service operating multiple data centers chooses Consul, the server set per DC maintains consistency with Raft while a WAN gateway relays cross-DC queries, obtaining both regional failure isolation and cross-DC lookup. Conversely, if one operates a single huge global service pool, the availability-first design of the Eureka family may be more advantageous for operational stability.

6. Application Cases and Design Examples

First, Kubernetes-native discovery. When a Pod is created, the kubelet reflects the IP of Pods that pass the readiness probe into Endpoints/EndpointSlice, and the Service provides a stable ClusterIP (virtual IP) and the DNS name svc.namespace.svc.cluster.local. A consumer simply calls a service name such as http://order-service, and CoreDNS resolves the name while kube-proxy distributes to the actual Pods. It is the archetype of third-party registration where developers need write no discovery code at all.

Second, Spring Cloud–based client-side discovery. A service registers itself with a Eureka Client, and the consumer selects from the list received from Eureka with Spring Cloud LoadBalancer to call directly. It is well suited to reducing hops and implementing fine-grained selection policies (regional preference, weighting) when many in-house services share the same JVM stack.

Third, service-mesh integration design. A mesh such as Istio or Linkerd reads the platform's discovery (Kubernetes Endpoints) so that the control plane distributes endpoints to each sidecar (Envoy). The application calls only the localhost proxy, and the proxy handles discovery, load balancing, mTLS, and retries. This way, hundreds of multi-language services receive a common discovery and security policy without code changes.

7. Advanced Topics: Recent Trends and Scaling Design

Discovery has recently been evolving in three directions. First, offloading discovery to the sidecar. With the spread of service meshes, discovery, load balancing, and failure handling move to the data plane outside the application, and the trend of achieving language neutrality and policy consistency at once is clear. Second, multi-cluster and multi-cloud discovery. Standardization efforts that bundle services across multiple Kubernetes clusters beyond a single cluster (e.g., the Multi-Cluster Service API) combine with global load balancing, handling failover to another cluster at the discovery layer during a regional failure. Third, refinement of health signals. Beyond a simple alive/dead judgment, it reflects quality signals such as response latency and error rate to distinguish a degraded state and combines with outlier detection that dynamically lowers weight.

From a scaling-design perspective, because a registry's lookup QPS grows as the product of the number of services and consumers, caching is essential. Combining a consumer-side local cache (with a certain TTL) and a server-side response cache, and propagating changes via watch/streaming instead of polling, can lower both lookup load and reflection delay at once. Also, in a large-scale environment, distributing all endpoints to all proxies explodes memory and computation, so scoping — selectively pushing down only the endpoints of services a consumer actually calls — becomes the key to scalability.

8. Considerations and Implications

First, explicit choice of consistency versus availability. Discovery data is often better off preserving availability even if somewhat stale, so the CAP character of the registry and the cache TTL must be designed to match the service's fault-tolerance level. An overly long TTL invites calls to dead instances, and an overly short TTL invites registry overload.

Second, separating the meaning of health checks and adding safeguards. Distinguish liveness, readiness, and startup, and add self-preservation and false-positive buffers so a network partition is not mistaken for an instance failure. Do not trust discovery alone; design consumer-side retry, timeout, and circuit breaker together to absorb failures caused by a stale list.

Third, security and trust boundaries. Because the registry is a sensitive asset holding the entire service topology, authentication/authorization (ACL) for registration and lookup and transport encryption (mTLS) are needed. Verifying the identity of the registering party is key to blocking the threat of a malicious instance registering arbitrarily and hijacking traffic (discovery spoofing), and from a zero-trust perspective there is a trend to extend to service-identity-based authorization.

Fourth, operational observability and vendor lock-in. Registration delay, lookup failures, expiration rate, and list freshness must be collected as metrics to track the health of discovery itself. Also, if discovery logic becomes deeply coupled to a specific registry product, switching costs grow, so it is advantageous in the long run for the application to lower implementation dependency through a discovery abstraction layer (or delegation to platform/mesh).

Fifth, a gradual adoption strategy. When migrating from monolith to microservices, rather than introducing a full mesh from the start, an approach that begins with platform built-in discovery (Kubernetes DNS) and gradually expands to sidecar/mesh as traffic-control and security demands grow is desirable in terms of balancing complexity and benefit.

References


In one line: Service discovery centralizes the frequently changing locations of instances in a registry and propagates them automatically along with health state, so that callers reach a healthy instance using only a logical name — a dynamic location-lookup mechanism.