0x01 前言
最近在kibana中添加可视化视图时需要增加一个流量统计的图标,可是遇到了很严重的问题。
我们都知道数据是具有类别之分的,例如:
- 数字:long, integer, short, byte, double, float
- 字符串
- 日期格式
- … …
而保存在elasticsearch中的数据一样需要指明格式,要不然就无法生成特定的图表。例如计算总和、均值这类运算则需要数字的支持,这也是合乎常理的,如果值的类型为字符串,这要怎样进行数学运算?
0x02 问题
因为我在配置elasticsearch的时候还没意识到格式的问题,当导入将近20G的数据后,在添加可视化试图时却发现无法添加某些图表。
最显而易见的就是流量统计,我的nginx日志包含bytes这个字段,可是在elasticsearch中是以默认的string类型来存储。这就是导致无法进行数学运算的根本原因。
0x03 思路
因为elasticsearch中已经处理存储着20G的数据,我不希望通过删除索引重新处理数据这种方式解决这个问题,而且日后再遇到这个问题也不可能通过这种粗暴的方式来解决问题。
0x03.1方法一
—————
修正:
2016.12.24:应该先添加模版再调整logstash
—————
在经过一天翻看文档后,我找到了解决办法。大概思路如下:
- 新建索引hk1-v2
- 将logstash输出索引从hk1指向hk1-v2并重启logstash服务
- 为elasticsearch添加”hk1*“模版
- 将hk1重新索引至hk1-v2
- 删除hk1
0x03.2 方法二
为什么要那么麻烦?通过模版重新定义字段的类型不就好了?
其实这也是可以的,如果你的索引是按天或按月生成,通过模版重新定义字段的类型这种方式很适合你。不过要注意的是,模版只对下一次生成的索引有效。例如:
如果你在24号这天添加模版,而且模版适用于索引格式:filebeat-*;
那么你目前正处于filebeat-2016.12.24这个索引的使用期,模版是不会对这个索引有任何作用的;
模版只对24号以后的索引(如:filebeat-2016.12.25)生效。
这样的话问题又来了:我不是使用这种方式来管理我的索引,我只有一个或者按月度生成,这该怎么办?那只好使用方法一来解决问题。
在这里还需要注意的是,如果你的logstash在实时接收日志,例如syslog的UDP 514端口的日志,那么在重启logstash的时候可能会有几十秒处于无法接收的状态,这段时间的日志可能会丢失!
0x04 模版
首先要向elasticsearch添加模版,在这里我使用kibana中的Dev Tools而不使用curl:
上面的Dev Tools中,左侧作为输入框,右侧为输出框。
我们不需要从头编写模版,因为kibana内置filebeat的模版,我们可以直接进行修改,在输入框中输入以下命令:
GET _template
并点击运行按钮:
左侧即为logstash中所有的模版,但我们只需要filebeat的模版,请通过以下命令导出:
GET _template/filebeat*
将输出的所有内容复制到文本编辑器中:
{ "filebeat": { "order": 0, "template": "filebeat-*", "settings": { "index": { "refresh_interval": "5s" } }, "mappings": { "_default_": { "dynamic_templates": [ { "template1": { "mapping": { "ignore_above": 1024, "index": "not_analyzed", "type": "{dynamic_type}", "doc_values": true }, "match": "*" } } ], "_all": { "norms": { "enabled": false }, "enabled": true }, "properties": { "@timestamp": { "type": "date" }, "geoip": { "dynamic": true, "type": "object", "properties": { "location": { "type": "geo_point" } } }, "offset": { "type": "long", "doc_values": "true" }, "client_ip": { "type": "ip" }, "message": { "index": "analyzed", "type": "string" } } } }, "aliases": {} } }
在properties字段下添加以下内容:
"bytes": { "type": "long" },
要注意这个模版中含有两处properties字段,其中一处是位于geoip下的,请不要修改geoip下的内容:
"geoip": { "dynamic": true, "type": "object", "properties": { "location": { "type": "geo_point" } } },
最后要修改模版名称和匹配的索引,修改2、4行:
#模版名称,可自定义 "hk1": { "order": 0, #匹配索引名称,以下的意思是所有hk1开头(包含hk1)的索引都使用这个模版 "template": "hk1*",
最后删除第一行与最后一行的”[“与”]”,删除第二行的 “hk1”:
完成后即可通过以下命令向elasticsearch中导入模版:
PUT _template/hk1 { "order": 0, "template": "hk1*", "settings": { "index": { "refresh_interval": "5s" } }, "mappings": { "_default_": { "dynamic_templates": [ { "template1": { "mapping": { "ignore_above": 1024, "index": "not_analyzed", "type": "{dynamic_type}", "doc_values": true }, "match": "*" } } ], "_all": { "norms": { "enabled": false }, "enabled": true }, "properties": { "@timestamp": { "type": "date" }, "bytes": { "type": "long" }, "geoip": { "dynamic": true, "type": "object", "properties": { "location": { "type": "geo_point" } } }, "offset": { "type": "long", "doc_values": "true" }, "client_ip": { "type": "ip" }, "message": { "index": "analyzed", "type": "string" } } } }, "aliases": {} }
0x05 logstash
如果你按照我之前写的文章进行安装(安装配置Elastic Stack 5),那么你可能在elasticsearch的配置文件中允许自动创建”hk1*”索引:
#打开文件 [root@web ~]# vim /etc/elasticsearch/elasticsearch.yml #允许自动创建索引 action.auto_create_index: +pfsense*,+hk1*,+syslog-*
如果没有,请手动创建:
PUT hk1-v2
然后修改output并重启logstash:
[root@web ~]# systemctl restart logstash.service
0x06 索引
这里要注意的是,如果你的elasticsearch不是以集群模式运行并且数据即为庞大,例如数据量为千万级的,那么重新索引的时间会极为漫长。在重新索引的时候,CPU将长时间处于高负荷,磁盘则处于高IO的环境中。
另一个需要注意的点是,重新索引并不是仅处理修改过的部分,而是所有字段都进行重新索引处理,如果你又多个字段都出现错误,那么可以一并解决。
在重新索引的时候,如果你的服务没有配置集群,那么很有可能通过kibana提取数据时会出现超时的情况。
一切准备就绪后就可以通过以下命令进行重新索引:
POST _reindex { "source": { "index": "hk1" }, "dest": { "index": "hk1-v2" } }
重新索引的操作是以task的形式在后台运行,如果你使用kibana提交这个操作,那么在经过预设的超时时间后,kibana会返还超时的提示;如果是使用curl提交操作,在经过一段时间后也会返还超时。
但这个问题不必担心,因为重新索引的操作是以任务(task)的形式在后台工作的,我们可以通过以下命令查看相关任务,在kibana中输入并运行:
GET _tasks
在输出框中搜索:reindex 即可看到相关任务信息。以下是类似的返还内容(并不是reindex的任务信息,仅用于说明情况):
"tasks": { "IPkWEFyNRWC3Gbm7hsoB-A:1167097": { "node": "IPkWEFyNRWC3Gbm7hsoB-A", "id": 1167097, "type": "direct", "action": "cluster:monitor/tasks/lists[n]", "start_time_in_millis": 1482558534346, "running_time_in_nanos": 3590645, "cancellable": false, "parent_task_id": "IPkWEFyNRWC3Gbm7hsoB-A:1167096" },
请注意第二行(高亮行),这是任务ID,如果遇到需要终止任务的情况,请通过以下命令进行终止:
POST _tasks/_cancel/IPkWEFyNRWC3Gbm7hsoB-A:1167097
还有更粗暴的方法:
POST _tasks/_cancel?actions=*reindex
在重新索引的过程,推荐通过elasticsearch-head插件进行监控,安装方法如下:
在Elasticsearch5中使用Elasticsearch-head插件
0x07 结语
在使用ELK时,最好是先配置elasticsearch,添加模版,确定字段与相关的类型,然后再通过logstash等软件向内导入内容。
使用上面介绍的方法会有几十秒的空档期会导致实时内容丢失,其实还有另一种方法在0停摆时间的情况下进行重新索引操作,这个日后再介绍。
不过重启logstash不会导致通过 *beat(如filebeat) 传入的内容丢失,因为相关的软件之间会有检验机制,这个有机会再作介绍。
修改完成后:
有疑问欢迎给我留言!