ADB日志分析最佳实践

jiayu.jjy 2019-07-17

nginx 大数据 hadoop 性能 日志 阿里技术协会 数据库 shell 配置 集群 request logstash 控制台 adb 日志分析

背景

利用服务器日志做分析是很多公司进入大数据分析的第一步,也是很关键的一步。大部分情况下,这些公司在考虑进行大数据分析的时候,都会遇到以下问题:

  • 团队里面缺乏了解大数据技术栈的工程师
  • 都听过Hadoop,想要学习Hadoop,但是不知道从何入手
  • 从市面上寻找大数据人才效果不理想
  • 不愿意一下子投入过多的资金去组建一个专门的大数据团队

虽然Hadoop没有办法一下子搭起来,但是其实在刚开始进入大数据的时候完全可以用MPP数据库来快速满足需求。但是你可能会有疑问,MPP能够代替Hadoop吗?要回答这个问题,首先要理解Hadoop的出现到底解决了什么问题:

  1. 传统的单节点关系型数据库,要提升性能,只能通过scale up的方式,即增加cpu/内存/硬盘。到后面提升5%的计算能力可能是前面10倍的成本投入。Hadoop利用分布式的思想,通过shared-nothing的架构,实现了scale out的能力。在这样的架构下面,加入同样性能的机器,可以达到线性提升处理性能的效果,投入产出成正比。
  2. 关系型数据库对于非/半结构化数据不是特别友好,主要表现在关系型数据库是以行列为结构存储数据的,无法直接把json,xml这类型的数据插入进去。Hadoop的优势是可以通过编写特定的input reader,来解释这些数据格式,从而达到处理非结构数据的能力。但是不能忘记的一点是,为了提升处理性能,即使使用Hadoop,最后也是要将数据结构化的。

通过上面的描述,其实思路就很清晰了。如果可以有一个shared-nothing架构的关系型数据库,将要导入的数据预先进行结构化处理,那么我们还是可以在关系型数据库(MPP)里面做大数据分析。显然ADB就是这么一个场景下最合适的选择。

组件介绍

这一章介绍一下本方案使用到的各种组件

名称 介绍
Nginx 反向代理,一般被用来做负载均衡。作为网络流量的总入口,会沉淀所有的用户行为。利用这里的日志作为分析可以得到最全面的数据视图。
Logstash 日志采集工具,可以对采集到的数据进行一定的预处理,通过配置文件进行格式化改造。
DataHub 数据管道,提供对数据的发布和订阅。支持直接导出到一些常用的数据存储包括MaxCompute,OSS,ADB等。
ADB for MySQL 高并发低延时的PB级别数据仓库,MPP架构,完全兼容MySQL协议。

搭建步骤

安装Nginx

在CentOS下面,安装Nginx

yum install nginx

成功安装的话,会看到如下内容

已安装:
  nginx.x86_64 1:1.12.2-3.el7

作为依赖被安装:
  nginx-all-modules.noarch 1:1.12.2-3.el7              nginx-mod-http-geoip.x86_64 1:1.12.2-3.el7     nginx-mod-http-image-filter.x86_64 1:1.12.2-3.el7     nginx-mod-http-perl.x86_64 1:1.12.2-3.el7
  nginx-mod-http-xslt-filter.x86_64 1:1.12.2-3.el7     nginx-mod-mail.x86_64 1:1.12.2-3.el7           nginx-mod-stream.x86_64 1:1.12.2-3.el7

完毕!

完成安装之后,需要定义Nginx打印日志的格式。在这个例子中,因为我们想要统计网站访问的UV和每个请求的响应时长,所以我们需要把 $request_time$http_cookie 添加进去。另外,Nginx日期的默认打印方式是 07/Jul/2019:03:02:59, 对于我们后面做分析时候查询不是十分友好,所以要把它们统一改成 2019-07-11T16:32:09 的格式。

找到/etc/nginx/nginx.conf,用vi打开,将log_format更改如下:

log_format  main  '$remote_addr - [$time_iso8601] "$request" '
                  '$status $body_bytes_sent $request_time "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for" "$http_cookie"' ;

执行下面命令,重启Nginx使配置生效:

service nginx restart

这时去看一下日志(位于/var/log/nginx/access.log),会发现日志打印格式如下

119.35.6.17 - [2019-07-14T16:39:17+08:00] "GET / HTTP/1.1" 304 0 0.000 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36" "-" "-"

由于默认的Nginx主页不会记录cookie,建议后面挂一个带登陆的系统来做验证

部署DataHub

DataHub的控制台地址 https://datahub.console.aliyun.com/datahub

首先要创建一个Project,取名为log_demo

project_create.png

进入到这个log_demo 项目之后,要创建一个Topic, 我们可以取名topic_nginx_log :

topic_create.png

Topic的类型选择Tuple,这是一种带schema的组织形式,更方便我们查看和后面的处理。Schema的定义跟我们要从日志中取得的字段是强相关的,所以建立的时候需要确保没有错误,否则可能导致后面日志写入的时候出错。

字段名称 意义 类型 对应Nginx日志的字段
remote_ip 请求的来源IP STRING $remote_addr
date 请求发生的日期 BIGINT $time_iso8601 的日期部分
time 请求发生的时间,24小时制,精确到秒 STRING $time_iso8601 的时间部分
method Http请求的方法 STRING $request 的请求动作部分,例如GET
request 请求的uri和param部分 STRING $request的uri和param部分
http_version Http版本号 STRING $request的http版本号部分
status 请求的返回状态码 STRING $status
bytes 请求体的大小 BIGINT $body_bytes_sent
request_time 请求的耗时 DOUBLE $request_time
referer 发出这个请求之前,用户在哪个页面 STRING $http_referer
agent 客户端使用的操作系统和浏览器 STRING $http_user_agent
xforward 请求经过的代理 STRING $http_x_forwarded_for
cookie 用户的标识 STRING $http_cookie

成功创建之后,可以在这个Topic的Schema里面看到如下图展示

topic_schema.jpg

安装Logstash

官方的Logstash没有提供DataHub的兼容工具,所以建议到DataHub的官网上面去下载最新的兼容版本,减少中间融合操作成本。具体的介绍页面在这里

回到控制台,输入下面命令,下载和解压Logstash到你想要的目录

##注意,这里的版本号和下载链接可能因为更新缘故有区别,建议到介绍页面去获取最新链接

wget http://aliyun-datahub.oss-cn-hangzhou.aliyuncs.com/tools/logstash-with-datahub-6.4.0.tar.gz?spm=a2c4g.11186623.2.17.60f73452tHbnZQ&file=logstash-with-datahub-6.4.0.tar.gz

tar -zxvf logstash-with-datahub-6.4.0.tar.gz

安装完成之后需要配置Logstash,这里有两个工作需要完成。第一,由于我们需要将日志处理成结构化的数据,所以在抓取的过程中,还需要做一点加工。Logstash的grok可以帮我们轻松地完成这个任务。第二,我们需要告诉Logstash从哪里(Nginx的日志存放位置)抓起日志文件,最后要同步到哪里(DataHub)去。

配置grok

我们需要让grok能够理解日志文件并进行转换,需要往grok-pattern文件里面添加一个新格式。这个文件存放在刚刚下载的Logstash目录里面,具体路径如下 :

logstash/vendor/bundle/jruby/1.9/gems/logstash-patterns-core-xxx/patterns/grok-patterns

用vi打开这个文件,把下面的新pattern填到文件最末端即可

DATE_CHS %{YEAR}\-%{MONTHNUM}\-%{MONTHDAY}
NGINXACCESS %{IP:remote_ip} \- \[%{DATE_CHS:date}T%{TIME:time}\+08:00\] "%{WORD:method} %{URIPATHPARAM:request} HTTP/%{NUMBER:http_version}" %{NUMBER:status} %{NUMBER:bytes} %{NUMBER:request_time} %{QS:referer} %{QS:agent} %{QS:xforward} %{QS:cookie}
配置Logstash

在Logstash的目录里面,有一个config文件夹,里面有一个logstash-sample.conf文件。打开它,把它改成

input {
  file {
    path => "/var/log/nginx/access.log" #nginx的日志位置,默认在这里。
    start_position => "beginning" #从什么位置开始读起日志文件,beginning表示从最开始。
  }
}

filter {
  grok {
    match => {"message" => "%{NGINXACCESS}"} #当grok获取到一个消息时,怎么去转换格式
  }
  mutate {
    gsub => [ "date", "-", ""] #因为日期在后面要用作ADB表的二级分区,所以需要把非数字字符去掉
  }
}

output {
   datahub {
        access_id => "<access-key>" #从RAM用户获取access key
        access_key => "<access-secrect>" #从RAM用户获取access secret
        endpoint => "http://dh-cn-shenzhen-int-vpc.aliyuncs.com" #DataHub的endpoint,取决于把project建立在哪个区域
        project_name => "log_demo" #刚刚在DataHub上面创建的项目名称
        topic_name => "topic_nginx_log" #刚刚在DataHub上面创建的Topic名称
        dirty_data_continue => true #脏数据是否继续运行
        dirty_data_file => "/root/dirty_file" #脏数据文件名称,脏数据会被写入到这里
        dirty_data_file_max_size => 1000 #脏数据的文件大小
    }
}

完成这个配置之后,我们可以启动Logstash来验证是否有日志被写入到DataHub里面了。在Logstash的根目录里面,执行如下命令

bin/logstash -f config/logstash-sample.conf

看到这个如下日志代表启动成功

ending Logstash logs to /root/logstash-with-datahub-6.4.0/logs which is now configured via log4j2.properties
[2019-07-12T13:36:49,946][WARN ][logstash.config.source.multilocal] Ignoring the 'pipelines.yml' file because modules or command line options are specified
[2019-07-12T13:36:51,000][INFO ][logstash.runner          ] Starting Logstash {"logstash.version"=>"6.4.0"}
[2019-07-12T13:36:54,756][INFO ][logstash.pipeline        ] Starting pipeline {:pipeline_id=>"main", "pipeline.workers"=>2, "pipeline.batch.size"=>125, "pipeline.batch.delay"=>50}
[2019-07-12T13:36:55,556][INFO ][logstash.outputs.datahub ] Init datahub success!
[2019-07-12T13:36:56,663][INFO ][logstash.inputs.file     ] No sincedb_path set, generating one based on the "path" setting {:sincedb_path=>"/root/logstash-with-datahub-6.4.0/data/plugins/inputs/file/.sincedb_d883144359d3b4f516b37dba51fab2a2", :path=>["/var/log/nginx/access.log"]}
[2019-07-12T13:36:56,753][INFO ][logstash.pipeline        ] Pipeline started successfully {:pipeline_id=>"main", :thread=>"#<Thread:0x445e23fb run>"}
[2019-07-12T13:36:56,909][INFO ][logstash.agent           ] Pipelines running {:count=>1, :running_pipelines=>[:main], :non_running_pipelines=>[]}
[2019-07-12T13:36:56,969][INFO ][filewatch.observingtail  ] START, creating Discoverer, Watch with file and sincedb collections
[2019-07-12T13:36:57,650][INFO ][logstash.agent           ] Successfully started Logstash API endpoint {:port=>9600}

这时候可以开始去刷一下Nginx代理的页面了。默认Nginx自带一个静态的网页,可以通过访问80端口打开。产生日志的话,Logstash的日志会马上打印如下信息

[2019-07-12T13:36:58,740][INFO ][logstash.outputs.datahub ] [2010]Put data to datahub success, total 10

然后在DataHub上,可以看到抽样数据如下

sample-on-datahub.jpg

配置ADB

先到ADB的控制台去创建一个新的集群,数据库的名字取nginx_logging。需要注意的是,当ADB创建好之后,在控制台那边看不到数据库的名字,转而变成了集群名称。目前看来称谓不一样,但是其实是一回事。

新的集群初始化会需要一点时间。待初始化完成之后,可以通过控制台的登陆数据库按钮跳转到DMS(数据管理)去创建用于存放日志的数据库。在创建数据库之前,需要先理解ADB的几个核心概念,这样设计出来的表性能才会更佳。

一级分区

要理解一级分区,需要先理解MPP数据库是怎么提升性能的。本文的背景中说到,MPP是可以线性扩展的,但是有一个前提,数据要均匀分布。这样在一个Query过来的时候,才能让尽量多的磁盘被利用起来。在ADB中,数据是否均匀分布,主要取决选择哪个列作为一级分区。千万不要选择数据倾斜的列作为分区列,例如日期,性别,或者存在大量空值的列。在本例中,比较适合的应该是remote_ip这一列。

二级分区

二级分区是在一级分区的基础上做二次切分,可以理解为隐性地帮用户把一个表切成了多个表,从而提升全表查询的性能。此外,二级分区还有一个优点,就是可以设置分区个数。在超过分区个数的时候,ADB会自动将历史最久的一个二级分区删除,实现历史数据自动清楚的效果。在本例中,date这个列特别时候做二级分区。假如我们想保留30天的日志数据,我们只需要将二级分区设置成30个即可。注意,二级分区的列一定要是整数类型,例如bigint或者long

下面是创建表nginx的SQL

CREATE TABLE nginx_logging.nginx(
  remote_ip varchar NOT NULL COMMENT '',
  date bigint NOT NULL COMMENT '',
  time varchar NOT NULL COMMENT '',
  method varchar NOT NULL COMMENT '',
  request varchar COMMENT '',
  http_version varchar COMMENT '',
  status varchar COMMENT '',
  bytes bigint COMMENT '',
  request_time double COMMENT '',
  referer varchar COMMENT '',
  agent varchar COMMENT '',
  xforward varchar COMMENT '',
  cookie varchar COMMENT '',
  PRIMARY KEY (remote_ip,date,time)
)
PARTITION BY HASH KEY (remote_ip) PARTITION NUM 128 -- remote_ip作为一级分区
SUBPARTITION BY LIST KEY (date) -- date作为二级分区
SUBPARTITION OPTIONS (available_partition_num = 30) -- 二级分区30个,代表保留30天的数据
TABLEGROUP logs
OPTIONS (UPDATETYPE='realtime') -- 由于要被DataHub更新,所以一定要选择是realtime的表
COMMENT ''''

成功配置创建表之后,ADB这边的准备任务就算完成了。需要提示一下,默认ADB的网络连接信息只有公有网络经典网络的连接。专有网络的连接地址需要通过配置打开。因为ADB完全兼容MySQL协议,所以我们要从本地登陆的话,可以利用公有网络的地址。而DataHub访问ADB,目前使用的是经典网络的连接。

配置DataConnector

回到刚刚创建的Topic, topic_nginx_log, 下面。右上角有一个按钮叫+DataConnector。这里需要输入几个关于ADB的信息:

Host : #ADB集群经典网络的连接地址
Port : #ADB集群经典网络的端口
Database : nginx_logging #之前建立ADB集群时候输入的名字,同时也是这个ADB集群的名字
Table : nginx #上面建立的表名
Username : <access-key> #RAM用户的access key
Password : <access-secret> #RAM用户的access secret
模式 : replace into #如果主键冲突,会覆盖掉记录

成功配置之后,在DataConnectors下面就可以看到相关的信息了。如果之前DataHub上面已经有数据,现在查询ADB里面的nginx表就可以将这些记录查出来。

sample.jpg

至此,整个数据链路就算搭建完成了。

登录 后评论
下一篇
corcosa
15584人浏览
2019-10-08
相关推荐
ADB笔记
527人浏览
2017-03-21 15:52:17
MUI开发大全
1937人浏览
2017-10-10 14:26:00
android 抓包分析
492人浏览
2013-03-15 13:47:00
0
0
0
568