0x01 前言
elastic stack在4月11日发布了7.0正式版,而我也在第一时间完成家里测试环境的升级工作。因为我是从6.7.0升级到7.0的,同时经过x-pack升级助手的检查,升级前后均没问题。
但在升级filebeat之后却出现大量错误日志,而在此之前,我QQ群内一位朋友也发现类似的问题,而他找出了报错的原因。
0x02 BUG
首先是日志,为了方便查看,我对日志格式做了些调整:
[2019-04-14T23:17:43,051][WARN ][logstash.outputs.elasticsearch]
Could not index event to Elasticsearch. {:status=>400, :action=>["index",
{:_id=>nil, :_index=>"public-nginx-alias", :_type=>"_doc", :routing=>nil},
#<LogStash::Event:0x2efc3092>],
:response=>{
"index"=>{
"_index"=>"public-nginx-index-v1",
"_type"=>"_doc",
"_id"=>"7NZsHGoB4Cf78Hum8oUF",
"status"=>400,
"error"=>{
"type"=>"mapper_parsing_exception",
"reason"=>"failed to parse field [agent] of type [text] in document with id '7NZsHGoB4Cf78Hum8oUF'",
"caused_by"=>{
"type"=>"illegal_state_exception",
"reason"=>"Can't get text on a START_OBJECT at 1:206"}
}
}
}
}
以上是logstash7.0的日志,而且这些日志只出现在使用7.0版本的filebeat服务器中。由以上日志信息可以发现处理名为agent的字段失败,而出现illegal_state_exception的原因一般是字段异常。
经过群友的耐心debug发现这个问题是因为我以前文章中logstash过滤器的grok规则导致的。
如果大家有关注我的博客,可以在以下文章中找到相关的nginx日志的grok规则:
以上文章中均包含以下字段:
# 分词规则
%{QS:agent}
# useragent分析规则
useragent {
source => "agent"
target => "ua"
}
它匹配的是日志中的useragent,如下所示:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36"
上述的分词规则会匹配被双引号包裹的相关字符,并赋值给名为agent的字段,最终导入到elasticsearch,结果如下:
而useragent分析规则则是从agent字段中取值并调用useragent 插件处理,处理完的结果赋值给名为ua的字段,结果如下:
"ua": {
"os_name": "Windows",
"minor": "0",
"build": "",
"name": "Chrome",
"os": "Windows",
"device": "Other",
"patch": "3683",
"major": "73"
},
这个日志分词和useragent分析功能在7.0之前是正常的;但从7.0开始,agent字段属于内置的保留字段,主要记录着该beat所在的主机的一些信息:
"agent": {
"id": "899ef414-7b63-4d75-b9dd-28e750504c44",
"type": "filebeat",
"hostname": "pub-ngx",
"version": "7.0.0",
"ephemeral_id": "9bcaed33-660f-45e0-9062-27aa860eb1bb"
},
所以问题很简单,就是新旧版本的字段冲突了。
0x03 DEBUG
既然字段名冲突,那只需要修改字段名即可,但实际上没那么简单。
因为数据是不断传入logstash进行处理的,所以logstash和elasticsearch的服务不能长时间中断;其次是历史数据不能丢失。
因为我只有一个logstash节点,所以修改过滤器规则后重启logstash会导致一小段时间的服务中断,而且服务中断期间的日志会丢失。但对于我的环境来说,这一部分日志的丢失是可以接受的;如果需要高可用,则建议部署多个logstash节点后再进行调整。
首先修改过滤器规则:
[root@logstash6-node1 ~]# cat /etc/logstash/conf.d/10-nginx-fliter.conf
filter {
if "server_ngx_access_log" in [tags] {
grok {
match => { "message" => "%{IPORHOST:client_ip} - (%{GREEDYDATA:auth}|-) \[%{HTTPDATE:timestamp}\] \"(%{WORD:verb} %{GREEDYDATA:request} HTTP/%{NUMBER:http_version}|%{GREEDYDATA:request})\" (%{IPORHOST:domain}|%{URIHOST:domain}|-) %{NUMBER:response} %{NUMBER:bytes} %{QS:referrer} %{QS:ngx_ua} \"(%{IPORHOST:x_forword_ip}, .*|%{IPORHOST:x_forword_ip}|unknown|-)\" (%{IPORHOST:upstream_host}|-)(\:%{NUMBER:upstream_port}|) (%{NUMBER:upstream_response}|-) (%{WORD:upstream_cache_status}|-) \"%{NOTSPACE:upstream_content_type}(; charset\=%{NOTSPACE:upstream_content_charset}|)\" (%{NUMBER:upstream_response_time}|-) > %{NUMBER:request_time}" }
}
geoip {
source => "client_ip"
}
geoip {
source => "x_forword_ip"
target => "x_forword_geo"
}
date {
match => [ "timestamp" , "dd/MMM/YYYY:HH:mm:ss Z" ]
}
useragent {
source => "ngx_ua"
target => "ua"
}
mutate {
split => { "x_forword" => ", " }
}
}
}
我修改了grok规则中与useragent插件中的字段名称为ngx_ua,修改完成后即可重启logstash。
然后需要使用kibana中的Dev Tools调用elasticsearch API。首先建立一个新的索引:
PUT public-nginx-index-v3
然后修改别名的指向,在此之前先获取目前的别名指向情况:
GET _cat/aliases
接下来即可进行修改:
POST /_aliases
{
"actions": [
{
"remove": {
"index": "public-nginx-index-v2",
"alias": "public-nginx-alias"
}
},
{
"add": {
"index": "public-nginx-index-v3",
"alias": "public-nginx-alias"
}
}
]
}
成功执行该API后,所有数据都会写入到public-nginx-index-v3索引中。
完成上述步骤后才可以进行历史数据字段名称的修改,要不然有可能会使历史数据受损或遭到污染。
对于历史数据,我才用较为简单的手段处理:将历史数据中的agent字段的数据传递给新字段ngx_ua,而后删除旧字段,最终将数据写入新索引。
这一系列步骤我通过reindex API完成:
POST _reindex
{
"source": {
"index": "public-nginx-index-v2",
},
"dest": {
"index": "public-nginx-index-v3"
},
"script": {
"inline": "ctx._source.ngx_ua = ctx._source.remove(\"agent\"); "
}
}
上面命令的意思是将v2索引中的数据迁移至v3中,在这过程中将旧索引的agent字段赋值给新索引的ngx_ua字段后删除自身。
根据数据量的不同,reindex所需要的时间也各有长短,完成后即可删除该索引以减少空间消耗:
DELETE public-nginx-index-v2
0x04 结语
为了减少不必要的操作,确保数据安全与洁净,建议对历史数据操作前先通过快照API对数据做快照;另外,在进行实操前需进行全盘规划。
整个过程会对IO造成极大压力,请适当调整elasticsearch节点数以均衡负载,减少对线上业务的影响。



























