基于http_reqstat模块和zabbix的Tengine监控方法

Tengine的http_reqstat_module提供了监控Tengine运行状态的方法,能根据自定义变量(req_status_zone)统计Tengine的运行状况(域名,URI等)。本文记录了一种使用http_reqstat模块作为数据源,zabbix作为数据存储及展示,grafana作为前端展示的Tengine监控方案。 本文使用的Tengine版本为2.1.1,zabbix版本为3.0, grafana版本为2.6。

Tengine reqstat数据形式

业务结构

reqsat能根据不同的Nginx变量来统计状态信息(例如统计每个域名,或者统计每个URI),因此首先要根据业务需求确定需要统计的变量。本文接触的业务分属于不同的集群,每个集群使用多台Tengine做反向代理,每个APP有自己独享的upstream,有一个或多个Location,但不一定使用相同的域名。

Tengine配置

使用URI作为自定义变量会导致统计项过多(考虑404的URI),使用域名又不能很好的区分APP。因此使用upstream名称作为自定义变量是最好的选择。

reqstat数据形式

访问 /reqstat,数据形式如下:

数据含义:

数据存储方案

物理结构与zabbix的对应关系

使用zabbix存储监控数据,物理结构对应zabbix组织形式如图:

本文使用zabbix LLD的host prototype功能自动生成主机,zabbix 3之后有了application prototype功能,这样item prototype里可以设置item所属的application prototype。因此可以实现上图中 APP upstream名称zabbix application 的对应关系。

监控项单位

监控的项目大致分为3类:

  • request 请求数量,包含总请求量,各http_status请求量等,期望存储单位 request/秒
  • request time 请求时间,期望存储单位 rt/请求
  • byte 流量,存储单位 B/秒

监控结构图

监控的大致流程为:

  1. 新建模板,添加LLD用于获取包含TAG的APP_NAME列表,并设置Filter:{#TAG} match {$CLUSTER.TYPE}
  2. 新建一个HOST作为列表生成器,并设置主机宏:{$CLUSTER.TYPE} => TAG
  3. 为列表生成器添加LLD,用于获取包含TAG的Tengine IP列表,并设置Filter:{#TAG} match {$CLUSTER.TYPE}
  4. 为列表生成器添加Host Prototype,使用 {#TAG}_{#REQSTAT_IP}作为NAME
  5. 基于步骤3、4, 一个列表生成器的Host Prototype只取TAG和主机宏设置的TAG相同的Tengine IP生成HOST并链接模板,新生成的HOST会继承列表生成器的宏变量{$CLUSTER.TYPE}。因此有多个集群需要监控时,需要建立多个列表生成器
  6. HOST链接模板,基于步骤1、5,当HOST的{$CLUSTER.TYPE}和模板LLD返回的APP_NAME的TAG一致时,才会使用此APP_NAME创建item,application等
  7. 数据采集LLD发送数据到zabbix

如图所示:

监控实现

本文需要建立一个zabbix模板,添加列表生成器主机和采集器主机,并实现相关控制脚本(即上节图中 reqstat.sh)。

zabbix设置

根据上节图形及描述,以集群名称CN为例,zabbix配置步骤如下:

  1. 准备nginx ip列表文件: nginx.list
  2. 新建模板 Tengine_reqstat
  3. 为模板添加LLD: app列表,设置key: reqstat.sh[applist], 并添加Filter: {#TAG} match {$CLUSTER.TYPE}
  4. 为模板LLD添加item prototypes,以{#APP_NAME}为参数,例如 http_200[{#APP_NAME}]。数据类型为 Numberic(float),Application Prototype使用宏{#APP_NAME}
  5. 模板LLD中按需添加Graph Prototypes和Trigger Prototypes
  6. 新建列表生成器HOST,设置主机宏: {$CLUSTER.TYPE} => CN
  7. 为列表生成器添加LLD,设置key: reqstat.sh[nginxlist], 并添加Filter: {#TAG} match {$CLUSTER.TYPE}
  8. 为列表生成器LLD添加Host Prototype,NAME为 {#TAG}_{#REQSTAT_IP}, 并链接模板 Tengine_reqstat
  9. 新建采集器HOST,添加LLD,设置key: reqstat.sh[getstat]
  10. 如果集群新增机器,编辑nginx.list,添加新机器
  11. 如果新增集群,编辑nginx.list,添加新集群IP,并重复步骤 6,7,8(可以通过Clone Host简化)

监控脚本

Tengine列表

列表文件格式为 TAG,IP形式,TAG主要用来标记集群名称。

app列表

获取带有TAG的APP列表:

数据采集器

数据采集流程
  1. 访问/reqstat,获取统计数据
  2. 一段时间后(T秒),再次访问/reqstat,获取统计数据
  3. 两次获取的数据对应项相减,得到结果 S, 数量相关的用 S/T,获取平均每秒的数据,响应时间相关的用 S/T秒内请求数 得到每个请求的平均响应时间

具体实现中,第一次访问获取的数据保存为 file_start, 第二次访问获取的数据保存为 file_end,然后对2个文件执行 join操作,然后对应列相减,获得需要的数据。 核心代码是一段awk脚本:

上面代码干的事情(简化)如图所示:

file_push的形式正好是zabbix_sender要求的格式,直接 zabbix_sender -z $zabbix_server -i file_push 就可以提交数据。

awk参数中有个 -M,这个参数是为了计算大数,需要编译MPFR:

grafana配置

使用grafana作为前端最好的特性是可以在一个screen里看到某个APP多个指标图形,另外一个好处是可以看到每个点的数据值。

附录

模板trigger示例

可以使用trigger发现出现5xx错误或者请求量异常突增突降的APP。例如下面的trigger表示最近一个qps如果是上2个的3倍以上,并且qps大于20(APP有流量),则认为这个APP请求量突增。

reqstat.sh完整代码

使用crontab做数据采集器

除了LLD方式,也可以使用Linux crontab来实现采集器。添加2条crontab,第一条0s执行,第二条30s执行,这样就实现了每30s执行采集一次数据。实践发现这种方式更好,基本不会出现断图的情况(LLD方式偶尔断图)。

参考资料




本文遵从CC版权协定,转载请以链接形式注明出处。
本文链接地址: http://www.annhe.net/article-3520.html
  1. 您好。我这边配置 req_status_zone app "$proxy_host" 2M后,访问uri为何kv 部分是空的不显示?如果改为req_status_zone app "$server_name(或者uri) 2M“都可以显示,是否其他地方要有响应的配置?希望不吝赐教。感激。

  2. 使用的也是tengine-2.1.1版本,2.1.2版本也测试过,也是这样的情况。从日志看,确实已经对proxy_host有访问量的。

        • 2 server { 3 listen 80; 4 server_name 10.121.50.170; 5 6 7 error_log /data/nginx/logs/tt2-error.log; 8 9 access_log /data/nginx/logs/tt2.log main buffer=32k flush=5s; 10 11 # error_log /dev/null; 12 # access_log off; 13 14 # Add location here 15 location / { 16 proxy_set_header Host $proxy_host; 17 #proxy_set_header X-Real-Ip $remote_addr; 18 #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 19 proxy_pass http://Test1; 20 } 21 22 location /ma2/open/ { 23 proxy_set_header Host $proxy_host; 24 #proxy_set_header X-Real-Ip $remote_addr; 25 #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 26 proxy_pass http://Test2; 27 # proxy_pass http://lingxuLoadbalanceTest; 28 } 29 30 }上面是server的配置,下面这个是upstream的配置 1 upstream Test1{ 2 server 10.15.210.133:80 weight=100 ; 3 4 5 #check interval=10000 rise=2 fall=3 timeout=1000 type=http default_down=false; 6 #check_http_send "GET /ma2/health HTTP/1.0rnrn"; 7 #check_http_expect_alive http_3xx http_2xx; 8 } 9 10 upstream Test2 { 11 server 10.153.72.30:80 weight=100; 12 13 }~

      • 怀疑是我在server字段或者location字段跟您的配置有差异?可否给一份完整的配置给我参看一下?

          • location字段的proxy_set_header Host $proxy_host;是后来改的,默认配置也不可以。关于http_reqsstat模块的配置我是完全照着您的配置来的。就是和本文1.2节完全一样。我是另外放到一个文件里,include进http字段内的。 1 req_status_zone app "$proxy_host" 10M; 2 req_status app; 3 4 server { 5 listen 9009; 6 location /reqstat { 7 req_status_show; 8 allow 10.0.0.0/8; 9 allow 127.0.0.0/8; 10 deny all; 11 } 12 }

          • 谢谢,问题已经解决,我试了2.1.0的版本是可以的。应该是2.1.1tengine团队对该模块做修改后出现的这个问题,看git上提的issue这个应该是一个bug。据说2.1.1这个问题偶尔也会存在。https://github.com/alibaba/tengine/issues/694

  3. 利用zabbix trigger的hysteresis(迟滞)实现更有效的请求量变化告警({TRIGGER.VALUE}=0 and {TEMPLATE:req_total[{#APP_NAME}].last()}>5 and {TEMPLATE:http_499[{#APP_NAME}].avg(#4)}/({TEMPLATE:req_total[{#APP_NAME}].avg(#4)}+0.01)>0.1) or ({TRIGGER.VALUE}=1 and {TEMPLATE:http_499[{#APP_NAME}].avg(#4)}/({TEMPLATE:req_total[{#APP_NAME}].avg(#4)}+0.01)>0.02)

    • ({TRIGGER.VALUE}=0 and {LETV_Tengine_reqstat:http_500[{#APP_NAME}].count(#4,0,"gt")}>3) or ({TRIGGER.VALUE}=1 and {LETV_Tengine_reqstat:http_500[{#APP_NAME}].count(#16,0,"gt")}>1)

    • 请求量异常({TRIGGER.VALUE}=0 and {LETV_Tengine_reqstat:req_total[{#APP_NAME}].last()}>50 and ({LETV_Tengine_reqstat:req_total[{#APP_NAME}].avg(120)}/({LETV_Tengine_reqstat:req_total[{#APP_NAME}].avg(120,130)}+0.01)>2.5 or {LETV_Tengine_reqstat:req_total[{#APP_NAME}].avg(120)}/({LETV_Tengine_reqstat:req_total[{#APP_NAME}].avg(120,130)}+0.01)<0.3)) or ({TRIGGER.VALUE}=1 and (({LETV_Tengine_reqstat:req_total[{#APP_NAME}].avg(120)}+0.01)/({LETV_Tengine_reqstat:req_total[{#APP_NAME}].avg(120,130)}+0.01)>1.7 or ({LETV_Tengine_reqstat:req_total[{#APP_NAME}].avg(120)}+0.01)/({LETV_Tengine_reqstat:req_total[{#APP_NAME}].avg(120,130)}+0.01)<0.7))