Apache APISIX集成OpenTelemetry以收集跟踪数据

本文介绍了Apache APISIX的opentelemetry插件概念以及如何启用和部署该插件。

背景信息

OpenTelemetry是一个开源的遥测数据采集和处理系统。它不仅提供各种用于应用端遥测数据收集和报告的SDK,还提供用于数据接收、处理和导出的数据收集端。可以导出到任何或多个OpenTelemetry后端,如Jaeger、Zipkin和OpenCensus。您可以在注册表中查看已适配OpenTelemetry Collector的插件列表。

架构示意图

插件介绍

Apache APISIX的opentelemetry插件通过HTTP协议实现了Tracing数据收集,并将其发送到OpenTelemetry Collector。Apache APISIX从v2.13.0开始支持此功能。

OpenTelemetry的一个特点是其代理或SDK不与后端实现绑定,这使得用户在选择自己的后端服务时具有灵活性。换句话说,用户可以选择他们想要的后端服务,如Zipkin和Jaeger,而不会影响应用程序端。

opentelemetry插件位于代理端,它集成了OpenTelemetry代理/SDK,并在Apache APISIX中采用其特性。它可以收集被跟踪的请求,生成trace,并将其转发到OpenTelemetry Collector。它支持trace协议,并将在下一个版本中支持OpenTelemetry的logsmetrics协议。

启用插件

您需要在conf/config.yaml配置文件中启用opentelemetry插件并修改收集器配置。

假设您已经在与APISIX相同的节点上部署了OpenTelemetry Collector,并开启了OTLP HTTP Receiver

需要帮助完成OpenTelemetry Collector的部署吗?请参考下面的示例

OTLP HTTP Receiver的默认端口是4318collector的地址是OpenTelemetry Collector的HTTP Receiver地址。有关相关字段,请参阅Apache APISIX文档

典型的配置如下所示:

plugins:
  ... # 已启用的其他插件
  - opentelemetry
plugin_attr:
  ...
  opentelemetry:
    trace_id_source: x-request-id
    resource:
      service.name: APISIX
    collector:
      address: 127.0.0.1:4318 # OTLP HTTP Receiver地址
      request_timeout: 3

方法1:为特定路由启用插件

为了更方便地显示测试效果,在示例中将sampler临时设置为完整采样,以确保在跟踪每个请求之后生成trace数据,这样您可以在Web UI上查看trace相关数据。您也可以根据实际情况设置相关参数。

curl http://127.0.0.1:9080/apisix/admin/routes/1 \
  -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
  -X PUT -d '
{
    "uri": "/get",
    "plugins": {
        "opentelemetry": {
            "sampler": {
                "name": "always_on"
            }
        }
    },
    "upstream": {
        "type": "roundrobin",
        "nodes": {
            "httpbin.org:80": 1
        }
    }
}'

方法2:全局启用插件

您还可以通过Apache APISIX的插件功能全局启用opentelemetry插件。完成全局配置后,仍然需要创建路由,否则无法进行测试。

curl 'http://127.0.0.1:9080/apisix/admin/global_rules/1' \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
-X PUT -d '{
    "plugins": {
        "opentelemetry": {
            "sampler": {
                "name": "always_on"
            }
        }
    }
}'

方法3:通过additional_attributes自定义Span的标签

有关sampleradditional_attributes的配置,请参阅Apache APISIX文档。其中,additional_attributes是一系列Key:Value对,您可以使用它来自定义Span的标签,并可以在Web UI上显示Span。通过additional_attributes向路由的Span中添加route_idhttp_x-custom-ot-key,请参考以下配置:

curl http://127.0.0.1:9080/apisix/admin/routes/1001 \
  -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
  -X PUT -d '
{
    "uri": "/put",
    "plugins": {
        "opentelemetry": {
            "sampler": {
                "name": "always_on"
            },
            "additional_attributes":[
                "route_id",
                "http_x-custom-ot-key"
            ]
        }
    },
    "upstream": {
        "type": "roundrobin",
        "nodes": {
            "httpbin.org:80": 1
        }
    }
}'

测试和验证插件

您可以在上述三种方法中任何一个中启用opentelemetry插件。下面的示例使用方法三的示例创建一个路由。创建成功后,查看下面的命令以访问路由:

curl -X PUT -H `x-custom-ot-key: test-ot-val` http://127.0.0.1:9080/put

访问成功后,您可以在Jaeger UI中看到类似于/put的span的详细信息,并且您可以看到路由中的自定义标签显示在标签列表中:http_x-custom-ot-keyroute_id

Jaeger UI

需要注意的是,additional_attributes配置设置为从Apache APISIX和NGINX变量中获取值作为attribute的值,因此additional_attributes必须是有效的Apache APISIX或NGINX变量。它还包括HTTP Header,但是在获取http*header时,您需要将http*作为变量名称的前缀。如果变量不存在,则不会显示相应的tag

示例

本场景示例通过简单修改OpenTelemetry Collector示例来部署Collector、Jaeger和Zipkin作为后端服务,并启动两个示例应用程序(客户端和服务器)。其中服务器提供HTTP服务,客户端会周期性地调用由服务器提供的HTTP接口,从而形成由两个span组成的调用链。

步骤1:部署OpenTelemetry

以下以docker compose为例。其他部署方式,请参考开始使用

以下是部署的命令[^1]:

git clone https://github.com/open-telemetry/opentelemetry-collector-contrib.git
cd opentelemetry-collector-contrib/examples/demo
docker compose up -d

在浏览器中访问http://127.0.0.1:16886(Jaeger UI)或http://127.0.0.1:9411/zipkin(Zipkin UI)。如果能够正常访问,则部署成功。

下面的截图显示了成功访问的示例。

Jaeger UIZipkin UI

步骤2:配置测试环境

引入Apache APISIX服务,并在下图中显示了最终应用程序的拓扑。

示例的拓扑结构

追踪数据的报告过程如下。其中,由于Apache APISIX是单独部署的,而不在docker-compose的网络中,Apache APISIX通过本地映射端口(127.0.0.1:4138)访问OpenTelemetry Collector的OTLP HTTP Receiver。

追踪数据的流程

您需要确保已启用opentelemetry插件并重新加载Apache APISIX。

以下示例创建一条路由并启用sampleropentelemetry插件进行采样:

curl http://127.0.0.1:9080/apisix/admin/routes/1 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
-X PUT -d '
{
  "uri": "/hello",
  "plugins": {
      "opentelemetry": {
          "sampler": {
            "name": "always_on"
          }
      }
  },
  "upstream": {
      "type": "roundrobin",
      "nodes": {
          "127.0.0.1:7080": 1
      }
  }
}'

修改./examples/demo/otel-collector-config.yaml文件以添加OTLP HTTP Receiver。

receivers:
otlp:
  protocols:
    grpc:
    http: ${ip:port} # 添加OTLP HTTP Receiver,默认端口是4318

修改docker-compose.yaml文件。

您需要修改配置文件,将Client调用Server的接口地址更改为Apache APISIX的地址,并将OTLP HTTP Receiver和Server服务的端口映射到本地。

以下示例是修改配置后的完整docker-compose.yaml

version: '2'
services:
  # Jaeger
  jaeger-all-in-one:
    image: jaegertracing/all-in-one:latest
    ports:
      - '16686:16686' # jaeger UI端口
      - '14268'
      - '14250'

  # Zipkin
  zipkin-all-in-one:
    image: openzipkin/zipkin:latest
    ports:
      - '9411:9411'

  # Collector
  otel-collector:
    image: ${OTELCOL_IMG}
    command: ['--config=/etc/otel-collector-config.yaml', '${OTELCOL_ARGS}']
    volumes:
      - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
    ports:
      - '1888:1888' # pprof扩展
      - '8888:8888' # 由collector暴露的Prometheus指标
      - '8889:8889' # Prometheus exporter指标
      - '13133:13133' # health_check扩展
      - '4317' # OTLP gRPC接收器
      - '4318:4318' # 添加OTLP HTTP Receiver端口映射
      - '55670:55679' # zpages扩展
    depends_on:
      - jaeger-all-in-one
      - zipkin-all-in-one

  demo-client:
    build:
      dockerfile: Dockerfile
      context: ./client
    environment:
      - OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4317
      - DEMO_SERVER_ENDPOINT=http://172.17.0.1:9080/hello # APISIX地址
    depends_on:
      - demo-server

  demo-server:
    build:
      dockerfile: Dockerfile
      context: ./server
    environment:
      - OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4317
    ports:
      - '7080:7080' # 将Server端口映射到主机
    depends_on:
      - otel-collector

  prometheus:
    container_name: prometheus
    image: prom/prometheus:latest
    volumes:
      - ./prometheus.yaml:/etc/prometheus/prometheus.yml
    ports:
      - '9090:9090'

需要注意的是,demo-client.environment.DEMO_SERVER_ENDPOINT需要更改为您的Apache APISIX地址,并确保在容器中能够正常访问。

当然,您也可以通过docker-compose.yaml部署Apache APISIX。详情请参见通过Docker安装

步骤3:验证输出结果

重新部署完成后,您可以访问Jaeger UI或Zipkin UI,查看APISIX包含在Trace中的Span,如下所示:

Jaeger UIZipkin UI

当demo-server未进行仪表化时,通过启用该插件仍然可以获得demo-server行为的可见性。虽然这不是一个典型案例,但它是模拟对demo-server进行了真实仪表化的替代方案,并提供了很多价值。

上游未仪表化

当请求未到达demo-server时,输出结果中将不包含demo-server的span。

demo-server未连接

禁用插件

如果暂时不需要某个路由的跟踪收集,您只需修改路由配置并删除配置中的plugins下的opentelemetry部分。

如果通过绑定Global Rules全局启用了opentelemetry插件,可以删除opentelemetry全局插件的配置。

注意,禁用opentelemetry插件只会导致APISIX的span断开连接,客户端和服务器的span仍然保持连接。

总结

在Apache APISIX集成OpenTelemetry之后,可以轻松与市场上的多个Trace系统进行连接。Apache APISIX还在积极与社区合作,创建一个更强大的生态系统。

Apache APISIX目前还在开发其他插件,以支持与更多服务的集成。如果您有兴趣,请随时在GitHub上开始讨论,或通过邮件列表进行交流。

本文的一个版本已经发布在Apache APISIX博客上。