OpenResty(Nginx Lua)统计网站访问信息

背景

之前的一篇文章openresty(nginx lua)统计域名状态码、平均响应时间和流量实现了对域名状态码,平均响应时间和流量的统计。但之前的统计方法没有实现当某一域名404或500等状态码超过一定数量后发送具体的url来快速定位位置。这个功能我们其实是通过统计网站日志来实现了。为了摆脱对网站日志的依赖以及提高统计性能,我们尝试把此功能也用nginx lua来实现。具体的使用方法与之前的文章一样,这里只是更新了两个lua脚本。

使用方法

1、获取域名devops.webres.wang 404状态码数量

  1. curl -s "localhost/domain_status?count=status&host=devops.webres.wang&status=404"

输出:
10 688
第一列为状态码数量,第二列为域名请求总数
2、获取当域名devops.webres.wang 404状态码超过50个时,输出前10个url

  1. curl -s "localhost/domain_status?count=statusUrl&host=devops.webres.wang&status=404&exceed=50&output=10"

输出:
/hello-world 90
/centos 10

第一列为url,第二列为url请求次数。
3、获取域名devops.webres.wang upstream一分钟内平均耗时

  1. curl -s "localhost/domain_status?count=upT&host=devops.webres.wang"

输出:
0.02 452
第一列为upstream平均耗时,第二列为域名总请求次数。
4、获取当域名devops.webres.wang upstream平均耗时超过0.5秒时,输出其url

  1. curl -s "localhost/domain_status?count=upTUrl&host=devops.webres.wang&exceed=0.5"

输出:
/hello.php 0.82 52
第一列为url,第二列为此url平均耗时,第三列为此url请求次数。监控此接口数据可以快速定位出具体哪些url慢了。
5、获取域名devops.webres.wang request time平均耗时

  1. curl -s "localhost/domain_status?count=reqT&host=devops.webres.wang"

输出:
1.82 52
第一列为平均耗时,第二列为域名请求数。request time是指完成整个请求所需要的时间(包括把数据传输到用户浏览器的时间)。对于php请求,upstream time指的是nginx把php请求传给fastcgi到完成数据接收所需时间。所以request time永远大于upstream time。
6、获取域名devops.webres.wang占用的带宽(单位:字节/秒)

  1. curl -s "localhost/domain_status?count=flow&host=devops.webres.wang"

输出:
1024 52
第一列为此域名一分钟内平均传输速率,单位为字节/秒,第二列为域名请求总数。

相关脚本

log_acesss.lua

  1. local access = ngx.shared.access
  2. local host = ngx.var.host or "unknow"
  3. local status = ngx.var.status
  4. local body_bytes_sent = ngx.var.body_bytes_sent
  5. local request_time = ngx.var.request_time
  6. local upstream_response_time = ngx.var.upstream_response_time or 0
  7. local request_uri = ngx.var.request_uri or "/unknow"
  8. local timestamp = ngx.time()
  9. local expire_time = 70
  10.  
  11. local status_key = table.concat({host,"-",status,"-",timestamp})
  12. local flow_key = table.concat({host,"-flow-",timestamp})
  13. local req_time_key = table.concat({host,"-reqt-",timestamp})
  14. local up_time_key = table.concat({host,"-upt-",timestamp})
  15. local total_key = table.concat({host,"-total-",timestamp})
  16.  
  17. — 域名总请求数
  18. local n,e = access:incr(total_key,1)
  19. if not n then
  20. access:set(total_key, 1, expire_time)
  21. end
  22.  
  23. — 域名状态码请求数
  24. local n,e = access:incr(status_key,1)
  25. if not n then
  26. access:set(status_key, 1, expire_time)
  27. end
  28.  
  29. — 域名流量
  30. local n,e = access:incr(flow_key,body_bytes_sent)
  31. if not n then
  32. access:set(flow_key, body_bytes_sent, expire_time)
  33. end
  34.  
  35. — 域名请求耗时
  36. local n,e = access:incr(req_time_key,request_time)
  37. if not n then
  38. access:set(req_time_key, request_time, expire_time)
  39. end
  40.  
  41. — 域名upstream耗时
  42. local n,e = access:incr(up_time_key,upstream_response_time)
  43. if not n then
  44. access:set(up_time_key, upstream_response_time, expire_time)
  45. end
  46.  
  47. — 获取不带参数的uri
  48. local m, err = ngx.re.match(request_uri, "(.*?)\?")
  49. local request_without_args = m and m[1] or request_uri
  50.  
  51. — 存储状态码大于400的url
  52. if tonumber(status) >= 400 then
  53. — 拼接url,状态码,字节数等字段
  54. local request_log_t = {}
  55. table.insert(request_log_t,host)
  56. table.insert(request_log_t,request_without_args)
  57. table.insert(request_log_t,status)
  58. local request_log = table.concat(request_log_t," ")
  59.  
  60. — 把拼接的字段储存在字典中
  61. local log_key = table.concat({"status-",timestamp})
  62. local request_log_dict = access:get(log_key) or ""
  63. if request_log_dict == "" then
  64. request_log_dict = request_log
  65. else
  66. request_log_dict = table.concat({request_log_dict,"n",request_log})
  67. end
  68. access:set(log_key, request_log_dict, expire_time)
  69. end
  70.  
  71. — 存储upstream time大于0.5的url
  72. if tonumber(upstream_response_time) > 0.5 then
  73. — 拼接url,状态码,字节数等字段
  74. local request_log_t = {}
  75. table.insert(request_log_t,host)
  76. table.insert(request_log_t,request_without_args)
  77. table.insert(request_log_t,upstream_response_time)
  78. local request_log = table.concat(request_log_t," ")
  79.  
  80. — 把拼接的字段储存在字典中
  81. local log_key = table.concat({"upt-",timestamp})
  82. local request_log_dict = access:get(log_key) or ""
  83. if request_log_dict == "" then
  84. request_log_dict = request_log
  85. else
  86. request_log_dict = table.concat({request_log_dict,"n",request_log})
  87. end
  88. access:set(log_key, request_log_dict, expire_time)
  89. end

domain_status.lua

  1. — 各参数用法:
  2. — count=status,host=xxx.com,status=404 统计xxx.com域名一分钟内404状态码个数.
  3. — count=statusUrl,host=xxx.com,status=404,exceed=50,output=30 当xxx.com域名404状态码一分钟内超过50个时,输出前30个url,否则返回空.
  4.  
  5. — count=upT,host=xxx.com 统计xxx.com域名一分钟内平均upsteam耗时
  6. — count=upTUrl,host=xxx.com,exceed=0.5 输出upstreamTime超过0.5秒的url,没有就返回空
  7.  
  8. — count=reqT,host=xxx.com 统计xxx.com域名一分钟内平均请求耗时
  9. — count=flow,host=xxx.com 统计xxx.com域名一分钟内流量(单位字节/秒)
  10.  
  11. — 函数: 获取迭代器值
  12. local get_field = function(iterator)
  13.     local m,err = iterator
  14.     if err then
  15.         ngx.log(ngx.ERR, "get_field iterator error: ", err)
  16.         ngx.exit(ngx.HTTP_OK)
  17.     end
  18.     return m[0]
  19. end
  20.  
  21. — 函数: 按值排序table
  22. local getKeysSortedByValue = function (tbl, sortFunction)
  23.   local keys = {}
  24.   for key in pairs(tbl) do
  25.     table.insert(keys, key)
  26.   end
  27.  
  28.   table.sort(keys, function(a, b)
  29.     return sortFunction(tbl[a], tbl[b])
  30.   end)
  31.  
  32.   return keys
  33. end
  34.  
  35. — 函数: 判断table是否存在某元素
  36. local tbl_contain = function(table,element)
  37.     for k in pairs(table) do
  38.         if k == element then
  39.             return true
  40.         end
  41.     end
  42.     return false
  43. end
  44.  
  45. local access = ngx.shared.access
  46. local now = ngx.time()
  47. local one_minute_ago = now – 60
  48.  
  49. — 获取参数
  50. local args = ngx.req.get_uri_args()
  51. local count_arg = args["count"]
  52. local host_arg = args["host"]
  53. local status_arg = args["status"]
  54. local exceed_arg = args["exceed"]
  55. local output_arg = args["output"]
  56. local count_t = {["status"]=0,["statusUrl"]=0,["upT"]=0,["upTUrl"]=0,["reqT"]=0,["flow"]=0}
  57.  
  58. — 检查参数是否满足
  59. if not tbl_contain(count_t,count_arg) then
  60.     ngx.print("count arg invalid.")
  61.     ngx.exit(ngx.HTTP_OK)
  62. end
  63.  
  64. if not host_arg then
  65.     ngx.print("host arg not found.")
  66.     ngx.exit(ngx.HTTP_OK)
  67. end
  68.  
  69. if count_arg == "status" and not status_arg then
  70.     ngx.print("status arg not found.")
  71.     ngx.exit(ngx.HTTP_OK)
  72. end
  73.  
  74. if count_arg == "statusUrl" and not (status_arg and exceed_arg and output_arg)  then
  75.     ngx.print("status or exceed or output arg not found.")
  76.     ngx.exit(ngx.HTTP_OK)
  77. end
  78.  
  79. if count_arg == "upTUrl" and not exceed_arg then
  80.     ngx.print("exceed arg not found.")
  81.     ngx.exit(ngx.HTTP_OK)
  82. end
  83.  
  84. — 检查参数是否合法
  85. if status_arg and ngx.re.find(status_arg, "^[0-9]{3}$") == nil then
  86.     ngx.print("status arg must be a valid httpd code.")
  87.     ngx.exit(ngx.HTTP_OK)
  88. end
  89.  
  90. if exceed_arg and ngx.re.find(exceed_arg, "^[0-9.]+$") == nil then
  91.     ngx.print("exceed arg must be a number.")
  92.     ngx.exit(ngx.HTTP_OK)
  93. end
  94.  
  95. if output_arg and ngx.re.find(output_arg, "^[0-9]+$") == nil then
  96.     ngx.print("output arg must be a number.")
  97.     ngx.exit(ngx.HTTP_OK)
  98. end
  99.  
  100. — 开始统计
  101. local url
  102. local status_code
  103. local upstream_time
  104. local status_total = 0
  105. local host
  106. local req_total = 0
  107. local flow_total = 0
  108. local reqtime_total = 0
  109. local upstream_total = 0
  110. local status_url_t = {}
  111. local upstream_url_t = {}
  112. local upstream_url_count_t = {}
  113.  
  114. local status_log
  115. local upt_log
  116.  
  117. for second_num=one_minute_ago,now do
  118.     local flow_key = table.concat({host_arg,"-flow-",second_num})
  119.     local req_time_key = table.concat({host_arg,"-reqt-",second_num})
  120.     local up_time_key = table.concat({host_arg,"-upt-",second_num})
  121.     local total_req_key = table.concat({host_arg,"-total-",second_num})
  122.     local log_key
  123.     local log_line
  124.  
  125.     — 合并状态码大于等于400的请求日志到变量status_log
  126.     log_key = table.concat({"status-",second_num})
  127.     log_line = access:get(log_key) or ""
  128.     if not (log_line == "") then
  129.         status_log = table.concat({log_line,"n",status_log})
  130.     end
  131.  
  132.     — 合并upstream time大于0.5秒的请求日志到变量upt_log
  133.     log_key = table.concat({"upt-",second_num})
  134.     log_line = access:get(log_key) or ""
  135.     if not (log_line == "") then
  136.         upt_log = table.concat({log_line,"n",upt_log})
  137.     end
  138.  
  139.     — 域名总请求数
  140.     local req_sum = access:get(total_req_key) or 0
  141.     req_total = req_total + req_sum
  142.  
  143.     if count_arg == "status" or count_arg == "statusUrl" then
  144.         local status_key = table.concat({host_arg,"-",status_arg,"-",second_num})
  145.         local status_sum = access:get(status_key) or 0
  146.         status_total = status_total + status_sum
  147.     end
  148.  
  149.     if count_arg == "flow" then
  150.         local flow_sum = access:get(flow_key) or 0
  151.         flow_total = flow_total + flow_sum
  152.     end
  153.  
  154.     if count_arg == "reqT" then
  155.         local req_time_sum = access:get(req_time_key) or 0
  156.         reqtime_total = reqtime_total + req_time_sum
  157.     end
  158.  
  159.     if count_arg == "upT" then
  160.         local up_time_sum = access:get(up_time_key) or 0
  161.         upstream_total = upstream_total + up_time_sum
  162.     end
  163. end
  164.  
  165. — 统计状态码url
  166. if count_arg == "statusUrl" and status_log and not (status_log == "") then
  167.     local iterator, err = ngx.re.gmatch(status_log,".+n")
  168.     if not iterator then
  169.         ngx.log(ngx.ERR, "status_log iterator error: ", err)
  170.         return
  171.     end
  172.     for line in iterator do
  173.         if not line[0] then
  174.             ngx.log(ngx.ERR, "line[0] is nil")
  175.             return
  176.         end
  177.         local iterator, err = ngx.re.gmatch(line[0],"[^ n]+")
  178.         if not iterator then
  179.             ngx.log(ngx.ERR, "line[0] iterator error: ", err)
  180.             return
  181.         end
  182.  
  183.         host = get_field(iterator())
  184.         url = get_field(iterator())
  185.         status_code = get_field(iterator())
  186.  
  187.         if status_code == status_arg then
  188.             if status_url_t[url] then
  189.                 status_url_t[url] = status_url_t[url] + 1
  190.             else
  191.                 status_url_t[url] = 1
  192.             end
  193.         end
  194.  
  195.     end   
  196. end
  197.  
  198. — 统计upstream time大于0.5秒url
  199. if count_arg == "upTUrl" and upt_log and not (upt_log == "") then
  200.     local iterator, err = ngx.re.gmatch(upt_log,".+n")
  201.     if not iterator then
  202.         ngx.log(ngx.ERR, "upt_log iterator error: ", err)
  203.         return
  204.     end
  205.     for line in iterator do
  206.         if not line[0] then
  207.             ngx.log(ngx.ERR, "line[0] is nil")
  208.             return
  209.         end
  210.         local iterator, err = ngx.re.gmatch(line[0],"[^ n]+")
  211.         if not iterator then
  212.             ngx.log(ngx.ERR, "line[0] iterator error: ", err)
  213.             return
  214.         end
  215.  
  216.         host = get_field(iterator())
  217.         url = get_field(iterator())
  218.         upstream_time = get_field(iterator())
  219.         upstream_time = tonumber(upstream_time) or 0
  220.  
  221.         — 统计各url upstream平均耗时
  222.         if host == host_arg then
  223.             if upstream_url_t[url] then
  224.                 upstream_url_t[url] = upstream_url_t[url] + upstream_time
  225.             else
  226.                 upstream_url_t[url] = upstream_time
  227.             end
  228.  
  229.             if upstream_url_count_t[url] then
  230.                 upstream_url_count_t[url] = upstream_url_count_t[url] + 1
  231.             else
  232.                 upstream_url_count_t[url] = 1
  233.             end
  234.         end   
  235.     end   
  236. end
  237.  
  238. — 输出结果
  239. if count_arg == "status" then
  240.     ngx.print(status_total," ",req_total)
  241.  
  242. elseif count_arg == "flow" then
  243.     ngx.print(flow_total," ",req_total)
  244.  
  245. elseif count_arg == "reqT" then
  246.     local reqt_avg = 0
  247.     if req_total == 0 then
  248.         reqt_avg = 0
  249.     else
  250.         reqt_avg = reqtime_total/req_total
  251.     end
  252.     ngx.print(reqt_avg," ",req_total)
  253.  
  254. elseif count_arg == "upT" then
  255.     local upt_avg = 0
  256.     if req_total == 0 then
  257.             upt_avg = 0
  258.     else
  259.             upt_avg = upstream_total/req_total
  260.     end
  261.     ngx.print(upt_avg," ",req_total)
  262.  
  263. elseif count_arg == "statusUrl" then
  264.     if status_total > tonumber(exceed_arg) then
  265.         — 排序table
  266.         status_url_t_key = getKeysSortedByValue(status_url_t, function(a, b) return a > b end)
  267.         local output_body = ""
  268.         for i, uri in ipairs(status_url_t_key) do
  269.             if output_body == "" then
  270.                 output_body = table.concat({uri," ",status_url_t[uri]})
  271.             else   
  272.                 output_body = table.concat({output_body,"n",uri," ",status_url_t[uri]})
  273.             end
  274.             if i >= tonumber(output_arg) then
  275.                 ngx.print(output_body)
  276.                 ngx.exit(ngx.HTTP_OK)
  277.             end               
  278.         end
  279.  
  280.         ngx.print(output_body)
  281.         ngx.exit(ngx.HTTP_OK)
  282.     end
  283.  
  284. elseif count_arg == "upTUrl" then
  285.     local max_output = 30
  286.     local total_time = 0
  287.     local total_count = 0
  288.     local output_body = ""
  289.     local i = 0
  290.     for url in pairs(upstream_url_t) do
  291.         i = i + 1
  292.         total_time = upstream_url_t[url]
  293.         total_count = upstream_url_count_t[url]
  294.         avg_time = upstream_url_t[url] / upstream_url_count_t[url]
  295.         if avg_time > tonumber(exceed_arg) then
  296.             output_body = table.concat({url," ",avg_time," ",total_count,"n",output_body})
  297.         end
  298.  
  299.         if i >= max_output then
  300.             ngx.print(output_body)
  301.             ngx.exit(ngx.HTTP_OK)
  302.         end           
  303.     end
  304.     ngx.print(output_body)
  305.     ngx.exit(ngx.HTTP_OK)
  306.  
  307. end

openresty(nginx lua)统计域名状态码、平均响应时间和流量

背景

 

之前我们统计域名状态码、平均响应时间和流量的方法是:在每台机器添加一个定时脚本,来获取每个域名最近一分钟的访问日志到临时文件。然后zabbix再对这个一分钟日志临时文件作相关统计。一直运行良好,最近发现某台服务器突然负载增高。使用iotop查看发现获取最近一分钟日志的脚本占用的IO特别高。停止这个定时任务之后恢复正常。于是就打算使用nginx lua来替换目前的方法。新的方法具有统计时占用资源少,实时的特点。

 

方法介绍

 

使用nginx lua统计网站相关数据的方法为(我们以统计devops.webres.wang 404状态码为例):
记录过程:

  • 1、定义了一个共享词典access,获取当前时间戳,获取当前域名,如devops.webres.wang;
  • 2、我们定义用来存储状态码的词典key为,devops.webres.wang-404-当前时间戳;
  • 3、自增1 key(devops.webres.wang-404-当前时间戳)的值;
  • 4、循环2,3步。

 

查询过程:
提供一个接口,来累加key为devops.webres.wang-404-(前60秒时间戳-当前时间戳)的值,返回结果。

 

方法实现

 

nginx.conf设置

  1. http {
  2. […]
  3. lua_shared_dict access 10m;
  4. log_by_lua_file conf/log_acesss.lua;
  5. server {
  6. […]
  7.    location /domain_status {
  8.         default_type text/plain;
  9.         content_by_lua_file "conf/domain_status.lua";
  10.     }
  11. […]
  12. }
  13. […]
  14. }

 

log_access.lua

  1. local access = ngx.shared.access
  2. local host = ngx.var.host
  3. local status = ngx.var.status
  4. local body_bytes_sent = ngx.var.body_bytes_sent
  5. local request_time = ngx.var.request_time
  6. local timestamp = os.date("%s")
  7. local expire_time = 70
  8.  
  9. local status_key = table.concat({host,"-",status,"-",timestamp})
  10. local flow_key = table.concat({host,"-flow-",timestamp})
  11. local req_time_key = table.concat({host,"-reqt-",timestamp})
  12. local total_req_key = table.concat({host,"-total_req-",timestamp})
  13.  
  14. — count total req
  15. local total_req_sum = access:get(total_req_key) or 0
  16. total_req_sum = total_req_sum + 1
  17. access:set(total_req_key, total_req_sum, expire_time)
  18.  
  19. — count status
  20. local status_sum = access:get(status_key) or 0
  21. status_sum = status_sum + 1
  22. access:set(status_key, status_sum, expire_time)
  23.  
  24. — count flow
  25. local flow_sum = access:get(flow_key) or 0
  26. flow_sum = flow_sum + body_bytes_sent
  27. access:set(flow_key, flow_sum, expire_time)
  28.  
  29. — count request time
  30. local req_sum = access:get(req_time_key) or 0
  31. req_sum = req_sum + request_time
  32. access:set(req_time_key, req_sum, expire_time)

domain_status.lua

 

  1. local access = ngx.shared.access
  2. local args = ngx.req.get_uri_args()
  3. local count = args["count"]
  4. local host = args["host"]
  5. local status = args["status"]
  6. local one_minute_ago = tonumber(os.date("%s")) – 60
  7. local now = tonumber(os.date("%s"))
  8.  
  9. local status_total = 0
  10. local flow_total = 0
  11. local reqt_total = 0
  12. local req_total = 0
  13.  
  14. if not host then
  15.         ngx.print("host arg not found.")
  16.         ngx.exit(ngx.HTTP_OK)
  17. end
  18.  
  19. if count == "status" and not status then
  20.         ngx.print("status arg not found.")
  21.         ngx.exit(ngx.HTTP_OK)
  22. end
  23.  
  24. if not (count == "status" or count == "flow" or count == "reqt") then
  25.         ngx.print("count arg invalid.")
  26.         ngx.exit(ngx.HTTP_OK)
  27. end
  28.  
  29. for second_num=one_minute_ago,now do
  30.         local flow_key = table.concat({host,"-flow-",second_num})
  31.         local req_time_key = table.concat({host,"-reqt-",second_num})
  32.         local total_req_key = table.concat({host,"-total_req-",second_num})
  33.  
  34.         if count == "status" then
  35.                 local status_key = table.concat({host,"-",status,"-",second_num})
  36.                 local status_sum = access:get(status_key) or 0
  37.                 status_total = status_total + status_sum
  38.         elseif count == "flow" then
  39.                 local flow_sum = access:get(flow_key) or 0
  40.                 flow_total = flow_total + flow_sum
  41.         elseif count == "reqt" then
  42.                 local req_sum = access:get(total_req_key) or 0
  43.                 local req_time_sum = access:get(req_time_key) or 0
  44.                 reqt_total = reqt_total + req_time_sum
  45.                 req_total = req_total + req_sum
  46.         end
  47. end
  48.  
  49. if count == "status" then
  50.         ngx.print(status_total)
  51. elseif count == "flow" then
  52.         ngx.print(flow_total)
  53. elseif count == "reqt" then
  54.         if req_total == 0 then
  55.                 reqt_avg = 0
  56.         else
  57.                 reqt_avg = reqt_total/req_total
  58.         end
  59.         ngx.print(reqt_avg)
  60. end

使用说明

1、获取域名状态码
如请求devops.webres.wang一分钟内404状态码数量
请求接口http://$host/domain_status?count=status&host=devops.webres.wang&status=404
2、获取域名流量
请求接口http://$host/domain_status?count=flow&host=devops.webres.wang
3、获取域名一分钟内平均响应时间
请求接口http://$host/domain_status?count=reqt&host=devops.webres.wang

使用zabbix根据时间监控多行格式的日志

我们目前想使用zabbix每五分钟监控一个错误日志文件,如果监控到有错误产生,就发邮件告警。像标准的访问日志,如nginx的access log,一行表示一条日志,解析起来比较容易,但当日志不是一行一条时,如tomcat,glassfish的日志,如下:
[2015-07-17T14:24:04.552+0800] [glassfish 4.0] [SEVERE] [AS-WEB-CORE-00037] [javax.enterprise.web.core] [tid: _ThreadID=26 _ThreadName=http-listener-1(3)] [timeMillis: 1437114244552] [levelValue: 1000] [[
An exception or error occurred in the container during the request processing
java.lang.IllegalArgumentException
at org.glassfish.grizzly.http.util.CookieParserUtils.parseClientCookies(CookieParserUtils.java:353)
at org.glassfish.grizzly.http.util.CookieParserUtils.parseClientCookies(CookieParserUtils.java:336)
at org.glassfish.grizzly.http.Cookies.processClientCookies(Cookies.java:220)
at org.glassfish.grizzly.http.Cookies.get(Cookies.java:131)
at org.glassfish.grizzly.http.server.Request.parseCookies(Request.java:1911)
at org.glassfish.grizzly.http.server.Request.getCookies(Request.java:1505)
at org.apache.catalina.connector.Request.parseSessionCookiesId(Request.java:4077)
at org.apache.catalina.connector.CoyoteAdapter.postParseRequest(CoyoteAdapter.java:649)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:297)
]]

这个时候解析起来就相对复杂,我们可以使用如下脚本来取得最近五分钟的日志:

  1. #!/bin/bash
  2.  
  3. # 取得前5分钟时间
  4. LAST_MINUTE=$(date -d ‘-5 minute’ +%H%M%S)
  5. # 初始化日志条数
  6. LOG_NUM=0
  7. # 最大获取日志条数
  8. MAX_LOG=3
  9. # 初始化最终匹配日志
  10. LOG_CONTENT=""
  11. # 初始化包含时间行的匹配值
  12. LOG_DATE_MATCH=false
  13. # 设置日志路径
  14. LOG_PATH="/data/log/glassfish/domain1/server.log"
  15.  
  16. while read line;do
  17.  
  18.      # 匹配包含时间的行
  19.      if echo "$line" | grep -q ‘^[20’;then
  20.           # 根据包含时间行获取出特定时间格式,如181320
  21.           date_time=$(echo $line | grep -E -o "[0-9]{2}:[0-9]{2}:[0-9]{2}" | tr -d ‘:’)
  22.  
  23.           date_time=$(echo $date_time | sed ‘s/^0//’)
  24.           LAST_MINUTES=$(echo $LAST_MINUTES | sed ‘s/^0//’)
  25.           # 当前行的时间是否大于5分钟前的时间         
  26.           if [[ "$date_time" -gt "$LAST_MINUTE" ]];then
  27.                LOG_CONTENT="$LOG_CONTENTn$log_entry"
  28.                ((LOG_NUM++))
  29.                LOG_DATE_MATCH=true
  30.                log_entry="$linen"
  31.           else
  32.                LOG_DATE_MATCH=false
  33.                continue
  34.           fi
  35.  
  36.      else
  37.           # 只当前面日志时间满足条件时才设置log_entry值
  38.           if $LOG_DATE_MATCH;then
  39.                log_entry="$log_entryn$line"
  40.           fi   
  41.      fi
  42.  
  43.      # 限制最大获取行数
  44.      if [[ "$LOG_NUM" -gt "$MAX_LOG" ]];then
  45.           break
  46.      fi
  47.  
  48. done < $LOG_PATH
  49.  
  50. # 输出全部日志
  51. echo -n -e "$LOG_CONTENT"

前面的脚本按顺序读取的,但当日志文件比较大时,获取日志的效率就非常低了,所以推荐下面倒序读取日志的方法,更高效。

  1. #!/bin/bash
  2.  
  3. # 取得前5分钟时间
  4. LAST_MINUTE=$(date -d ‘-5 minute’ +%H%M%S)
  5. # 初始化日志条数
  6. LOG_NUM=0
  7. # 最大获取日志条数
  8. MAX_LOG=3
  9. # 初始化最终匹配日志
  10. LOG_CONTENT=""
  11. # 设置日志路径
  12. LOG_PATH="/data/log/glassfish/domain1/server.log"
  13.  
  14. while read line;do
  15.  
  16.      # 匹配包含时间的行
  17.      if echo "$line" | grep -q ‘^[20’;then
  18.           # 根据包含时间行获取出特定时间格式,如181320
  19.           date_time=$(echo $line | grep -E -o "[0-9]{2}:[0-9]{2}:[0-9]{2}" | tr -d ‘:’)
  20.         
  21.           # 当前行的时间是否大于5分钟前的时间   
  22.           if [[ "$date_time" > "$LAST_MINUTE" ]];then
  23.                ((LOG_NUM++))
  24.                log_entry="$linen$log_entry"
  25.                LOG_CONTENT="$LOG_CONTENTn$log_entry"
  26.           else
  27.                break
  28.           fi
  29.  
  30.           log_entry=""
  31.  
  32.      else
  33.           log_entry="$linen$log_entry"
  34.      fi
  35.  
  36.      # 限制最大获取行数
  37.      if [[ "$LOG_NUM" > "$MAX_LOG" ]];then
  38.           break
  39.      fi
  40.  
  41. done < <(tac $LOG_PATH)
  42.  
  43. # 输出全部日志
  44. echo -n -e "$LOG_CONTENT"

之后就可以在zabbix添加一个监控项用来获取日志内容,触发器就使用{itemName.strlen(0)}#0表达式来检测获取到的日志内容是否不为空。itemName为监控项名称。

Zabbix监控Memcached PHP-FPM Tomcat Nginx MySQL 网站日志

Zabbix作为监控软件非常的灵活,支持的数据类型非常丰富,比如数字(无正负),数字(浮点),日志,文字等。我们需要做的就是使用脚本来收集好数据,然后zabbix收集并画图,设置告警线。这里我们来学习使用Zabbix监控Memcached、PHP-FPM、Tomcat、Nginx、MySQL及网站日志。

 

Memcached监控

 

自定义键值

 

  1. UserParameter=memcached.stat[*],/data/sh/memcached-status.sh "$1"

memcached-status.sh脚本内容为:

  1. #!/bin/bash
  2.  
  3. item=$1
  4. ip=127.0.0.1
  5. port=11211
  6. (echo "stats";sleep 0.5) | telnet $ip $port 2>/dev/null | grep "STAT $itemb" | awk ‘{print $3}’

 

导入模板

 

memcached zabbix模板下载

 

PHP-FPM监控

 

配置php-fpm状态页

 

打开php-fpm.conf配置文件,添加如下配置后重启php:

  1. pm.status_path = /fpm_status

 

自定义键值

 

  1. UserParameter=php-fpm[*],/data/sh/php-fpm-status.sh "$1"

php-fpm-status.sh脚本内容:

  1. #!/bin/bash
  2. ##################################
  3. # Zabbix monitoring script
  4. #
  5. # php-fpm:
  6. #  – anything available via FPM status page
  7. #
  8. ##################################
  9. # Contact:
  10. [email protected]
  11. ##################################
  12. # ChangeLog:
  13. #  20100922        VV        initial creation
  14. ##################################
  15.  
  16. # Zabbix requested parameter
  17. ZBX_REQ_DATA="$1"
  18.  
  19. # FPM defaults
  20. URL="http://localhost/fpm_status"
  21. WGET_BIN="/usr/bin/wget"
  22.  
  23. #
  24. # Error handling:
  25. #  – need to be displayable in Zabbix (avoid NOT_SUPPORTED)
  26. #  – items need to be of type "float" (allow negative + float)
  27. #
  28. ERROR_NO_ACCESS_FILE="-0.9900"
  29. ERROR_NO_ACCESS="-0.9901"
  30. ERROR_WRONG_PARAM="-0.9902"
  31. ERROR_DATA="-0.9903" # either can not connect /        bad host / bad port
  32.  
  33. # save the FPM stats in a variable for future parsing
  34. FPM_STATS=$($WGET_BIN -q $URL -O – 2> /dev/null)
  35.  
  36. # error during retrieve
  37. if [ $? -ne 0 -o -z "$FPM_STATS" ]; then
  38.   echo $ERROR_DATA
  39.   exit 1
  40. fi
  41.  
  42. #
  43. # Extract data from FPM stats
  44. #
  45. RESULT=$(echo "$FPM_STATS" | sed -n -r "s/^$ZBX_REQ_DATA: +([0-9]+)/1/p")
  46. if [ $? -ne 0 -o -z "$RESULT" ]; then
  47.     echo $ERROR_WRONG_PARAM
  48.     exit 1
  49. fi
  50.  
  51. echo $RESULT
  52.  
  53. exit 0

 

导入模板

 

php-fpm zabbix模板下载

 

Tomcat监控

 

刚开始决定监控Tomcat时,使用的是JMX,不过这货设置太复杂了,而且对防火墙要求还挺高,需要开放几个端口。只好使用Tomcat自带的状态页来监控了。

 

自定义键值

 

  1. UserParameter=tomcat.status[*],/data/sh/tomcat-status.py $1

因为需要解析到xml,所以还是决定用python实现比较方便。
/data/sh/tomcat-status.py脚本内容:

  1. #!/usr/bin/python
  2. import urllib2
  3. import xml.dom.minidom
  4. import sys
  5.  
  6. url = ‘http://127.0.0.1:8080/manager/status?XML=true’
  7. username = ‘username’
  8. password = ‘password’
  9.  
  10. passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
  11. passman.add_password(None, url, username, password)
  12. authhandler = urllib2.HTTPBasicAuthHandler(passman)
  13. opener = urllib2.build_opener(authhandler)
  14. urllib2.install_opener(opener)
  15. pagehandle = urllib2.urlopen(url)
  16. xmlData = pagehandle.read()
  17. doc = xml.dom.minidom.parseString(xmlData) 
  18.  
  19. item = sys.argv[1]
  20.  
  21. if item == "memory.free":
  22. print  doc.getElementsByTagName("memory")[0].getAttribute("free")
  23. elif item == "memory.total":
  24. print  doc.getElementsByTagName("memory")[0].getAttribute("total")
  25. elif item == "memory.max":
  26. print  doc.getElementsByTagName("memory")[0].getAttribute("max")
  27. elif item == "threadInfo.maxThreads":
  28. print  doc.getElementsByTagName("threadInfo")[0].getAttribute("maxThreads")
  29. elif item == "threadInfo.currentThreadCount":
  30. print  doc.getElementsByTagName("threadInfo")[0].getAttribute("currentThreadCount")
  31. elif item == "threadInfo.currentThreadsBusy":
  32. print  doc.getElementsByTagName("threadInfo")[0].getAttribute("currentThreadsBusy")
  33. elif item == "requestInfo.maxTime":
  34. print  doc.getElementsByTagName("requestInfo")[0].getAttribute("maxTime")
  35. elif item == "requestInfo.processingTime":
  36. print  doc.getElementsByTagName("requestInfo")[0].getAttribute("processingTime")
  37. elif item == "requestInfo.requestCount":
  38. print  doc.getElementsByTagName("requestInfo")[0].getAttribute("requestCount")
  39. elif item == "requestInfo.errorCount":
  40. print  doc.getElementsByTagName("requestInfo")[0].getAttribute("errorCount")
  41. elif item == "requestInfo.bytesReceived":
  42. print  doc.getElementsByTagName("requestInfo")[0].getAttribute("bytesReceived")
  43. elif item == "requestInfo.bytesSent":
  44. print  doc.getElementsByTagName("requestInfo")[0].getAttribute("bytesSent")
  45. else:
  46. print "unsupport item."

这个脚本是监控Tomcat7的,Tomcat6没有试过,应该区别在状态页的url以及管理页面的用户密码设置上。以上脚本可运行需要在tomcat-users.xml里添加用户,至少权限为manager-status。

 

导入模板

 

tomcat zabbix模板下载

 

Nginx监控

 

配置Nginx状态页

 

在nginx配置文件server{}中加入:

  1. location /nginx_status {
  2.     stub_status on;
  3.     access_log off;
  4. }

 

自定义键值

 

  1. UserParameter=nginx[*],/data/sh/nginx-status.sh "$1"

nginx-status.sh脚本内容:

  1. #!/bin/bash
  2. ##################################
  3. # Zabbix monitoring script
  4. #
  5. # nginx:
  6. #  – anything available via nginx stub-status module
  7. #
  8. ##################################
  9. # Contact:
  10. [email protected]
  11. ##################################
  12. # ChangeLog:
  13. #  20100922        VV        initial creation
  14. ##################################
  15.  
  16. # Zabbix requested parameter
  17. ZBX_REQ_DATA="$1"
  18. ZBX_REQ_DATA_URL="$2"
  19.  
  20. # Nginx defaults
  21. URL="http://127.0.0.1/nginx_status"
  22. WGET_BIN="/usr/bin/wget"
  23.  
  24. #
  25. # Error handling:
  26. #  – need to be displayable in Zabbix (avoid NOT_SUPPORTED)
  27. #  – items need to be of type "float" (allow negative + float)
  28. #
  29. ERROR_NO_ACCESS_FILE="-0.9900"
  30. ERROR_NO_ACCESS="-0.9901"
  31. ERROR_WRONG_PARAM="-0.9902"
  32. ERROR_DATA="-0.9903" # either can not connect /        bad host / bad port
  33.  
  34. # save the nginx stats in a variable for future parsing
  35. NGINX_STATS=$($WGET_BIN -q $URL -O – 2> /dev/null)
  36.  
  37. # error during retrieve
  38. if [ $? -ne 0 -o -z "$NGINX_STATS" ]; then
  39.   echo $ERROR_DATA
  40.   exit 1
  41. fi
  42.  
  43. #
  44. # Extract data from nginx stats
  45. #
  46. case $ZBX_REQ_DATA in
  47.   active_connections)   echo "$NGINX_STATS" | head -1             | cut -f3 -d’ ‘;;
  48.   accepted_connections) echo "$NGINX_STATS" | grep -Ev ‘[a-zA-Z]’ | cut -f2 -d’ ‘;;
  49.   handled_connections)  echo "$NGINX_STATS" | grep -Ev ‘[a-zA-Z]’ | cut -f3 -d’ ‘;;
  50.   handled_requests)     echo "$NGINX_STATS" | grep -Ev ‘[a-zA-Z]’ | cut -f4 -d’ ‘;;
  51.   reading)              echo "$NGINX_STATS" | tail -1             | cut -f2 -d’ ‘;;
  52.   writing)              echo "$NGINX_STATS" | tail -1             | cut -f4 -d’ ‘;;
  53.   waiting)              echo "$NGINX_STATS" | tail -1             | cut -f6 -d’ ‘;;
  54.   *) echo $ERROR_WRONG_PARAM; exit 1;;
  55. esac
  56.  
  57. exit 0

 

导入模板

 

nginx zabbix模板下载

 

MySQL监控

 

MySQL的监控,zabbix是默认支持的,已经有现成的模板,现成的键值,我们需要做的只是在/var/lib/zabbix里新建一个.my.cnf文件,内容如下:

  1. [client]
  2. host=127.0.0.1
  3. port=1036
  4. user=root
  5. password=root

 

网站日志监控

 

配置日志格式

 

我们假设你用的web服务器是Nginx,我们添加一个日志格式,如下:

  1. log_format withHost  ‘$remote_addrt$remote_usert$time_localt$hostt$requestt’
  2.                 ‘$statust$body_bytes_sentt$http_referert’
  3.                 ‘$http_user_agent’;

我们使用tab作分隔符,为了方便awk识别列的内容,以防出错。
然后再设置全局的日志,其它server就不需要设置日志了:

  1. access_log  /data/home/logs/nginx/$host.log withHost;

 

定时获取一分钟日志

 

设置一个定时任务:

  1. * * * * * /data/sh/get_nginx_access.sh

脚本内容为:

  1. #!/bin/bash
  2.  
  3. logDir=/data/home/logs/nginx/
  4. logNames=`ls ${logDir}/*.*.log  |awk -F"/" ‘{print $NF}’`
  5.  
  6. for $logName in $logNames;
  7. do
  8. #设置变量
  9. split_log="/tmp/split_$logName"
  10. access_log="${logDir}/$logName"
  11. status_log="/tmp/$logName"
  12.  
  13. #取出最近一分钟日志
  14. tac $access_log  | awk ‘
  15. BEGIN{
  16. FS="t"
  17. OFS="t"
  18. cmd="date -d "1 minute ago" +%H%M%S"
  19. cmd|getline oneMinuteAgo
  20. }
  21. {
  22. $3 = substr($3,13,8)
  23. gsub(":","",$3)
  24. if ($3>=oneMinuteAgo){
  25. print
  26. } else {
  27. exit;
  28. }
  29. }’ > $split_log
  30.  
  31.  
  32. #统计状态码个数
  33. awk -F’t’ ‘{
  34. status[$4" "$6]++
  35. }
  36. END{
  37. for (i in status)
  38. {
  39. print i,status[i]
  40. }
  41. }
  42. ‘ $split_log  > $status_log
  43. done

这个定时任务是每分钟执行,因为我们监控的频率是每分钟。添加这个任务是为了取得最近一分钟各域名的日志,以及统计各域名的所有状态码个数,方便zabbix来获取所需的数据。

 

自定义键值

 

  1. UserParameter=nginx.detect,/data/sh/nginx-detect.sh
  2. UserParameter=nginx.access[*],awk -v sum=0 -v domain=$1 -v code=$2 ‘{if($$1 == domain && $$2 == code ){sum+=$$3} }END{print sum}’ /tmp/$1.log
  3. UserParameter=nginx.log[*],awk -F’t’ -v domain=$1 -v code=$2 -v number=$3 -v sum=0 -v line="" ‘{if ($$4 == domain && $$6 == code ){sum++;line=line$$5"n" }}END{if (sum > number) print line}’ /tmp/split_$1.log | sort | uniq -c | sort -nr | head -10 | sed -e ‘s/^/<p>/’ -e ‘s/$/</p>/’

nginx-detect.sh脚本内容为:

  1. #!/bin/bash
  2.  
  3. function json_head {
  4.     printf "{"
  5.     printf ""data":["
  6. }
  7.  
  8. function json_end {
  9.     printf "]"
  10.     printf "}"
  11. }
  12.  
  13. function check_first_element {
  14.     if [[ $FIRST_ELEMENT -ne 1 ]]; then
  15.         printf ","
  16.     fi
  17.     FIRST_ELEMENT=0
  18. }
  19.  
  20. FIRST_ELEMENT=1
  21. json_head
  22.  
  23. logNames=`ls /data/home/logs/nginx/*.*.log |awk -F"/" ‘{print $NF}’`
  24. for logName in $logNames;
  25. do
  26. while read domain code count;do
  27.         check_first_element
  28.         printf "{"
  29.         printf ""{#DOMAIN}":"$domain","{#CODE}":"$code""
  30.         printf "}"
  31. done < /tmp/$logName
  32. done
  33. json_end

这里我们定义了三个键值,nginx.detect是为了发现所有域名及其所有状态码,nginx.access[*]是为了统计指定域名的状态码的数量,nginx.log[*]是为了测试指定域名的状态码超过指定值时输出排在前十的url。我们监控nginx访问日志用到了zabbix的自动发现功能,当我们增加域名时,不需要修改脚本,zabbix会帮助我们自动发现新增的域名并作监控。

 

配置探索规则

 

添加一个探索规则,用来发现域名及状态码,如图:
监控

 

配置监控项原型

 

监控所有的域名及状态码:
监控
域名状态码404超过200次监控:
监控
域名状态码500超过50次监控:
监控

 

配置触发器

 

404状态码超过200告警:
监控

监控
500状态码超过50告警:
监控

使用监控宝监控php-fpm状态

上次我们介绍如何开启php-fpm的状态页,这对于php-fpm的参数调整有很高的参考价值。我们可以使用监控宝的自定义监控来保存php-fpm的状态,来达到了解网站各时候php的请求情况。在开始之前,请确保已经开启php-fpm的status。
一、创建收集数据脚本
新建脚本/home/sh/monitor_fpm.sh,并添加到cronjob,每五分钟运行一次。脚本代码为:
fpm_status=$(curl -s http://devops.webres.wang/fpm_status)
start_since_now=$(echo “$fpm_status” | awk -F’:’ ‘/start since/{gsub(/ /,””,$2);print $2}’)
listen_queue=$(echo “$fpm_status” | awk -F’:’ ‘/^listen queue:/{gsub(/ /,””,$2);print $2}’)
idle_processes=$(echo “$fpm_status” | awk -F’:’ ‘/idle processes/{gsub(/ /,””,$2);print $2}’)
active_processes=$(echo “$fpm_status” | awk -F’:’ ‘/^active processes:/{gsub(/ /,””,$2);print $2}’)
total_processes=$(echo “$fpm_status” | awk -F’:’ ‘/total processes/{gsub(/ /,””,$2);print $2}’)
accepted_conn_now=$(echo “$fpm_status” | awk -F’:’ ‘/accepted conn/{gsub(/ /,””,$2);print $2}’)
max_listen_queue=$(echo “$fpm_status” | awk -F’:’ ‘/max listen queue/{gsub(/ /,””,$2);print $2}’)
max_active_processes=$(echo “$fpm_status” | awk -F’:’ ‘/max active processes/{gsub(/ /,””,$2);print $2}’)
max_children_reached=$(echo “$fpm_status” | awk -F’:’ ‘/max children reached/{gsub(/ /,””,$2);print $2}’)
if [ -f “/tmp/accepted_conn78” ];then
accepted_conn_pre=$(cat /tmp/accepted_conn78)
((accepted_conn_inc=$accepted_conn_now – $accepted_conn_pre))
[[ $accepted_conn_inc -lt 0 ]] && accepted_conn_inc=0
else
accepted_conn_inc=0
fi
echo $accepted_conn_now > /tmp/accepted_conn78

if [ -f “/tmp/start_since78″ ];then
start_since_pre=$(cat /tmp/start_since78)
((start_since_inc=$start_since_now – $start_since_pre))
[[ $start_since_inc -lt 0 ]] && per_request=0 || ((per_request=$accepted_conn_inc/$start_since_inc))
else
per_request=0
fi
echo $start_since_now > /tmp/start_since78
echo ”

accepted_conn:$accepted_conn_inc 
listen_queue:$listen_queue 
idle_processes:$idle_processes 
active_processes:$active_processes 
total_processes:$total_processes 
per_request:$per_request 
max_listen_queue:$max_listen_queue 
max_active_processes:$max_active_processes 
max_children_reached:$max_children_reached

” > /home/devops.webres.wang/web/php_status.html
二、到监控宝添加自定义监控
1、点击网站头部的创建监控项目,拉到底部,选择创建自定义监控,再点击创建自定义监控规则,
2、基本信息填写
监控
3、规则指标添加
监控
3、添加php请求图
监控
4、添加php进程图
监控
5、添加php最大值图
监控
6、点击完成,输入监控fpm的页面
监控
完成了以上步骤之后,过一段时间就可以看到php-fpm的状态统计信息了。
如图:
监控

使用rrdtool统计网站PV和IP

现在网站服务器已经使用snmp进行监控,已经对CPU,内存,流量等进行了监控,但觉得还需要加一项监控,就是网站的PV和IP的监控,这样可以快速知道服务器负载上升是否是网站访问量增加的原因。这几天初学rrdtool,这个工具既能存储数据,又能画图,非常的方便。
下面是统计近一天的pv和ip图。
监控

1、安装rrdtool

  1. centos: yum install rrdtool
  2. ubuntu: sudo apt-get install rrdtool

2、创建rrdtool数据库

  1. rrdtool create /var/www/test.rrd             
  2. -s 300     
  3. DS:pv:GAUGE:600:U:U   
  4. DS:ip:GAUGE:600:U:U   
  5. RRA:AVERAGE:0.5:1:288

这里创建一个test.rrd数据文件,相关参数说明如下:
-s 300 300秒存储一次数据
DS:pv:GAUGE:600:U:U
DS:ip:GAUGE:600:U:U 指定两个数据源DS,字段分别为pv和ip
RRA:AVERAGE:0.5:1:288 指定RRA,相当于数据表,存储一天的数据。

3、创建更新脚本

  1. #!/bin/bash
  2. becur=`date -d "5 minute ago" +%H%M%S`
  3. list=`tac /var/log/apache2/access.log |  awk  -v a="$becur"  -F [‘ ‘:] ‘{t=$5$6$7;if (t>=a) {print;} else {exit;} }’ | egrep -v ".(gif|jpg|jpeg|png|css|js)" `
  4. #获取五分钟内PV
  5. pv=`echo "$list" | wc -l`
  6. #获取五分钟内IP
  7. ip=`echo "$list" | awk ‘{print $1}’ | sort | uniq | wc -l `
  8. #每五分钟更新数据库
  9. rrdtool update /var/www/test.rrd N:${pv}:${ip}
  10. #每五分钟更新图片
  11. rrdtool graph /var/www/1h-pv.png
  12. -t "PV and IP statistics in an hour" 
  13. –start now-3600
  14. –watermark "`date`"
  15. –no-gridfit
  16. –slope-mode
  17. -l 0
  18. -y 1000:5
  19. -X 0 
  20. DEF:mypv=/var/www/test.rrd:pv:AVERAGE
  21. DEF:myip=/var/www/test.rrd:ip:AVERAGE
  22. AREA:mypv#9F35FF:"PV Num" 
  23. AREA:myip#00DB00:"IP Num"

把此脚本添加进计划任务,每五分钟执行一次。
这是一个包含数据更新和图片生成的脚本,相关参数说明如下:
-t “PV and IP statistics in an hour” 指定图表标题
–start now-3600 获取近一小时数据
-l 0 Y轴从0开始
-y 1000:5 定义y轴分隔线为1000,5条显示一刻度
-X 0 以原值显示y轴

rrdtool相关教程:http://oss.oetiker.ch/rrdtool/

监控mysql主从健康状态shell脚本

  1. #!/bin/bash
  2. #define mysql variable
  3. mysql_user="root"
  4. mysql_pass="123456"
  5. email_addr="[email protected]"
  6.  
  7. mysql_status=`netstat -nl | awk ‘NR>2{if ($4 ~ /.*:3306/) {print "Yes";exit 0}}’`
  8. if [ "$mysql_status" == "Yes" ];then
  9.         slave_status=`mysql -u${mysql_user} -p${mysql_pass} -e"show slave statusG" | grep "Running" | awk ‘{if ($2 != "Yes") {print "No";exit 1}}’`
  10.         if [ "$slave_status" == "No" ];then
  11.                 echo "slave is not working!"
  12.                 [ ! -f "/tmp/slave" ] && echo "Slave is not working!" | mail -s "Warn!MySQL Slave is not working" ${email_addr}
  13.                 touch /tmp/slave
  14.         else
  15.                 echo "slave is working."
  16.                 [ -f "/tmp/slave" ] && rm -f /tmp/slave
  17.         fi
  18.         [ -f "/tmp/mysql_down" ] && rm -f /tmp/mysql_down
  19. else
  20.         [ ! -f "/tmp/mysql_down" ] && echo "Mysql Server is down!" | mail -s "Warn!MySQL server is down!" ${email_addr}
  21.         touch /tmp/mysql_down
  22. fi

此脚本首先判断mysql服务器是否运行,如果正常,继续判断主从,否则发邮件告警,只发一次。
判断主从状态是判断IO和SQL线程是否都为yes,如果不是则发邮件通知,只发一次。

nagios监控mysql主从状态

使用nagios监控mysql主从可以有两种方法,一种是使用nagios的nrpe插件来执行远程的shell脚本,并把数据发回监控服务器分析,二种方法是使用snmp的extend功能来执行远程脚本。我们这里介绍后一种方法。

一、mysql从服务器设置

1、mysql从服务器用户添加
执行如下语句添加用户:

  1. mysql> GRANT REPLICATION CLIENT ON *.* TO monitor@localhost IDENTIFIED BY ‘PassWord’;

2、下载check-mysql-slave.pl脚本

  1. cd /usr/local/bin/
  2. wget http://devops.webres.wang/wp-content/uploads/2012/10/check-mysql-slave.pl
  3. chmod +x check-mysql-slave.pl

3、在mysql从服务器上配置extend mysql-slave
在/etc/snmp/snmpd.conf文件的末尾添加如下代码:

  1. extend mysql-slave /usr/local/bin/check-mysql-slave.pl –user monitor –pass PassWord –sock /var/lib/mysql/mysql.sock

注意相关参数修改为自己的。
之后重载snmp:

  1. service snmpd reload

二、监控服务器设置

1、下载check_snmp_extend.sh脚本

  1. mkdir /usr/local/nagios/libexec.local
  2. cd /usr/local/nagios/libexec.local
  3. wget http://devops.webres.wang/wp-content/uploads/2012/10/check_snmp_extend.sh
  4. chmod +x check_snmp_extend.sh

2、定义USER10变量
在文件/usr/local/nagios/etc/resource.cfg添加如下变量:

  1. $USER10$=/usr/local/nagios/libexec.local

3、定义check_snmp_extend命令
在/usr/local/nagios/etc/objects/commands.cfg添加:

  1. define command{
  2. command_name check_snmp_extend
  3. command_line $USER10$/check_snmp_extend.sh $HOSTADDRESS$ $ARG1$
  4. }

4、定义监控mysql主从服务
在主机配置文件,如/usr/local/nagios/etc/objects/devops.webres.wang.cfg中添加如下service(注意,此www.cnetos.bz.cfg文件已经在nagios.cfg配置文件中包含)

  1. define host{
  2.         use                     linux-server
  3.         host_name               devops.webres.wang
  4.         alias                   devops.webres.wang
  5.         address                 142.4.33.74
  6.         }
  7. ……
  8. ……
  9. define service{
  10. ## This is an example service configured as
  11. ## extend servicename /path/to/service-check.sh
  12. ## on remote.server in /etc/snmp/snmpd.conf
  13. use generic-service
  14. host_name devops.webres.wang
  15. service_description mysql slave status
  16. check_command check_snmp_extend!mysql-slave
  17. }

参考:http://www.logix.cz/michal/devel/nagios/

nagios snmp监控服务常用command定义

  1. # ‘check_system’ command definition
  2. define command{
  3. command_name check_system
  4. command_line /usr/lib/nagios/plugins/check_snmp -H ‘$HOSTADDRESS$’ -C ‘$ARG1$’ -o sysDescr.0
  5. }
  6.  
  7. # ‘snmp_load’ command definition
  8. define command{
  9. command_name snmp_load
  10. command_line /usr/lib/nagios/plugins/check_snmp -H ‘$HOSTADDRESS$’ -C ‘$ARG1$’ -o .1.3.6.1.4.1.2021.10.1.5.1,.1.3.6.1.4.1.2021.10.1.5.2,.1.3.6.1.4.1.2021.10.1.5.3 -w :’$ARG2$’,:’$ARG3$’,:’$ARG4$’ -c :’$ARG5$’,:’$ARG6$’,:’$ARG7$’ -l load
  11. }
  12.  
  13.  
  14. # ‘snmp_cpustats’ command definition
  15. define command{
  16. command_name snmp_cpustats
  17. command_line /usr/lib/nagios/plugins/check_snmp -H ‘$HOSTADDRESS$’ -C ‘$ARG1$’ -o .1.3.6.1.4.1.2021.11.9.0,.1.3.6.1.4.1.2021.11.10.0,.1.3.6.1.4.1.2021.11.11.0 -l ‘CPU usage (user system idle)’ -u ‘%’
  18. }
  19.  
  20.  
  21. # ‘snmp_procname’ command definition
  22. define command{
  23. command_name snmp_procname
  24. command_line /usr/lib/nagios/plugins/check_snmp -H ‘$HOSTADDRESS$’ -C ‘$ARG1$’ -o .1.3.6.1.4.1.2021.2.1.5.’$ARG2$’ -w ‘$ARG3$’:’$ARG4$’ -c ‘$ARG5$’:’$ARG6$’
  25. }
  26.  
  27.  
  28. # ‘snmp_disk’ command definition
  29. define command{
  30. command_name snmp_disk
  31. command_line /usr/lib/nagios/plugins/check_snmp -H ‘$HOSTADDRESS$’ -C ‘$ARG1$’ -o .1.3.6.1.4.1.2021.9.1.7.’$ARG2$’,.1.3.6.1.4.1.2021.9.1.9.’$ARG2$’ -w ‘$ARG3$’:,:’$ARG4$’ -c ‘$ARG5$’:,:’$ARG6$’ -u ‘kB free (‘,’% used)’ -l ‘disk space’
  32. }
  33.  
  34.  
  35. # ‘snmp_mem’ command definition
  36. define command{
  37. command_name snmp_mem
  38. command_line /usr/lib/nagios/plugins/check_snmp -H ‘$HOSTADDRESS$’ -C ‘$ARG1$’ -o .1.3.6.1.4.1.2021.4.6.0,.1.3.6.1.4.1.2021.4.5.0 -w ‘$ARG2$’: -c ‘$ARG3$’:
  39. }
  40.  
  41. # ‘snmp_uptime’ command definition
  42. define command{
  43. command_name snmp_uptime
  44. command_line /usr/lib/nagios/plugins/check_snmp -H ‘$HOSTADDRESS$’ -C ‘$ARG1$’ -o sysUpTime.0
  45.         }
  46.  
  47. # ‘snmp_swap’ command definition
  48. define command{
  49. command_name snmp_swap
  50. command_line /usr/lib/nagios/plugins/check_snmp -H ‘$HOSTADDRESS$’ -C ‘$ARG1$’ -o .1.3.6.1.4.1.2021.4.4.0,.1.3.6.1.4.1.2021.4.3.0 -w ‘$ARG2$’: -c ‘$ARG3$’:
  51. }
  52.  
  53.  
  54. # ‘snmp_procs’ command definition
  55. define command{
  56. command_name snmp_procs
  57. command_line /usr/lib/nagios/plugins/check_snmp -H ‘$HOSTADDRESS$’ -C ‘$ARG1$’ -o host.hrSystem.hrSystemProcesses -w :’$ARG2$’ -c :’$ARG3$’ -l processes
  58. }
  59.  
  60.  
  61. # ‘snmp_users’ command definition
  62. define command{
  63. command_name snmp_users
  64. command_line /usr/lib/nagios/plugins/check_snmp -H ‘$HOSTADDRESS$’ -C ‘$ARG1$’ -o host.hrSystem.hrSystemNumUsers -w :’$ARG2$’ -c :’$ARG3$’ -l users
  65. }
  66.  
  67.  
  68. # ‘snmp_mem2’ command definition
  69. define command{
  70. command_name snmp_mem2
  71. command_line /usr/lib/nagios/plugins/check_snmp -H ‘$HOSTADDRESS$’ -C ‘$ARG1$’ -o host.hrStorage.hrStorageTable.hrStorageEntry.hrStorageUsed.’$ARG2$’,host.hrStorage.hrStorageTable.hrStorageEntry.hrStorageSize.’$ARG2$’ -w ‘$ARG3$’ -c ‘$ARG4$’
  72. }
  73.  
  74.  
  75. # ‘snmp_swap2’ command definition
  76. define command{
  77. command_name snmp_swap2
  78. command_line /usr/lib/nagios/plugins/check_snmp -H ‘$HOSTADDRESS$’ -C ‘$ARG1$’ -o host.hrStorage.hrStorageTable.hrStorageEntry.hrStorageUsed.’$ARG2$’,host.hrStorage.hrStorageTable.hrStorageEntry.hrStorageSize.’$ARG2$’ -w ‘$ARG3$’ -c ‘$ARG4$’
  79. }
  80.  
  81.  
  82. # ‘snmp_mem3’ command definition
  83. define command{
  84. command_name snmp_mem3
  85. command_line /usr/lib/nagios/plugins/check_snmp -H ‘$HOSTADDRESS$’ -C ‘$ARG1$’ -o host.hrStorage.hrStorageTable.hrStorageEntry.hrStorageUsed.’$ARG2$’,host.hrStorage.hrStorageTable.hrStorageEntry.hrStorageSize.’$ARG2$’ -w ‘$ARG3$’ -c ‘$ARG4$’
  86. }
  87.  
  88.  
  89. # ‘snmp_swap3’ command definition
  90. define command{
  91. command_name snmp_swap3
  92. command_line /usr/lib/nagios/plugins/check_snmp -H ‘$HOSTADDRESS$’ -C ‘$ARG1$’ -o host.hrStorage.hrStorageTable.hrStorageEntry.hrStorageUsed.’$ARG2$’,host.hrStorage.hrStorageTable.hrStorageEntry.hrStorageSize.’$ARG2$’ -w ‘$ARG3$’ -c ‘$ARG4$’
  93. }
  94.  
  95.  
  96. # ‘snmp_disk2’ command definition
  97. define command{
  98. command_name snmp_disk2
  99. command_line /usr/lib/nagios/plugins/check_snmp -H ‘$HOSTADDRESS$’ -C ‘$ARG1$’ -o host.hrStorage.hrStorageTable.hrStorageEntry.hrStorageUsed.’$ARG2$’ -w ‘$ARG3$’ -c ‘$ARG4$’
  100. }
  101.  
  102.  
  103. # ‘snmp_tcpopen’ command definition
  104. define command{
  105. command_name snmp_tcpopen
  106. command_line /usr/lib/nagios/plugins/check_snmp -H ‘$HOSTADDRESS$’ -C ‘$ARG1$’ -o tcp.tcpCurrEstab.0 -w ‘$ARG2$’ -c ‘$ARG3$’
  107. }
  108.  
  109.  
  110. # ‘snmp_tcpstats’ command definition
  111. define command{
  112. command_name snmp_tcpstats
  113. command_line /usr/lib/nagios/plugins/check_snmp -H ‘$HOSTADDRESS$’ -C ‘$ARG1$’ -o tcp.tcpActiveOpens.0,tcp.tcpPassiveOpens.0,tcp.tcpInSegs.0,tcp.tcpOutSegs.0,tcp.tcpRetransSegs.0 -l ‘TCP stats’
  114. }
  115.  
  116.  
  117. # ‘check_snmp_bgpstate’ command definition
  118. define command{
  119. command_name check_snmp_bgpstate
  120. command_line /usr/lib/nagios/plugins/check_bgpstate ‘$HOSTADDRESS$’ -c ‘$ARG1$’
  121. }
  122.  
  123.  
  124. # ‘check_netapp_uptime’ command definition
  125. define command{
  126. command_name check_netapp_uptime
  127. command_line /usr/lib/nagios/plugins/check_snmp -H ‘$HOSTADDRESS$’ -C ‘$ARG1$’ -o .1.3.6.1.2.1.1.3.0 –delimiter=’)’ -l "Uptime is"
  128. }
  129.  
  130.  
  131. # ‘check_netapp_cpuload’ command definition
  132. define command{
  133. command_name check_netapp_cpuload
  134. command_line /usr/lib/nagios/plugins/check_snmp -H ‘$HOSTADDRESS$’ -C ‘$ARG1$’ -o .1.3.6.1.4.1.789.1.2.1.3.0 -w 90 -c 95 -u ‘%’ -l "CPU LOAD "
  135. }
  136.  
  137.  
  138. # ‘check_netapp_numdisks’ command definition
  139. define command{
  140. command_name check_netapp_numdisks
  141. command_line /usr/lib/nagios/plugins/check_snmp -H ‘$HOSTADDRESS$’ -C ‘$ARG1$’ -o .1.3.6.1.4.1.789.1.6.4.1.0,.1.3.6.1.4.1.789.1.6.4.2.0,.1.3.6.1.4.1.789.1.6.4.8.0,.1.3.6.1.4.1.789.1.6.4.7.0 -u ‘Total Disks’,’Active’,’Spare’,’Failed’ -l ""
  142. }
  143.  
  144.  
  145. # ‘check_compaq_thermalCondition’ command definition
  146. define command{
  147. command_name check_compaq_thermalCondition
  148. command_line /usr/lib/nagios/plugins/check_snmp -H ‘$HOSTADDRESS$’ -C ‘$ARG1$’ -o .1.3.6.1.4.1.232.6.2.1.0,.1.3.6.1.4.1.232.6.2.2.0,.1.3.6.1.4.1.232.6.2.3.0,.1.3.6.1.4.1.232.6.2.4.0 -u ‘ThermalCondition’,’ThermalTemp’,’ThermalSystem’,’ThermalCPUFan’ -w 2:2,2:2,2:2,2:2 -c 1:2,1:2,1:2,1:2 -l "Thermal status "
  149. }

转载自:https://nsrc.org/workshops/configs/2010/apricot/etc/nagios-plugins/config/snmp.cfg

cacti监控磁盘IO

1、检查net-snmp是否支持IO监控

  1. snmpwalk -v 3 -u username -A password -a md5 -l authNoPriv localhost .1.3.6.1.4.1.2021.13.15.1.1.1

执行如上命令,如果返回类似如下数据,则表示支持disk io的监控,否则需要重新编译增加diskio-module模块。

  1. UCD-DISKIO-MIB::diskIOIndex.1 = INTEGER: 1
  2. UCD-DISKIO-MIB::diskIOIndex.2 = INTEGER: 2
  3. UCD-DISKIO-MIB::diskIOIndex.3 = INTEGER: 3
  4. ……

2、下载Cacti_Net-SNMP_DevIO_v3.1.zip

下载Cacti_Net-SNMP_DevIO_v3.1.zip,解压并上传net-snmp_devio.xml到 /resource/snmp_queries/目录。

3、导入模板

通过cacti后台的”Import Templates”导入所有的*_TMPL.xml文件,最后导入net-snmp_devIO-Data_query.xml文件。完成后,你就可以在“Data Queries”看到“ucd/net – Get Device I/O”。

4、为已存在的”ucd/net SNMP Host”增加磁盘IO监控。

切换到”devices”,点击已存在的”ucd/net snmp host”主机,如我的devops.webres.wang-ucd/net-snmp。往页尾看,在“Associated Data Queries”的”Add Data Query: “中选择”ucd/net – Get Device I/O”,”Re-Index Method: “选择”Index Count Changed”,点击”Add”增加Data Queries。

5、创建IO图形监控

接着点击页头的”Create Graphs for this Host”,在”
Data Query [ucd/net – Get Device I/O]”下面选择需要监控的磁盘,点击”create”开始创建图形。
至此cacti监控磁盘IO的设置已经完成。
参考:http://forums.cacti.net/about8777-0-asc-0.html