Automation, Cloud, Containerization and Beyond

NGINX + OpenTelemetry

by Sam

Bring distributed tracing to your infrastructure

This article is also available in french


Navigating microservices without visibility is like sailing the ocean without a compass.

To avoid sinking, precise transaction mapping is essential. This is where the strategic alliance of two giants comes in: OpenTelemetry (OTel) and NGINX. Together, they form the backbone of robust distributed tracing, providing DevOps teams with unprecedented clarity to monitor and debug their applications.

Based on the official OpenTelemetry and NGINX module documentation, this article explores how to orchestrate this synergy. We will review its decisive advantages, limitations, and provide concrete examples to solidify your observability strategy.

Examining the OTel Module for NGINX

OpenTelemetry is an open-source tool that collects traces, metrics, and logs to make your systems transparent. It standardizes observability with APIs, SDKs, and a Collector that integrates with over 40 backends. The NGINX OpenTelemetry module (ngx_otel_module) enables NGINX to generate traces compliant with the W3C standard (notably via traceparent and tracestate headers) and send them via OTLP/gRPC. It doesn't handle direct metrics, but trace attributes (e.g., http.status_code) can do the job.

Why This Combo?

Combining OTel with NGINX is like giving your proxy X-ray glasses. You can follow requests end-to-end, even through a maze of microservices.

The Advantages:

  • Distributed Traces: Visualize the journey of requests through NGINX and backend services.
  • W3C Standard: Ensures interoperability with other OTel-compatible tools.
  • Customization: Add specific data (e.g., latency) for tailored monitoring.
  • Flexible Ecosystem: Export to Jaeger, Prometheus (via transformations), and more.

But beware of the pitfalls: compiling NGINX with the module, configuring a Collector, or managing the overhead of traces without sampling.

Implementation: NGINX and OTel in Action

Let's take a typical case: NGINX relays requests to a backend, and you want to know why some are slow.

Step 1: Install the OTel Module

The module is not included by default. Compile NGINX with:

./configure --with-compat --add-dynamic-module=/path/to/ngx_otel_module
make && make install

Add this to nginx.conf:

load_module modules/ngx_otel_module.so;

Step 2: Configure Tracing

Here is a sample configuration to trace /api requests and export them to an OTel Collector:

http {
    otel_exporter {
        endpoint localhost:4317; # OTel Collector
        batch_size 512;
        batch_count 4;
        interval 5s;
    }

    server {
        listen 80;
        server_name example.com;

        location /api {
            otel_trace on;
            otel_trace_context propagate; # Link with the backend
            otel_span_attr "latency=$request_time"; # Custom latency attribute
            proxy_pass http://backend:8080;
        }
    }
}
  • otel_trace on; enables tracing.
  • otel_trace_context propagate; links NGINX traces to the backend via W3C headers.
  • otel_span_attr captures request latency.
  • Traces are sent in batches to avoid overhead.

Step 3: Deploy an OTel Collector

The Collector processes and exports traces. Example configuration (config.yaml):

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
processors:
  batch:
    timeout: 5s
exporters:
  otlp:
    endpoint: jaeger:4317
service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp]

Traces flow from NGINX to the Collector, then to Jaeger for visualization.

Step 4: Optimize with Sampling

For high-traffic sites, limit tracing to 10% of requests:

http {
    split_clients "${remote_addr}AAA" $otel_trace_on {
        10% on;
        * off;
    }

    server {
        location /api {
            otel_trace $otel_trace_on;
            otel_trace_context propagate;
            proxy_pass http://backend:8080;
        }
    }
}

This reduces load while still collecting relevant data.

Example: Tracking Latency

Your /api endpoint is slow. Using Jaeger, you analyze the traces:

  • NGINX Span: http.status_code=200, latency=0.300s.
  • Backend Span: Latency of 0.250s.

You add otel_span_attr "upstream_time=$upstream_response_time"; and discover NGINX adds 0.050s. Perhaps a configuration or network issue? You adjust and monitor for improvement.

Why Add This Attribute?

Adding upstream_time=$upstream_response_time as a span attribute enriches traces with specific information about the backend server's performance. This allows you to monitor upstream server latency in tracing tools like Jaeger, Zipkin, or Grafana Tempo and correlate this data with other metrics or logs to diagnose problems.

When to Use It?

This combo shines in:

  • Microservices architectures with NGINX as a gateway. (A nod to NGINX users and those using an Ingress NGINX).
  • Scenarios requiring precise performance debugging.
  • Teams wanting a unified observability pipeline.

Conclusion

NGINX and OpenTelemetry form a powerful duo that enhances visibility at strategic points in your infrastructure. With a few configuration lines and a collector, you gain access to insights that usually require significant resources and advanced DevOps skills. This can help you move from blurry to crystal clear when it comes to understanding your requests. Test it with the examples above and empower your NGINX service!

Share twitter/ facebook/ copy link
Success! Your email is updated.
Your link has expired
Success! Check your email for magic link to sign-in.