1. 前言
针对nginx的访问日志需要进行一个近实时的监控,以便统计用户的访问情况,包括用户的请求IP,请求数据等。因为服务器是和负载均衡用的是阿里云的,阿里云本身也有提供针对负载均衡的日志统计功能,但是作为一个通用日志其是有严重缺陷的:
- 无法收集用户自定义的请求头(我们的实际情况是通过一个自定义的请求头来做用户的身份识别,请求头有用户的唯一识别信息)
- 无法获取到用户的请求数据($request_body) (很多时候,当出现异常访问的时候,是需要通过查询用户的访问数据的,特别像搜索这种业务模块时)
由于存在上面比较严重的缺点,基本上阿里云的日志对我来说就有点鸡肋(食之无味,弃之可惜,你不得不同意他除了上面两点缺点外,日志的其他方面都是做得不错的)。因此我决定自己动手搭建符合自己业务的ELK日志服务。
之前我是有搭建过ELK的经验的,但是没在生产环境试过,起初在搭建的时候我考虑到可能会有以下几个问题:
- Elasticsearch对内存的占用情况
- 收集nginx访问日志,相当于需要打开nginx的access日志开关,硬盘的读写情况,硬盘的占用,会不会影响到用户。
- 日志收集对应用服务器的影响
我现在还是在一两台服务器开启了日志收集,做一个灰度测试。
2. Elasticsearch搭建
2.1 Elasticsearch下载安装
Elasticsearch的安装比较简单,直接下载解压缩即可
- 官网地址:https://www.elastic.co/cn/downloads/elasticsearch
### 下载
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.4.2-linux-x86_64.tar.gz
### 解压缩:
tar -zxvf elasticsearch-7.4.2-linux-x86_64.tar.gz
2.2 Elasticsearch配置
1. 修改cluster-name和node-name
cluster.name: my-application
node.name: node-1
node.master: true
node.data: true
2. 修改网络相关
network.host: 0.0.0.0 ## 0.0.0.0允许所有的ip,这里我是为了方便,后面可以根据实际情况填写
http.port: 9200 ## default
transport.tcp.port: 9300 ## defalut
3. 修改head插件的跨域问题
# 以下的两个配置是为了解决跨域问题,在head插件访问es的时候报跨域了
http.cors.enabled: true
http.cors.allow-origin: "*"
4. 发现配置
# Pass an initial list of hosts to perform discovery when this node is started:
# The default list of hosts is ["127.0.0.1", "[::1]"]
discovery.seed_hosts: ["192.168.1.57"] ## 可被发现的节点IP集合
cluster.initial_master_nodes: ["node-1", "node-2"]
5. 解决es启动时的报错问题,.ElasticSearch集群启动错误,错误的原因是:因为Centos6不支持SecComp,而ES默认bootstrap.system_call_filter为true进行检测,所以导致检测失败
bootstrap.memory_lock: false
bootstrap.system_call_filter: false
2.3 问题
1. 外网访问不了9200的Elasticsearch?
- 配置防火墙端口
- 修改elasticsearch.yml,主要是要修改host(我改成0.0.0.0,方便调试,后期稳定之后会再修改)
2. 启动报错
- vm.max_map_count过小,java虚拟机的virtual memory过小
在/etc/sysctl.conf最后加入一行vm.max_map_count=262144数量根据实际情况定
- max user threads过小(root和dves两个用户的max user threads不一样)
针对这个问题,可以通过
ulimit -a
查看目前可支持的max user threads
修改 max user process
Linux系统为每一个用户都设置了一个最大进程数,这个特性可以让我们控制服务器上现有用户可以创建的进程数量
- 修改方式1:
修改
/etc/security/limits.conf
文件,添加以下配置
## 针对所有用户的可开启最大线程数为4096
* soft nproc 4096
* hard nproc 4096
- 修改方式2:
修改
/etc/security/limits.d/90-nproc.conf
文件,添加以下配置(先把其他默认的给注释掉):
* soft nproc 4096 ## 非root用户配置为4096,这个对es来说够用了
root soft nproc unlimited ## root用户配置最大 128354
针对这两种修改方式解释下,我是在
/etc/security/limits.d/90-nproc.conf
这个文件下修改的。对于max user process
的配置,Linux系统默认先读取/etc/security/limits.conf
中的信息,如果/etc/security/limits.d/
目录下还有配置文件的话,也会依次遍历读取,最终/etc/security/limits.d/
的配置会覆盖/etc/security/limits.conf
的相同配置信息
3. FileBeat搭建
3.1 Filebeat下载安装
curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.4.2-x86_64.rpm
rpm -vi filebeat-7.4.2-x86_64.rpm
安装完成,
filebeat
命令已配置到service服务中,配置文件在/etc/filebeat
,日志文件在/var/log/filebeat/filebeat
,注意,每次重启filebeat的时候,该日志文件都会重新生成,这个时候若如果你是通过tail
命令来观察的,需要结束当前的tail
命令再重复执行一次tail
命令tail -900f filebeat
3.2 Filebeat收集nginx访问日志
3.2.1 nginx的访问日志格式及配置
nginx访问日志是如何配置的,我这里不细说,我只是说下我是如何配置的,如果对nginx的访问日志配置不熟悉的,可以查看这个:ngx_http_log_module
1. 日志格式
因为我们最终要通过Kibana进行展示的,我们需要对访问日志的每个字段都进行解析,所以我们就需要确定nginx的日志是json格式的
## 这个是写在http模块的
log_format json_main '{"@times" : "$time_iso8601",' ## 该time作为Kibana的date类型,在创建Kibana的index_pattern时作为时间筛选的key
'"userAgent" : "$http_user_agent",'
'"remoteAddr" : "$remote_addr", '
'"realIp" : "$http_x_forwarded_for", '
'"requestUri" : "$request", '
'"status" : "$status", '
'"referer" : "$http_referer", '
'"requestTime" : "$request_time", '
'"requestBody" : "$request_body", ' ## 请求数据
'"domoCustom" : "$http_domo_custom"}'; ## 自定义请求头
2. 打开请求日志
对map模块有兴趣的可以查看这里:ngx_http_map_module
## 这个在server模块
access_log /opt/nginx/logs/requestLog.log json_main if=$myUri;
## /opt/nginx/logs/requestLog.log:这个是日志收集的地方,若该日志不存在,在reload或重启nginx的是,会自动创建
## json_main:`log_format`定义的日志格式名
## if=$myUri:一个判断,如果`$myUri`为0或者空字符串,则该日志不收集。这个的作用可以过滤掉一些我们不想收集的日志信息,我是根据请求头来设置的。
## 这个写在http模块,使用map这个内置模块,可以通过判断`$request`这个
## 内置变量是否符合下面的判断,然后把0或1赋值给`$myUri`.
## 这里我的用法就是,如果是OPTIONS请求为0不记录日志信息,
## 如果是`/api/qiqyu`这个第三方的请求也为0不记录日志信息
## 其他的`/api/`正则的则为1记录日志信息
## 非上面集中情况的一律为0不记录相应的日志信息
map $request $myUri {
~OPTIONS 0;
~/api/qiyu/ 0;
~/api/ 1;
default 0;
}
reload
nginx之后就能在/opt/nginx/logs/requestLog.log
这个文件夹下看到相应的日志了
3.3 Filebeat配置
3.3.1 基本配置文件配置
vim /etc/filebeat/filebeat.yml
1. Filebeat inputs
- type: log
# Change to true to enable this input configuration.
enabled: true ## 打开开关
exclude_lines: ['^DBG'] ## 忽略的行
# Paths that should be crawled and fetched. Glob based paths.
paths:
#- /var/log/*.log
- /opt/nginx/logs/requestLog.log ## 配置从哪里按行读取日志
#- c:\programdata\elasticsearch\logs\*
## 如果要让filebeat输入json格式的数据,除了nginx的日志本身是json格式
## 的外,还必须在filebeat中配置一下设置
json.keys_under_root: true
json.overwrite_keys: true
json.add_error_key: true
json.message_key: message ## 如果message在启动的时候报错,可以改成"json"(带双引号)
2. Elasticsearch template setting
setup.ilm.enabled: false ## 这个必须要配置成false,否则自定义index就不会生效
setup.template.enabled: false ## 关掉filebeat默认的es模板
setup.template.overwrite: true ## 表示我们自定义的模板和index会覆盖默认的
setup.template.fields: "/etc/filebeat/self/fields.yml" ## 自定义的fields.yml,这个文件不熟悉的话就别瞎折腾了,我直接从原来的copy了一份而已
setup.template.name: "nginx-access-log" ## 模板名,这个和后面要创建的es模板名保持一致
setup.template.pattern: "nginx-access-log-*" ## 模板对应的pattern,也是对应的index,这些都是有规律的。因为你模板名是`nginx-access-log`,则你接下来的自定义索引名就最好是`nginx-access-log-%{[agent.version]}-%{+yyyy.MM.dd}`,pattern的正则就是`nginx-access-log-*`
3. Dashboards(这个配置可不配,主要是看你的filebeat是否安装了默认看板,这个安装后会生效的前提是:你必须使用filebeat的默认模板,默认索引,一旦你执行了上面第二步,这里的配置就不会生效)
setup.dashboards.enabled: true
setup.dashboards.index: "nginx-access-log-*"
4. Kibana
setup.kibana:
host: "192.168.1.57:5601"
# Kibana Host
# Scheme and port can be left out and will be set to the default (http and 5601)
# In case you specify and additional path, the scheme is required: http://localhost:5601/path
# IPv6 addresses should always be defined as: https://[2001:db8::1]:5601
#host: "localhost:5601"
5. Outputs(Elasticsearch)
#-------------------------- Elasticsearch output ------------------------------
output.elasticsearch:
# Array of hosts to connect to.
hosts: ["192.168.1.57:9200"]
## 这里的index的前缀要和上面第二步配置的模板名保持一致,而且,index的名字中不能包含大写字母,这是我的教训来着
index: "nginx-access-log-%{[agent.version]}-%{+yyyy.MM.dd}"
# Optional protocol and basic auth credentials.
#protocol: "https"
#username: "elastic"
#password: "changeme"
3.3.2 启用nginx模块
其实对于filebeat中,它提供了多个模块,比如nginx,mysql等,这些能够让我们快速地上手filebeat。但是我一直搞不懂这些模块的用途是什么,明明我可以在
filebeat.yml
中配置的。nginx的模块格式如下:
我虽然安装了该模块,但是该模块我是关掉的,一方面是我对该模块的理解不够,我不启用该模块也能正常收集日志,另一方面,开启该模块的时候,在收集日志的时候会不定时地报错
Provided Grok expressions do not match field value:
# Module: nginx
# Docs: https://www.elastic.co/guide/en/beats/filebeat/7.4/filebeat-module-nginx.html
- module: nginx
# Access logs
access:
enabled: false
var.paths: ["/opt/nginx/logs/requestLog.log"]
# Set custom paths for the log files. If left empty,
# Filebeat will choose the paths depending on your OS.
#var.paths:
# Error logs
error:
enabled: false
# Set custom paths for the log files. If left empty,
# Filebeat will choose the paths depending on your OS.
#var.paths:
安装过程
在配置好
filebeat.ymt
后执行如下命令:
filebeat modules enable nginx
3.3.3 启动filebeat模块
filebeat setup
service filebeat start
4. Kibana搭建
4.1 Kibana下载安装
注意:如果es是7版本,那么kibana的版本也要是7,两者之间的大版本要互相对应
wget https://artifacts.elastic.co/downloads/kibana/kibana-7.4.2-linux-x86_64.tar.gz
## 解压缩后即可使用
tar -zxvf kibana-7.4.2-linux-x86_64.tar.gz
4.2 Kibana配置
vim kibana.yml
## 端口号,默认5601
#server.port: 5601
## 指定kibana指定的IP
server.host: "192.168.1.57"
## 因为我的kibana和Elasticsearch是在同一台机器上的,所以我不需要去配置es的IP和端口等信息,kibana会自动在本机寻找
#elasticsearch.hosts: ["http://localhost:9200"]
4.2.1 自定义Elasticsearch的template
这个
template_name
和index_patterns
要和前面我们在Elasticsearch中配置的一致。除了template_name
和index_patterns
要修改外,还有一个要修改的地方就是mapping
,这个因人而异,我觉得默认的mapping输出太多我不需要用到的东西,所以才去简化的,其余的都保持默认。新建好template
后重启filebeat即可。重启filebeat之前建议把nginx的访问日志也清空掉,免得有脏数据影响
mapping信息:
{
"_meta": {
"beat": "filebeat",
"version": "7.4.2"
},
"dynamic_templates": [
{
"labels": {
"path_match": "labels.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"container.labels": {
"path_match": "container.labels.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"dns.answers": {
"path_match": "dns.answers.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"fields": {
"path_match": "fields.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"docker.container.labels": {
"path_match": "docker.container.labels.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"kubernetes.labels.*": {
"path_match": "kubernetes.labels.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "*"
}
},
{
"kubernetes.annotations.*": {
"path_match": "kubernetes.annotations.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "*"
}
},
{
"docker.attrs": {
"path_match": "docker.attrs.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"cef.extensions": {
"path_match": "cef.extensions.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"kibana.log.meta": {
"path_match": "kibana.log.meta.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"strings_as_keyword": {
"mapping": {
"ignore_above": 1024,
"type": "keyword"
},
"match_mapping_type": "string"
}
}
],
## 这里是指是否要指定一个key作为date类型来进行时间筛选,如果不为true,
## 后面你就无法根据@times进行时间筛选
"date_detection": true
}
4.2.2 devtool工具查询
在未建立kibana的
index_pattern
之前,我们是无法在discover
发现面板上去查看自己的数据的,这个时候我们并不着急去创建index_pattern
,我们要先验证一下日式数据是否已成功收集。打开kibana的dev tool
1. 查看目前es中的索引,确认我们的自定义索引是否已生成
get _cat/indices?v
这个索引出现了表明索引已自动生成了
2. 查看该索引目前的数据,是否符合我们预期
GET /nginx-access-log-7.4.2-2019.11.18/_search
数据结果是json格式的,而且我们需要的数据都有,符合我们的预期。
4.2.3 自定义index_pattern
在上面的步骤确认好之后,就可以创建kibana的
index_pattern
4.2.3 Discover功能
选择刚我们创建的
index_pattern
就可以看到和我们index_pattern格式相对应的数据了
4.2.4 Visualize功能
新建一个可视化的图表,它里面帮我们内嵌了多个图表,但数据源都不是我们自定义的数据,可以参考,但是没多大意义,我们可以自己创建。
4.2.5 Dashboard功能
多个 Visualize放在一起就是一个面板,这里不多赘述,没什么技术含量
5. 参考链接
开始使用Filebeat
快速开始Elasticsearch
Es的官网教学
自定义index template
Filebeat自定义index的一个坑
Filebeat模块与配置
Linux修改max user threads
ElkStack运维手册
Es中文手册
nginx中文地址
6. 总结
搭建这个简单的ELK还是花费了我一些时间的,一方面主要是我太久没接触ES,另一方面我对filebeat和kibana一直都是一知半解的状态。在搭建的过程中,没有像我上面写得那么顺利,一路磕磕碰碰,遇到很多问题,最后通过查询大量资料加上自己的理解才终于搞定。大家如果在搭建的时候有不明白的可以直接联系
[email protected]
这个邮箱