Service Discovery Patterns
debt(d8/e6/b7/t6)
Closest to 'silent in production until users hit it' (d9), slightly better at d8 because misconfigured readiness probes or DNS caching only manifest during rolling deployments or scaling events — no linter or SAST catches 'you forgot to configure a readiness probe' or 'your PHP-FPM worker is caching DNS'. Kubernetes manifest linters can flag missing probes but not their correctness.
Closest to 'cross-cutting refactor across the codebase' (e7), adjusted down to e6 because while the quick_fix (use service names not IPs) is mechanical, fixing the systemic issues — adding retries with backoff, configuring DNS TTL across PHP-FPM workers, switching service types — touches deployment manifests, client code, and infrastructure config across multiple components.
Closest to 'strong gravitational pull' (b7) — service discovery choice shapes every inter-service call, deployment strategy, health-check design, and failure-handling pattern. Switching from client-side (Consul) to server-side (Kubernetes Services) or vice versa is a major undertaking that affects all callers.
Closest to 'serious trap' (t7), adjusted to t6 because the misconception is explicit: developers assume Kubernetes 'handles it' and skip readiness probes, leading to traffic hitting unready pods. The behavior contradicts the naive mental model where 'Service exists = traffic routes correctly', but it's a well-documented gotcha most teams hit once.
Also Known As
TL;DR
Explanation
In a dynamic microservices environment, service instances start, stop, and move constantly — hardcoded IP addresses are untenable. Service discovery solves this with two main patterns. Client-side discovery: the client queries a service registry (Consul, etcd, ZooKeeper) directly and load-balances among returned instances. Server-side discovery: the client sends requests to a load balancer or API gateway that queries the registry internally — the client is unaware of the registry. Service registries can be self-registration (each service registers itself on startup and deregisters on shutdown) or third-party registration (an external system like Kubernetes watches for containers and updates the registry). DNS-based discovery (Kubernetes Services, AWS Route 53) is a common simplification.
Common Misconception
Why It Matters
Common Mistakes
- Not configuring readiness probes — pods receive traffic before they are ready, causing request failures during deployments.
- Using NodePort services for internal communication — NodePort exposes on every node IP; use ClusterIP for internal traffic.
- Ignoring DNS caching in long-running PHP processes — PHP-FPM workers cache DNS resolutions; configure dns_cache_ttl or use a service mesh for real-time discovery.
- Not handling connection refused gracefully during rolling updates — implement retries with exponential backoff when calling other services.
- Treating service discovery as a single mechanism — client-side (Eureka, Consul) vs server-side (load balancer) have different failure modes; know which one you are using.
Code Examples
# ❌ Hardcoded IP — breaks on every pod restart
# config.php
define('PAYMENTS_URL', 'http://10.0.2.15:8080');
# ❌ Using pod IP from kubectl get pods — ephemeral
curl http://$(kubectl get pod payments-xxx -o jsonpath='{.status.podIP}'):8080/health
# ✅ Kubernetes Service DNS — stable, load-balanced
# config.php
define('PAYMENTS_URL', 'http://payments-service:8080');
// Resolves to the ClusterIP of the payments Service
// Kubernetes kube-proxy load-balances across healthy pods
# ✅ Kubernetes Service manifest
apiVersion: v1
kind: Service
metadata:
name: payments-service
spec:
selector:
app: payments # Matches pods with this label
ports:
- port: 8080
targetPort: 8080
# ✅ With readiness probe — pod only registered when ready
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10