暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

服务端性能监控最佳实践(一)—— 炫酷的Nginx请求分析监控

重口味码农 2020-03-23
824

一、Nginx监控的重要性

在服务端性能监控领域中,Nginx是其中非常重要的一部分,不仅仅是因为它是所有请求的入口、必经之处。还因为它的日志包含了请求的关键信息,像每次请求的返回状态码、处理时间、由哪一台后端机器处理,从这些信息里面,能分析出请求的处理情况,从中定位到有问题的请求,为我们的性能优化提供很重要的参考。

网络上常见的Nginx监控,一般是通过nginx-module-vts模块,使用这个模块,能获取到某个模块的分域名请求数量、1xx 2xx的请求占比,和nginx的进出流量,因为只能获取到统计数据,对监控nginx正常运行能启动一定作用,但对实际请求分析作用不大。

今天我们要采用的是借助lua脚本的形式,对Nginx的请求进行具体到url的监控,通过grafana提供多维度的数据分析。先上图,最终能对每个Url进行非常详细的请求监控

二、配置Nginx

1、Nginx支持lua

首先nginx当然要支持lua脚本,这个可以进行验证,当然一般nginx默认是不会安装支持这个模块的,需要进行重新编译,这里提供两种方式。如下

①直接使用openresty

openresty是基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

建议懒得动手的同学可以直接安装openresty,使用起来和只用nginx并没有区别,相反包含了很多第三方模块,省了很多麻烦。

②安装nginx-lua-module

nginx-lua-module是nginx的一个模块,可以支持nginx运行lua脚本,需要重新编译nginx,这个步骤晚上的教程有很多,可以参考https://blog.csdn.net/qq_25551295/article/details/51744815。

2、配置nginx.conf

需要在conf文件夹进行配置lua脚本。

① conf.d

创建conf.d文件夹,新建文件counter.conf,写入内容

  1. # Please copy to nginx's conf.d directory

  2. # Set search paths for pure Lua external libraries (';;' is the default path):

  3. lua_package_path "/usr/local/openresty/nginx/conf/conf.d/?.lua;;/usr/local/openresty/nginx/conf/lua/?.lua;;";


  4. # Set Prometheus global dict

  5. lua_shared_dict prometheus_metrics 10M; #init 10M memory

  6. lua_shared_dict uri_by_host 10M;

  7. lua_shared_dict global_set 1M;

  8. # Development option, if deploy production, pls cache on!

  9. lua_code_cache off;


  10. init_by_lua_block {

  11. counter = require 'counter'

  12. counter.init()

  13. }


  14. log_by_lua_block {

  15. counter.log()

  16. }


  17. # Expose prometheus's metrics scrape port

  18. server {

  19. listen 9145;

  20. allow all;

  21. deny all;

  22. access_log off;

  23. location /metrics {

  24. content_by_lua 'prometheus:collect()';

  25. }

  26. }

②lua文件夹

创建lua文件夹,创建两个文件 counter.lua prometheus.lua 内容如下:

counter.lua

  1. end

  2. local function split(inputstr, sep)

  3. if sep == nil then

  4. sep = "%s"

  5. end

  6. local t={} ; i=1

  7. for str in string.gmatch(inputstr, "([^"..sep.."]+)") do

  8. t[i] = str

  9. i = i + 1

  10. end

  11. return t

  12. end

  13. local function parse_fullurl(request_uri)

  14. result_table = {}

  15. if string.find(request_uri, "%.") ~= nil then

  16. return nil

  17. end

  18. parts = split(request_uri, "/")

  19. if table.getn(parts) == 1 then

  20. return nil

  21. end

  22. for j=1, #parts do

  23. if(j == 1) then

  24. endpoint = "/"..parts[j]

  25. fullurl = "/"..parts[j]

  26. elseif(j <= 5) then

  27. if tonumber(parts[j]) ~= nil then

  28. break

  29. end

  30. fullurl = fullurl.."/"..parts[j]

  31. else

  32. break

  33. end

  34. end

  35. result_table["endpoint"] = endpoint

  36. result_table["fullurl"] = fullurl

  37. return result_table

  38. end

  39. function _M.log()

  40. local request_host = ngx.var.host

  41. local request_uri = ngx.unescape_uri(ngx.var.uri)

  42. local request_status = ngx.var.status

  43. local request_scheme = ngx.var.scheme

  44. local request_method = ngx.var.request_method

  45. local remote_ip = ngx.var.remote_addr

  46. local ngx_sent = ngx.var.body_bytes_sent

  47. local latency = ngx.var.upstream_response_time or 0



  48. result_table = parse_fullurl(request_uri)

  49. if result_table == nil then

  50. return

  51. end

  52. ngx_log(ngx_err,"latency=", tonumber(latency), ",status=", request_status, ",endpoint=", result_table["endpoint"], ",fullurl=", result_table["fullurl"])

  53. metric_latency:observe(tonumber(latency), {request_host, request_status, request_scheme, request_method, result_table["endpoint"], result_table["fullurl"]})

  54. end

  55. return _M

prometheus.lua

  1. -- vim: ts=2:sw=2:sts=2:expandtab

  2. --

  3. -- This module uses a single dictionary shared between Nginx workers to keep

  4. -- all metrics. Each counter is stored as a separate entry in that dictionary,

  5. -- which allows us to increment them using built-in `incr` method.

  6. --

  7. -- Prometheus requires that (a) all samples for a given metric are presented

  8. -- as one uninterrupted group, and (b) buckets of a histogram appear in

  9. -- increasing numerical order. We satisfy that by carefully constructing full

  10. -- metric names (i.e. metric name along with all labels) so that they meet

  11. -- those requirements while being sorted alphabetically. In particular:

  12. --

  13. -- * all labels for a given metric are presented in reproducible order (the one

  14. -- used when labels were declared). "le" label for histogram metrics always

  15. -- goes last;

  16. -- * bucket boundaries (which are exposed as values of the "le" label) are

  17. -- presented as floating point numbers with leading and trailing zeroes.

  18. -- Number of of zeroes is determined for each bucketer automatically based on

  19. -- bucket boundaries;

  20. -- * internally "+Inf" bucket is stored as "Inf" (to make it appear after

  21. -- all numeric buckets), and gets replaced by "+Inf" just before we

  22. -- expose the metrics.

  23. --

  24. -- For example, if you define your bucket boundaries as {0.00005, 10, 1000}

  25. -- then we will keep the following samples for a metric `m1` with label

  26. -- `site` set to `site1`:

  27. --

  28. -- m1_bucket{site="site1",le="0000.00005"}

  29. -- m1_bucket{site="site1",le="0010.00000"}

  30. -- m1_bucket{site="site1",le="1000.00000"}

  31. -- m1_bucket{site="site1",le="Inf"}

  32. -- m1_count{site="site1"}

  33. -- m1_sum{site="site1"}

  34. --

  35. -- "Inf" will be replaced by "+Inf" while publishing metrics.

  36. --

  37. -- You can find the latest version and documentation at

  38. -- https://github.com/knyar/nginx-lua-prometheus

  39. -- Released under MIT license.



  40. -- Default set of latency buckets, 5ms to 10s:

  41. local DEFAULT_BUCKETS = {0.005, 0.01, 0.02, 0.03, 0.05, 0.075, 0.1, 0.2, 0.3,

  42. 0.4, 0.5, 0.75, 1, 1.5, 2, 3, 4, 5, 10}


  43. -- Metric is a "parent class" for all metrics.

  44. local Metric = {}

  45. function Metric:new(o)

  46. o = o or {}

  47. setmetatable(o, self)

  48. self.__index = self

  49. return o

  50. end


  51. -- Checks that the right number of labels values have been passed.

  52. --

  53. -- Args:

  54. -- label_values: an array of label values.

  55. --

  56. -- Returns:

  57. -- an error message or nil

  58. function Metric:check_label_values(label_values)

  59. if self.label_names == nil and label_values == nil then

  60. return

  61. elseif self.label_names == nil and label_values ~= nil then

  62. return "Expected no labels for " .. self.name .. ", got " .. #label_values

  63. elseif label_values == nil and self.label_names ~= nil then

  64. return "Expected " .. #self.label_names .. " labels for " ..

  65. self.name .. ", got none"

  66. elseif #self.label_names ~= #label_values then

  67. return "Wrong number of labels for " .. self.name .. ". Expected " ..

  68. #self.label_names .. ", got " .. #label_values

  69. else

  70. for i, k in ipairs(self.label_names) do

  71. if label_values[i] == nil then

  72. return "Unexpected nil value for label " .. k .. " of " .. self.name

  73. end

  74. end

  75. end

  76. end


  77. local Counter = Metric:new()

  78. -- Increase a given counter by `value`

  79. --

  80. -- Args:

  81. -- value: (number) a value to add to the counter. Defaults to 1 if skipped.

  82. -- label_values: an array of label values. Can be nil (i.e. not defined) for

  83. -- metrics that have no labels.

  84. function Counter:inc(value, label_values)

  85. local err = self:check_label_values(label_values)

  86. if err ~= nil then

  87. self.prometheus:log_error(err)

  88. return

  89. end

  90. if value ~= nil and value < 0 then

  91. self.prometheus:log_error_kv(self.name, value, "Value should not be negative")

  92. return

  93. end


  94. self.prometheus:inc(self.name, self.label_names, label_values, value or 1)

  95. end


  96. local Gauge = Metric:new()

  97. -- Set a given gauge to `value`

  98. --

  99. -- Args:

  100. -- value: (number) a value to set the gauge to. Should be defined.

  101. -- label_values: an array of label values. Can be nil (i.e. not defined) for

  102. -- metrics that have no labels.

  103. function Gauge:set(value, label_values)

  104. if value == nil then

  105. self.prometheus:log_error("No value passed for " .. self.name)

  106. return

  107. end

  108. local err = self:check_label_values(label_values)

  109. if err ~= nil then

  110. self.prometheus:log_error(err)

  111. return

  112. end

  113. self.prometheus:set(self.name, self.label_names, label_values, value)

  114. end



  115. -- Increase a given gauge by `value`

  116. --

  117. -- Args:

  118. -- value: (number) a value to add to the gauge (a negative value when you

  119. -- need to decrease the value of the gauge). Defaults to 1 if skipped.

  120. -- label_values: an array of label values. Can be nil (i.e. not defined) for

  121. -- metrics that have no labels.

  122. function Gauge:inc(value, label_values)

  123. local err = self:check_label_values(label_values)

  124. if err ~= nil then

  125. self.prometheus:log_error(err)

  126. return

  127. end

  128. self.prometheus:inc(self.name, self.label_names, label_values, value or 1)

  129. end


  130. local Histogram = Metric:new()

  131. -- Record a given value in a histogram.

  132. --

  133. -- Args:

  134. -- value: (number) a value to record. Should be defined.

  135. -- label_values: an array of label values. Can be nil (i.e. not defined) for

  136. -- metrics that have no labels.

  137. function Histogram:observe(value, label_values)

  138. if value == nil then

  139. self.prometheus:log_error("No value passed for " .. self.name)

  140. return

  141. end

  142. local err = self:check_label_values(label_values)

  143. if err ~= nil then

  144. self.prometheus:log_error(err)

  145. return

  146. end

  147. self.prometheus:histogram_observe(self.name, self.label_names, label_values, value)

  148. end


  149. local Prometheus = {}

  150. Prometheus.__index = Prometheus

  151. Prometheus.initialized = false


  152. -- Generate full metric name that includes all labels.

  153. --

  154. -- Args:

  155. -- name: string

  156. -- label_names: (array) a list of label keys.

  157. -- label_values: (array) a list of label values.

  158. -- Returns:

  159. -- (string) full metric name.

  160. local function full_metric_name(name, label_names, label_values)

  161. if not label_names then

  162. return name

  163. end

  164. local label_parts = {}

  165. for idx, key in ipairs(label_names) do

  166. local label_value = (string.format("%s", label_values[idx])

  167. :gsub("[^\032-\126]", "") -- strip non-printable characters

  168. :gsub("\\", "\\\\")

  169. :gsub('"', '\\"'))

  170. table.insert(label_parts, key .. '="' .. label_value .. '"')

  171. end

  172. return name .. "{" .. table.concat(label_parts, ",") .. "}"

  173. end


  174. -- Construct bucket format for a list of buckets.

  175. --

  176. -- This receives a list of buckets and returns a sprintf template that should

  177. -- be used for bucket boundaries to make them come in increasing order when

  178. -- sorted alphabetically.

  179. --

  180. -- To re-phrase, this is where we detect how many leading and trailing zeros we

  181. -- need.

  182. --

  183. -- Args:

  184. -- buckets: a list of buckets

  185. --

  186. -- Returns:

  187. -- (string) a sprintf template.

  188. local function construct_bucket_format(buckets)

  189. local max_order = 1

  190. local max_precision = 1

  191. for _, bucket in ipairs(buckets) do

  192. assert(type(bucket) == "number", "bucket boundaries should be numeric")

  193. -- floating point number with all trailing zeros removed

  194. local as_string = string.format("%f", bucket):gsub("0*$", "")

  195. local dot_idx = as_string:find(".", 1, true)

  196. max_order = math.max(max_order, dot_idx - 1)

  197. max_precision = math.max(max_precision, as_string:len() - dot_idx)

  198. end

  199. return "%0" .. (max_order + max_precision + 1) .. "." .. max_precision .. "f"

  200. end


  201. -- Extract short metric name from the full one.

  202. --

  203. -- Args:

  204. -- full_name: (string) full metric name that can include labels.

  205. --

  206. -- Returns:

  207. -- (string) short metric name with no labels. For a `*_bucket` metric of

  208. -- histogram the _bucket suffix will be removed.

  209. local function short_metric_name(full_name)

  210. local labels_start, _ = full_name:find("{")

  211. if not labels_start then

  212. -- no labels

  213. return full_name

  214. end

  215. local suffix_idx, _ = full_name:find("_bucket{")

  216. if suffix_idx and full_name:find("le=") then

  217. -- this is a histogram metric

  218. return full_name:sub(1, suffix_idx - 1)

  219. end

  220. -- this is not a histogram metric

  221. return full_name:sub(1, labels_start - 1)

  222. end


  223. -- Makes a shallow copy of a table

  224. local function copy_table(table)

  225. local new = {}

  226. if table ~= nil then

  227. for k, v in ipairs(table) do

  228. new[k] = v

  229. end

  230. end

  231. return new

  232. end


  233. -- Check metric name and label names for correctness.

  234. --

  235. -- Regular expressions to validate metric and label names are

  236. -- documented in https://prometheus.io/docs/concepts/data_model/

  237. --

  238. -- Args:

  239. -- metric_name: (string) metric name.

  240. -- label_names: label names (array of strings).

  241. --

  242. -- Returns:

  243. -- Either an error string, or nil of no errors were found.

  244. local function check_metric_and_label_names(metric_name, label_names)

  245. if not metric_name:match("^[a-zA-Z_:][a-zA-Z0-9_:]*$") then

  246. return "Metric name '" .. metric_name .. "' is invalid"

  247. end

  248. for _, label_name in ipairs(label_names or {}) do

  249. if label_name == "le" then

  250. return "Invalid label name 'le' in " .. metric_name

  251. end

  252. if not label_name:match("^[a-zA-Z_][a-zA-Z0-9_]*$") then

  253. return "Metric '" .. metric_name .. "' label name '" .. label_name ..

  254. "' is invalid"

  255. end

  256. end

  257. end


  258. -- Initialize the module.

  259. --

  260. -- This should be called once from the `init_by_lua` section in nginx

  261. -- configuration.

  262. --

  263. -- Args:

  264. -- dict_name: (string) name of the nginx shared dictionary which will be

  265. -- used to store all metrics

  266. -- prefix: (optional string) if supplied, prefix is added to all

  267. -- metric names on output

  268. --

  269. -- Returns:

  270. -- an object that should be used to register metrics.

  271. function Prometheus.init(dict_name, prefix)

  272. local self = setmetatable({}, Prometheus)

  273. dict_name = dict_name or "prometheus_metrics"

  274. self.dict = ngx.shared[dict_name]

  275. if self.dict == nil then

  276. ngx.log(ngx.ERR,

  277. "Dictionary '", dict_name, "' does not seem to exist. ",

  278. "Please define the dictionary using `lua_shared_dict`.")

  279. return self

  280. end

  281. self.help = {}

  282. if prefix then

  283. self.prefix = prefix

  284. else

  285. self.prefix = ''

  286. end

  287. self.type = {}

  288. self.registered = {}

  289. self.buckets = {}

  290. self.bucket_format = {}

  291. self.initialized = true


  292. self:counter("nginx_metric_errors_total",

  293. "Number of nginx-lua-prometheus errors")

  294. self.dict:set("nginx_metric_errors_total", 0)

  295. return self

  296. end


  297. function Prometheus:log_error(...)

  298. ngx.log(ngx.ERR, ...)

  299. self.dict:incr("nginx_metric_errors_total", 1)

  300. end


  301. function Prometheus:log_error_kv(key, value, err)

  302. self:log_error(

  303. "Error while setting '", key, "' to '", value, "': '", err, "'")

  304. end


  305. -- Register a counter.

  306. --

  307. -- Args:

  308. -- name: (string) name of the metric. Required.

  309. -- description: (string) description of the metric. Will be used for the HELP

  310. -- comment on the metrics page. Optional.

  311. -- label_names: array of strings, defining a list of metrics. Optional.

  312. --

  313. -- Returns:

  314. -- a Counter object.

  315. function Prometheus:counter(name, description, label_names)

  316. if not self.initialized then

  317. ngx.log(ngx.ERR, "Prometheus module has not been initialized")

  318. return

  319. end


  320. local err = check_metric_and_label_names(name, label_names)

  321. if err ~= nil then

  322. self:log_error(err)

  323. return

  324. end


  325. if self.registered[name] then

  326. self:log_error("Duplicate metric " .. name)

  327. return

  328. end

  329. self.registered[name] = true

  330. self.help[name] = description

  331. self.type[name] = "counter"


  332. return Counter:new{name=name, label_names=label_names, prometheus=self}

  333. end


  334. -- Register a gauge.

  335. --

  336. -- Args:

  337. -- name: (string) name of the metric. Required.

  338. -- description: (string) description of the metric. Will be used for the HELP

  339. -- comment on the metrics page. Optional.

  340. -- label_names: array of strings, defining a list of metrics. Optional.

  341. --

  342. -- Returns:

  343. -- a Gauge object.

  344. function Prometheus:gauge(name, description, label_names)

  345. if not self.initialized then

  346. ngx.log(ngx.ERR, "Prometheus module has not been initialized")

  347. return

  348. end


  349. local err = check_metric_and_label_names(name, label_names)

  350. if err ~= nil then

  351. self:log_error(err)

  352. return

  353. end


  354. if self.registered[name] then

  355. self:log_error("Duplicate metric " .. name)

  356. return

  357. end

  358. self.registered[name] = true

  359. self.help[name] = description

  360. self.type[name] = "gauge"


  361. return Gauge:new{name=name, label_names=label_names, prometheus=self}

  362. end



  363. -- Register a histogram.

  364. --

  365. -- Args:

  366. -- name: (string) name of the metric. Required.

  367. -- description: (string) description of the metric. Will be used for the HELP

  368. -- comment on the metrics page. Optional.

  369. -- label_names: array of strings, defining a list of metrics. Optional.

  370. -- buckets: array if numbers, defining bucket boundaries. Optional.

  371. --

  372. -- Returns:

  373. -- a Histogram object.

  374. function Prometheus:histogram(name, description, label_names, buckets)

  375. if not self.initialized then

  376. ngx.log(ngx.ERR, "Prometheus module has not been initialized")

  377. return

  378. end


  379. local err = check_metric_and_label_names(name, label_names)

  380. if err ~= nil then

  381. self:log_error(err)

  382. return

  383. end


  384. for _, suffix in ipairs({"", "_bucket", "_count", "_sum"}) do

  385. if self.registered[name .. suffix] then

  386. self:log_error("Duplicate metric " .. name .. suffix)

  387. return

  388. end

  389. self.registered[name .. suffix] = true

  390. end

  391. self.help[name] = description

  392. self.type[name] = "histogram"


  393. self.buckets[name] = buckets or DEFAULT_BUCKETS

  394. self.bucket_format[name] = construct_bucket_format(self.buckets[name])


  395. return Histogram:new{name=name, label_names=label_names, prometheus=self}

  396. end


  397. -- Set a given dictionary key.

  398. -- This overwrites existing values, so it should only be used when initializing

  399. -- metrics or when explicitely overwriting the previous value of a metric.

  400. function Prometheus:set_key(key, value)

  401. local ok, err = self.dict:safe_set(key, value)

  402. if not ok then

  403. self:log_error_kv(key, value, err)

  404. end

  405. end


  406. -- Increment a given metric by `value`.

  407. --

  408. -- Args:

  409. -- name: (string) short metric name without any labels.

  410. -- label_names: (array) a list of label keys.

  411. -- label_values: (array) a list of label values.

  412. -- value: (number) value to add (a negative value when you need to decrease

  413. -- the value of the gauge). Optional, defaults to 1.

  414. function Prometheus:inc(name, label_names, label_values, value)

  415. local key = full_metric_name(name, label_names, label_values)

  416. if value == nil then value = 1 end


  417. local newval, err = self.dict:incr(key, value)

  418. if newval then

  419. return

  420. end

  421. -- Yes, this looks like a race, so I guess we might under-report some values

  422. -- when multiple workers simultaneously try to create the same metric.

  423. -- Hopefully this does not happen too often (shared dictionary does not get

  424. -- reset during configuation reload).

  425. if err == "not found" then

  426. self:set_key(key, value)

  427. return

  428. end

  429. -- Unexpected error

  430. self:log_error_kv(key, value, err)

  431. end


  432. -- Set the current value of a gauge to `value`

  433. --

  434. -- Args:

  435. -- name: (string) short metric name without any labels.

  436. -- label_names: (array) a list of label keys.

  437. -- label_values: (array) a list of label values.

  438. -- value: (number) the new value for the gauge.

  439. function Prometheus:set(name, label_names, label_values, value)

  440. local key = full_metric_name(name, label_names, label_values)

  441. self:set_key(key, value)

  442. end


  443. -- Record a given value into a histogram metric.

  444. --

  445. -- Args:

  446. -- name: (string) short metric name without any labels.

  447. -- label_names: (array) a list of label keys.

  448. -- label_values: (array) a list of label values.

  449. -- value: (number) value to observe.

  450. function Prometheus:histogram_observe(name, label_names, label_values, value)

  451. self:inc(name .. "_count", label_names, label_values, 1)

  452. self:inc(name .. "_sum", label_names, label_values, value)


  453. -- we are going to mutate arrays of label names and values, so create a copy.

  454. local l_names = copy_table(label_names)

  455. local l_values = copy_table(label_values)


  456. -- Last bucket. Note, that the label value is "Inf" rather than "+Inf"

  457. -- required by Prometheus. This is necessary for this bucket to be the last

  458. -- one when all metrics are lexicographically sorted. "Inf" will get replaced

  459. -- by "+Inf" in Prometheus:collect().

  460. table.insert(l_names, "le")

  461. table.insert(l_values, "Inf")

  462. self:inc(name .. "_bucket", l_names, l_values, 1)


  463. local label_count = #l_names

  464. for _, bucket in ipairs(self.buckets[name]) do

  465. if value <= bucket then

  466. -- last label is now "le"

  467. l_values[label_count] = self.bucket_format[name]:format(bucket)

  468. self:inc(name .. "_bucket", l_names, l_values, 1)

  469. end

  470. end

  471. end


  472. -- Prometheus compatible metric data as an array of strings.

  473. --

  474. -- Returns:

  475. -- Array of strings with all metrics in a text format compatible with

  476. -- Prometheus.

  477. function Prometheus:metric_data()

  478. if not self.initialized then

  479. ngx.log(ngx.ERR, "Prometheus module has not been initialized")

  480. return

  481. end


  482. local keys = self.dict:get_keys(0)

  483. -- Prometheus server expects buckets of a histogram to appear in increasing

  484. -- numerical order of their label values.

  485. table.sort(keys)


  486. local seen_metrics = {}

  487. local output = {}

  488. for _, key in ipairs(keys) do

  489. local value, err = self.dict:get(key)

  490. if value then

  491. local short_name = short_metric_name(key)

  492. if not seen_metrics[short_name] then

  493. if self.help[short_name] then

  494. table.insert(output, string.format("# HELP %s%s %s\n",

  495. self.prefix, short_name, self.help[short_name]))

  496. end

  497. if self.type[short_name] then

  498. table.insert(output, string.format("# TYPE %s%s %s\n",

  499. self.prefix, short_name, self.type[short_name]))

  500. end

  501. seen_metrics[short_name] = true

  502. end

  503. -- Replace "Inf" with "+Inf" in each metric's last bucket 'le' label.

  504. if key:find('le="Inf"', 1, true) then

  505. key = key:gsub('le="Inf"', 'le="+Inf"')

  506. end

  507. table.insert(output, string.format("%s%s %s\n", self.prefix, key, value))

  508. else

  509. self:log_error("Error getting '", key, "': ", err)

  510. end

  511. end

  512. return output

  513. end


  514. -- Present all metrics in a text format compatible with Prometheus.

  515. --

  516. -- This function should be used to expose the metrics on a separate HTTP page.

  517. -- It will get the metrics from the dictionary, sort them, and expose them

  518. -- aling with TYPE and HELP comments.

  519. function Prometheus:collect()

  520. ngx.header.content_type = "text/plain"

  521. ngx.print(self:metric_data())

  522. end


  523. return Prometheus

③修改nginx.conf

在http最后引入conf.d即可

  1. include conf.d/*.conf;

3、重启Nginx

注意不能通过./nginx -s命令重启,而是要kill掉原来的nginx,再重新启动。

重启nginx后,访问ip:9145,就可以获取到监控所需的数据,如下:

三、配置prometheus

将nginx的ip:9145配置到prometheus中,定时拉取数据。

四、配置grafana

数据存储到prometheus后,就可以在grafana中配置进行展示了。

1、配置prometheus数据源

在datasource中添加自己的prometheus数据源。

2、导入模板,展示数据

没错,在grafana官网已经提供了非常完善、精美的模板,我们下载下来,导入到自己的grafana中就能直接展示,而不需要自己进行配置了。

下载地址:

  • https://grafana.com/dashboards/10442

  • https://grafana.com/dashboards/10443

  • https://grafana.com/dashboards/10444

  • https://grafana.com/dashboards/10445

四个模板,下载后导入到自己的grafana就可以了。

五、查看grafana

这个模板有4个维度,指标非常详细,如下:

1、Nginx整体

2、单个host

3、endpoint

4、url

结语

这份文档是基于github上的一个开源项目https://github.com/zrbcool/prometheus-lua-nginx 修改而成,原项目是一个集成demo。我简化了部分配置,给grafana增加了几个维度的数据。

这种方式比普通的nginx-vts-exporter提供了更多更详细的数据,但相应的也会给nginx带来一定的负担,我自己测试过会有稍微的影响,大家可以实际评估后使用。

文章首发于重口味博客(https://blog.csdn.net/acingdreamer)


文章转载自重口味码农,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论