本文介绍如何使用 OpenTelemetry 对 Nginx 进行链路追踪。 Nginx 是一个高性能、轻量级的开源 Web 服务器和反向代理服务器,支持通过模块以扩展其功能。Nginx OTel 模块支持采集 Nginx 的调用链并转发上报至可观测链路 OpenTelemetry 版。
使用限制
Nginx OTel 模块目前仅支持 gRPC 方式上报,不支持 HTTP 方式上报。
Nginx OTel 模块版本说明:
≥ 0.1.2:支持将 Nginx OTel 采集到的 Nginx 调用链直接转发上报至可观测链路 OpenTelemetry 版。
≤ 0.1.1:Nginx OTel 模块不支持设置 gRPC 鉴权 Token,因此您需要部署 OpenTelemetry Collector。将 Nginx OTel 采集到的 Nginx 调用链通过 OpenTelemetry Collector 转发上报至可观测链路 OpenTelemetry 版。详细步骤,请参见三、(可选)部署 OpenTelemetry Collector。
前提条件
接入步骤
一、下载 Nginx OTel 模块
方式一:系统包管理器安装
Alibaba Cloud Linux、RedHat、RHEL 及其衍生版本
sudo yum install nginx-module-otelDebian、Ubuntu 及其衍生版本
sudo apt install nginx-module-otel
方式二:使用预构建Docker镜像
Nginx 1.27.5 版本及以上默认采用的是 0.1.2 版本的 Nginx OTel 模块。
前往Docker官网,搜索并拉取带有-otel后缀的Nginx镜像(如nginx:1.23.3-otel),通过标准容器启动流程部署服务。
Supported tags and respective Dockerfile links
- 1.27.4, mainline, 1, 1.27, latest, 1.27.4-bookworm, mainline-bookworm, 1-bookworm, 1.27-bookworm, bookworm
- 1.27.4-perl, mainline-perl, 1-perl, 1.27-perl, perl, 1.27.4-bookworm-perl, mainline-bookworm-perl, 1-bookworm-perl, 1.27-bookworm-perl, bookworm-perl
- 1.27.4-otel, mainline-otel, 1-otel, 1.27-otel, otel, 1.27.4-bookworm-otel, mainline-bookworm-otel, 1-bookworm-otel, 1.27-bookworm-otel, bookworm-otel
- 1.27.4-alpine, mainline-alpine, 1-alpine, 1.27-alpine, alpine, 1.27.4-alpine3.21, mainline-alpine3.21, 1-alpine3.21, 1.27-alpine3.21, alpine3.21
- 1.27.4-alpine-perl, mainline-alpine-perl, 1-alpine-perl, 1.27-alpine-perl, alpine-perl, 1.27.4-alpine3.21-perl, mainline-alpine3.21-perl, 1-alpine3.21-perl, 1.27-alpine3.21-perl, alpine3.21-perl
- 1.27.4-alpine-slim, mainline-alpine-slim, 1-alpine-slim, 1.27-alpine-slim, alpine-slim, 1.27.4-alpine3.21-slim, mainline-alpine3.21-slim, 1-alpine3.21-slim, 1.27-alpine3.21-slim, alpine3.21-slim二、启用 Nginx OTel 模块
若要为 Nginx 启用链路追踪,您需要在 Nginx 主配置文件/etc/nginx/nginx.conf中加载 OTel 模块并添加配置项。关于 OTel 模块的更多参数配置信息,请参见ngx_otel_module 模块文档。
全局配置:为所有 HTTP 请求开启链路追踪。
Nginx OTel 模块版本 ≥ 0.1.2
load_module modules/ngx_otel_module.so; # 加载 ngx_otel_module ... http { ... otel_exporter { endpoint "${GRPC_ENDPOINT}"; # 前提条件中获取的 gRPC 接入点 header Authentication "${GRPC_TOKEN}"; # 前提条件中获取的鉴权 Token } otel_trace on; # 开启链路追踪 otel_service_name ${SERVICE_NAME}; # 应用名 otel_trace_context propagate; # 向下游服务注入Trace上下文 ... }Nginx OTel 模块版本 ≤ 0.1.1
说明请替换配置中的两个变量:
${OTEL_COLLECTOR_GRPC_RECEIVER_ENDPOINT}:OpenTelemetry Collector gRPC 上报点。此上报点为您部署的 OpenTelemetry Collector 的 gRPC 上报点,例如localhost:4317,而不是前提条件中获取的可观测链路 OpenTelemetry 版的接入点。${SERVICE_NAME:应用名称。此名称将会作为 Nginx 的应用名称在可观测链路 OpenTelemetry 版控制台中展示。
load_module modules/ngx_otel_module.so; # 加载 ngx_otel_module ... http { ... otel_exporter { endpoint ${OTEL_COLLECTOR_GRPC_RECEIVER_ENDPOINT}; # OpenTelemetry Collector 的上报地址,例如 localhost:4317 } otel_trace on; # 开启链路追踪 otel_service_name ${SERVICE_NAME}; # 应用名 otel_trace_context propagate; # 向下游服务注入Trace上下文 ... }单个 Location 配置:为单个 Location 开启链路追踪。
Nginx OTel 模块版本 ≥ 0.1.2
load_module modules/ngx_otel_module.so; # 加载 ngx_otel_module ... http { otel_exporter { endpoint "${GRPC_ENDPOINT}"; # 前提条件中获取的 gRPC 接入点 header Authentication "${GRPC_TOKEN}"; # 前提条件中获取的鉴权 Token } server { listen 127.0.0.1:80; location /hello { otel_trace on; # 只为 127.0.0.1:80/hello 开启链路追踪 otel_service_name ${SERVICE_NAME} # 应用名 otel_trace_context propagate; # 向下游服务注入Trace上下文 ... } } }Nginx OTel 模块版本 ≤ 0.1.1
说明请替换配置中的两个变量:
${OTEL_COLLECTOR_GRPC_RECEIVER_ENDPOINT}:OpenTelemetry Collector gRPC 上报点。此上报点为您部署的 OpenTelemetry Collector 的 gRPC 上报点,例如localhost:4317,而不是前提条件中获取的可观测链路 OpenTelemetry 版的接入点。${SERVICE_NAME:应用名称。此名称将会作为 Nginx 的应用名称在可观测链路 OpenTelemetry 版控制台中展示。
load_module modules/ngx_otel_module.so; # 加载 ngx_otel_module ... http { otel_exporter { endpoint ${OTEL_COLLECTOR_GRPC_RECEIVER_ENDPOINT}; # OpenTelemetry Collector 的上报地址,例如 localhost:4317 } server { listen 127.0.0.1:80; location /hello { otel_trace on; # 只为 127.0.0.1:80/hello 开启链路追踪 otel_service_name ${SERVICE_NAME} # 应用名 otel_trace_context propagate; # 向下游服务注入Trace上下文 ... } } }
三、(可选)部署 OpenTelemetry Collector
Nginx OTel 模块版本 ≥ 0.1.2时,无需部署OpenTelemetry Collector。
Nginx OTel 模块版本 ≤ 0.1.1时,需要部署OpenTelemetry Collector。
方式一:通过ACK控制台部署
登录容器服务管理控制台,在左侧导航栏选择。
在应用市场中搜索并单击
opentelemetry-collector,单击页面右上角一键部署。在创建页面选择需要部署
opentelemetry-collector的集群,然后单击下一步。在参数配置页面,增加如下参数,然后单击确定。
说明请将
${GRPC_ENDPOINT}和${GRPC_ENDPOINT_TOKEN}替换为前提条件中获取的 gRPC 接入点和鉴权 Token。receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 exporters: otlp: endpoint: ${GRPC_ENDPOINT} tls: insecure: true headers: "Authentication": "${GRPC_ENDPOINT_TOKEN}" processors: batch: service: pipelines: traces: receivers: [otlp] processors: [batch] exporters: [otlp]
方式二:手动部署
下文以 Docker 方式为例,介绍如何部署 OpenTelemetry Collector。更多部署方式,请参见下载并部署OpenTelemetry Collector。
创建
opentelemetry-config.yaml文件,并将下面的内容拷贝至文件。该文件用于定义和配置OpenTelemetry Collector的行为和功能,包括如何接收、处理和导出数据。
说明请将
${GRPC_ENDPOINT}和${GRPC_ENDPOINT_TOKEN}替换为前提条件中获取的 gRPC 接入点和鉴权 Token。receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 exporters: otlp: endpoint: ${GRPC_ENDPOINT} tls: insecure: true headers: "Authentication": "${GRPC_ENDPOINT_TOKEN}" processors: batch: service: pipelines: traces: receivers: [otlp] processors: [batch] exporters: [otlp]启动 OpenTelemetry Collector。
docker run -v $(pwd)/opentelemetry-config.yaml:/etc/otelcol-contrib/config.yaml otel/opentelemetry-collector-contrib:0.105.0
四、查看 Nginx 调用链
完成以上配置并重启 Nginx 后,您可向 Nginx 发送请求,以生成调用链。然后登录可观测链路 OpenTelemetry 版控制台,查看由 OpenTelemetry 生成的 Nginx 调用链。
在应用列表页面查看 Nginx 应用。
在调用链分析页面打开 Nginx 的调用链,查看请求耗时、客户端 IP、 HTTP 状态码等信息。如果您的后端服务也接入了可观测链路 OpenTelemetry 版,您将会看到串联在一起的 Nginx 与后端服务调用链。
在Trace 详情页面,Span 瀑布图展示了请求经过 nginx 和 Node.js 后端服务(ot-node-demo)的完整调用链:首先由 nginx 接收
/hello请求(总耗时 6ms,HTTP 200),然后转发至 ot-node-demo 的GET /hello(耗时 5.76ms),其下包含middleware-query、middleware-expressInit、request handler-/hello三个内部 Span。右侧 Span 详情面板显示 nginx 应用的属性信息,包括http.status_code=200、http.route=/hello、http.user_agent=curl/7.61.1等。
实践教程
接下来将通过案例演示如何采集 Nginx 及后端应用的调用链,并上报至可观测链路 OpenTelemetry 版。
一、准备工作
确认已安装 Git、Docker 和 Docker Compose。
二、项目结构
nginx-otel-demo
│
├── docker-compose.yml # Docker Compose 配置文件
│
├── nginx_conf/ # nginx 配置文件
│ ├── default.conf
│ └── nginx.conf
│
├── otel_conf/ # OpenTelemetry Collector 配置文件
│ └── config.yaml
│
└── backend/ # Node.js 后端服务
├── Dockerfile
├── main.js
├── package.json
└── package-lock.json创建文件夹:
mkdir nginx-otel-demo && cd nginx-otel-demo
mkdir -p nginx_conf otel_conf backend三、创建 Nginx 配置
创建 Nginx 主配置文件
nginx.conf。Nginx OTel 模块版本 ≥ 0.1.2
cat << 'EOF' > nginx_conf/nginx.conf load_module modules/ngx_otel_module.so; user nginx; worker_processes auto; error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; otel_exporter { endpoint "${GRPC_ENDPOINT}"; header Authentication "${GRPC_TOKEN}"; } otel_trace on; otel_trace_context propagate; otel_service_name nginx; include /etc/nginx/conf.d/*.conf; } EOFNginx OTel 模块版本 ≤ 0.1.1
load_module modules/ngx_otel_module.so; user nginx; worker_processes auto; error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; otel_exporter { endpoint otel-collector:4317; } otel_trace on; otel_trace_context propagate; otel_service_name nginx; include /etc/nginx/conf.d/*.conf; } EOF创建 Nginx 配置文件
default.conf。cat << 'EOF' > nginx_conf/default.conf server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html index.htm; } # 在这里添加 location /hello { proxy_pass http://backend-api:7001/hello; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } EOF
四、(可选)创建 OpenTelemetry Collector 配置
Nginx OTel 模块版本 ≥ 0.1.2时,无需部署OpenTelemetry Collector。
Nginx OTel 模块版本 ≤ 0.1.1时,需要部署OpenTelemetry Collector。
创建 OpenTelemetry Collector 配置文件,该配置文件定义了数据如何被接收、处理和上报。
请将${GRPC_ENDPOINT}和${GRPC_ENDPOINT_TOKEN}替换为前提条件中获取的 gRPC 接入点和鉴权 Token。
cat << 'EOF' > otel_conf/config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
exporters:
otlp:
endpoint: ${GRPC_ENDPOINT}
tls:
insecure: true
headers:
"Authentication": "${GRPC_ENDPOINT_TOKEN}"
processors:
batch:
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp]
EOF五、创建后端服务(Node.js)
创建 package.json 文件。文件描述了项目的配置信息,包括项目名称、版本、依赖项等。
cat << 'EOF' > backend/package.json { "name": "backend", "version": "1.0.0", "main": "index.js", "scripts": {}, "keywords": [], "author": "", "license": "ISC", "description": "", "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/auto-instrumentations-node": "^0.52.0", "axios": "^1.7.7", "express": "^4.21.1" } } EOF创建 main.js 文件,包含一个基本的 Express 框架 Web 应用程序。
cat << 'EOF' > backend/main.js "use strict"; const axios = require("axios").default; const express = require("express"); const app = express(); app.get("/", async (req, res) => { const result = await axios.get("http://localhost:7001/hello"); return res.status(201).send(result.data); }); app.get("/hello", async (req, res) => { console.log("hello world!") res.json({ code: 200, msg: "success" }); }); app.use(express.json()); app.listen(7001, () => { console.log("Listening on http://localhost:7001"); }); EOF创建 Dockerfile 文件。
Nginx OTel 模块版本 ≥ 0.1.2
cat << 'EOF' > backend/Dockerfile FROM node:20.16.0 WORKDIR /app COPY package*.json ./ RUN npm install COPY . . ENV OTEL_TRACES_EXPORTER="otlp" ENV OTEL_LOGS_EXPORTER=none ENV OTEL_METRICS_EXPORTER=none ENV OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=grpc ENV OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="${GRPC_ENDPOINT}" export OTEL_EXPORTER_OTLP_HEADERS=Authentication=${GRPC_TOKEN} ENV OTEL_NODE_RESOURCE_DETECTORS="env,host,os" ENV OTEL_SERVICE_NAME="ot-nodejs-demo" ENV NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register" EXPOSE 7001 CMD ["node", "main.js"] EOFNginx OTel 模块版本 ≤ 0.1.1
cat << 'EOF' > backend/Dockerfile FROM node:20.16.0 WORKDIR /app COPY package*.json ./ RUN npm install COPY . . ENV OTEL_TRACES_EXPORTER="otlp" ENV OTEL_LOGS_EXPORTER=none ENV OTEL_METRICS_EXPORTER=none ENV OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=grpc ENV OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="http://otel-collector:4317" ENV OTEL_NODE_RESOURCE_DETECTORS="env,host,os" ENV OTEL_SERVICE_NAME="ot-nodejs-demo" ENV NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register" EXPOSE 7001 CMD ["node", "main.js"] EOF
六、创建 Docker Compose 文件
该文件定义了一个多容器应用程序的配置,包括 Nginx 反向代理、OpenTelemetry Collector 和 Node.js 后端服务,以及它们之间的网络连接和端口映射。
Nginx OTel 模块版本 ≥ 0.1.2
cat << 'EOF' > docker-compose.yml
version: "3"
services:
nginx:
image: nginx:1.27.5-alpine-otel # 默认包含 0.1.2 版本 nginx-moduel-otel 模块的 nginx 镜像
volumes:
- ./nginx_conf/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx_conf/default.conf:/etc/nginx/conf.d/default.conf:ro
ports:
- "80:80"
networks:
- nginx-otel-demo
backend-api:
build:
context: ./backend
dockerfile: Dockerfile
environment:
- NODE_ENV=production
ports:
- "7001:7001"
networks:
- nginx-otel-demo
networks:
nginx-otel-demo:
driver: bridge
EOFNginx OTel 模块版本 ≤ 0.1.1
cat << 'EOF' > docker-compose.yml
version: "3"
services:
nginx:
image: nginx:1.27.2-alpine-otel # 默认包含 opentelemetry 模块的 nginx 镜像
volumes:
- ./nginx_conf/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx_conf/default.conf:/etc/nginx/conf.d/default.conf:ro
ports:
- "80:80"
networks:
- nginx-otel-demo
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
volumes:
- ./otel_conf/config.yaml:/etc/otelcol-contrib/config.yaml # 挂载 OpenTelemetry Collector 配置文件
ports:
- "4317:4317" # OTLP gRPC receiver
networks:
- nginx-otel-demo
backend-api:
build:
context: ./backend
dockerfile: Dockerfile
environment:
- NODE_ENV=production
ports:
- "7001:7001"
networks:
- nginx-otel-demo
networks:
nginx-otel-demo:
driver: bridge
EOF七、启动服务
在 nginx-otel-demo 目录中执行以下命令。
docker compose up -d预期输出:
Nginx OTel 模块版本 ≥ 0.1.2
[+] Running 4/4 ✔ Network nginx-otel-demo_nginx-otel-demo Created ✔ Container nginx-otel-demo-nginx-1 Started ✔ Container nginx-otel-demo-backend-api-1 StartedNginx OTel 模块版本 ≤ 0.1.1
[+] Running 4/4 ✔ Network nginx-otel-demo_nginx-otel-demo Created ✔ Container nginx-otel-demo-nginx-1 Started ✔ Container nginx-otel-demo-backend-api-1 Started ✔ Container nginx-otel-demo-otel-collector-1 Started访问应用。
curl http://localhost:80/hello预期输出:
{"code":200,"msg":"success"}登录可观测链路 OpenTelemetry 版控制台查看 Nginx 及后端服务的调用链。
在本例中,Nginx 在应用列表页面的名称为 nginx,后端应用名称为 ot-nodejs-demo。
常见问题
无法下载 nginx-module-otel,提示
Unable to find a match: nginx-module-otel。下载 nginx-module-otel 需要已配置 nginx packages repository,请检查是否配置过 nginx packages repository。如未配置,配置操作请参见nginx-otel。
在 Nginx 配置文件中添加 Nginx OTel 模块配置后,Nginx 启动失败。
请使用
nginx -t命令检查 Nginx 配置是否正确,或查看 Nginx 日志中的错误信息。sudo tail -n 50 /var/log/nginx/error.log启用 Nginx OTel 模块后,在可观测链路 OpenTelemetry 版控制台没有看到 Nginx 的调用链。
请检查 Nginx 配置中 otel_exporter.endpoint 是否配置正确。该地址由 OpenTelemetry Collector 地址以及 gRPC 接收器端口组成,例如
localhost:4317。可通过查看 Nginx 日志确认是否配置正确,当出现如下报错时,说明 otel_exporter.endpoint 未配置正确。2024/11/01 15:21:48 [notice] 137091#137091: worker process 137093 exited with code 0 2024/11/01 15:21:48 [notice] 137091#137091: signal 29 (SIGIO) received 2024/11/01 15:22:02 [error] 137644#137653: OTel export failure: failed to connect to all addresses 2024/11/01 15:22:07 [error] 137645#137652: OTel export failure: failed to connect to all addresses 2024/11/01 15:22:42 [error] 137644#137653: OTel export failure: failed to connect to all addresses 2024/11/01 15:22:47 [error] 137644#137653: OTel export failure: failed to connect to all addresses 2024/11/01 15:23:37 [error] 137644#137653: OTel export failure: failed to connect to all addresses 2024/11/01 15:25:23 [error] 137644#137653: OTel export failure: failed to connect to all addresses 2024/11/01 15:25:28 [error] 137644#137653: OTel export failure: failed to connect to all addressesNginx 的调用链无法与其他应用串联。
请确保 Nginx 配置中添加了
otel_trace_context propagate;,并确认其他应用使用的链路透传协议与 Nginx 的透传协议是否相同。Nginx OTel 模块使用 OpenTelemetry W3C 作为透传协议,这需要其他应用也要使用 OpenTelemetry W3C 透传协议才能串联。Nginx OTel 插件模块是否会对 Nginx 的性能产生影响?
Nginx OTel 插件模块为 Nginx 原生模块,官方提示该模块对 Nginx 性能的影响限制在10-15%。详细信息,请参见NGINX Native OpenTelemetry (OTel) Module。

