Zero-Downtime Deployments with Kubernetes
Understand readiness, surge capacity, graceful shutdown, and telemetry without treating rolling updates as an availability guarantee.
Build a usable telemetry model around user-facing SLIs, bounded cardinality, and alerts that have a next step.
A useful observability practice answers three questions during an incident: is the user path broken, where is time going, and what changed. Metrics, logs, and traces are tools for those questions. A wall of graphs is not an operating model.
This guide describes a hypothetical request-driven API with Prometheus-compatible metrics, structured logs, and OpenTelemetry traces. Vendor names are interchangeable. The constraints are not.
Pick signals that follow the user:
An SLO is a target for that SLI over a window. An error budget is the remainder. These are negotiation tools: they tell you whether to ship features or stop and repair. They are not marketing numbers. Do not publish an SLO you do not measure.
Infrastructure gauges (CPU, memory, disk) explain saturation. They should not be the first page-worthy alert unless the host is the product.
Prometheus-style metrics fail when labels are unbounded. user_id, raw URLs, and emails explode series count, scrape time, and query cost.
# Educational Prometheus scrape fragment. Not a production scrape config.
scrape_configs:
- job_name: example-api
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_app]
regex: example-api
action: keepPrefer a small set: service, route (normalized), status_class, method. Put the request id in logs and traces. Recording rules can pre-aggregate SLI numerators and denominators so dashboards do not rescan raw series every thirty seconds.
RED (rate, errors, duration) fits request services. USE (utilization, saturation, errors) fits nodes, queues, and thread pools. Use both; do not pretend one panel explains checkout.
A log line should be parseable. Timestamp, severity, service, trace_id, and a short event name beat a paragraph of prose.
# Educational structured log. Do not log tokens or personal data.
ts=2026-10-12T09:14:02Z level=info service=example-api
trace_id=6b4c2a91 route=/checkout status=503 duration_ms=1842 error=payments_timeoutRetention is a cost and a privacy decision. Debug logs on every replica at information volume will dominate the bill and the leak surface. Sample or gate verbose logs behind an explicit flag.
OpenTelemetry traces connect the incoming request to downstream calls. A span that says the handler waited 1.8s in payments is worth more than a CPU graph that says the pod was fine.
# Educational collector sketch. Exporters and sampling are environment-specific.
receivers:
otlp:
protocols:
http: {}
processors:
batch: {}
tail_sampling:
policies:
- name: errors-and-slow
type: status_code
status_code: {status_codes: [ERROR]}
exporters:
otlp/tempo:
endpoint: tempo.example.svc:4317
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [tail_sampling, batch]
exporters: [otlp/tempo]insecure: true is for a local example, not a production collector. Sample in production. Exporting 100% of spans from a busy edge will hurt the application and the backend. Tail sampling on errors and slow traces is a common pattern; it is a recommendation, not a law.
Never copy trace ids into Prometheus labels.
Page a human when a user-facing SLI is burning budget and someone can act. A runbook should include impact, the first two checks, and a safe mitigation (rollback, shed load, disable a feature). “Look at the dashboard” is not a runbook.
A practical investigation path:
trace_id.Compare the verification window after a deploy to the previous revision. That is how you catch a bad rollout before the error budget is gone.
Logs and traces contain tokens, emails, and internal URLs if you let them. Redact at the source. Restrict who can query raw telemetry. Collector identities should not be able to change production workloads.
The observability stack is itself a dependency. A missing metrics backend should not take down the API, but it will blind responders. Document a backup page path if the chat or paging vendor is down.
Zero-downtime Kubernetes deployments uses the same verification window. Autoscaling explains why CPU utilization is a poor standalone SLI. See Observability and the observability lab.
Understand readiness, surge capacity, graceful shutdown, and telemetry without treating rolling updates as an availability guarantee.
How HPA, resource requests, and cluster capacity interact—and why a target utilization is not a capacity plan.
Isolate blast radius with accounts, identities, state, and promotion of the same artifact—not a shared admin role with three VPC names.