Nginx Ingress Controller 集成

编辑

Nginx Ingress Controller 集成

编辑

版本

0.0.3 [beta] 此功能为 Beta 版,可能会发生变化。其设计和代码不如官方 GA 功能成熟,并且按“原样”提供,不提供任何保证。Beta 版功能不受官方 GA 功能的支持 SLA 约束。 (查看全部)

兼容的 Kibana 版本

8.16.0 或更高版本

支持的无服务器项目类型
这是什么?

安全性
可观测性

订阅级别
这是什么?

基本

支持级别
这是什么?

Elastic

此集成从 Nginx Ingress Controller 实例收集和解析日志。它可以解析入口创建的访问和错误日志。

兼容性

编辑

该集成已在 Nginx Ingress Controller v0.30.0 和 v0.40.2 上测试。日志格式在此处描述:这里

支持的 EDOT 收集器版本 8.16.0

OpenTelemetry 收集器组件

  • Filelog 接收器 v0.112.0+
  • 转换处理器 v0.112.0+
  • 资源检测器处理器 v0.112.0+
  • (可选)GeoIP 处理器 v0.112.0+
  • Elasticsearch 导出器 v0.112.0+
  • 文件存储扩展 v0.112.0+

用法

编辑
extensions:
  file_storage:

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

processors:
  transform/parse_nginx_ingress_error/log:
    error_mode: ignore
    log_statements:
      - context: log
        conditions:
            # ^[EWF]: Matches logs starting with E (Error), W (Warning), or F (Fatal).
            # \d{4}: Matches the four digits after the log level (representing the date, like 1215 for December 15).
            # .+: Matches the rest of the log line (the message part, without needing specific timestamp or file format).
          - IsMatch(body, "^[EWF]\\d{4} .+")
        statements:
          - set(body, ExtractGrokPatterns(body, "%{LOG_LEVEL:log.level}%{MONTHNUM}%{MONTHDAY} %{HOUR}:%{MINUTE}:%{SECOND}\\.%{MICROS}%{SPACE}%{NUMBER:thread_id} %{SOURCE_FILE:source.file.name}:%{NUMBER:source.line_number}\\\] %{GREEDYMULTILINE:message}", true, ["LOG_LEVEL=[A-Z]", "MONTHNUM=(0[1-9]|1[0-2])", "MONTHDAY=(0[1-9]|[12][0-9]|3[01])", "HOUR=([01][0-9]|2[0-3])", "MINUTE=[0-5][0-9]", "SECOND=[0-5][0-9]", "MICROS=[0-9]{6}", "SOURCE_FILE=[^:]+", "GREEDYMULTILINE=(.|\\n)*"]))

          - set(attributes["data_stream.dataset"], "nginx_ingress_controller.error")

          # LogRecord event: https://github.com/open-telemetry/semantic-conventions/pull/982
          - set(attributes["event.name"], "nginx_ingress_controller.error")

  transform/parse_nginx_ingress_access/log:
    error_mode: ignore
    log_statements:
      - context: log
        conditions:
            #     # ^([0-9a-fA-F:.]+): Matches the remote address (IPv4 or IPv6 format).
            #     # [^ ]+: Matches the remote user (including the hyphen for missing user).
            #     # .*[0-9a-fA-F]+$: Ensures the log line ends with a hexadecimal string (request ID).
          - IsMatch(body, "^([0-9a-fA-F:.]+) - [^ ]+ .*[0-9a-fA-F]+$")
        statements:
          # Log format: https://github.com/kubernetes/ingress-nginx/blob/nginx-0.30.0/docs/user-guide/nginx-configuration/log-format.md
          # Based on https://github.com/elastic/integrations/blob/main/packages/nginx_ingress_controller/data_stream/access/elasticsearch/ingest_pipeline/default.yml
          - set(body, ExtractGrokPatterns(body, "(%{NGINX_HOST} )?\"?(?:%{NGINX_ADDRESS_LIST:nginx_ingress_controller.access.remote_ip_list}|%{NOTSPACE:source.address}) - (-|%{DATA:user.name}) \\\[%{HTTPDATE:nginx_ingress_controller.access.time}\\\] \"%{DATA:nginx_ingress_controller.access.info}\" %{NUMBER:http.response.status_code:long} %{NUMBER:http.response.body.size:long} \"(-|%{DATA:http.request.referrer})\" \"(-|%{DATA:user_agent.original})\" %{NUMBER:http.request.size:long} %{NUMBER:http.request.time:double} \\\[%{DATA:upstream.name}\\\] \\\[%{DATA:upstream.alternative_name}\\\] (%{UPSTREAM_ADDRESS_LIST:upstream.address}|-) (%{UPSTREAM_RESPONSE_SIZE_LIST:upstream.response.size_list}|-) (%{UPSTREAM_RESPONSE_TIME_LIST:upstream.response.time_list}|-) (%{UPSTREAM_RESPONSE_STATUS_CODE_LIST:upstream.response.status_code_list}|-) %{GREEDYDATA:http.request.id}", true, ["NGINX_HOST=(?:%{IP:destination.ip}|%{NGINX_NOTSEPARATOR:destination.domain})(:%{NUMBER:destination.port})?", "NGINX_NOTSEPARATOR=[^\t ,:]+", "NGINX_ADDRESS_LIST=(?:%{IP}|%{WORD}) (\"?,?\\s*(?:%{IP}|%{WORD}))*", "UPSTREAM_ADDRESS_LIST=(?:%{IP}(:%{NUMBER})?)(\"?,?\\s*(?:%{IP}(:%{NUMBER})?))*", "UPSTREAM_RESPONSE_SIZE_LIST=(?:%{NUMBER})(\"?,?\\s*(?:%{NUMBER}))*", "UPSTREAM_RESPONSE_TIME_LIST=(?:%{NUMBER})(\"?,?\\s*(?:%{NUMBER}))*", "UPSTREAM_RESPONSE_STATUS_CODE_LIST=(?:%{NUMBER})(\"?,?\\s*(?:%{NUMBER}))*", "IP=(?:\\\[?%{IPV6}\\\]?|%{IPV4})"]))
          - merge_maps(body, ExtractGrokPatterns(body["nginx_ingress_controller.access.info"], "%{WORD:http.request.method} %{DATA:url.original} HTTP/%{NUMBER:http.version}", true), "upsert")
          - delete_key(body, "nginx_ingress_controller.access.info")

          # Extra URL parsing
          - merge_maps(body, URL(body["url.original"]), "upsert")
          - set(body["url.domain"], body["destination.domain"])

          # set source.address as attribute for GeoIP processor
          - set(attributes["source.address"], body["source.address"])

          - set(attributes["data_stream.dataset"], "nginx_ingress_controller.access")

          # LogRecord event: https://github.com/open-telemetry/semantic-conventions/pull/982
          - set(attributes["event.name"], "nginx_ingress_controller.access")
          - set(attributes["event.timestamp"], String(Time(body["nginx_ingress_controller.access.time"], "%d/%b/%Y:%H:%M:%S %z")))

          - delete_key(body, "nginx_ingress_controller.access.time")

      - context: log
        conditions:
            # Extract user agent when not empty
          - body["user_agent.original"] != nil
        statements:
          # Extract UserAgent
          # TODO: UserAgent OTTL function does not provide os specific metadata yet: https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/35458
          - merge_maps(body, UserAgent(body["user_agent.original"]), "upsert")

      - context: log
        conditions:
          - body["upstream.response.time_list"] != nil
        statements:
          # Extract comma separated list
          # TODO: We would like to get the sum over all upstream.response.time_list values instead of providing a slice with all the values
          - set(body["upstream.response.time"], Split(body["upstream.response.time_list"], ","))
          - delete_key(body, "upstream.response.time_list")

      - context: log
        conditions:
          - body["upstream.response.size_list"] != nil
        statements:
          # Extract comma separated list
          # TODO: We would like to get the Last upstream.response.size_list value instead of providing a slice with all the values
          # See: https://github.com/elastic/integrations/blob/main/packages/nginx_ingress_controller/data_stream/access/elasticsearch/ingest_pipeline/default.yml#L94b
          - set(body["upstream.response.size"], Split(body["upstream.response.size_list"], ","))
          - delete_key(body, "upstream.response.size_list")

      - context: log
        conditions:
          - body["upstream.response.status_code_list"] != nil
        statements:
          # Extract comma separated list
          # TODO: We would like to get the Last upstream.response.status_code_list value instead of providing a slice with all the values
          - set(body["upstream.response.status_code"], Split(body["upstream.response.status_code_list"], ","))
          - delete_key(body, "upstream.response.status_code_list")

  # TODO: add other detectors
  resourcedetection/system:
    detectors: ["system"]
    system:
      hostname_sources: [ "os" ]
      resource_attributes:
        host.name:
          enabled: true
        host.id:
          enabled: false
        host.arch:
          enabled: true

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

exporters:
  elasticsearch:
    endpoints:
    - YOUR_ELASTICSEARCH_ENDPOINT
    api_key: YOUR_ELASTICSEARCH_API_KEY
    logs_dynamic_index:
      enabled: true
    mapping:
      mode: otel
  debug:
    verbosity: detailed

service:
  extensions: [file_storage]
  pipelines:
    logs:
      receivers: [filelog]
      processors: [transform/parse_nginx_ingress_access/log, transform/parse_nginx_ingress_error/log, resourcedetection/system]
      # Uncomment the following line if geoip is configured
      # processors: [transform/parse_nginx_ingress_access/log, transform/parse_nginx_ingress_error/log, geoip, resourcedetection/system]
      exporters: [debug, elasticsearch]

不要忘记替换

  • YOUR_ELASTICSEARCH_ENDPOINT:您的 Elasticsearch 端点(带有 https:// 前缀,例如:https://1234567.us-west2.gcp.elastic-cloud.com:443)。
  • YOUR_ELASTICSEARCH_API_KEY:您的 Elasticsearch API 密钥
GeoIP 元数据
编辑

默认情况下,传入的 Nginx Ingress Controller 请求的地理 IP 元数据处于禁用状态。要启用它,您需要在处理器的配置中提供本地 GeoIP 数据库路径

  1. 取消注释 GeoIP 处理器配置
geoip:
 context: record
 providers:
   maxmind:
     database_path: /tmp/GeoLite2-City.mmdb
  1. 将处理器包含在日志管道中
processors: [transform/parse_nginx_ingress_access/log, transform/parse_nginx_ingress_error/log, geoip, resourcedetection/system]

日志

编辑
访问日志
编辑

access 数据流收集 Nginx Ingress Controller 访问日志。

导出的字段
字段 描述 类型

@timestamp

事件时间戳。

日期

data_stream.dataset

数据流数据集。

constant_keyword

data_stream.namespace

数据流命名空间。

constant_keyword

data_stream.type

数据流类型。

constant_keyword

event.name

事件名称

constant_keyword

resource.attributes.k8s.cluster.name

keyword

resource.attributes.k8s.container.name

keyword

resource.attributes.k8s.container.restart_count

long

resource.attributes.k8s.deployment.name

keyword

resource.attributes.k8s.namespace.name

keyword

resource.attributes.k8s.node.name

keyword

resource.attributes.k8s.pod.name

keyword

resource.attributes.k8s.pod.start_time

日期

resource.attributes.cloud.account.id

keyword

resource.attributes.cloud.availability_zone

keyword

resource.attributes.cloud.instance.id

keyword

resource.attributes.cloud.platform

keyword

resource.attributes.cloud.provider

keyword

resource.attributes.deployment.environment

keyword

resource.attributes.host.arch

keyword

resource.attributes.host.cpu.cache.l2.size

keyword

resource.attributes.host.cpu.family

keyword

resource.attributes.host.cpu.model.id

keyword

resource.attributes.host.cpu.model.name

keyword

resource.attributes.host.cpu.stepping

keyword

resource.attributes.host.cpu.vendor.id

keyword

resource.attributes.host.id

keyword

resource.attributes.host.ip

keyword

resource.attributes.host.mac

keyword

resource.attributes.host.name

keyword

resource.attributes.os.type

keyword

resource.attributes.os.description

keyword

body.structured.http.request.method

HTTP 请求方法。该值应保留其原始事件中的大小写。例如,GETgetGeT 都是此字段的有效值。

keyword

body.structured.http.request.referrer

此 HTTP 请求的引用者。

keyword

body.structured.http.response.body.size

响应正文的大小(以字节为单位)。

long

body.structured.http.response.status_code

HTTP 响应状态代码。

long

body.structured.http.version

HTTP 版本。

keyword

attribute.log.file.path

此事件来自的日志文件的完整路径,包括文件名。它应包括驱动器号(如果适用)。如果该事件不是从日志文件中读取的,则不要填充此字段。

keyword

attribute.log.iostream

keyword

body.structured.http.request.id

请求的随机生成的 ID

text

body.structured.http.request.size

请求长度(包括请求行、标头和请求正文)

long

body.structured.http.request.time

自从从客户端读取第一个字节以来经过的时间

double

body.structured.upstream.address

上游服务器的 IP 地址。如果在请求处理期间联系了多个服务器,它们的地址将用逗号分隔。

ip

body.structured.upstream.name

上游的名称。

keyword

body.structured.upstream.response.size

从上游服务器获得的响应的长度

long

body.structured.upstream.response.status_code

从上游服务器获得的响应的状态代码

long

body.structured.upstream.response.time

接收来自上游服务器的响应所花费的时间,以毫秒级分辨率的秒为单位

double

attribute.source.address

某些事件源地址的定义不明确。该事件有时会列出 IP、域名或 Unix 套接字。您应该始终将原始地址存储在 .address 字段中。然后应将其复制到 .ip.domain,具体取决于它是哪个。

keyword

attribute.geo.city_name

城市名称。

keyword

attribute.geo.continent_name

大洲名称。

keyword

attributes.geo.country_iso_code

国家/地区 ISO 代码。

keyword

attributes.geo.country_name

国家/地区名称。

keyword

attributes.geo.location.lat

纬度。

geo_point

attributes.geo.location.lon

经度。

geo_point

attributes.geo.region_iso_code

地区 ISO 代码。

keyword

attributes.geo.region_name

地区名称。

keyword

body.structured.url.domain

URL 的域,例如“https://elastic.ac.cn[www.elastic.co]”。在某些情况下,URL 可能会直接引用 IP 和/或端口,而没有域名。在这种情况下,IP 地址将转到 domain 字段。如果 URL 包含用 [] (IETF RFC 2732) 括起来的文本 IPv6 地址,则 [] 字符也应在 domain 字段中捕获。

keyword

body.structured.url.extension

该字段包含来自原始请求 URL 的文件扩展名,不包括前导点。仅当存在文件扩展名时才设置文件扩展名,因为并非每个 URL 都有文件扩展名。不得包括前导句点。例如,该值必须是“png”,而不是“.png”。请注意,当文件名具有多个扩展名(example.tar.gz)时,应仅捕获最后一个扩展名(“gz”,而不是“tar.gz”)。

keyword

body.structured.url.original

在事件源中看到的未修改的原始 URL。请注意,在网络监控中,观察到的 URL 可能是完整的 URL,而在访问日志中,URL 通常仅表示为路径。此字段旨在表示观察到的 URL,完整与否。

wildcard

body.structured.url.path

请求的路径,例如“/search”。

wildcard

body.structured.url.scheme

请求的方案,例如“https”。注意:: 不是方案的一部分。

keyword

body.structured.user.name

用户的短名称或登录名。

keyword

body.structured.user_agent.name

用户代理的名称。

keyword

body.structured.user_agent.original

未解析的 user_agent 字符串。

keyword

body.structured.user_agent.version

用户代理的版本。

keyword

错误日志
编辑

error 数据流收集 Nginx Ingress Controller 错误日志。

导出的字段
字段 描述 类型

@timestamp

事件时间戳。

日期

data_stream.dataset

数据流数据集。

constant_keyword

data_stream.namespace

数据流命名空间。

constant_keyword

data_stream.type

数据流类型。

constant_keyword

event.name

事件名称

constant_keyword

resource.attributes.k8s.cluster.name

keyword

resource.attributes.k8s.container.name

keyword

resource.attributes.k8s.container.restart_count

long

resource.attributes.k8s.deployment.name

keyword

resource.attributes.k8s.namespace.name

keyword

resource.attributes.k8s.node.name

keyword

resource.attributes.k8s.pod.name

keyword

resource.attributes.k8s.pod.start_time

日期

resource.attributes.cloud.account.id

keyword

resource.attributes.cloud.availability_zone

keyword

resource.attributes.cloud.instance.id

keyword

resource.attributes.cloud.platform

keyword

resource.attributes.cloud.provider

keyword

resource.attributes.deployment.environment

keyword

resource.attributes.host.arch

keyword

resource.attributes.host.cpu.cache.l2.size

keyword

resource.attributes.host.cpu.family

keyword

resource.attributes.host.cpu.model.id

keyword

resource.attributes.host.cpu.model.name

keyword

resource.attributes.host.cpu.stepping

keyword

resource.attributes.host.cpu.vendor.id

keyword

resource.attributes.host.id

keyword

resource.attributes.host.ip

keyword

resource.attributes.host.mac

keyword

resource.attributes.host.name

keyword

resource.attributes.os.type

keyword

resource.attributes.os.description

keyword

attribute.log.file.path

此事件来自的日志文件的完整路径,包括文件名。它应包括驱动器号(如果适用)。如果该事件不是从日志文件中读取的,则不要填充此字段。

keyword

attributes.log.level

日志事件的原始日志级别。如果事件源提供日志级别或文本严重性,则此级别将放入 log.level 中。如果您的源未指定日志级别,则可以将您的事件传输的严重性放在此处(例如,Syslog 严重性)。一些示例包括 warnerriinformational

keyword

body.structured.message

对于日志事件,message 字段包含日志消息,该消息已针对在日志查看器中查看进行了优化。对于没有原始消息字段的结构化日志,可以连接其他字段以形成事件的人类可读摘要。如果存在多个消息,可以将它们合并为一个消息。

match_only_text

body.structured.source.file.name

源文件

keyword

body.structured.source.line_number

源行号

long

body.structured.thread_id

线程 ID

long

更新日志

编辑
更新日志
版本 详细信息 Kibana 版本

0.0.3

错误修复 (查看拉取请求)
修复 HTTP 响应状态代码细分

0.0.2

错误修复 (查看拉取请求)
将 body.structured 字段用于仪表板

0.0.1

增强 (查看拉取请求)
OpenTelemetry 包的初始 Nginx Ingress Controller 日志