Nginx的访问日志记录每条请求的来龙去脉,通过日志可以分析出很多有用的监控信息,如下面的这些信息。
- 请求的响应时间。
- 请求到达的后端服务器的地址和端口。
- 请求是否存在缓存配置。
- 请求体、请求头、响应体和响应头的大小等。
- 客户端的IP地址、User_Agent等信息。
- 自定义变量的内容。
通过这些信息,可以对请求的各项指标进行监控,这对应用级别的服务来说是非常重要的。下面将会对Nginx日志分析中常见的需求进行说明。
实战需求分析
首先需要确认Nginx日志分析应该使用什么类型的工具。与筛选动态upstream管理工具的方式一样,它应该至少满足如下条件。
- 可以计算出URI响应的平均值,以及p90、p99等任意比例的值。
URI在指定的某段时间内按照请求的响应时间进行升序排列,p99的意思是指在99%这个位置的响应时间,即确认出99%的请求所花费的时间,用于体现服务的响应能力。
- 可以对URI进行分组和汇总统计,特别是当URI为正则表达式类型时。
在很多业务中,URI会使用正则表达式类型的路由,以“折800商城”的详情页为例:shop.zhe800 .com/products/ze171126205509136896和 shop. zhe800.com/products/ze170814104348738286。这两个网址属于同一个URI服务类型,只是后面的数字不一样,它们在服务中都可以表示为shop.zhe800.com/products/[a-z0-9] 的正则表达式。如果不将这些带有正则表达式的URI进行归类,在汇总时URI会非常分散,从而无法做到准确的定位和报警。因此对URI进行归类分组是非常重要的。
- 可以监控URI的走向,确认是否存在缓存,确认后端服务器属于哪个Web应用。
确认是否存在缓存的主要目的是避免出现上线时需要加缓存的服务没加的情况。可以通过日志分析定期梳理这些服务,找到没有添加缓存的服务,然后和业务部门确认是否需要配置缓存。(URI是否有缓存可以通过在响应头中加入特定的头信息进行标识,例如CDN缓存一般有Cache-Control头。)
- 可以支持集群模式,大多数互联网公司会使用多台Nginx服务器,数据的分析需要在日志集中搜集后再进行。
- 可以提供实时监控,及时进行分析并反馈异常信息。
- 可以提供定制化服务,满足不同业务的需求。
上述功能的实现不必一蹴而就,可以在使用中通过不断迭代完成,在高并发、多业务模式下的服务均可参考这些需求。但如果Nginx的服务单一且并发程度较低,只需用一些简单的分析工具甚至脚本即可。
下面将会介绍一些工具,利用它们来完成上述各种需求。
ngxtop实时分析
ngxtop是用Python语言开发的在线分析工具,它可以对Nginx请求进行实时分析,使用方法也非常简单。
首先,安装ngxtop。ngxtop是Python的包,所以使用Python的pip命令安装即可(ngxtop支持Python 2和Python 3):
# yum install python-pip
# pip install ngxtop
在使用ngxtop时,需要确保Nginx日志格式是默认格式,因为ngxtop是通过对日志格式进行匹配得到数据的,所以格式改变将会导致数据分析异常。如果有新的变量要加入日志格式,请将其添加到默认配置的后面(新的变量在ngxtop中无法被分析,只能将其记录到硬盘上,以方便业务的其他需求),代码如下:
执行分析命令如下:
ngxtop会找到配置文件中access_log的位置,-n的作用是显示所输出URI的行数,默认是10行。ngxtop日志分析结果如图1所示。
图1 ngxtop日志分析结果
从图1中可以看到请求的总量、URI的访问次数、平均发送字节数及HTTP状态码。
ngxtop还支持如下功能。
- 支持直接分析指定的日志文件,命令为“ngxtop -l“Nginx日志文件路径”;”。
- 支持对IP地址进行不同条件的排名,命令为“ngxtop --config/usr/local/nginx/ conf/nginx. conf top remote_addr”。
- 支持日志过滤,若只需要HTTP状态码为502的数据,则命令为“ngxtop -l /data1/access.log --filter 'status == 502'”。
在网站搭建初期,使用ngxtop对实时分析有很大的帮助,但随着网站规模的扩大、业务的增多,继续使用此工具会遇到瓶颈。总结ngxtop的优缺点如下。
优点:
- 安装和使用非常简单。
- 有多种实时工具和数据分析模型。
- 对Nginx版本没有依赖性,不必担心Nginx版本升级造成的兼容问题,只要日志格式符合要求即可。
缺点:
- 无法自定义监控数据。
- 数据汇总能力欠佳,无法对有正则表达式的服务进行监控。
- 不支持集群化,适合对单点的Nginx服务器进行分析。
- 日志格式不灵活,只能在后面追加格式。
总而言之,它比较适合小型单机网站或临时问题的分析。另外,还有一个工具GoAccess与ngxtop比较类似。
Flume方案的日志分析
由于ngxtop工具的局限性,特别是它在集群化方面的缺陷,我们需要重新寻找工具,经过筛选决定采用Flume来收集数据完成日志汇总,并利用比较流行的Elasticsearch来进行数据存储。Flume方案的日志分析流程图如图2所示。
图2 Flume方案的日志分析流程图
- Flume负责收集日志,并将数据格式规范化后写入Kafka。
- Kafka中的数据被Storm实时分析,并在计算后写入Elasticsearch。
- Elasticsearch中的数据被用于实时和离线的各类数据统计。
先说说这个方案的优缺点。
优点:
- 使用Flume来收集日志,性能有保证,并且可以自定义日志格式。
- Kafka可以横向扩展,性能好且数据不易丢失,适用于资源消耗压力较大的情况。
- Storm是非常有名的实时分析工具,可以很方便地实现自定义的需求。
- Elasticsearch也支持扩展,并支持多种SQL查询,使数据的汇总分析变得更加简单。
缺点:
- 使用了过多的组件,如果只是用来监控服务,有点小题大做。
- 当自定义需求时,如把正则表达式类型的URI服务归类,此时,在Storm中计算需要和开发语言(如Java)进行互动,在高并发状态下,资源消耗会过多。
- Elasticsearch的SQL功能虽多,但仍然无法满足各种数据分析的需要,会导致很多计算仍须依靠代码分析或混用多条SQL语句来完成。
- 在高并发情况下,Flume的收集和格式化操作容易对Nginx服务器的资源产生过多消耗。
组件的安装和使用方法在网上有很多资料,这里不再赘述。
Flume方案对资源消耗过多,维护成本也高,随着业务的发展,最终会被放弃,取而代之的是一个全新的数据收集和分析工具,这也是本文要介绍的重点内容——智能化nginx_log_ analysis。
智能化nginx_log_analysis
- 架构重构
现在除进行日志分析外,日志分析工具还需要同时满足高性能、低消耗、迭代方便等需求。看上去确实有点令人头疼,毕竟在开源社区中,能够产品化的Nginx日志分析工具非常少,那么可以自己开发一套工具来实现这些功能。
如果可以在Nginx上将数据格式化后直接通过网络发送给数据库,再由数据库完成分析,这样中间的其他环节(如读取硬盘、格式化日志等)都可以省略了,那么将会极大地降低维护成本。但如何才能让这种设想得以实现呢?这就要用到前面学习过的Ngx_Lua(参见《Nginx实战》的相关章节)了。具体流程分析如下。
- Nginx的日志内容是由Nginx的变量组成的,Ngx_Lua可以直接获取这些变量。
- Ngx_Lua可以对Nginx变量进行数据处理,如格式化、对URI进行分类等。
- Ngx_Lua可以利用log_by_lua*在log执行阶段将整合好的数据传到远程服务器上。
- 远程服务器是一个时序数据库,它可以执行多种函数,如p90计算、平均数计算、热点数据计算、分组、正则匹配,甚至设置定时任务等。
- 数据库要求是高性能的,能处理实时的数据分析。
最后,将计算后的数据和监控系统打通,以便提供报表和报警的功能。但是,鉴于代码过多,笔者选择将这个系统的所有代码在GitHub上进行了开源,地址是https://github.com/leehomewl/nginx_ log_analysis。
注意:本文不会对所有代码都进行讲解,但会选取一部分代码进行说明,主要目的是让读者了解如何在开发中使用Ngx_Lua来完成架构设计和流程规划,从而提升开发水平。
- 日志远程传输
首先,需要解决日志远程传输的问题,而且要支持集群化,即数据的传输要统一存储、统一计算。
这里需要用到模块lua-resty-logger-socket,它的主要功能是以非阻塞I/O的方式推送数据到远程服务器上。它属于Nginx的log执行阶段,是在请求反馈给客户端后执行的操作,所以在此处传输日志,即使数据推送失败,也不会影响客户端的响应。
- 时序数据库
Nginx对日志的分析基于时间的维度,如波动的报表、请求PV(Page View,即页面浏览量)的涨幅、平均响应时间的对比等都是在时间的基础上进行的。再如常用的Nagios和Zabbix,在报警和监控中也是以时间为基础的,所以需要找一个时序数据库来支撑数据分析。
在时序数据库的选择上,可以使用InfluxDB,它是一款基于Go语言开发的开源分布式时序、事件和指标数据库,非常适合处理监控数据,它提供了很多函数,这些函数包含了绝大部分的数据计算方式,可以简化数据分析的代码。关于InfluxDB的用法请参考《Nginx实战》的相关章节。
- 日志规则设计
在Nginx中,用来记录后端响应时间的变量是$upstream_response_time,但有时它会有多个值,各值之间以逗号来分隔,这样做是因为受proxy_next_upstream的控制,当后端服务响应异常时会将请求代理到另一台后端服务器上进行响应,所以就出现了多个值。这样会导致存放在数据库中的响应时间字段的部分数据不是数字,InfluxDB函数无法执行计算。为了避免出现这种情况,在Nginx写入时就要将以逗号分隔的数字累加后再插入数据库中。
在Nginx中URI的变量是$uri,它不包含URL中的参数,$uri即服务。之前提到过,如果URI无法区分正则表达式,就会导致计算分散化,从而失去分析和监控的意义。因此需要在Nginx中将这些正则表达式的数据区分出来。
那么如何让Ngx_Lua知道URI是否是正则表达式的呢?这就涉及URI认领的问题了,大致方式如下。
- 清理公司业务线上使用的URI。
- 对URI进行筛选,确认哪些是精确URI、哪些是可以合并到正则URI上的URI。
- 将筛选后的URI数据存放到MySQL中。
- Ngx_Lua从MySQL读取URI数据并将其存放到内存中。
- 当客户端请求发送到Nginx时,Ngx_Lua在log阶段判断当前请求属于哪个URI服务(正则URI或精确URI)。
URI认领并非只为了方便数据汇总,它还可以实现更多功能,例如,想要判断某个项目是否使用了新的URI,可以用Ngx_Lua在测试环境下做一层验证,判断请求的URI是否为认领中的服务,如果不是,则为新URI。这样每次上线前都可以提前配置和监控URI。在补充监控时也可以对新增的URI添加其他属性,如配置缓存、监控POST的数据长度范围、进行降级容灾处理等。总之,通过这种方式,可以对URI做非常细致的监控。
关于正则表达式URI的存放和匹配格式,下面举例说明。例如,MySQL中存放了一个路由是/a/b/[0-9] 的URI服务,并由Nginx将该数据读取到内存中进行缓存,当客户端请求Nginx且URI是/a/b/123或/a/b/345时,就会被Ngx_Lua匹配成/a/b/[0-9] ,最后将/a/b/[0-9] 写入InfluxDB,并会用它来完成数据分析。
通过这套流程,就可以得到想要的分析报告了。
注意:以上对MySQL的应用,只是为了区分URI是正则表达式还是精确类型,有些读者的服务可能只有精确的URI,在这种情况下,MySQL是可以去掉的,但如果读者希望使用更多的监控功能,MySQL还是非常重要的,这在《Nginx实战》的相关章节中会有详细的讲解。