基于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方式偶尔断图)。

参考资料


16 thoughts on “基于http_reqstat模块和zabbix的Tengine监控方法

  1. 利用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))

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

        • server {
          listen 80;
          server_name 10.121.50.170;
          error_log /data/nginx/logs/tt2-error.log;
          access_log /data/nginx/logs/tt2.log main buffer=32k flush=5s;

          # error_log /dev/null;
          # access_log off;

          # Add location here
          location / {
          proxy_set_header Host $proxy_host;
          #proxy_set_header X-Real-Ip $remote_addr;
          #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_pass http://Test1;
          }

          location /ma2/open/ {
          proxy_set_header Host $proxy_host;
          #proxy_set_header X-Real-Ip $remote_addr;
          #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_pass http://Test2;
          # proxy_pass http://lingxuLoadbalanceTest;
          }

          }
          上面是server的配置,下面这个是upstream的配置
          upstream Test1{
          server 10.15.210.133:80 weight=100 ;

          #check interval=10000 rise=2 fall=3 timeout=1000 type=http default_down=false;
          #check_http_send "GET /ma2/health HTTP/1.0rnrn";
          #check_http_expect_alive http_3xx http_2xx;
          }

          upstream Test2 {
          server 10.153.72.30:80 weight=100;

          }

          • location字段的proxy_set_header Host $proxy_host;是后来改的,默认配置也不可以。关于http_reqsstat模块的配置我是完全照着您的配置来的。就是和本文1.2节完全一样。我是另外放到一个文件里,include进http字段内的。


            req_status_zone app "$proxy_host" 10M;
            req_status app;
            server {
            listen 9009;
            location /reqstat {
            req_status_show;
            allow 10.0.0.0/8;
            allow 127.0.0.0/8;
            deny all;
            }
            }

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

  3. 您好。我这边配置 req_status_zone app "$proxy_host" 2M后,访问uri为何kv 部分是空的不显示?如果改为req_status_zone app "$server_name(或者uri) 2M“都可以显示,是否其他地方要有响应的配置?希望不吝赐教。感激。

回复 annhe_net 取消回复

您的电子邮箱地址不会被公开。 必填项已用*标注