✦ For everyone, free.

Practical knowledge for real and everyday life

Home

Kubernetes Trace Observation

Kubernetes Trace Observation tracks requests across microservices, enabling observability and troubleshooting in Kubernetes environments.

Kubernetes Trace Observation is the practice of capturing distributed tracing data that follows a single request as it propagates across multiple pods, services, and namespaces within a cluster, reconstructing the causal chain of operations that produced a given outcome, including timing, dependency structure, and failure points along the path.


Foundations of Distributed Tracing in Kubernetes

Traces, Spans, and Context Propagation

A trace represents the full lifecycle of a request and is composed of one or more spans, each representing a single unit of work such as an HTTP call, a database query, or a message queue publish. Spans carry:

  • A trace_id shared by every span in the same trace
  • A span_id unique to that unit of work
  • A parent_span_id linking it to the operation that triggered it
  • Start and end timestamps, defining duration
  • Attributes describing the operation (HTTP method, status code, pod name, namespace)

Context propagation is the mechanism by which trace_id and span_id travel across process and network boundaries, typically carried in the traceparent HTTP header as defined by the W3C Trace Context specification, ensuring that a span created in one pod correctly links to the span that called it in another.

Where Kubernetes Adds Complexity

Because a single request in a microservice architecture may cross multiple pods, potentially on different nodes, and pass through a service mesh sidecar, an ingress controller, and asynchronous queues, trace observation in Kubernetes depends on every hop in that path being instrumented and propagating context consistently. A single uninstrumented hop breaks the trace, producing orphaned spans that cannot be joined to the parent request.


Instrumentation Approaches

Application-Level Instrumentation

Applications use an OpenTelemetry SDK to create spans explicitly around meaningful operations:

public class OrderService {
  private final Tracer tracer;

  public void processOrder(String orderId) {
    Span span = tracer.spanBuilder("processOrder")
        .setAttribute("order.id", orderId)
        .startSpan();
    try (Scope scope = span.makeCurrent()) {
      // business logic
    } finally {
      span.end();
    }
  }
}

Automatic Instrumentation via Service Mesh

When applications cannot be modified directly, a service mesh sidecar (such as an Envoy proxy injected by Istio) can generate spans for every inbound and outbound HTTP call transparently, without application code changes, though it can only observe network-level detail rather than internal application logic.

Collector-Side Aggregation

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317

processors:
  batch: {}
  tail_sampling:
    policies:
      - name: errors-policy
        type: status_code
        status_code:
          status_codes: [ERROR]
      - name: latency-policy
        type: latency
        latency:
          threshold_ms: 500

exporters:
  otlp:
    endpoint: tempo:4317

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [tail_sampling, batch]
      exporters: [otlp]

Sampling Strategies

Head-Based Sampling

A sampling decision is made at the start of a trace, before its full outcome is known, typically based on a fixed probability. It is cheap to implement but risks discarding traces that would have been interesting, such as ones that eventually error out.

Tail-Based Sampling

A sampling decision is deferred until the entire trace has been collected, allowing rules like "keep all traces containing an error" or "keep all traces slower than 500ms" to be applied with full knowledge of the outcome. This requires buffering complete traces in the collector before the sampling decision, increasing memory and latency cost at the collection layer.

Sampled Traces = Total Traces × Sampling Rate

Querying and Visualizing Traces

Trace Retrieval

Backends such as Jaeger or Grafana Tempo expose query interfaces that retrieve a full trace by trace_id and render it as a waterfall diagram, showing each span's duration nested beneath its parent, making it immediately visible which hop in a request path contributed the most latency.

Kubernetes-Specific Attributes

Effective trace observation in Kubernetes enriches spans with k8s.pod.name, k8s.namespace.name, k8s.node.name, and k8s.deployment.name attributes, allowing an operator to filter traces by which specific pod replica or node handled a given request, which is essential when diagnosing issues isolated to a single misbehaving replica.


Relationship to Metrics and Logs

Trace observation answers "what was the path and timing of this specific request," a question neither aggregate metrics nor unstructured logs can answer alone. Correlated with exemplars from metrics pipelines and trace identifiers embedded in log lines, traces close the loop between "something is slow" (detected via metrics), "here is the specific occurrence" (a sampled trace), and "here is the surrounding context" (correlated logs).

Span: gateway (0-120ms) Span: order-service (10-100ms) Span: db-query (20-90ms)