How to monitor applications in Kubernetes
Observability is like the instrument flight deck and flight data recorder of a modern commercial airliner:
🟢 Junior Level
30-Second Summary
Comprehensive Observability in Kubernetes is anchored on the “Three Pillars of Observability”:
- Metrics (What is happening, and when, quantified in numbers?): Numerical time-series telemetry (CPU/memory utilization, request throughput RPS, p99 latency, HTTP 5xx error rates). The industry standard stack is Prometheus + Grafana.
- Logs (Why did it happen?): Textual event streams emitted to container
stdout/stderr. In Kubernetes, log agents (Promtail, Fluent Bit, Vector) deployed as DaemonSets scrape and ship logs to centralized datastores (Grafana Loki, Elasticsearch / OpenSearch). - Distributed Tracing (Where was the request delayed?): End-to-end request path propagation across distributed microservice hops with granular span timings (OpenTelemetry, Jaeger, Grafana Tempo).
Real-World Analogy
Observability is like the instrument flight deck and flight data recorder of a modern commercial airliner:
- Metrics: The speedometer, altimeter, engine tachometer, and fuel gauges. The flight crew views aggregate aircraft status at a glance on electronic screens (Grafana).
- Logs: The flight data and cockpit voice recorder (“Black Box”), storing a timestamped transcript of every crew command and system alarm.
- Tracing: An airline passenger luggage tracking tag: check-in $\to$ conveyor sorting $\to$ cargo hold loading $\to$ transit transfer $\to$ baggage claim (pinpointing the precise transit airport where a bag encountered delays).
Quick Start: Spring Boot 3 + Micrometer + Prometheus
In modern enterprise Java applications, telemetry exposure requires two dependencies:
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
# application.yaml
management:
endpoints:
web:
exposure:
include: health,info,prometheus # Expose /actuator/prometheus endpoint
metrics:
export:
prometheus:
enabled: true
🟡 Middle Level
Architecture of kube-prometheus-stack (The Pull Model)
[ Physical Worker Nodes ] [ Kubelet / Cgroups ] [ K8s API Server ] [ Java Spring Boot Pods ]
│ │ │ │
Node Exporter cAdvisor kube-state-metrics Micrometer
(OS, CPU, RAM, Disk I/O) (Container CPU/Memory) (Pod phases, crash counts) (/actuator/prometheus)
▲ ▲ ▲ ▲
└───────────────────────────┼─────────────────────────┼──────────────────────────┘
│ (HTTP GET Scrape every 15–30 seconds)
[ Prometheus Server ] ──► Time-Series Database (TSDB)
│
┌────────────────┴────────────────┐
▼ ▼
[ Grafana Dashboards ] [ Alertmanager (Slack / PagerDuty) ]
- Prometheus Pull Model: Prometheus actively polls (scrapes) discovered HTTP metric endpoints every 15–30 seconds via dynamic Kubernetes Service Discovery.
- Specialized Metric Collectors:
Node Exporter(DaemonSet): Host hardware metrics (physical memory, network bandwidth, disk I/O).cAdvisor(built intokubelet): Container-level resource consumption directly from Linux cgroups.kube-state-metrics(Deployment): Kubernetes object states (unready replicas, container restart counts, PVC storage capacities).
Declarative Monitoring via ServiceMonitor (Prometheus Operator)
Modern Kubernetes clusters eliminate manual prometheus.yml edits by defining declarative ServiceMonitor Custom Resources:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: payment-monitor
namespace: billing
labels:
release: prometheus # Bound to Prometheus instance selector
spec:
selector:
matchLabels:
app: payment-service
endpoints:
- port: http-metrics
path: /actuator/prometheus
interval: 15s
Top 5 PromQL Queries for Java Engineers
# 1. JVM Heap Memory Utilization Percentage:
(jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"}) * 100
# 2. JVM Garbage Collection Pause Duration Rate (Stop-The-World Time):
rate(jvm_gc_pause_seconds_sum[5m])
# 3. HTTP Request Throughput per URI (RPS):
sum(rate(http_server_requests_seconds_count[1m])) by (uri)
# 4. HTTP 5xx Error Rate Percentage:
sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m]))
/ sum(rate(http_server_requests_seconds_count[5m])) * 100
# 5. API Response Time 99th Percentile (Latency p99):
histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket[5m])) by (le))
🔴 Senior Level
The Cardinality Explosion Catastrophe
The most frequent cause of Prometheus production outages in high-throughput microservices is High Cardinality:
- Theory: In Prometheus TSDB, every unique combination of
label=valuepairs provisions a completely separate, dedicated time-series stream in memory and on disk. - The Fatal Developer Anti-Pattern:
// ❌ DISASTROUS ANTI-PATTERN: Emitting unique transaction/user identifiers as metric labels meterRegistry.counter("orders.created", "userId", order.getUserId(), // 10,000,000 unique values "orderId", order.getOrderId() // 50,000,000 unique values ).increment(); - Consequences: Within hours, the time-series count surges from 200,000 to 50,000,000. Prometheus memory usage spirals, triggering
OOMKilled(Exit Code 137). Upon restarting, the daemon hangs indefinitely attempting to replay its bloated Write-Ahead Log (WAL). - Architectural Rule: Metric labels must adhere to Low Cardinality: fixed HTTP methods (
GET/POST), status groups (200/500), or error categories (TimeoutException/ValidationError). Dynamic entity identifiers (userId,orderId) must be emitted strictly to Application Logs or Tracing Spans!
Long-Term Retention and Scaling: Thanos and VictoriaMetrics
Prometheus TSDB is architecturally optimized for local, short-term SSD storage (15–30 days) and lacks native horizontal clustering. Enterprise environments scale Prometheus horizontally using distributed extensions:
- Thanos: Deploys a
thanos-sidecaradjacent to each Prometheus pod. The sidecar uploads historical TSDB metric blocks to inexpensive object storage (AWS S3, Google Cloud Storage, Ceph).Thanos Querierfederates multi-cluster queries over 3–5 years of data with automated downsampling (5m/1h resolution rollups). - VictoriaMetrics: A high-performance, cost-efficient drop-in replacement for Prometheus TSDB that provides native PromQL support, 4–7x lower RAM consumption, and transparent long-term storage clustering.
Managing OpenTelemetry Tracing Overhead in High-Throughput Systems
Tracing every transaction in a 50,000 RPS microservice topology produces immense CPU serialization overhead and floods cluster networks with terabytes of telemetry.
The Solution — Tail-Based Sampling:
- Microservices forward 100% of raw spans to an OpenTelemetry Collector cluster via OTLP.
- The collector temporarily buffers full trace lifecycles in memory.
- If a trace finishes with
HTTP 200 OKin 15 ms, the collector discards it (drops standard traffic). - If any span inside the trace records
HTTP 500or exceeds a 1-second latency threshold, the collector persists the complete trace (100% anomaly capture).
4 Tricky Questions
1. Why does Prometheus rely on a Pull model by default, and how do you monitor short-lived batch jobs (CronJobs, Serverless functions) that finish before the scrape interval?
Answer:
- Why Pull: The pull model allows the monitoring server to control metric ingestion rates (Rate Limiting), preventing monitoring infrastructure from being overwhelmed during application traffic spikes. Furthermore, if an application stops responding to scrape polls, Prometheus immediately flags
up == 0, providing instant out-of-the-box health checks. - Handling Short-Lived Jobs: If a batch container launches, completes execution in 3 seconds, and terminates, a 15-second scrape loop will never discover or scrape it.
- Solution — Prometheus Pushgateway:
- The short-lived batch job pushes its calculated metrics to an intermediary Pushgateway service via HTTP POST prior to process exit.
- The Pushgateway retains the metrics in memory.
- Prometheus scrapes the Pushgateway on its normal 15-second pull schedule.
2. What is the fundamental architectural difference between cAdvisor and kube-state-metrics, and which tool reveals why a Pod crashed with CrashLoopBackOff?
Answer:
cAdvisor(Container Advisor): Gathers physical runtime resource usage telemetry from Linux kernel cgroups (actual CPU nanoseconds, resident memory RSS bytes, network interface packets, disk I/O). It has no awareness of Kubernetes manifests, labels, or controller states.kube-state-metrics: Watches thekube-apiservervia informers and transforms the declarative state of Kubernetes objects into Prometheus metrics.- Only
kube-state-metricscan expose why a Pod is failing, emitting metrics such askube_pod_container_status_waiting_reason{reason="CrashLoopBackOff"}alongsidekube_pod_container_status_last_terminated_reason{exit_code="137"}.cAdvisorcannot provide the exit status or crash reason.
3. How can HotSpot JVM Garbage Collection Stop-The-World (STW) pauses distort in-application latency metrics, and how is this discrepancy detected?
Answer:
When an application computes latency using Micrometer code annotations (@Timed) or filter timers:
- During a Full GC pause, all application threads are suspended at JVM safepoints, including the internal application timer threads.
- If an incoming HTTP packet reaches the node network interface during a 2-second STW pause, the client experiences a 2010 ms response time. However, the in-application filter only begins timing after the GC pause clears, falsely recording a “clean” execution time of 10 ms!
- Detection: Correlate internal application latency metrics against external edge measurements recorded by the Ingress Controller (NGINX
$request_time) or a Service Mesh (Envoy). A large delta between Ingress Latency and Application Latency indicates severe GC STW pauses or TCP socket connection queue saturation.
4. How is distributed trace context propagated across asynchronous boundaries in Java (CompletableFuture, Spring WebFlux, Virtual Threads)?
Answer:
Legacy tracing frameworks relied on ThreadLocal storage to hold the active TraceId and SpanId:
- When switching execution to asynchronous worker pools (
CompletableFuture.supplyAsync, Project Reactor / WebFlux), the operating system thread changes, causingThreadLocalcontext to be lost, breaking distributed traces into detached fragments. - Modern Architectural Solutions:
- Reactive Programming (Spring WebFlux): Context is propagated via Reactor’s immutable subscriber context (
reactor.util.context.Context), flowing upstream through operators. - OpenTelemetry Java Agent: Bytecode instrumentation (
-javaagent:opentelemetry-javaagent.jar) interceptsExecutor,ForkJoinPool, andCallablefactories, automatically capturing and attaching activeContextinstances across thread transitions. - Java 21 Virtual Threads: High-scale virtual threads preserve execution boundaries cleanly using Java 21 Scoped Values (JEP 446 / JEP 481) or optimized inheritable thread contexts.
- Reactive Programming (Spring WebFlux): Context is propagated via Reactor’s immutable subscriber context (
🎯 Interview Cheat Sheet
Core Kubernetes Monitoring Principles
- Three Pillars of Observability: Metrics (Prometheus), Logs (Loki / ELK), Traces (OpenTelemetry / Jaeger).
- Prometheus Standard: Pull-based metric polling (every 15–30s) targeting
/actuator/prometheus. - Infrastructure Metric Architecture:
Node Exporter$\to$ Physical node hardware stats.cAdvisor$\to$ Container cgroup resource consumption.kube-state-metrics$\to$ Kubernetes API object lifecycle states and restart codes.
- Top Enemy — Cardinality Explosion: Never write dynamic IDs (
userId,orderId, UUIDs) to Prometheus metric labels. - Long-Term Scaling: Use Thanos (S3 storage offload + downsampling) or VictoriaMetrics for multi-year telemetry retention.
- SRE Golden Signals: Latency (p95/p99), Traffic (RPS), Errors (HTTP 5xx), Saturation (CPU/RAM).
Observability Tooling by Domain
| Telemetry Type | Standard Tools | Transport / Protocol | Primary Purpose | | :— | :— | :— | :— | | Metrics | Prometheus, VictoriaMetrics | PromQL / HTTP Pull | Real-time dashboards, alerting, capacity planning | | Logs | Grafana Loki, Fluent Bit, Vector | LogQL / HTTP Push | Error debugging, exception stack traces, auditing | | Traces | OpenTelemetry, Jaeger, Tempo | OTLP / W3C TraceContext | Pinpointing latency bottlenecks in microservice call chains |
Red Flags (What NOT to Say)
- ❌ “We include
user_idas a label in Prometheus to track individual user activity.” — Guarantees a Cardinality Explosion and crashes the monitoring server. - ❌ “Prometheus gathers application error logs from stdout.” — Prometheus processes time-series numbers only; log shipping requires Loki or ELK.
- ❌ “We retain 3 years of raw metrics in standard Prometheus TSDB on the worker node SSD.” — Standard TSDB is not horizontally scalable; multi-year retention requires Thanos or VictoriaMetrics.
Related Topics
- Why are health checks needed — Health check probe configuration
- How does scaling work in Kubernetes — Metrics-driven autoscaling
- What is Kubernetes and Why is It Needed — Cluster architecture
- What is Pod in Kubernetes — Container cgroup resource limits