Service Discovery and Configuration Management Questions
Letting services find and configure each other at runtime: service registries, client-side versus server-side discovery, DNS-based discovery, dynamic configuration, feature flags, and secrets distribution. Covers how services stay wired together as instances come and go, and how config changes propagate safely. The connective plumbing of a microservices deployment.
Implement a configuration validator in Python that reads a JSON configuration file, validates required keys and types against a provided JSON Schema (you may assume use of the jsonschema package), and writes an atomic validated output file for services to consume. Discuss handling of defaults, deprecated keys, and helpful validation errors for operators.
Sample Answer
To validate a JSON config against a JSON Schema, I'll use jsonschema for validation, apply defaults from schema where present, warn on deprecated keys, and write the validated output atomically (temp file + os.replace). The script produces operator-friendly errors with context and suggestions.
import json, tempfile, os
from jsonschema import Draft7Validator, validators, exceptions
# extend validator to set defaults
def extend_with_default(validator_class):
validate_props = validator_class.VALIDATORS["properties"]
def set_defaults(validator, properties, instance, schema):
for prop, subschema in properties.items():
if "default" in subschema and prop not in instance:
instance[prop] = subschema["default"]
for error in validate_props(validator, properties, instance, schema):
yield error
return validators.extend(validator_class, {"properties": set_defaults})
DefaultValidatingValidator = extend_with_default(Draft7Validator)
def validate_and_write(input_path, schema_path, out_path, deprecated_keys=None):
deprecated_keys = set(deprecated_keys or [])
with open(input_path) as f:
cfg = json.load(f)
with open(schema_path) as f:
schema = json.load(f)
validator = DefaultValidatingValidator(schema)
errors = sorted(validator.iter_errors(cfg), key=lambda e: e.path)
if errors:
msgs = []
for e in errors:
path = ".".join([str(p) for p in e.absolute_path]) or "<root>"
msgs.append(f"{path}: {e.message}")
raise ValueError("Validation failed:\n" + "\n".join(msgs))
# warn about deprecated keys present
present_deprecated = [k for k in deprecated_keys if k in cfg]
if present_deprecated:
print("WARNING: deprecated keys present:", present_deprecated)
# atomic write
dirn = os.path.dirname(out_path) or "."
fd, tmp = tempfile.mkstemp(dir=dirn, prefix=".tmpcfg")
try:
with os.fdopen(fd, "w") as tf:
json.dump(cfg, tf, indent=2, sort_keys=True)
tf.flush()
os.fsync(tf.fileno())
os.replace(tmp, out_path)
finally:
if os.path.exists(tmp):
os.remove(tmp)
Key points:
- Defaults: applied by extending jsonschema to inject "default" into instance before validation.
- Deprecated keys: reported as warnings so operators know to migrate.
- Helpful errors: report JSON path and message for each error to speed troubleshooting.
- Atomic output: write to temp file, fsync, then os.replace to ensure consumers never see partial files.
Time/space: O(n) to read/serialize config, negligible extra memory. Edge cases: invalid JSON, schema parsing errors, permission errors on write — handle by catching and surfacing clear messages.
Design a global multi-region service discovery and configuration system for an application deployed across 5 regions serving 1M RPS. Requirements: region-local discovery for low latency, automatic regional failover, versioned global config propagation with audit history, and ability to perform region-scoped rollbacks. Discuss DNS TTLs, geo-DNS, control plane replication, and trade-offs between strong and eventual consistency.
Sample Answer
Requirements & constraints:
- 5 regions, 1M RPS total; region-local discovery for low latency; automatic regional failover; global versioned config with audit history; region-scoped rollbacks; SRE-focused observability and operability.
High-level architecture:
- Data plane: region-local service registry (e.g., envoy sidecar + local control-plane cache) per region that handles discovery and config distribution to services.
- Control plane: global config store (primary in multiple control-plane clusters) that provides versioned configs, audit logs, and publishes diffs to regions.
- DNS: geo-DNS (global authoritative DNS with EDNS-client-subnet/geo routing) returns region-local endpoints; short TTLs for failover but balanced with DNS load.
- Health & failover coordinator: global orchestrator monitors region health, triggers geo-DNS updates + control-plane failover, and activates cross-region routing policies.
Components and flow:
- Global config authoring -> stored in a strongly versioned write-ahead store (e.g., CockroachDB or multi-master etcd with global consensus for critical metadata). Every change creates an immutable version and audit entry.
- Control-plane replication: configs push to region control-plane instances via a streaming replication (gRPC/changefeed) that guarantees ordered delivery. Regions keep local copy and apply config versions atomically.
- Data-plane discovery: services query local registry (cached). Registry syncs upstream control-plane and uses local health checks for liveness.
- Failover: health aggregator marks region unhealthy → orchestrator increments region weights in geo-DNS; for immediate cutover uses low TTL (30s–2min) OR leverages global anycast + local failover via client-side fallback to next region.
DNS TTL discussion:
- Short TTL (30–60s) enables fast failover but increases query volume and cache churn; ensure DNS infra can handle peak QPS (use authoritative DNS scaling + CDNs).
- Longer TTL reduces load but delays failover. Hybrid: set TTL=60s for critical services; use very-low TTL (10s) only when actively failing over (or use DNS-based weighted routing plus BGP/anycast).
Consistency trade-offs:
- Strong consistency (consensus-backed writes for config metadata) ensures a single source-of-truth and linearizable versioning — useful for critical configs and rollbacks; costs increased write latency and operational complexity across regions.
- Eventual consistency (async replication) gives low-latency writes and high availability but risks temporary divergence and harder reasoning during rollbacks.
Recommendation: Use hybrid model: - Strong consistency for control metadata (version IDs, audit log, access control) using global consensus.
- Eventual but ordered streaming for bulk config blobs pushed to regions; include version checksums and monotonic version numbers so regions can detect gaps and reconcile.
Region-scoped rollbacks:
- Because versions are immutable, a rollback is publishing a new config that points services to an earlier version ID. Region control-plane can apply rollbacks scoped to a region by applying the version only to that region’s namespace. Implement safe rollout primitives: canary in region, automatic health-based abort, and fast rollback via local version pinning.
Audit/history & safety:
- Immutable audit log (WAL + append-only store) with SHA checksums; include operator, timestamp, diff, and CI/CD job ID.
- Soft- and hard-guards: preflight validation, dry-run apply to simulated region, rate-limited rollouts, and RBAC + signed approvals for production changes.
Operational concerns:
- Observability: per-region metrics (latency, error rates), replication lag alerts, DNS query volume, and health dashboards. Runbook: automatic versus manual failover thresholds.
- Testing: chaos testing for control-plane partitions, DNS outage drills, and rollback exercises.
- Capacity: ensure control-plane scaling and DNS QPS headroom for short TTLs at 1M RPS.
Trade-offs summary:
- Low-latency local discovery needs local caches and eventual replication of configs.
- Strong consistency centralized yields safer global state at cost of latency and availability; eventual replication scales better but requires robust detection/reconciliation, and careful rollback semantics.
- Use hybrid: strong for metadata/audit, eventual-ordered for payloads, versioned immutable artifacts to enable safe regional rollbacks and reliability.
Using a Python etcd v3 client, write a watcher process that subscribes to a prefix such as /config/services/, applies updates to a local JSON config file atomically (write to temp and rename), and recovers gracefully from transient network partitions without losing updates. Describe how to handle compaction of etcd revision history and missed events.
Sample Answer
Approach: use the etcd v3 client to watch a prefix with a restartable loop that persists the last processed revision. On errors (network, timeout) retry with exponential backoff. If etcd signals compaction / missed events, recover by doing a full GetRange on the prefix to rebuild state and continue watching from the latest revision. Apply updates to a local JSON file atomically by writing to a temp file and os.replace.
import etcd3
import json
import os
import tempfile
import time
from backoff import expo # simple exponential backoff generator or implement your own
ETCD_PREFIX = "/config/services/"
LOCAL_PATH = "/etc/myapp/services.json"
RETRY_MAX = 8
client = etcd3.client() # configure host/port/credentials as needed
def atomic_write_json(path, data):
dirn = os.path.dirname(path)
with tempfile.NamedTemporaryFile("w", dir=dirn, delete=False) as tf:
json.dump(data, tf, indent=2, sort_keys=True)
tf.flush()
os.fsync(tf.fileno())
os.replace(tf.name, path) # atomic on POSIX
def build_snapshot():
"""Fetch full state under prefix and return dict and current revision."""
kvs = client.get_prefix(ETCD_PREFIX)
out = {}
latest_rev = 0
for value, meta in kvs:
key = meta.key.decode()
# strip prefix
subkey = key[len(ETCD_PREFIX):]
out[subkey] = json.loads(value.decode())
latest_rev = max(latest_rev, meta.mod_revision)
return out, latest_rev
def apply_event_to_state(state, ev):
"""ev is an etcd watch event: Put or Delete"""
if ev.event_type == "put":
key = ev.key.decode()[len(ETCD_PREFIX):]
state[key] = json.loads(ev.value.decode())
elif ev.event_type == "delete":
key = ev.key.decode()[len(ETCD_PREFIX):]
state.pop(key, None)
def watch_loop():
state, last_rev = build_snapshot()
atomic_write_json(LOCAL_PATH, state)
retry = 0
while True:
try:
# start watching from last_rev + 1 to avoid re-processing last applied
events_iterator, cancel = client.watch_prefix(ETCD_PREFIX, start_revision=last_rev + 1)
for ev in events_iterator:
# ev has attributes: events (list), header.revision
# etcd3-py yields WatchResponse; iterate ev.events
for e in ev.events:
apply_event_to_state(state, e)
# update last_rev from header
last_rev = ev.header.revision
atomic_write_json(LOCAL_PATH, state)
# if watcher exits normally, reset retry
retry = 0
except client.exceptions.RevisionCompactedError:
# missed events due to compaction -> rebuild snapshot from current state
state, last_rev = build_snapshot()
atomic_write_json(LOCAL_PATH, state)
retry = 0
except Exception as exc:
# network partition or transient error: backoff and retry
backoff = min(2 ** retry, 60)
time.sleep(backoff)
retry = min(retry + 1, RETRY_MAX)
continue
if __name__ == "__main__":
watch_loop()
Key points and reasoning:
- Use start_revision to avoid replaying duplicates; persist last applied revision in memory (or on disk for stronger durability).
- Handle compaction by re-scanning the prefix (GetPrefix) and rebuilding state; etcd compacts older revisions so watches starting before compaction will fail.
- Atomic writes via tempfile + os.replace prevent partial reads by consumers.
- Exponential backoff on network errors avoids tight loops; resume watching from the latest known revision to avoid lost updates.
- Consider persisting last_rev to disk to survive process restarts; also add health metrics/alerts if rebuilds/compactions occur frequently (indicates configs changing too fast).
Edge cases: - Large state: streaming snapshot or incremental sync may be needed.
- Conflicting concurrent writers: rely on etcd linearizability and mod_revision ordering.
Design a dynamic configuration service that supports versioned configurations, atomic rollouts, and quick rollback. Requirements: services can query a specific version or subscribe to change notifications, audit history is preserved, and rollbacks across many instances complete within minutes. Sketch APIs, storage approaches, and how to coordinate rollout and rollback safely.
Sample Answer
Requirements summary:
- Versioned configs with immutable history and audit
- Services may read a specific version or subscribe to changes
- Atomic rollouts to many instances and fast rollback (minutes)
- Strong safety and observability
High-level design:
- Central Config Service (CS) + distributed watchers. CS stores immutable config blobs keyed by (service, env, version, stage).
- Storage: write-optimized append-only store (e.g., DynamoDB/CockroachDB for metadata + S3/GCS for blobs). Use MVCC and cryptographic hashes for integrity.
- Audit: every write includes user, timestamp, change-diff, and signed commit ID persisted in audit DB.
APIs (REST/gRPC):
- CreateConfig(service, env, baseVersion, blob) -> versionID, diff, signature
- GetConfig(service, env, versionID|latest) -> blob, meta
- Subscribe(service, env, fromVersion) -> server-sent events / gRPC stream with versions and commit metadata
- StartRollout(service, env, targetVersion, cohortSpec, canarySpec, waitPolicy) -> rolloutID
- Rollback(rolloutID|service,env,targetVersion) -> rollbackID
- QueryAudit(service, env, versionRange) -> changelog
Rollout coordination:
- Rollout controller evaluates cohortSpec (e.g., percentage, zone, k8s labels). It orchestrates via:
- Tagging targetVersion as "pending-rollout" and creating rollout record with desired state
- Using health checks and SLO-driven metrics (latency, error rate) to gate progression automatically
- Controller instructs service meshes/k8s to route cohorts to new config (via Envoy xDS, ConfigMap rollout, or sidecar notification)
- Subscribers get change events and pull specific version; controllers drive gradual traffic shift
Atomicity & fast rollback:
- Atomic seen at client level: clients apply version only after successful local validation; use two-phase apply: PREPARE (download & validate) then ACTIVATE (switch pointer). Use a short-lived local staging and an atomic pointer swap (e.g., update symlink/atomic file rename or update in-memory pointer).
- Rollback: Rollback API sets targetVersion and triggers controllers to reverse cohorts. Because versions are immutable and clients support PREPARE+ACTIVATE, rollback completes quickly (seconds per client) and orchestrator completes all cohorts in minutes.
- To ensure global bound on rollback time, parallelize cohort operations across zones and set per-cohort timeouts. For very large fleets, use hierarchical rollout (region controllers) to coordinate.
Safety & observability:
- Require validation hooks: pre-commit schema validation + optional canary automated tests.
- Integrate with monitoring: rollout emits events, metrics (progress, failures), and exposes SLOs to gate.
- Enforce RBAC and approvals for production rollouts; signed approvals stored in audit.
- Circuit-breaker: if metrics exceed thresholds, automatic immediate rollback to last known good.
Edge cases:
- Divergent clients: clients that ignore subscription must be periodically polled and have grace periods
- Network partitions: controllers honor last-committed activated version; use leases to avoid split-brain
- Large binary blobs: store in object store, keep metadata small in DB
Why this works:
- Immutable versions + PREPARE/ACTIVATE give atomic local switch and safe rollback.
- Orchestrated, metrics-driven rollouts allow automated canaries and rollback within minutes.
- Audit trail and RBAC cover compliance and postmortem needs.
Design a service discovery strategy for a microservices platform deployed in two regions (region-a and region-b). Requirements: local low-latency discovery for most traffic, automatic failover if a whole region fails, minimal cross-region synchronization for config changes, and support for rolling upgrades. Discuss options such as regional registries with eventual replication, geo-DNS, and pros/cons for consistency and failover behavior.
Sample Answer
Requirements:
- Low-latency local discovery for normal traffic
- Automatic failover if an entire region dies
- Minimal cross-region synchronization for config changes
- Support rolling upgrades (zero-downtime)
Recommended design (hybrid regional registry + geo-DNS + health fencing):
High-level:
- Each region runs a full local service registry (e.g., Consul/etcd/istio control plane) used by local clients for discovery (local reads, low latency).
- Registries asynchronously replicate lightweight metadata (service name, instance health, region, version) between regions via an append-only changefeed (eventual replication).
- Global geo-DNS (managed DNS or Route53 latency-based + health checks) maps service FQDN to region-specific endpoints (regional load balancers or anycast IPs). Default: prefer local region; on region outage, failover to remote region by DNS health checks and short TTLs (30–60s).
- Use health fencing: each registry only advertises instances that pass local health checks + a region-wide quorum check to avoid split-brain.
Components & flow:
- Local lookup: service -> query local registry (fast).
- Registry replication: config/service-registration writes are pushed to remote registry asynchronously; conflicts resolved by last-writer-wins or version vectors for config.
- Global routing: clients use DNS to resolve primary region VIP; on DNS failover, requests route to remote region’s endpoints.
- Rolling upgrades: new instances register with version tags; traffic can be shifted via registry tags or gradual DNS weighted failover; health checks ensure only healthy versions promoted.
Pros/cons:
- Regional registries (eventual replication)
- Pros: low latency, local autonomy, limited cross-region traffic, supports rolling upgrades via tags.
- Cons: eventual consistency — short window where a newly-registered instance in region-b may not be known to region-a clients; must handle stale reads.
- Geo-DNS with short TTLs
- Pros: simple global failover, minimal cross-region sync, transparent to clients.
- Cons: TTL-related delay on failover, DNS caching in intermediaries, coarse-grained control (per-FQDN).
- Active-active multi-region with global control plane
- Pros: immediate global view.
- Cons: higher cross-region consistency cost, more risk of cascading failures.
Operational notes (SRE focus):
- Use health checks (application + region heartbeat). Only flip DNS when regional health failing sustained threshold (e.g., 3 consecutive probes * 10s).
- Keep replication payload minimal (service metadata); avoid syncing ephemeral telemetry.
- Monitor replication lag, DNS failover events, split-brain indicators; alert on high lag or dual-region write conflicts.
- Test failover with chaos engineering and rehearse DNS TTL impact; automate rollback and canary cutover during rolling upgrades.
- Provide client libraries that prefer registry + fall back to geo-DNS resolution if local registry unreachable.
Trade-offs summary:
- Prioritize local performance and availability with regional registries + async replication. Accept short windows of inconsistency for much lower latency and limited cross-region traffic. Use geo-DNS for automated failover and orchestration of region-level outages.
Unlock Full Question Bank
Get access to all 44 Service Discovery and Configuration Management interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.