2025-6-23 ingress nginx contrller 内存使用过高分析

ingress nginx controller 文件描述符,内存增加,内存缓存收集nginx传递的指标数据时存在延迟,导致socket goroutine和指标对象堆积,收集延迟的原因是,在汇总数据累积过程中,存在锁的争用,prometheus 指标损失,prometheus服务器从ingress nginx controller 收集指标时发生超时的原因是,在ingress nginx controller 生成指标响应时,获取摘要数据的锁发生争用

Ingress-NGINX Controller 性能瓶颈诊断与缓解:文件描述符、内存泄漏和 Prometheus 指标收集挑战 #

1. 引言 #

Ingress-NGINX Controller 的作用概述 #

Ingress-NGINX Controller 是 Kubernetes 环境中一个至关重要的组件,其核心功能是将 Kubernetes 的 Ingress 对象转换为 NGINX 配置,从而实现对集群内部服务的外部访问管理。作为一个功能强大的反向代理和负载均衡器,Ingress-NGINX Controller 不仅负责流量路由和 SSL 终止,还与 Prometheus 等监控系统深度集成,提供丰富的可观测性指标 1。这种双重角色意味着其性能表现直接影响着应用程序的可用性和整个系统的监控健全性。

问题陈述 #

在实际操作中,Ingress-NGINX Controller 可能会遭遇一系列复杂的性能问题,这些问题往往相互关联,形成性能下降的连锁反应。用户观察到的具体症状包括:文件描述符耗尽、内存持续增长、Prometheus 指标收集延迟、socket goroutine 和指标对象堆积、数据聚合过程中存在的锁争用、Prometheus 指标丢失,以及 Prometheus 服务器从 Ingress-NGINX Controller 收集指标时发生的超时。本报告旨在深入分析这些现象,揭示其深层原因,并提供基于现有证据的缓解策略。

2. Ingress-NGINX Controller 中的文件描述符耗尽 #

理解 NGINX 中的文件描述符 #

文件描述符(FDs)是操作系统为进程提供的抽象句柄,用于访问各种 I/O 资源,包括网络套接字、打开的文件和管道。NGINX 作为一款事件驱动的高性能 Web 服务器,高度依赖文件描述符来管理并发连接、代理请求以及处理内部通信。每一个活跃的客户端连接、每一个到上游服务器的连接,以及 NGINX 内部的各个组件,通常都会消耗一个或多个文件描述符。

文件描述符限制达到的症状 #

当 NGINX 达到其文件描述符限制时,通常会在错误日志中出现明确的错误信息,例如 'too many open files'socket() failed (24: Too many open files)accept() failed (24: Too many open files) 4。这些错误直接表明 NGINX 无法打开新的连接或文件。在可观测到的层面,系统性能会显著下降,表现为延迟增加、连接失败以及最终的服务不可用 4。此外,控制器日志中出现

pthread_create() failed (11: Resource temporarily unavailable) 等间接指标也可能指向资源耗尽,这可能与文件描述符限制或内存问题有关 5。

根本原因与证据 #

  • 配置限制不足: 最直接的原因是 NGINX 在高并发负载下触及其系统或进程级别的文件描述符限制 4。这通常是由于默认配置未能适应实际的流量需求。

  • 内部文件描述符泄漏(缺陷/漏洞):

    • 历史泄漏的证据: ingress-nginx v1.11 及更早版本中存在内部文件描述符泄漏的严重漏洞(例如 CVE-2025-1974),这些泄漏可能导致严重的安全漏洞,允许攻击者访问宿主机文件系统,甚至实现容器逃逸 6。这使得文件描述符问题从单纯的性能瓶颈上升为关键的安全风险。

    • 特定配置导致的泄漏: GitHub 上的问题报告显示,在处理来自上游服务器的 Transfer-Encoding: chunked 响应时,特别是当客户端也发送请求体时,可能导致 “Bad file descriptor” 错误 8。这表明某些特定的流量模式或上游服务配置可能触发控制器内部的文件描述符泄漏。例如,一个 GitHub 问题明确指出,当上游响应采用

      Transfer-Encoding: chunked 且设置了 Content-Length,同时客户端发送请求体时,就会出现 “Bad file descriptor” 错误。该问题还建议通过添加 nginx.ingress.kubernetes.io/proxy-request-buffering: "off" 注解作为潜在的解决方案 8。这提供了关于文件描述符泄漏机制的具体证据,超越了简单的限制耗尽。

缓解策略 #

  • 增加系统级文件描述符限制:
    • 编辑 /etc/sysctl.conf 文件,添加或更新 fs.file-max = <新限制>(例如,fs.file-max = 100000)。
    • 使用 sudo sysctl -p 命令应用更改 4。
  • 增加进程级文件描述符限制:
    • 编辑 /etc/security/limits.conf 文件,为 NGINX 用户设置 softhard nofile 限制(例如,nginx soft nofile 100000nginx hard nofile 100000)。
    • 确保 NGINX 服务以正确的用户身份运行 4。
  • 重启 NGINX: 完成上述系统级更改后,需要重启 NGINX 服务以使新限制生效 4。
  • 解决特定泄漏场景:
    • 升级 ingress-nginx 优先升级到已打补丁的版本(v1.11.5、v1.12.1 或更高版本),以解决已知的文件描述符泄漏和关键安全漏洞 1。这是一项至关重要的预防措施。
    • 应用特定注解: 如果怀疑存在 chunked 编码导致的问题,可以考虑对相关的 Ingress 资源应用 nginx.ingress.kubernetes.io/proxy-request-buffering: "off" 注解 8。

更深层次的理解 #

文件描述符耗尽问题并非总是简单的配置不足。虽然增加限制是标准的运维操作 4,但如果文件描述符问题持续出现,特别是伴随

pthread_create() 失败 5 或 “Bad file descriptor” 错误 8,这强烈表明存在更深层次的软件缺陷或由特定流量模式引起的泄漏,而不仅仅是简单的容量问题。例如,当系统日志中反复出现

pthread_create() 错误时,这可能表明系统资源(包括文件描述符)已接近耗尽,进而影响了进程创建新的线程。这揭示了:仅仅提高限制可能只会掩盖更深层的问题,导致问题最终再次出现或引发内存溢出(OOM)。

此外,研究发现,旧版本 ingress-nginx 中的内部文件描述符泄漏与关键安全漏洞(例如 CVE-2025-1974)直接相关 6。这些漏洞可能导致容器逃逸,允许攻击者访问宿主机文件系统,甚至完全控制 Kubernetes 集群。这意味着文件描述符问题不再仅仅是性能优化的问题,而是一项需要立即采取行动的关键安全修复。

3. Ingress-NGINX Controller 中的内存增长与泄漏 #

观察到的症状 #

Ingress-NGINX Controller 的内存问题通常表现为持续且无界限的内存消耗增长,最终导致内存溢出(OOM)错误和 Pod 崩溃 9。在这些情况下,指标处理程序的响应会显著延迟 9,导致吞吐量指标变得不可靠或出现延迟,从而使监控变得困难 9。此外,Ingress-NGINX Controller Pod 可能会无法通过就绪探针,并进入反复重启的循环 10。在配置更改或伸缩事件期间,内存使用模式也可能变得不稳定且出现“尖峰” 12。

根本原因与证据 #

  • Prometheus Summary 指标(主要泄漏源):

    • 昂贵的分位数重新计算: ingress_upstream_latency_seconds 指标,当其被实现为 Prometheus 的 Summary 类型时,需要进行计算密集型且非无锁的分位数重新计算 13。这个过程是性能瓶颈的一个重要来源。

    • 锁争用: Summary 指标更新的非无锁特性导致控制器内部出现“大规模锁等待”,这会阻塞其他关键操作,包括传入指标数据的处理 13。

    • 内存累积: 控制器 socket.go 组件中的 handleMessage 函数负责将 socket 数据解组到内存中。当发生锁争用时,这部分内存无法及时释放,导致未释放内存的持续累积 13。

    • Goroutine 泄漏: SocketCollector 会持续生成新的 goroutine 来处理 handleMessages。当 latencyMetric.Observe()(与 ingress_upstream_latency_seconds 相关联)尝试获取锁时,这些 goroutine 会被阻塞并累积,导致 goroutine 数量“急剧增加”,并显著促进内存增长 9。这已被确认为一种 goroutine 泄漏 9。

      • 对研究材料的分析(9)表明,这些片段是用户查询中关于内存增长、goroutine 累积和指标相关锁争用的最直接和全面的证据。13 明确指出

        Summary 指标的非无锁特性是“大规模锁等待和内存增加”的原因。9 证实了这一点,将

        ingress_upstream_latency_seconds 标识为罪魁祸首,并将其直接与 OOM 和通过 socket.go 代码引起的 goroutine 累积联系起来,并证实其移除可以修复泄漏。

  • 配置重载与 NGINX Worker 进程管理:

    • 重载期间过多的 Worker 进程生成: Kubernetes 部署更新或 Ingress 资源更改会触发 NGINX 配置的重复重载。在这些重载期间,NGINX 可能会生成新的 worker 进程。如果旧的 worker 进程未能优雅关闭,它们可能会堆积,导致整体内存消耗急剧增加(例如,从 480MB 增加到超过 5GB) 11。

    • 外部因素导致的 Worker 进程延迟关闭: 外部依赖(例如,由于网络问题导致 OCSP Stapling 超时)可能阻止旧的 NGINX worker 进程终止。这导致它们保持活跃,消耗内存并堆积,尤其是在频繁重载期间 11。

    • Lua 片段泄漏: 使用 nginx.ingress.kubernetes.io/configuration-snippet 注解与 Lua 脚本结合,特别是那些实现自适应缓存并带有异步计时器的脚本,可能导致 Lua 堆、NGINX 计时器队列和上下文切换的无界增长。这最终会导致 NGINX worker 进程的 OOM 5。

      • 对研究材料的分析(5)表明,这些片段共同证明了内存问题并非仅与指标相关。11 直接将“配置重载时内存消耗过高”与 OCSP 超时导致的 worker 进程累积联系起来。5 和 5 指出“Lua 片段泄漏”是 worker 内存泄漏和 OOM 的原因,尤其是在大型 vCPU 节点上与内存限制结合时。15 讨论了复杂 SSL 配置如何增加每次重载的内存。12 显示了与 worker 进程循环相关的“内存增加和不稳定”。这突显了

        ingress-nginx 中内存问题的多方面性质。

  • 低效的 NGINX 配置/注解:

    • 某些注解的使用,例如 nginx.ingress.kubernetes.io/enable-modsecurity: 'true'nginx.ingress.kubernetes.io/enable-owasp-core-rules: 'true',已被确定为效率低下,导致显著的资源消耗和内存尖峰 10。

缓解策略 #

  • 排除问题指标:
    • 针对 Summary 指标引起的泄漏,最有效的即时解决方案是使用控制器的命令行参数 --exclude-socket-metrics=nginx_ingress_controller_ingress_upstream_latency_seconds 来排除 ingress_upstream_latency_seconds 指标 9。
    • 值得注意的是,ingress_upstream_latency_seconds 指标已被标记为弃用,并且一个移除它的拉取请求已经合并。未来的版本可能不再包含此指标,从而从根本上解决此问题 9。
  • 管理指标基数:
    • 使用 --metrics-per-host=false 禁用按主机指标,以减少多主机环境中的时间序列数量 2。
    • 默认禁用路径标签,因为其曾因基数问题而被更改 2。
    • 在较新版本中,可以使用 --report-status-classes=true 将 HTTP 响应报告为 2xx、3xx、4xx、5xx 等组,而不是每个具体状态码。这可以显著减少 nginx_ingress_controller_requests 等指标的基数 2。
    • 通过 --bucket-factor--max-buckets--time-buckets--length-buckets--size-buckets 等标志控制直方图桶配置,以管理直方图指标的粒度 2。
    • 采用 Prometheus 记录规则或外部工具(例如 Grafana Cloud 的基数管理功能)来预聚合高基数数据(例如,按主机和路径求和),并在仪表板中使用这些聚合后的指标,而不是原始的高基数数据 2。
  • 优化 NGINX 配置和 Worker 管理:
    • 使用 worker-processes 配置选项设置 NGINX worker 进程的具体限制,而不是依赖默认值(CPU 数量),尤其是在具有大量 vCPU 的节点上,以防止 worker 进程静默 OOM 5。
    • 增加控制器 Pod 在大型节点上的内存限制,但需要理解这仅是症状缓解,如果存在泄漏,问题仍可能复发 5。
    • 调查并解决导致旧 worker 进程在重载期间挂起的外部依赖(例如,影响 OCSP stapling 的网络问题) 11。
    • 仔细审查和优化 nginx.ingress.kubernetes.io/configuration-snippet 注解,特别是涉及 Lua 脚本的注解,以确保它们不会引入无界资源增长 5。
    • 如果性能影响超过其益处,应避免或仔细配置资源密集型注解,如 enable-modsecurityenable-owasp-core-rules 10。
    • 在适当的工作负载下,考虑将 NGINX worker 进程数量减少到 1,并禁用 TLS 会话恢复 (ssl_session_tickets off) 和 HTTP keepalive,因为这些可以减少内存占用 15。

更深层次的理解 #

用户观察到的内存增长、socket goroutine 累积和指标收集延迟并非孤立的问题,而是由 Summary 指标实现导致的因果链条中的各个环节。ingress_upstream_latency_seconds 指标的 Summary 类型在进行分位数重新计算时,其操作并非无锁,且计算成本高昂 13。这种特性导致控制器内部出现“大规模锁等待”,进而阻塞后续指标数据的处理 13。由于锁争用,

handleMessage 函数(在 socket.go 中)解组到内存中的 socket 数据无法及时释放,导致内存持续累积。同时,SocketCollector 不断生成新的 handleMessages goroutine;当 latencyMetric.Observe() 尝试获取锁时,这些 goroutine 就会被阻塞并大量堆积,导致 goroutine 数量“急剧增加”,并显著加剧内存增长问题 9。这是一种典型的 goroutine 泄漏 14。这清晰地展示了:特定的

Summary 指标实现导致争用,进而引起 goroutine 堆积和内存耗尽,因为数据无法高效处理和释放。

尽管 Summary 指标的缺陷是具体的问题,但高指标基数这一更广泛的问题(2)会显著放大指标管道中任何潜在的性能问题。时间序列数量越多,需要处理的数据量就越大,内存消耗也越高,锁争用的可能性也越大,从而使现有问题变得更加严重和频繁。例如,当控制器进行横向扩展时,

controller_pod 标签会线性增加时间序列的总数 2。这意味着:虽然

Summary 指标是特定的缺陷,但高基数会像放大器一样作用。如果指标收集系统因 Summary 的锁定行为而效率低下,那么生成“过多时间序列”自然会使问题变得更糟,导致更频繁和更严重的 OOM 和延迟。

此外,ingress-nginx 的内存问题并非完全归因于指标收集。频繁的配置重载(例如,由后端服务的滚动更新触发 11)、复杂的 NGINX 配置 10 或使用特定功能(如 Lua 片段 5)都可能独立导致显著的内存峰值和 OOM。例如,当 Kubernetes 部署更新时,NGINX 会触发重复的配置重载,这可能导致旧的 worker 进程未能优雅关闭而堆积,从而大幅增加内存消耗 11。这表明:即使解决了指标相关的内存泄漏问题,其他配置或操作模式仍可能导致内存问题,因此需要采取更全面的性能调优方法,而不仅仅局限于指标。

4. Prometheus 指标丢失与抓取超时 #

症状 #

用户报告 Prometheus 服务器在尝试从 ingress-nginx controller/metrics 端点抓取指标时发生超时 [用户查询]。这导致 Prometheus 指标丢失,进而造成监控数据出现空白 [用户查询]。直接观察 /metrics 端点会发现其响应存在显著延迟(例如,使用 curl 命令响应时间长达 13 秒) 9。

根本原因与证据 #

  • 控制器性能下降: Prometheus 抓取超时和随后的指标丢失的主要原因是 ingress-nginx controller 内部的性能瓶颈,这使其无法及时提供 /metrics 端点服务。

    • 指标生成期间的锁争用: 如第 3 节详细所述,由 Summary 指标的分位数重新计算(ingress_upstream_latency_seconds 13)以及由此导致的 goroutine 和内存累积 9 引起的锁争用,直接导致

      /metrics 端点变得延迟且无响应。当 Prometheus 尝试抓取时,它会遇到一个缓慢或无响应的端点,从而触发超时。

      • 对研究材料的分析(9)提供了直接证据:“在高压下,指标处理程序的响应会延迟。一个简单的

        curl 命令显示该端点需要大约 13 秒才能响应。”这清楚地解释了 Prometheus 为什么会超时,直接将症状与控制器的内部性能问题联系起来。

  • Prometheus 抓取配置: 虽然这不是控制器性能问题的根本原因,但 Prometheus 中 scrape_timeout 配置不当可能会加剧问题。如果超时设置过低,即使控制器指标端点出现短暂的减速,Prometheus 也会过早地放弃抓取 16。

    • 对研究材料的分析(16)详细说明了 Prometheus 配置中的

      scrape_timeout 参数,并指出它“不能大于抓取间隔”。这对于 Prometheus 的正常运行至关重要,但调整它只是对控制器端问题的症状性修复。

缓解策略 #

  • 优先解决控制器性能问题: 最有效和可持续的缓解措施是解决 ingress-nginx controller 内部潜在的内存和 goroutine 问题。这包括排除或移除有问题的 ingress_upstream_latency_seconds 指标,并实施基数管理策略(如第 3 节所述) 2。
  • 调整 Prometheus 抓取配置(作为微调):
    • 如果已知控制器的指标端点偶尔会变慢但总体健康,则可以增加 Prometheus 配置中的 scrape_timeout。然而,这不应被视为解决控制器性能问题根本原因的替代方案 16。
    • 确保 scrape_timeout 始终小于或等于 scrape_interval,以防止抓取重叠和意外行为 16。
  • 监控和警报抓取失败: 对 Prometheus 抓取失败、高错误率或抓取任务执行时间异常长的情况实施健全的监控和警报 17。这有助于及时发现控制器指标端点何时变得无响应,从而指示需要进行更深入的调查。

更深层次的理解 #

Prometheus 抓取超时是 ingress-nginx 控制器内部性能下降的直接表现,特别是其无法及时响应 /metrics 端点 9。仅仅增加 Prometheus 的

scrape_timeout 16 而不解决控制器底层问题,只会延迟发现系统故障,而不能解决根本问题。例如,当

ingress-nginx 的指标处理程序响应延迟长达 13 秒时 9,Prometheus 即使配置了默认的 10 秒超时也会立即超时。这表明:虽然可以调整

scrape_timeout,但这只是治标不治本。真正的问题在于控制器由于锁争用和内存问题而无法高效地提供指标服务。超时是底层性能问题的结果,而非其原因。

指标丢失和抓取超时直接损害了监控和可观测性数据的可靠性和完整性。这可能导致 SRE 和运维人员“盲目运行” 9,从而使诊断其他系统问题、响应事件或确保整体系统健康和遵守服务水平指标(SLI)变得异常困难。例如,如果关键的

ingress_upstream_latency_seconds 指标因抓取超时而丢失,运维人员将失去对上游服务延迟的关键洞察,从而无法及时发现和解决潜在的性能瓶颈。这揭示了:这些性能问题会 Cascading 地导致关键运维智能的丧失,使得未来的故障排除、主动管理以及 SLI/SLO 的遵守变得更加困难。

5. 关键 Prometheus 指标与基数管理 #

指标对可观测性的重要性 #

ingress-nginx 为 NGINX/NGINX Plus 和 Ingress Controller 本身提供了丰富的指标,这些指标对于理解其健康状况和性能至关重要 2。这些指标涵盖了 CPU 和内存使用情况、连接计数、请求/响应持续时间以及重载成功率等关键数据点 18。

高基数挑战 #

ingress-nginx 因“生成过多时间序列”而闻名 2。这可能会使 Prometheus 不堪重负,导致存储成本增加、查询性能下降以及 Prometheus 本身可能出现 OOM。历史上,包含精确请求路径或为通配符主机 Ingress 启用按主机指标曾导致“基数爆炸” 2。

controller_pod 标签虽然有助于按实例进行指标分解,但会随着控制器横向扩展而线性增加总时间序列数量 2。直方图指标,例如

nginx_ingress.controller.response.*,默认情况下也具有高标签基数 18。

特定问题指标:ingress_upstream_latency_seconds #

当启用时,此指标被实现为 Prometheus 的 Summary 类型 3。

Summary 指标在进行分位数计算时计算成本高昂且非无锁,导致控制器指标收集管道内出现显著的锁争用 13。它无法在 Prometheus 中跨多个实例可靠聚合,使其在集群范围的延迟分析方面不如

Histogram 有用 19。该指标直接导致内存增加、goroutine 累积、OOM 和指标收集延迟 9。此指标已被标记为弃用,并且一个移除它的拉取请求(#11795)已经合并,这表明官方承认了其问题性质 9。

基数与指标管理的缓解策略 #

  • 禁用按主机指标: 使用 --metrics-per-host=false 标志(或等效的 Helm 值)可防止按请求主机名标记指标。这在多主机环境中显著减少了时间序列数量 2。
  • 保持路径标签禁用: 默认情况下,ingress-nginx 避免使用完整 URL 路径作为标签。确保保持此默认行为,因为启用按路径指标可能导致严重的基数问题 2。
  • 按类别聚合状态码: 使用 --report-status-classes=true 运行控制器,以便将 HTTP 响应报告为聚合组(2xx、3xx、4xx、5xx),而不是每个单独的状态码(例如,200、404、500)。这显著降低了请求指标的基数 2。
  • 控制直方图桶配置: 利用内置选项,如 --bucket-factor--max-buckets--time-buckets--length-buckets--size-buckets,精细调整直方图指标的粒度和桶数量,从而控制其基数 2。
  • 使用记录规则预聚合数据: 采用 Prometheus 记录规则或外部工具(例如 Grafana Cloud 的基数管理仪表板)来预聚合高基数数据(例如,对主机和路径求和),并在仪表板和警报中使用这些聚合指标,而不是原始的高基数数据 2。
  • 显式启用指标(升级后): 如果将 ingress-nginx 升级到 v1.12 或更高版本,请记住 --enable-metrics CLI 参数默认情况下是禁用的。必须将其显式设置为 true 才能重新启用指标暴露 20。

关键监控指标(示例) #

  • 控制器健康状况: nginx_ingress.controller.cpu.timenginx_ingress.controller.mem.residentnginx_ingress.controller.mem.virtualnginx_ingress.controller.last.reload.successnginx_ingress.controller.reload.success 18。
  • NGINX 性能: nginx_ingress.nginx.connections.currentnginx_ingress.nginx.connections.totalnginx_ingress.nginx.requests.totalnginx_ingress.nginx.bytes.readnginx_ingress.nginx.bytes.write 18。
  • Go 运行时指标: go_goroutines(对于检测 goroutine 泄漏至关重要) 14。

表:Ingress-NGINX 性能推荐指标配置 #

下表提供了针对 Ingress-NGINX Controller 性能优化的关键指标配置建议,旨在帮助运维人员快速识别并应用可操作的更改。

配置标志/Helm 值描述对基数/性能的影响推荐设置
--metrics-per-host / controller.metrics.perHost禁用按主机指标减少多主机环境中的时间序列数量false
--report-status-classes / controller.metrics.reportStatusClasses聚合 HTTP 状态码(例如 2xx、3xx)大幅减少 nginx_ingress_controller_requests 等指标的基数true
--exclude-socket-metrics=nginx_ingress_controller_ingress_upstream_latency_seconds排除有问题的 Summary 指标直接解决内存/goroutine 泄漏和锁争用问题nginx_ingress_controller_ingress_upstream_latency_seconds
--enable-metrics / controller.metrics.enabled启用 Prometheus 指标(v1.12+ 默认禁用)确保指标正常暴露true

此表通过整合来自不同来源的建议 2,为解决指标和基数相关的性能问题提供了实用指南。

更深层次的理解 #

Prometheus 指标类型的选择(SummaryHistogram)对系统稳定性有着深远的影响,这不仅仅是数据表示的问题。Summary 指标的底层实现(非无锁操作 13)可以直接导致严重的性能下降、资源泄漏和系统不稳定。例如,用户查询中提到的“锁的争用”和“指标对象堆积”问题,正是由于 Prometheus

Summary 指标的非无锁特性导致“大规模锁等待” 13。早在 2019 年,就有功能请求提出为

upstream_latency_seconds 添加 _histogram 版本,因为 Summary 无法按照 Prometheus 文档进行有效聚合 19。这表明:指标类型的设计选择直接导致了关键的性能缺陷,这说明了看似微小的技术决策也可能对整个系统稳定性产生重大影响。

在可观测性中,粒度(高基数)与指标收集的性能开销之间存在固有的权衡。盲目启用所有指标或使用过于细粒度的标签可能会使监控系统本身(Prometheus)和生成指标的应用程序(ingress-nginx)不堪重负,从而导致监控自身的“拒绝服务” 2。例如,为每个请求路径启用指标曾导致“基数爆炸” 2。这意味着:并非“数据越多越好”。过高的基数本身可能成为性能问题,迫使运维人员必须战略性地决定哪些指标对于 SLI 和系统健康真正有意义,哪些可以“削减或整合” 2。

6. 进一步调查与长期优化建议 #

性能分析与监控 #

  • 如果问题持续存在,可利用 Go 语言的性能分析工具(例如 pprof)来识别 goroutine 泄漏和内存热点 9。
  • 在 Prometheus 中持续监控 go_goroutines 指标,以检测 goroutine 的累积情况 14。
  • 定期检查堆内存剖析,以发现内存泄漏 9。
  • 密切关注 NGINX worker 进程及其内存消耗 11。

版本管理 #

  • 确保 ingress-nginx controller 运行在已打补丁的版本(v1.11.5、v1.12.1 或更高版本),以解决关键漏洞和已知泄漏 1。
  • 请注意,在 v1.12+ 版本中,指标可能默认被禁用,需要显式地通过 --enable-metrics=true 重新启用 20。

配置审查 #

  • 定期审查 Ingress 配置中的复杂注解,特别是 configuration-snippet 和安全相关注解,这些注解可能影响性能 5。
  • 如果配置重载期间观察到超时,应审计外部依赖项,如 OCSP stapling 11。

Prometheus 配置调优 #

  • 根据观察到的负载和控制器性能,审查 Prometheus 配置中的 scrape_intervalscrape_timeoutsample_limitlabel_limit 16。

表:Ingress-NGINX 性能常见故障排除步骤 #

下表提供了一个结构化的指南,用于诊断 Ingress-NGINX Controller 中常见的性能问题,将症状与故障排除方法和相关数据点联系起来。

症状故障排除方法相关日志/指标潜在原因
“Too many open files” 错误检查 ulimit -nfs.file-maxNGINX 错误日志,ulimit -n 输出文件描述符限制过低,文件描述符泄漏
内存 OOM,高内存使用检查 nginx_ingress.controller.mem.* 指标,堆剖析,go_goroutines控制器日志,Prometheus,pprof 工具Summary 指标泄漏,配置重载问题,Lua 片段问题,低效注解
Prometheus 抓取超时检查 /metrics 端点延迟,Prometheus 抓取日志Prometheus 目标状态,curl /metrics 命令控制器指标端点因锁争用导致延迟

此表作为 SRE 和 DevOps 工程师的实用诊断指南,提供了一个快速参考,以启动对所讨论问题的调查。

7. 结论 #

本报告深入分析了 Ingress-NGINX Controller 中常见且复杂的性能瓶颈。分析表明,这些问题具有多方面性,其中 ingress_upstream_latency_seconds 这个 Prometheus Summary 指标是导致内存和 goroutine 泄漏、锁争用以及 Prometheus 监控问题的主要因素。该指标的非无锁特性和高昂的分位数计算成本,在负载下引发了连锁反应,导致控制器资源耗尽和监控数据失真。

除了指标问题,NGINX 的配置重载机制、worker 进程管理(特别是当外部依赖如 OCSP stapling 出现问题时),以及某些低效的配置注解(如 ModSecurity)也可能独立或共同导致显著的内存增长和性能下降。

因此,解决这些性能挑战需要采取全面的优化方法,包括:调整系统级文件描述符限制、精细化 ingress-nginx 配置(尤其是避免问题指标和管理指标基数)、以及审慎地调整 Prometheus 抓取配置。持续的监控和及时更新到最新版本的 ingress-nginx 至关重要,这不仅能利用已修复的缺陷,还能获得最新的安全补丁,从而确保系统的长期稳定性和可观测性。