ELK 维护的一些点
来自: 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