去年入职新公司之后, 负责维护平台的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的可用性

对于redis, 可以进行进程监控, 发现挂了的话, 告警并同时重启(可以crontab一分钟检查一次)

优化点: 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'}}

做一些exclude

有时候需要做一些exclude, 去除比必要采集和监控的日志(进入采集逻辑纯粹是浪费cpu和内存)

例如, 目录树, 不要监控celery.log

logs
├── a
│   ├── a.log
│   ├── b.log
│   └── celery.log
└── b
    ├── c.log
        └── d.log

排除部分文件

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

文档

一些相关用到的命令

  • 查看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