Here's how to configure Nginx with OpenTelemetry (Otel) for traffic monitoring, in cloud VM environments such as AWS, GCP, Azure and Digital Ocean. It also covers troubleshooting common issues encountered during configuration.

Nginx integration

Case 1: Nginx not present in my VM

This section details the steps required to install and configure the Nginx load balancer to successfully instrument incoming HTTP requests.

Pre-requisite:

Steps:

  1. SSH into the VM and navigate to your working directory.

mkdir ~/astra-obs
cd ~/astra-obs
  1. Create nginx.conf file with following content.

load_module modules/ngx_http_js_module.so;
load_module modules/ngx_otel_module.so;

user nginx;
worker_processes auto;

pid /var/run/nginx.pid;
error_log /var/log/nginx/error.log;

events {
      worker_connections 1024;
      # multi_accept on;
}

http {
      # Basic Settings
      sendfile on;
      tcp_nopush on;
      types_hash_max_size 2048;

      include /etc/nginx/mime.types;
      default_type application/octet-stream;

      # SSL Settings
      ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
      ssl_prefer_server_ciphers on;

      # Logging Settings
      access_log /var/log/nginx/access.log;

      # Gzip Settings
      gzip on;

      # Virtual Host Configs
      include /etc/nginx/conf.d/*.conf;
      include /etc/nginx/sites-enabled/*;

      js_import ga_nginx_script.js;
      js_set $req_headers ga_nginx_script.req_headers_json;
      js_set $resp_headers ga_nginx_script.resp_headers_json;
      otel_exporter {
          endpoint  :4317; # OTEL collector's gRPC endpoint

          interval    5s;
          batch_size  512;
          batch_count 4;
      }
          
      server {
          listen 19443 ssl;
          server_name nginx;
          ssl_certificate     /etc/nginx/server.crt;
          ssl_certificate_key /etc/nginx/server.key;

          location / {
            #...
            otel_trace on;
            otel_trace_context propagate;
            otel_span_attr sensor.version $nginx_version;

            otel_span_attr sensor.id ;

            otel_span_attr http.host $host;
            otel_span_attr http.request.headers $req_headers;
            otel_span_attr http.response.headers $resp_headers;
            otel_span_attr http.request.body $request_body;
            
            #...
            #proxy_pass http://backend;
            #...
        }
      }
}
  1. ** Edit the nginx.conf created in previous step to serve the incoming requests by giving the address of backend servers.**

  1. ** Create ga_nginx_script.js file with following content.**

function req_headers_json(r) {
     return JSON.stringify(r.headersIn);
}

function resp_headers_json(r) {
     return JSON.stringify(r.headersOut);
}

export default { req_headers_json, resp_headers_json };
  1. ** Create docker-compose.yaml file with following content.**

version: '3.7'

services:

 otel-collector:
   image: nginx:otel
   volumes:
     - /path/to/server.crt:/etc/nginx/server.crt
     - /path/to/server.key:/etc/nginx/server.key
     - ~/astra-obs/nginx.conf:/etc/nginx/nginx.conf
     - ~/astra-obs/ga_nginx_script.js:/etc/nginx/ga_nginx_script.js
   network_mode: host
   restart: always
  1. ** Start the nginx container by executing sudo docker compose up -d from working directory.**

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 29f430a5ac1f nginx:otel "/docker-entrypoint.…" About a minute ago Up About a minute nginx-lb-container

  1. ** To stop the nginx container executing sudo docker compose down from working directory.**

Case 2: Nginx already present in my VM

  1. **Take backup of current nginx configuration. Usually found in /etc/nginx/nginx.conf **

||| It's essential to inject custom configuration into nginx config to have nginx successfully instrument the incoming request and response. Although these customization doesn't alter the functionality of nginx, we strongly recommend to take the backup of existing nginx configuration

  1. Register nginx repository in your VM by following this . After registering, run following to install required modules

  1. Create ga_nginx_script.js file with following content under /etc/nginx/

function req_headers_json(r) {
     return JSON.stringify(r.headersIn);
 }

 function resp_headers_json(r) {
     return JSON.stringify(r.headersOut);
 }

 export default { req_headers_json, resp_headers_json };
  1. Edit the /etc/nginx/nginx.conf file with following content to instrument the incoming requests.

load_module modules/ngx_http_js_module.so;
load_module modules/ngx_otel_module.so;

user  nginx;
#...
#...events{}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    #...
    js_import ga_nginx_script.js;
    js_set $req_headers ga_nginx_script.req_headers_json;
    js_set $resp_headers ga_nginx_script.resp_headers_json;
    otel_exporter {
        endpoint  :4317; # OTEL collector's gRPC endpoint

        interval    5s;
        batch_size  512;
        batch_count 4;
    }
    #...
    server {
        #...

        location / {
            #...
            otel_trace on;
            otel_trace_context propagate;
            otel_span_attr sensor.version $nginx_version;

            otel_span_attr sensor.id ;

            otel_span_attr http.host $host;
            otel_span_attr http.request.body $request_body;
            otel_span_attr http.request.headers $req_headers;
            otel_span_attr http.response.headers $resp_headers;
            #...
            #proxy_pass http://backend;
            #...
        }
    }
}
  1. Restart nginx

systemctl restart nginx

Troubleshooting

  1. **Unable to send trace from nginx to traffic collector **

Symptoms

[Error] File: /tmp/build/opentelemetry-cpp/exporters/otlp/src/otlp_grpc_exporter.cc:66 [OTLP TRACE GRPC Exporter] Export() failed with status_code: "UNAVAILABLE" error_message: "DNS resolution failed for ...

Cause

otel_exporter {
  endpoint  :4317
  ...
}
  1. Unable to see entries in inventory

Symptoms

FAQ (Frequently Asked Questions)

  1. Can I see what trace are sent from my environment?

Yes, one can see the traces sent by traffic-collector by inspecting docker logs. Run docker logs astra-traffic-collector

  1. How to get IP Address Of Traffic Collector in Non Kubernetes environment?

As container network is the same network as host network, the IP of the container would be the same as the Virtual Machine IP.