nginx-lua-prometheus

Nginx 的 Prometheus 指标库

$ opm get knyar/nginx-lua-prometheus

[!覆盖率状态](https://coveralls.io/github/knyar/nginx-lua-prometheus?branch=master)

Nginx 的 Prometheus 指标库

这是一个 Lua 库,可与 Nginx 一起使用,用于跟踪指标并在单独的网页上公开它们,以便 Prometheus 获取。

安装

要使用此库,您需要 [ngx_lua]( https://github.com/openresty/lua-nginx-module) nginx 模块。您可以使用支持 Lua 的基于 Nginx 的服务器,如 OpenResty,或使用启用该模块的常规 Nginx 服务器:例如,在 Debian 10 上,您可以简单地安装 libnginx-mod-http-lua(但如果您使用的是更高版本的 Debian,请阅读 [已知问题]( #known-issues))。

库文件 - prometheus.lua - 需要在 LUA_PATH 中可用。如果这是您唯一使用的 Lua 库,您只需将 lua_package_path 指向检出此 git 存储库的目录(请参阅下面的示例)。

OpenResty 用户将在 opm 中找到此库。它也可以通过 luarocks 获得。

快速入门指南

要跟踪按服务器名称细分的请求延迟以及按服务器名称和状态细分的请求计数,请将以下内容添加到 nginx.confhttp 部分

    lua_shared_dict prometheus_metrics 10M;
    lua_package_path "/path/to/nginx-lua-prometheus/?.lua;;";
    
    init_worker_by_lua_block {
      prometheus = require("prometheus").init("prometheus_metrics")
    
      metric_requests = prometheus:counter(
        "nginx_http_requests_total", "Number of HTTP requests", {"host", "status"})
      metric_latency = prometheus:histogram(
        "nginx_http_request_duration_seconds", "HTTP request latency", {"host"})
      metric_connections = prometheus:gauge(
        "nginx_http_connections", "Number of HTTP connections", {"state"})
    }
    
    log_by_lua_block {
      metric_requests:inc(1, {ngx.var.server_name, ngx.var.status})
      metric_latency:observe(tonumber(ngx.var.request_time), {ngx.var.server_name})
    }

  • 为您的指标配置一个名为 prometheus_metrics 的共享字典,大小限制为 10MB;

  • 注册一个名为 nginx_http_requests_total 的计数器,它有两个标签:hoststatus

  • 注册一个名为 nginx_http_request_duration_seconds 的直方图,它有一个标签 host

  • 注册一个名为 nginx_http_connections 的仪表盘,它有一个标签 state

  • 在每个 HTTP 请求上测量其延迟,将其记录在直方图中并递增计数器,将当前服务器名称设置为 host 标签,将 HTTP 状态码设置为 status 标签。

最后一步是配置一个单独的服务器,它将公开这些指标。请确保它只能从您的 Prometheus 服务器访问

    server {
      listen 9145;
      allow 192.168.0.0/16;
      deny all;
      location /metrics {
        content_by_lua_block {
          metric_connections:set(ngx.var.connections_reading, {"reading"})
          metric_connections:set(ngx.var.connections_waiting, {"waiting"})
          metric_connections:set(ngx.var.connections_writing, {"writing"})
          prometheus:collect()
        }
      }
    }

指标将在 http://your.nginx:9145/metrics 上可用。请注意,此示例中的仪表盘指标包含从 nginx 全局状态获得的值,因此它们在指标返回给客户端之前立即设置。

API 参考

init()

语法: require("prometheus").init(dict_name, [options]])

初始化模块。这应该从 nginx 配置的 init_worker_by_lua_block 部分调用一次。

  • dict_name 是用于存储所有指标的 nginx 共享字典的名称。如果未指定,默认为 prometheus_metrics

  • options 是可以提供的配置选项表。接受的选项是

    • prefix(字符串):指标名称前缀。此字符串将在输出时添加到指标名称之前。

    • error_metric_name(字符串):可用于更改错误指标的默认名称(有关详细信息,请参阅 "内置指标")。

    • sync_interval(数字):设置每个工作线程的计数器和键索引的同步间隔(以秒为单位)。这将为计数器指标递增以及指标重置/删除设置最终一致性的边界。默认为 1。

返回一个 prometheus 对象,该对象应该用于注册指标。

示例

    init_worker_by_lua_block {
      prometheus = require("prometheus").init("prometheus_metrics", {sync_interval=3})
    }

prometheus:counter()

语法: prometheus:counter(name, description, label_names)

注册一个计数器。应该从 [init_worker_by_lua_block]( https://github.com/openresty/lua-nginx-module#init_worker_by_lua_block) 部分为每个计数器调用一次。

  • name 是指标的名称。

  • description 是将与指标一起呈现给 Prometheus 的文本描述。可选(如果您仍然需要定义标签名称,则传递 nil)。

  • label_names 是指标的标签名称数组。可选。

Prometheus 文档的命名部分 提供了有关选择指标和标签名称的良好指南。

返回一个 counter 对象,该对象以后可以递增。

示例

    init_worker_by_lua_block {
      prometheus = require("prometheus").init("prometheus_metrics")
    
      metric_bytes = prometheus:counter(
        "nginx_http_request_size_bytes", "Total size of incoming requests")
      metric_requests = prometheus:counter(
        "nginx_http_requests_total", "Number of HTTP requests", {"host", "status"})
    }

prometheus:gauge()

语法: prometheus:gauge(name, description, label_names)

注册一个仪表盘。应该从 [init_worker_by_lua_block]( https://github.com/openresty/lua-nginx-module#init_worker_by_lua_block) 部分为每个仪表盘调用一次。

  • name 是指标的名称。

  • description 是将与指标一起呈现给 Prometheus 的文本描述。可选(如果您仍然需要定义标签名称,则传递 nil)。

  • label_names 是指标的标签名称数组。可选。

返回一个 gauge 对象,该对象以后可以设置。

示例

    init_worker_by_lua_block {
      prometheus = require("prometheus").init("prometheus_metrics")
    
      metric_connections = prometheus:gauge(
        "nginx_http_connections", "Number of HTTP connections", {"state"})
    }

prometheus:histogram()

语法: prometheus:histogram(name, description, label_names, buckets)

注册一个直方图。应该从 [init_worker_by_lua_block]( https://github.com/openresty/lua-nginx-module#init_worker_by_lua_block) 部分为每个直方图调用一次。

  • name 是指标的名称。

  • description 是文本描述。可选。

  • label_names 是指标的标签名称数组。可选。

  • buckets 是一个数字数组,用于定义桶边界。可选,默认为 20 个延迟桶,涵盖从 5 毫秒到 10 秒(以秒为单位)的范围。

返回一个 histogram 对象,该对象以后可用于记录样本。

示例

    init_worker_by_lua_block {
      prometheus = require("prometheus").init("prometheus_metrics")
    
      metric_latency = prometheus:histogram(
        "nginx_http_request_duration_seconds", "HTTP request latency", {"host"})
      metric_response_sizes = prometheus:histogram(
        "nginx_http_response_size_bytes", "Size of HTTP responses", nil,
        {10,100,1000,10000,100000,1000000})
    }

prometheus:collect()

语法: prometheus:collect()

以与 Prometheus 兼容的文本格式呈现所有指标。这应该在 content_by_lua_block 中调用,以在单独的 HTTP 页面上公开指标。

示例

    location /metrics {
      content_by_lua_block { prometheus:collect() }
      allow 192.168.0.0/16;
      deny all;
    }

prometheus:metric_data()

语法: prometheus:metric_data()

将指标数据返回为字符串数组。

counter:inc()

语法: counter:inc(value, label_values)

递增先前注册的计数器。这通常从 log_by_lua_block 全局调用或按服务器/位置调用。

  • value 是应该添加到计数器的值。默认为 1。

  • label_values 是标签值的数组。

标签值的数量应与使用 prometheus:counter() 注册计数器时定义的标签名称的数量相匹配。对于没有标签的计数器,不应该提供任何标签值。不可打印的字符将从标签值中删除。

示例

    log_by_lua_block {
      metric_bytes:inc(tonumber(ngx.var.request_length))
      metric_requests:inc(1, {ngx.var.server_name, ngx.var.status})
    }

counter:del()

语法: counter:del(label_values)

删除先前注册的计数器。这通常在您不再需要观察此计数器(或此计数器中具有特定标签值的指标)时调用。如果此计数器具有标签,您必须传递 label_values 以删除此计数器的特定指标。如果您想删除具有标签的计数器的所有指标,则应调用 Counter:reset()

  • label_values 是标签值的数组。

标签值的数量应与使用 prometheus:counter() 注册计数器时定义的标签名称的数量相匹配。对于没有标签的计数器,不应该提供任何标签值。不可打印的字符将从标签值中删除。

此函数将在删除指标之前等待 sync_interval,以允许所有工作线程同步其计数器。

counter:reset()

语法: counter:reset()

删除先前注册的计数器的所有指标。如果此计数器没有标签,它与 Counter:del() 函数完全相同。如果此计数器有标签,它将删除具有不同标签值的 所有指标。

此函数将在删除指标之前等待 sync_interval,以允许所有工作线程同步其计数器。

gauge:set()

语法: gauge:set(value, label_values)

设置先前注册的仪表盘的当前值。这可以从 log_by_lua_block 全局调用或按服务器/位置调用,以在每个请求上修改仪表盘,或从 content_by_lua_blockprometheus::collect() 之前调用,以返回实时值。

  • value 是仪表盘应该设置的值。必需的。

  • label_values 是标签值的数组。

gauge:inc()

语法: gauge:inc(value, label_values)

递增或递减先前注册的仪表盘。当您想要观察可以同时增加和减少的指标的实时值时,这通常被调用。

  • value 是应该添加到仪表盘的值。当您需要减少仪表盘的值时,它可以是负值。默认为 1。

  • label_values 是标签值的数组。

标签值的数量应与使用 prometheus:gauge() 注册仪表盘时定义的标签名称的数量相匹配。对于没有标签的仪表盘,不应该提供任何标签值。不可打印的字符将从标签值中删除。

gauge:del()

语法: gauge:del(label_values)

删除先前注册的仪表盘。这通常在您不再需要观察此仪表盘(或此仪表盘中具有特定标签值的指标)时调用。如果此仪表盘具有标签,您必须传递 label_values 以删除此仪表盘的特定指标。如果您想删除具有标签的仪表盘的所有指标,则应调用 Gauge:reset()

  • label_values 是标签值的数组。

标签值的数量应与使用 prometheus:gauge() 注册仪表盘时定义的标签名称的数量相匹配。对于没有标签的仪表盘,不应该提供任何标签值。不可打印的字符将从标签值中删除。

gauge:reset()

语法: gauge:reset()

删除先前注册的仪表盘的所有指标。如果此仪表盘没有标签,它与 Gauge:del() 函数完全相同。如果此仪表盘有标签,它将删除具有不同标签值的 所有指标。

histogram:observe()

语法: histogram:observe(value, label_values)

在先前注册的直方图中记录一个值。通常从 log_by_lua_block 全局调用或按服务器/位置调用。

请注意,记录观察需要递增几个直方图,这不会原子地发生,并且可能会与指标收集发生冲突(请参阅 #161)。

  • value 是应该记录的值。必需的。

  • label_values 是标签值的数组。

示例

    log_by_lua_block {
      metric_latency:observe(tonumber(ngx.var.request_time), {ngx.var.server_name})
      metric_response_sizes:observe(tonumber(ngx.var.bytes_sent))
    }

histogram:reset()

语法: histogram:reset()

删除先前注册的直方图的所有指标。

此函数将在删除指标之前等待 sync_interval,以允许所有工作线程同步其计数器。

内置指标

如果模块遇到错误(例如,当 lua_shared_dict 变得满时),它将递增一个名为 nginx_metric_errors_total 的错误指标(除非在 init() 中配置了另一个名称)。您可能希望为此指标配置警报。

注意事项

在 stream 模块中的使用

目前,在 Nginx 中无法在 HTTP 和 Stream 模块之间共享字典。如果您使用此库从 stream 模块收集指标,则需要配置一个单独的端点来返回它们。以下是一个示例。

    server {
      listen 9145;
      content_by_lua_block {
        local sock = assert(ngx.req.socket(true))
        local data = sock:receive()
        local location = "GET /metrics"
        if string.sub(data, 1, string.len(location)) == location then
          ngx.say("HTTP/1.1 200 OK")
          ngx.say("Content-Type: text/plain")
          ngx.say("")
          ngx.say(table.concat(prometheus:metric_data(), ""))
        else
          ngx.say("HTTP/1.1 404 Not Found")
        end
      }
    }

已知问题

libnginx-mod-http-lua 在某些 Debian 和 Ubuntu 版本中已损坏

请注意,最近的稳定版 Debian 和 Ubuntu 已知会打包与相同发行版中提供的 nginx 版本不兼容的 ngx_lua 版本。这会导致 nginx 进程在使用 lua 模块时发生段错误,使其完全无法使用。在这种情况下,nginx 错误日志将清楚地表明进程崩溃,例如

    [alert] 123#123: worker process 45678 exited on signal 11

已知以下版本的 Debian 和 Ubuntu 存在此问题

  • Debian 11(错误 #994178

  • Ubuntu 20.04 和 21.04(错误 #1893753

故障排除

确保已启用 nginx lua 模块

如果您遇到表明 nginx 不知道如何解释 lua 脚本的问题,请确保已启用 [lua 模块](https://github.com/openresty/lua-nginx-module)。您可能需要在 nginx.conf 中添加类似以下内容

    load_module modules/ndk_http_module.so;
    load_module modules/ngx_http_lua_module.so;

保持启用 lua 代码缓存

此模块期望 lua_code_cache 选项为 on(这是默认值)。

尝试使用旧版本的库

如果您看到库初始化错误,随后是每个指标更改请求的错误(例如,尝试索引全局 '...'(一个 nil 值)),则您可能正在使用旧版本的 lua-nginx-module。例如,如果您尝试将此库的最新版本与 Ubuntu 16.04 附带的 nginx-extras 包一起使用,就会发生这种情况。

如果您无法升级 nginx 和 lua-nginx-module,可以尝试使用该库的旧版本;它将没有最新的性能优化,但仍然可以使用。推荐使用的旧版本是 0.20181120

开发

安装测试依赖项

  • luarocks install luacheck

  • luarocks install luaunit

运行测试

  • luacheck --globals ngx -- prometheus.lua

  • lua prometheus_test.lua

  • cd integration && ./test.sh(需要 Docker 和 Go)

发布新版本

  • 更新 CHANGELOG.md

  • 更新 dist.ini 中的版本

  • 重命名 .rockspec 文件并在其中更新版本

  • 提交更改

  • 创建一个新的 Git 标签:git tag 0.XXXXXXXX && git push origin 0.XXXXXXXX

  • 推送到 luarocks:luarocks upload nginx-lua-prometheus-0.20181120-1.rockspec

  • 上传到 OPM:opm build && opm upload

贡献者

  • 创建和维护者:Anton Tolchanov (@knyar)

  • Metrix 前缀支持由 david birdsong ([@davidbirdsong]( https://github.com/davidbirdsong)) 贡献

  • Gauge 支持由 Cosmo Petrich ([@cosmopetrich]( https://github.com/cosmopetrich)) 贡献

  • 性能改进和每个工作线程的计数器由 Wangchong Zhou (@fffonion) / [@Kong]( https://github.com/Kong) 贡献。

  • 指标名称跟踪改进由 Jan Dolinár ([@dolik-rce]( https://github.com/dolik-rce)) 贡献

许可证

根据 MIT 许可证授权。

第三方许可证

以下第三方模块在此库中使用

此模块根据 Apache 2.0 许可证授权。

版权所有 (C) 2019, Kong Inc.

保留所有权利。

作者

Anton Tolchanov

许可证

mit

版本