Files
2nd/10_Wiki/Topics/Coding/Backend_Service_Discovery.md
T
2026-05-09 21:08:02 +09:00

6.8 KiB

id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
id title category status source_trust_level verification_status created_at updated_at tags tech_stack applied_in aliases
backend-service-discovery Service Discovery — DNS / Consul / K8s Coding draft B conceptual 2026-05-09 2026-05-09
backend
service-discovery
vibe-coding
language applicable_to
TS / K8s / Consul
Backend
service discovery
service mesh
DNS-SD
Consul
Eureka
K8s service

Service Discovery

마이크로서비스 = 어떻게 서로 찾지? K8s Service (DNS) / Consul / Eureka / Service Mesh. Client-side vs server-side discovery.

📖 핵심 개념

  • DNS-based: 가장 단순. K8s 가 사용.
  • Server-side: LB 가 routing. AWS ALB.
  • Client-side: client 가 instance 선택. Eureka.
  • Service mesh: sidecar 가 처리. Istio.

💻 코드 패턴

K8s Service (DNS)

apiVersion: v1
kind: Service
metadata:
  name: orders
  namespace: prod
spec:
  selector:
    app: orders
  ports:
    - port: 80
      targetPort: 3000

→ DNS: orders.prod.svc.cluster.local.

// Client
const r = await fetch('http://orders.prod.svc.cluster.local/api/list');
// 또는 same-namespace
const r = await fetch('http://orders/api/list');

→ K8s 가 자동 LB + health check.

Headless service (직접 IP list)

apiVersion: v1
kind: Service
metadata:
  name: redis
spec:
  clusterIP: None  # headless
  selector: { app: redis }
  ports: [{ port: 6379 }]

→ DNS 가 모든 pod IP 반환. Client 가 선택 — Redis cluster 같은 stateful.

Consul

# Consul agent 설치
consul agent -dev
// Service 등록
import Consul from 'consul';
const consul = new Consul();

await consul.agent.service.register({
  name: 'orders',
  id: `orders-${hostname}`,
  address: '10.0.0.5',
  port: 3000,
  check: {
    http: 'http://10.0.0.5:3000/health',
    interval: '10s',
    timeout: '5s',
  },
});

// Service 찾기
const services = await consul.health.service('orders');
const healthy = services.filter(s => s.Checks.every(c => c.Status === 'passing'));
const target = healthy[Math.floor(Math.random() * healthy.length)].Service;

const r = await fetch(`http://${target.Address}:${target.Port}/api/list`);

Consul DNS interface

# Consul 가 자동 DNS server (port 8600)
dig @127.0.0.1 -p 8600 orders.service.consul

# Or 시스템 DNS forwarding 설정 후
dig orders.service.consul

Service Mesh (Istio / Linkerd) discovery

Sidecar proxy 가 자동:
- Service registry
- Health check
- Traffic split
- Retry / circuit breaker

App 코드는 그냥 "http://orders" — mesh 가 routing.

→ 위 DevOps_Service_Mesh_Deep.

AWS ECS / App Runner / ALB

resource "aws_service_discovery_service" "orders" {
  name = "orders"
  
  dns_config {
    namespace_id = aws_service_discovery_private_dns_namespace.main.id
    dns_records {
      ttl = 10
      type = "A"
    }
  }
  
  health_check_custom_config { failure_threshold = 1 }
}

# ECS service
resource "aws_ecs_service" "orders" {
  name = "orders"
  service_registries {
    registry_arn = aws_service_discovery_service.orders.arn
  }
}

orders.acme.local DNS.

Eureka (Netflix, Java/Spring)

// Spring Cloud
@EnableDiscoveryClient
public class App { ... }

// Application.yml
eureka:
  client:
    serviceUrl:
      defaultZone: http://eureka:8761/eureka/

→ Spring 사용자.

Health check

app.get('/healthz', (req, res) => {
  // Liveness — process 살아있나
  res.status(200).end();
});

app.get('/readyz', async (req, res) => {
  // Readiness — 준비됐나 (DB OK, deps OK)
  try {
    await db.query('SELECT 1');
    await redis.ping();
    res.status(200).end();
  } catch {
    res.status(503).end();
  }
});

app.get('/startupz', (req, res) => {
  // Startup — 처음 시작 OK?
  if (initialized) res.status(200).end();
  else res.status(503).end();
});
# K8s
livenessProbe:
  httpGet: { path: /healthz, port: 3000 }
  periodSeconds: 10
  failureThreshold: 3
readinessProbe:
  httpGet: { path: /readyz, port: 3000 }
  periodSeconds: 5
  failureThreshold: 2
startupProbe:
  httpGet: { path: /startupz, port: 3000 }
  failureThreshold: 30
  periodSeconds: 2

패턴: Liveness vs Readiness 차이

Liveness 실패 → pod restart.
Readiness 실패 → traffic 차단 (그러나 살아있음).

Use case:
- DB connection 끊김 → Readiness fail (try reconnect)
- Memory leak / deadlock → Liveness fail (restart)
- 시작 중 (DB migration) → Startup probe

Client-side load balancing (round-robin)

class ServicePool {
  private instances: Instance[] = [];
  private idx = 0;
  
  async refresh() {
    const services = await consul.health.service('orders');
    this.instances = services.filter(s => s.Checks.every(c => c.Status === 'passing'));
  }
  
  next(): Instance {
    if (this.instances.length === 0) throw new Error('no instances');
    const inst = this.instances[this.idx % this.instances.length];
    this.idx++;
    return inst;
  }
}

// 매 30초 refresh
setInterval(() => pool.refresh(), 30000);

gRPC built-in resolver

gRPC = DNS resolver 자동.
Round-robin LB built-in.
xDS protocol (Envoy) 통합.
// gRPC client
const client = new OrderServiceClient('dns:///orders.prod.svc.cluster.local:50051', grpc.credentials.createInsecure(), {
  'grpc.lb_policy_name': 'round_robin',
});

Service Mesh discovery 의 장점

- 자동 mTLS
- 자동 retry / CB
- Traffic split
- Observability built-in
- Multi-cluster discovery

→ Istio / Linkerd / Linkerd / Consul Connect.

External services

# K8s ExternalName service
apiVersion: v1
kind: Service
metadata: { name: stripe }
spec:
  type: ExternalName
  externalName: api.stripe.com

→ App 가 http://stripe 호출.

Dynamic config (env vs DNS)

Env var:    배포 시 정해짐.
DNS:        runtime 변경 가능.

→ 자주 변경 = DNS / discovery.

🤔 의사결정 기준

환경 추천
K8s Service (DNS)
비-K8s + 다중 instance Consul / Eureka
AWS ECS Service Discovery
Service mesh 전체 Istio / Linkerd
단일 service DNS / env var 충분
매우 dynamic xDS / Consul

안티패턴

  • IP hardcode prod: 변경 시 깨짐.
  • DNS TTL 길음 (3600s): stale endpoint. 10-60s.
  • Health check 없음: dead instance 트래픽.
  • Liveness = Readiness 같음: restart 무한.
  • Discovery 의존 + cache 없음: registry 다운 시 모두 dead.
  • Single-zone: AZ 다운 = 모두.
  • Manual scale: K8s HPA / AWS auto-scaling.

🤖 LLM 활용 힌트

  • K8s = Service + DNS 자동.
  • Consul = 비-K8s 표준.
  • Liveness ≠ Readiness.
  • Service mesh = 큰 cluster 의 답.

🔗 관련 문서