nginx [engine x] is an HTTP and reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy server.关于nginx的介绍就不多谈了,这里主要聊下如何打造nginx集群的监控系统。
监控演化
目前国内大多数互联网都是选择nginx构建web转发集群,那么如何构建nginx集群的监控系统?
下面这个监控方案的历程,大体也说明了nginx的进化发展。
2011年左右
当时并没有合适的开源工具,只能自写python脚本,一方面是通过nginx的status模块获取少量指标如处理请求数,连接数等等,另一方面是通过定期跑nginx日志生成监控数据,写入mysql进行存储,然后通过自研的监控系统展示相关监控指标。其中,指标的主要维度是域名。这个方案除了跑nginx日志消耗过多资源以及需要大量开发工作外,没啥大问题,就是有新的监控需求时很头疼,得改脚本。
2013年左右
这时候阿里的tengine开始发力,开发各种插件并在阿里内部推广使用,其中就包括了更为强大的status模块。只是tengine魔改了大量nginx的内部实现,导致大多数模块与nginx并不兼容。于是就有了这个项目hnlq715/status-nginx-module,完美支持nginx,并成功重构了当时上百台nginx,日请求量几十亿的的监控系统,此时能满足绝大多数监控需求。指标的主要维度仍然是域名。
2015年左右
这时候注意到了prometheus的出现,感叹于其强大,于是考虑基于prometheus实现nginx的监控。而此时,vozlt/nginx-module-vts已经使用很广,于是创建了这个项目hnlq715/nginx-vts-exporter,用于将vts的输出内容转化为prometheus的格式,便于prometheus抓取。基于grafana提供监控查询及UI展示,这时候算是步入真正的现代化监控系统,此时的指标维度已经细化到具体的URI。
2017年左右
然而上一个方案还是有个短板,那就是nginx-module-vts对响应时间的处理太过粗糙,只给了一个平均值,无法对P99,P90或是P50给出直观的数据。于是基于lua内嵌在nginx里跑监控指标,demo在这hnlq715/nginx-prometheus-metrics,这种方式可以直接用prometheus的histogram类型统计响应时间等指标,并在prometheus层聚合。此时,才算是真正的现代化监控系统。指标维度可以完全自定义,且更加多维化。
总结
基于prometheus聚合nginx的监控数据是非常好的选择。上述项目能够在一定程度上帮助实现,不用手写代码或是通过极少的lua代码实现一套现代化的nginx监控系统。当然,也可以聚合其他诸如redis、mysql、node等各类系统的监控数据到prometheus进行统一管理。
监控
status
原版的 NGINX 会在一个简单的状态页面上显示几个与服务器状态有关的基本指标,它们由你启用的 HTTP stub status module 所提供。
你可以浏览状态页看到你的指标:
Active connections: 24
server accepts handled requests
1156958 1156958 4491319
Reading: 0 Writing: 18 Waiting : 6
下表是 Nginx 提供的监控参数及其简单释义。
参数名称 参数描述
Active connections 当前活跃的用户连接(包含Waiting状态)
accepts 接收到的用户连接总数
handled Nginx处理的用户连接总数
requests 用户请求总数
Reading 当前连接中Nginx读取请求首部的个数
Writing 当前连接中Nginx写返回给用户的个数
Waiting 当前没有请求的活跃用户连接数
这些提供了我们简单的指标。
- 当用户请求连接Nginx服务器时,accepts计数器会加一。且当服务器处理该连接请求时,handled计数器同样会加一。一般而言,两者的值是相等的,除非达到了某些资源极限(如worker_connection的限制)。
- 用户连接请求被处理,就会进入 active 状态。如果该连接没有其他 request,则进入 waiting 的子状态;如果有 request,nginx 会读取 request 的 header,计数器 request 加一,进入 reading 的子状态。 reading 状态持续时间非常短,header 被读取后就会进入 writing 状态。事实上,直到服务器将响应结果返回给用户之前,该连接会一直保持 writing 状态。所以说,writing 状态一般会被长时间占用。
监控需求
三类指标
基本活动指标
Accepts(接受)、Handled(已处理)、Requests(请求数)是一直在增加的计数器。Active(活跃)、Waiting(等待)、Reading(读)、Writing(写)随着请求量而增减。
提醒指标: 丢弃连接 被丢弃的连接数目等于 Accepts 和 Handled 之差(NGINX 中),或是可直接得到的标准指标(NGINX Plus 中)。在正常情况下,丢弃连接数应该是零。如果在每个单位时间内丢弃连接的速度开始上升,那么应该看看是否资源饱和了。
提醒指标: 每秒请求数 按固定时间间隔采样你的请求数据(开源 NGINX 的requests或者 NGINX Plus 中total) 会提供给你单位时间内(通常是分钟或秒)所接受的请求数量。监测这个指标可以查看进入的 Web 流量尖峰,无论是合法的还是恶意的,或者突然的下降,这通常都代表着出现了问题。每秒请求数若发生急剧变化可以提醒你的环境出现问题了,即使它不能告诉你确切问题的位置所在。请注意,所有的请求都同样计数,无论 URL 是什么。
错误指标
NGINX 错误指标告诉你服务器是否经常返回错误而不是正常工作。客户端错误返回4XX状态码,服务器端错误返回5XX状态码。
提醒指标: 服务器错误率
服务器错误率等于在单位时间(通常为一到五分钟)内5xx错误状态代码的总数除以状态码(1XX,2XX,3XX,4XX,5XX)的总数。如果你的错误率随着时间的推移开始攀升,调查可能的原因。如果突然增加,可能需要采取紧急行动,因为客户端可能收到错误信息。
收集错误度量 配置 NGINX 的日志模块将响应码写入访问日志
性能指标
- 提醒指标: 请求处理时间 请求处理时间指标记录了 NGINX 处理每个请求的时间,从读到客户端的第一个请求字节到完成请求。较长的响应时间说明问题在上游。
实现
目前prometheus还没有官方的exporter。目前有两种采集的办法
nginx-lua-prometheus
这个是以lua插件的形式暴露出一些基础连接信息
下载地址:https://github.com/knyar/nginx-lua-prometheus
nginx-lua-prometheus测试
解压源码包
tar -zxvf nginx-1.6.0.tar.gz
开始进行编译。
[root@test nginx-1.6.0]# pwd
/opt/nginx-1.6.0
[root@test nginx-1.6.0]# ./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_realip_module --add-module=./nginx-sticky-module-1.1 --add-module=./nginx_upstream_check_module-master --add-module=./nginx_upstream_hash-0.3.1 --add-module=./lua-nginx-module-0.9.10 --add-module=./nginx-concat-module
查看编译后安装的模块
[root@test sbin]# ./nginx -V
nginx version: nginx/1.6.0
built by gcc 4.1.2 20080704 (Red Hat 4.1.2-48)
TLS SNI support disabled
configure arguments: --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_realip_module --add-module=./nginx-sticky-module-1.1 --add-module=./nginx_upstream_check_module-master --add-module=./nginx_upstream_hash-0.3.1 --add-module=./lua-nginx-module-0.9.10 --add-module=./nginx-concat-module
[root@test sbin]# pwd
/usr/local/nginx/sbin
[root@test sbin]#
之后make & make install
启动nginx
[root@test sbin]# ./nginx
[root@test sbin]# ps -ef |grep nginx
root 15585 1 0 15:45 ? 00:00:00 nginx: master process ./nginx
nobody 15586 15585 0 15:45 ? 00:00:00 nginx: worker process
nobody 15587 15585 0 15:45 ? 00:00:00 nginx: worker process
nobody 15588 15585 0 15:45 ? 00:00:00 nginx: worker process
nobody 15589 15585 0 15:45 ? 00:00:00 nginx: worker process
nobody 15590 15585 0 15:45 ? 00:00:00 nginx: worker process
nobody 15591 15585 0 15:45 ? 00:00:00 nginx: worker process
nobody 15592 15585 0 15:45 ? 00:00:00 nginx: worker process
nobody 15593 15585 0 15:45 ? 00:00:00 nginx: worker process
root 15597 3959 0 15:45 pts/2 00:00:00 grep nginx
浏览器访问
开始安装对应监控模块
下载最新的release版本nginx-lua-prometheus-0.1-20170610.tar.gz
创建目录
/usr/local/nginx/lua
上传gz包,并解压
[root@test nginx-lua-prometheus-0.1-20170610]# pwd
/usr/local/nginx/lua/nginx-lua-prometheus-0.1-20170610
[root@test nginx-lua-prometheus-0.1-20170610]# ls
nginx-lua-prometheus-0.1-20170610.rockspec prometheus.lua prometheus_test.lua README.md
[root@test nginx-lua-prometheus-0.1-20170610]#
修改nginx.conf
lua_shared_dict prometheus_metrics 10M;
lua_package_path "/usr/local/nginx/lua/nginx-lua-prometheus-0.1-20170610/?.lua";
init_by_lua '
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 '
local host = ngx.var.host:gsub("^www.", "")
metric_requests:inc(1, {host, ngx.var.status})
metric_latency:observe(ngx.now() - ngx.req.start_time(), {host})
';
server {
listen 9145;
#allow 0.0.0.0/16;
#deny all;
location /metrics {
content_by_lua '
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()
';
}
}
重新加载nginx
[root@test conf]# ../sbin/nginx -s reload
访问nginx的页面
http://10.19.250.191:9145/metrics
nginx-vts-exporter
目前的版本是nginx-vts-exporter-0.8.3.linux-amd64.tar.gz
下载地址是:https://github.com/hnlq715/nginx-vts-exporter/releases
nginx-vts-exporter测试
由于nginx-vts-exporter依赖于Nginx的nginx-module-vts模块,所以这里需要重新编译下Nginx。之后再安装这个exporter
VTS安装步骤
1、 下载nginx-module-vts
https://github.com/vozlt/nginx-module-vts/releases/tag/v0.1.15
解压后目录为
/opt/nginx-module-vts-0.1.15
2、 重新编译nginx
说明 由于 nginx_upstream_check_module-master 模块有问题。
所以编译的时候的配置语句为
./configure --with-http_stub_status_module --with-http_ssl_module --with-http_realip_module --add-module=./nginx-sticky-module-1.1 --add-module=./lua-nginx-module-0.9.10 --add-module=./nginx-concat-module --add-module=/opt/nginx-module-vts-0.1.15
3、 开始安装
make & make install
4、 修改配置文件
http {
vhost_traffic_status_zone;
...
server {
listen 8088;
location /status {
vhost_traffic_status_display;
vhost_traffic_status_display_format html;
}
}
}
5、 重新加载配置
[root@test conf]# ../sbin/nginx -s reload
访问页面
http://10.19.250.191:8088/status
可见,vts的本身监控就是比较全面的,就是为监控而生。
安装启动探针
[root@test nginx-vts-exporter-0.8.3.linux-amd64]# nohup /opt/nginx-vts-exporter-0.8.3.linux-amd64/./nginx-vts-exporter -nginx.scrape_uri="http://10.19.250.191:8088/status/format/json" 2>&1 &
[root@test nginx-vts-exporter-0.8.3.linux-amd64]# nohup: appending output to `nohup.out
配置访问
curl -XPUT http://10.27.136.227:9996/v1/agent/service/register -d '
{
"id": "prometheus-exporter11",
"name": "promether-exporter",
"address": "10.19.250.191",
"port": 9913,
"tags": [
"SNMON",
"NJXZ",
"DEV",
"10.19.250.191",
"nginx-9913"
],
"checks": [
{
"script": "curl http://10.19.250.9913/metrics >/dev/null 2>&1",
"interval": "10s"
}
]
}'
查看
http://10.27.136.227:9099/targets
查看Metric
http://10.19.250.191:9913/metrics
综合来说,vts的指标本来就比较全面,结合prometheus,更加匹配我们的监控需求,但是要重新编译nginx比较麻烦,最终还是使用vts。