0x01 前言

最近在kibana中添加可视化视图时需要增加一个流量统计的图标,可是遇到了很严重的问题。

我们都知道数据是具有类别之分的,例如:

  1. 数字:long, integer, short, byte, double, float
  2. 字符串
  3. 日期格式
  4. … …

而保存在elasticsearch中的数据一样需要指明格式,要不然就无法生成特定的图表。例如计算总和、均值这类运算则需要数字的支持,这也是合乎常理的,如果值的类型为字符串,这要怎样进行数学运算?

0x02 问题

因为我在配置elasticsearch的时候还没意识到格式的问题,当导入将近20G的数据后,在添加可视化试图时却发现无法添加某些图表。

最显而易见的就是流量统计,我的nginx日志包含bytes这个字段,可是在elasticsearch中是以默认的string类型来存储。这就是导致无法进行数学运算的根本原因。

0x03 思路

因为elasticsearch中已经处理存储着20G的数据,我不希望通过删除索引重新处理数据这种方式解决这个问题,而且日后再遇到这个问题也不可能通过这种粗暴的方式来解决问题。

0x03.1方法一

—————

修正:
2016.12.24:应该先添加模版再调整logstash

—————

在经过一天翻看文档后,我找到了解决办法。大概思路如下:

  1. 新建索引hk1-v2
  2. 将logstash输出索引从hk1指向hk1-v2并重启logstash服务
  3. 为elasticsearch添加”hk1*“模版
  4. 将hk1重新索引至hk1-v2
  5. 删除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) 传入的内容丢失,因为相关的软件之间会有检验机制,这个有机会再作介绍。

修改完成后:

有疑问欢迎给我留言!