Observability:组装 OpenTelemetry NGINX Ingress Controller 集成

2025年2月6日   |   by mebius

作者:来自 ElasticRoger Coll

%title插图%num

这篇博文探讨了如何为 NGINX Ingress Controller 设置 OpenTelemetry 集成,详细介绍了配置过程、关键转换以及即将推出的模块化配置支持增强功能。

我们的愿景很明确:在 Elastic 中支持 OpenTelemetry。此次转型的一个关键方面是集成 —— 我们如何无缝调整所有现有集成以适应 OpenTelemetry 模型?

Elastic 集成旨在通过提供工具来提取应用程序数据、通过 Ingest 管道处理数据以及提供预构建的仪表板进行可视化,从而简化可观察性。借助 OpenTelemetry 支持,数据收集和处理将过渡到 OpenTelemetry Collector,而仪表板将需要采用 OpenTelemetry 数据结构

从日志到集成

虽然 OpenTelemetry 集成的概念尚未正式定义,但我们将其设想为一个结构化的工件集合,使用户能够从头开始监控应用程序。每个工件都有特定的角色;例如,OpenTelemetry Collector 配置文件必须集成到主 Collector 设置中。此捆绑配置指示 Collector 如何收集和处理来自相关应用程序的数据。

在 OpenTelemetry Collector 中,数据收集由接收器(receivers)组件处理。一些接收器是为特定应用程序量身定制的,例如 Kafka 或 MySQL,而其他接收器则旨在支持通用数据收集方法。专用接收器将数据收集和转换结合在一个组件中。但是,对于更通用的接收器,需要额外的组件来细化和转换传入的数据,使其成为更特定于应用程序的格式。让我们看看如何构建用于监控 Nginx Ingress Controller 的集成。

Ingress Nginx 是 Kubernetes 的 Ingress 控制器,使用 NGINX 作为反向代理和负载均衡器。它被广泛采用,在将外部流量引导到 Kubetgcodernetes 服务方面发挥着至关重要的作用,因此观察它的使用情况、性能和健康状况至关重要。我们如何开始观察对 Ingress 控制器的外部请求?幸运的是,NGINX Ingress 控制器会为每个处理的请求生成一个结构化的日志条目。这种结构化的格式可确保每个日志条目遵循一致的结构,从而可以轻松解析并生成一致的输出。

log_format upstreaminfo '$remote_addr - $remote_user [$time_local]
	"$request" ' '$status $body_bytes_sent "$http_referer" "$http_user_agent" '
	'$request_length $request_time [$proxy_upstream_name]
	[$proxy_alternative_upstream_name] $upstream_addr ' '$upstream_response_length
	$upstream_response_time $upstream_status $req_id';

所有字段的定义都可以在这里找到。

OpenTelemetry Contrib Collector 不包含能够读取和解析 NGINX Ingress 日志中所有字段的接收器。这有两个主要原因:

  • 应用程序多样性:应用程序的范围非常广泛,每个应用程序都会以独特的格式生成日志。为每个应用程序开发和维护专用接收器会耗费大量资源,并且难以扩展。
  • 数据源灵活性:接收器通常设计为从特定源(如 HTTP 端点)收集数据。但是,在某些情况下,我们可能希望解析来自备用源的日志,例如存储在 AWS S3 存储桶中的 NGINX Ingress 日志文件。

这些挑战可以通过结合接收器和处理器来解决。接收器处理原始数据的收集,而处理器可以在检测到已知数据结构时提取特定值。我们是否需要专用处理器来解析 NGINX 日志?不一定。转换处理器可以通过根据指定的配置修改遥测数据来处理此问题。此配置以 OpenTelemetry 转换语言 (OTTL) 编写,该语言基于 OpenTelemetry Collector 处理探索来转换开放遥测数据。

OpenTelemetry 中的处理器概念与 Elastic 集成中当前使用的 Ingest 管道策略非常相似。因此,主要挑战在于将 Ingest 管道配置迁移到 OpenTelemetry Collector 配置。要深入了解此类迁移的挑战,请查看本文

作为参考,你可以在以下链接中查看当前的 Elastic NGINX Ingress Controller Ingest 管道配置:Elastic NGINX Ingress Controller Ingest Pipeline。

%title插图%num

让我们从数据收集开始。默认情况下,NGINX Ingress Controller 会将日志记录到 stdout,Kubernetes 会捕获这些日志并将其存储在文件中。假设运行以下配置的 OpenTelemetry Collector 可以访问 Kubernetes Pod 日志,我们可以使用 filelog 接收器读取控制器日志:

receivers:
  filelog/nginx:
    include_file_path: true
    include: [/var/log/pods/*nginx-ingress-nginx-controller*/controller/*.log]
    operators:
      - id: container-parser
        type: container

此配置旨在专门读取控制器的 pod 日志,重点关注 tgcodeKubernetes 节点内的默认文件路径。此外,由于 Ingress 控制器本身无法访问其关联的 Kubernetes 元数据,因此已实施container-parser operator 来弥补这一差距。此运算符仅根据文件名中可用的信息附加 Kubernetes 特定的属性,例如 k8s.pod.name 和 k8s.namespace.name。有关容器解析器运算符的详细概述,请参阅以下 OpenTelemetry 博客文章

避免重复日志

本博客中概述的配置是为 Kubernetes 环境设计的,其中收集器作为 Kubernetes Pod 运行。在这样的设置中,正确处理 Pod 重启至关重要。默认情况下,filelog 接收器在启动时读取日志文件的全部内容。如果收集器 Pod 重新启动,此行为可能导致重复的日志条目被重新处理并通过管道发送。

为了使配置能够适应重新启动,你可以使用存储扩展来跟踪文件偏移量。这些偏移量允许 filelog 接收器在重新启动后从日志文件中最后处理的位置恢复读取。以下是如何添加文件存储扩展并更新文件日志接收器配置以将偏移量存储在文件中的示例:

extensions:
  file_storage:
  directory: /var/lib/otelcol

receivers:
  filelog/nginx:
    storage: file_storage
    ...

重要提示:必须将 /var/lib/otelcol 目录作为 Kubernetes 持久卷的一部分挂载,以确保存储的偏移量在 Pod 重启后仍然有效。

使用 OpenTelemetry 处理器进行数据转换

现在是时候解析结构化日志字段并将其转换为可查询的 OpenTelemetry 字段了。最初,我们考虑使用 OpenTelemetry 转换语言 (OpenTelemetry Transformation Language – OTTL) 中提供的 extract_patterns 函数的正则表达式。然而,Elastic 最近贡献了一个基于 Grok 的新 OTTL 函数 ExtractGrokPatterns—— 一种支持可重用别名表达式的正则表达式方言。该函数的底层库 Elastic Go-Grok 附带了许多预定义的 grok 模式,可简化模式匹配的工作,例如 %NUMBER 可匹配任何数字类型;“123”、“456.789”、“-0.123”。

每个 Ingress Controller 日志条目都以客户端的源 IP 地址(可能是单个 IP 或 IP 列表)和通过基本身份验证提供的用户名开头,表示为 “$remote_addr – $remote_user”。 Grok IP 别名可用于从 remote_addr 字段解析 IPv4 或 IPv6 地址,而 %GREEDYDATA 别名可捕获 remote_user 值。

例如,以下 OTTL 配置将把非结构化正文消息转换为具有两个字段的结构化正文消息:

  • 解析单个 IP 地址并将其分配给 source.address 键。
  • 以 “-” 分隔,捕获 user.name 键中经过身份验证的用户名的可选值。
transform/parse_nginx_ingress_access/log:
  log_statements:
    - context: log
      statements:
        - set(body, ExtractGrokPatterns(body, "%{IP:source.address} - (-|%{GREEDYDATA:user.name})", true))

下面的截图说明了转换过程,显示了原始输入数据以及生成的结构化格式(差异):

%title插图%num

在实际场景中,NGINX Ingress Controller 日志可能以 IP 地址列表或有时域名开头。可以使用扩展的 Grok 模式处理这些变化。同样,我们可以使用 Grok 来解​​析 HTTP UserAgent 和 URL 字符串,但需要额外的 OTTL 函数(例如 URLUserAgent)才能从这些字段中提取有意义的数据。

完整配置可在 Elastic 的 OpenTelemetry NGINX Ingress Controller 集成文档中找到:集成文档

使用

Elastic OpenTelemetry NGINX Ingress Controller 目前处于技术预览阶段。要访问它,你必须在 Kibana 中的集成菜单中启用 “Display beta integrations” 切换。

%title插图%num

通过安装 Elastic OpenTelemetry NGINX Ingress Controller 集成,你的 Kibana 配置文件中将出现几个仪表板。其中一个仪表板提供了对控制器访问事件的洞察,显示诸如随时间变化的 HTTP 响应状态代码、每个 URL 的请求量、浏览器传入请求的分布、最常请求的页面等信息。下面的屏幕截图显示了 NGINX Ingress Controller 访问日志仪表板,显示了将请求路由到 OpenTelemetry Demo 部署的控制器的数据:

%title插图%num

第二个仪表板重点关注 Nginx Ingress 控制器内的错误,突出显示随时间推移生成的错误事件的数量:

%title插图%num

要开始收集和处理控制器日志,我们建议将集成文档中概述的 OpenTelemetry Collector 管道合并到你的收集器配置中:集成文档。请记住,此配置需要访问 Kubernetes 节点的 Pods 日志,这些日志通常存储在 /var/log/pods/* 中。为确保正确访问,我们建议将 OpenTelemetry Collector 作为 Kubernetes 中的守护进程集部署,因为此部署类型允许收集器访问每个节点上必要的日志目录。

OpenTelemetry Collector 配置服务管道应包括类似的配置:

service:
  extensions: [file_storage]
  pipelines:
    logs/nginx_ingress_controller:
      receivers:
        - filelog
      processors:
        - transform/parse_nginx_ingress_access/log
        - transform/parse_nginx_ingress_error/log
        - resourcedetection/system
      exporters:
        - elasticsearch

添加 GeoIP 元数据

作为可选增强功能,可以配置 OpenTelemetry Collector GeoIP 处理器并将其添加到管道中,以丰富每个 NGINX Ingress Controller 日志的地理属性,例如请求的来源国家、地区和城市,从而使 Kibana 中的地理地图能够可视化流量分布和地理模式。

虽然 OpenTelemetry GeoIP 处理器与 Elastic 的 GeoIP 处理器类似,但它要求用户提供自己的本地 GeoLite2 数据库。以下配置扩展了 Integratiotgcoden 的配置,以将 GeoIP 处理器与MaxMind 的数据库结合在一起。

processors:
  geoip:
    context: record
    providers:
      maxmind:
        database_path: /tmp/GeoLite2-City.mmdb

service:
  extensions: [file_storage]
  pipelines:
    logs/nginx_ingress_controller:
      receivers:
        - filelog
      processors:
        - transform/parse_nginx_ingress_access/log
        - transform/parse_nginx_ingress_error/log
        - resourcedetection/system
        - geoip
      exporters:
        - elasticsearch

与 OpenTelemetry Nginx Ingress Controller 集成的示例 Kibana Map:

%title插图%num

下一步

OpenTelemetry 日志事件

仔细查看 OTTL 集成的语句可以发现,原始日志消息已被解析的字段所取代。换句话说,配置将正文日志字段*从字符串转换为键值对的结构化映射,如“set(body, ExtractGrokPatterns(body,…)”中所示。此方法基于将每个 NGINX Ingress Controller 日志条目视为 OpenTelemetry 事件 – 一种特殊类型的 LogRecord。事件是 OpenTelemetry 对 LogRecords 的标准化语义格式,包含一个 “event.name” 属性,该属性定义正文字段的结构。NGINX Ingress Controller 日志记录与 OpenTelemetry 事件数据模型非常吻合。它遵循结构化格式,并明确区分两种事件类型:访问日志和错误日志。目前正在进行 PR,以将 NGINX Ingress 控制器日志纳入 OpenTelemetry 语义约定:https://github.com/open-telemetry/semantic-conventions/pull/982

操作系统故障

每个控制器日志都包含源 UserAgent,集成从中提取发起请求的浏览器。这些信息对于理解用户访问模式非常有价值,因为它可以深入了解通常与你的服务交互的浏览器类型。此外,OTTL 的持续拉取请求旨在通过提取操作系统 (OS) 详细信息来扩展此功能,从而更深入地了解与 NGINX Ingress Controller 交互的环境。

配置封装

设置 NGINX Ingress Controller 集成的配置可能有些繁琐,因为它涉及向现有收集器管道添加几个复杂的处理器配置。这个过程很快就会变得繁琐,尤其是对于非专家用户或在收集器配置已经相当复杂的情况下。在理想情况下,用户只需引用预定义的集成配置,收集器就会自动将所有必要的组件 “解包” 到相应的管道中。这将大大简化设置过程,使其更易于访问并降低配置错误的风险。为了解决这个问题,有一个 RFC(Request for Comments – 征求意见书)提议在 OpenTelemetry Collector 中支持可共享的模块化配置。此功能将允许用户通过引用模块化配置轻松收集来自特定服务或应用程序的信号,从而简化设置并增强复杂场景的可用性。

*OpenTelemetry 社区目前正在讨论是否应将结构化的主体提取信息存储在属性或主体字段中。有关详细信息,请参阅此正在进行的问题

本产品包含由 MaxMind 创建的 GeoLite2 数据,可从 https://www.maxmind.com 获取

原文:Assembling an OpenTelemetry NGINX Ingress Controller Integration — Elastic Observability Labs

文章来源于互联网:Observability:组装 OpenTelemetry NGINX Ingress Controller 集成