ELK 维护的一些点

hepeace 9年前

来自: http://www.wklken.me/posts/2016/02/16/elk-about-upgrade.html

去年入职新公司之后, 负责维护平台的elk

这套东西是2013年搭建的, 年久失修, 所以做了个方案, 开始了批量升级

将logstash从1.3升级到2.1, 将elasticsearch从1.4.1升级到2.0

期间踩了很多坑, 搞了一个多月, 总算搞完

从纯手工落后隔三差五有人找查问题的自行车, 改成自动化最新版本新架构运维便捷上了两个月无人反馈的, 额, 小汽车:) - 继承安装包/shell脚本/fabric实现部署/升级/增删/加黑名单等等功能

每天日志量大概10G上下, 几十个采集端, 两个redis, 两个indexer, 两台es机器扛起

以下, 不那么严谨地, 记录一些遇到的问题

1. logstash升级策略

logstash1.3到2.x, 变化点还是很多的

所以, 首先第一步要去阅读官方文档, 将所有change log过一遍, 对一些关键性的东西进行了解, 比如, 干掉了哪些语法(旧的功能需要如何实现), 哪些语法有变更, 新增了哪些特性等.

然后, 将线上不同类型agent的配置文件拉下来, 先, 归类, 然后, 开始改-测-改-测-直到测试通过

bin/logstash agent -t -f test.conf

直到, 语法验证通过

现在要做的是, 验证数据正确性

从线上拉取对应日志, 启动, 查看输出

output {      stdout{          debug => true      }  }

这里需要验证的是, 1. 过滤, 该过滤的过滤了 2. 转换, 该转换的转换了 3.新增, 新增字段

注意, 测试时, 使用逻辑分支覆盖到所有配置文件中的分支即可.

然后, 可以挑一台机器, 停老的服务, 部署新的服务进行测试

建议, 部署agent的时候, 如果读的是文件, 建议配置 sincedb_path 这样假设下次升级, 就可以从老的服务最后读取的位置开始了

input {      file {          path => ["/data/logs/*.log"]          sincedb_path => "/data/LogNew/logstash/sincedb/celery.sincedb"      }  }

2. elasticsearch升级的策略

elasticsearch从1.4到2.0, 部署上变化不大, 变化最大的是存储doc的schema变了......

使用原来的语法查询, 发现查不到, 因为字段名以及嵌套层级完全不一样了, 这里, 要修改查询端, 兼容新老版本的格式

{'from': 0,   'query': {'filtered': {'filter': {'bool': {'must': [{'bool': {'should': [{'term': {'type': 'app'}},                                                                            {'term': {'@type': 'app'}}]}},                                                       {'bool': {'should': [{'term': {'log_level': u'error'}},                                                                            {'term': {'@fields.log_level': u'error'}}]}},                                                       {'range': {'@timestamp': {'gt': 'now-5h'}}},                                                       {'bool': {'should': [{'term': {'log_type': u'celery'}},                                                                            {'term': {'@fields.log_type': u'celery'}}]}}]}}}},   'size': 100,   'sort': [{'@timestamp': 'desc'}]}

另一个是, 取到数据进行解析的时候, 发现解析逻辑跪了, 没办法, 返回的json也完全变了, 这里, 要修改解析逻辑, 兼容新老版本格式

for hit in log_hits:      try:          source = hit.get('_source')          if '@fields' in source:              log = source.get('@fields', {})          else:              log = source

为了让用户感觉不到集群升级, 首先要做的就是上面两个变更

然后, 搭建新的集群, 最好找新的机器搭建(我在新的机器搭完才发现妈蛋硬盘才100G, 坑死, 无奈在老集群上搭新的集群, 硬盘1t)

ready, 所有节点起好维护好, 然后, 改indexer, 将同一份日志灌到两个集群

output {      elasticsearch {          hosts => ["10.1.1.1:9100", "10.1.1.2:9100"]      }      elasticsearch {          hosts => ["10.1.1.1:9110", "10.1.1.2:9110"]      }  }

简单测试下, 没问题就放着甭管了, 等数据攒齐了....

数据够了, 就, 停indexer, 停老集群, 挺新集群, 改新集群端口, 起来....同时去掉indexer只输出到新的集群, 起来......测试, 切换完毕, 收工吧.

优化点: 集成安装包和supervisord

额, logstash和es, 如果要配置节点, 其实还是挺蛋疼的

要做的, 就是, logstash+不同类型配置文件+运维脚本, 达成一个包

然后, 如果要部署一台机器, 扔上去一键执行安装, 测试, 启动即可

例如, 运维脚本 logstashd.sh

#!/bin/bash    BASEDIR=$(dirname $0)  cd $BASEDIR  CURRENT_DIR=`pwd`    function help_msg() {      echo "===================== usage ====================="      echo "./logstashd.sh  - enter command line"      echo "./logstashd.sh status - show all configured process"      echo "./logstashd.sh start ${name} - start program"      echo "./logstashd.sh stop ${name} - stop program"      echo "./logstashd.sh restart ${name} - restart program"      echo "./logstashd.sh reread && ./logstashd.sh update - update config and just update the modified programs"      echo "./logstashd.sh reload - reload config files and restart all programs(stopeed not included)"      echo "================================================="      echo ""  }    if [ "${1}" = "-h" -o "${1}" = "--help" ]  then      help_msg      exit 0  fi    SUPERVISORCTL='/data/LogNew/python27/bin/supervisorctl'    CONFIG_FILE_PATH="${CURRENT_DIR}/conf/supervisord.conf"    $SUPERVISORCTL -c $CONFIG_FILE_PATH $@

使用

./logstashd.sh  ===================== usage =====================  ./logstashd.sh  - enter command line  ./logstashd.sh status - show all configured process  ./logstashd.sh start  - start program  ./logstashd.sh stop  - stop program  ./logstashd.sh restart  - restart program  ./logstashd.sh reread && ./logstashd.sh update - update config and just update the modified programs  ./logstashd.sh reload - reload config files and restart all programs(stopeed not included)  =================================================    111_indexer                      RUNNING   pid 27058, uptime 1:25:10  indexer                          RUNNING   pid 24731, uptime 1:31:29  supervisor> restart indexer

这里, 我引入了 stackless python (独立), 然后装pip/supervisord, 使用supervisord对logstash/es进程进行管理

使用supervisord管理进程, 有个注意点

默认supervisord相关的文件在

/tmp/supervisor*

而线上, 存在tmp被删/清理了情况, 导致要进行进程启停操作才发现,妈蛋找不到

处理方式 => 放到集成安装包的run目录下

注意点: logstash存在两个output时, 必须要保证二者的可用性

logstash indexer, 分别转发数据到两个不同的output

output {      elasticsearch {          hosts => ["10.1.1.1:8080", "10.1.1.2:8080"]      }      redis {          host => "10.1.1.3"          port => 6379          password => "7oEsjqUNoTdgE4"          data_type => "list"          key => "log_queue"          db => 0          batch => true      }  }

此时, 若是redis挂了, 则日志也不会刷到es中, 所以, 需要同时保证所有output的可用性

优化点: ELK增加agent_ip字段

需求: 在实际使用中, 有时候需要反向根据查询结果, 获知日志的来源机器

处理:

1.先获取ip

GetLanIp () {       ## get associated LAN ip address       ## usage: GetLanIp       /sbin/ifconfig | awk '           /eth/{               getline;               if (/inet addr:(172|10|192)\./) {                   gsub(".*addr:|  *Bcast.*","");                   print $0;                   exit;               }           }'       return 0  }

2.放入环境变量

ETH1_IP=10.1.1.1

3.修改logstash配置

注意, 这里是logstash2.x的语法

environment {          add_metadata_from_env => ["agent_ip", "ETH1_IP"]          add_field => {"agent_ip" =>  "%{[@metadata][agent_ip]}" }      }

问题: elk的utc问题

elasticsearch内部使用的是utc, 存储为long (milliseconds since the epoch) e.g. timestamp=1420070400000

可以看下 es 时间处理

logstash 接受了这种设定, 往es传数据的时候, 根据UTC, 每天00:00新建一个index

kibana也接受这种设定, 在查询和展示时根据用户的时区进行处理

问题描述

这导致了, 对于东八区, 2015-11-6日, 8点之前, 只有 logstash-2015.11.05 这个index, 到8点的时候, 创建新的index logstash-2015.11.06 , 即, 对于我们这个时区的人来说, 一天的数据存在了两个index里面

同类问题 Elasticsearch doesn't care about timezone and creates indexes with UTC

修正方案1: 修改logstash的数据时间

logstash团队对于支持localtime的问题, 不予修复 讨论 , 但是可以自行去修改logstash的代码

当然, 可以修改每个logstash indexer的时间, 但是会带来问题 问题 : 1. logstash都要修改 getLocalTime 2.相对时间搜索 3. kibana等相关插件/组件要修正

运维/升级和后续使用上会有很多地雷

修正方案2: 不修正

接受这种设定, 学习kibana, 类似自行确定要搜索的index 对于 00:00-08:00 的, 程序处理使用昨天的 indexer

所以, 更好的方式是, 不修正......原来不变的才是最好的

rolling restart

当存在配置变更时, 需要重启es集群, 不可能全部重启的, 这样会导致服务不可用....

所以, 要一个个重启

先执行

curl -XPUT 'http://localhost:9200/_cluster/settings' -d '  {      "transient" : {          "cluster.routing.allocation.enable" : "none"      }  }'

然后, shutdown, 改配置, start

then : 一定要记得执行, 否则不会执行recovery.....会一直等着

curl -XPUT 'http://localhost:9200/_cluster/settings' -d '  {      "transient" : {          "cluster.routing.allocation.enable" : "all"      }  }'

logstash文本解析配置grok语法

一个线上的工具, https://grokdebug.herokuapp.com/

挺好用的, 但是有时候变更频繁相应有些缓慢

暂时没有找到命令行工具

坑: GROK语法

上线后发现, 尼玛, 部分应用日志没有被采集

定位发现, 原来在 grok 的解析中使用了 WORD

而 WORD : 不支持连字符和下划线

跪了, 需要自定义 LOGFILENAME [a-z\-A-Z0-9_\.]+ 放到pattern中

然后, 搜索的时候, 尼玛, 也搜不到....语法要做处理, 使用 raw , es建索引的时候自动拆掉了导致搜索不到

{'term': {'app_name.raw': 'nms-t'}}

一些相关用到的命令

  • 查看plugin版本

https://www.elastic.co/guide/en/logstash/current/working-with-plugins.html

bin/plugin list --verbose
  • create empty index
curl -XPUT 'http://localhost:9100/logstash-2015.12.15/'
  • 查看健康度
curl 'http://localhost:9100/_cluster/health?pretty=true'
  • 查看indices
#!/bin/bash    curl 'http://localhost:9100/_cat/indices?v' | sort -k 3

删除30天前crontab脚本

#!/bin/bash    now=`date +%Y%m%d`  echo $now  days_30_before=`date -d "$now 31 days ago" +%Y.%m.%d`  echo $days_30_before  echo "http://10.1.1.1:9100/logstash-$days_30_before"  curl -XDELETE "http://10.1.1.1:9100/logstash-$days_30_before" > /dev/null 2>&1

尚未处理

logstash2.1 muline codec, 配置多个数据来源, 存在串的情况, 生产中大数据量有, 小规模没有复现....

好了, 先这些, 还有一些窝在某些目录下, 后续整理好了发

wklken

2016-02-16