公司应用日志收集架构进化过程

来源:互联网 发布:windows ssh使用 编辑:程序博客网 时间:2024/06/05 06:44

    我司于16年使用elasticsearch。将所有应用的日志;收集到elasticsearch中,方便技术人员定位、分析。相对于原本登录到服务器上查看日志,使用elasticsearch极大的提高了工作效率,也提升了整个系统的安全。

    整个日志收集架构,经历了下面几个发展阶段。

1.0阶段

    应用日志收集1.0版本整个架构图如下所示:

     

其中:
    1、在每台应用程序上部署logstash,使用plugins-inputs-file插件,收集应用的日志,同时发往两个redis;

    2、使用两个redis,是为了将同一份日志数据写入日志文件服务器和elasticsearch;

    3、使用一组logstash消费redis,将消息写入file或者elasticsearch;

此架构优点:

    1、整个架构很简单;在服务器、应用较少时,部署方便;

    2、监控方便;有问题能及时定位到;

    3、使用redis,消息的生产、消费速度快;

    4、单个日志文件顺序写入redis;没有乱序问题;

缺点:

    1、没有容错性;单一组件异常,会影响整条线;

    2、数据量过大时;由于logstash的性能问题;导致丢数据严重;

    3、多服务器、多应用部署复杂;

在此架构使用初期,能够很好的满足日志收集的需求;当整个平台的数据量上涨后;发现丢日志越来越严重;redis的压力也越来越大;运维组决定对此重构;

2.0阶段

    为了解决应用日志在日志文件增加时丢失严重的问题;决定将日志文件的收集改成更轻量级,专门用于采集文件数据的filebeat;随着ELK5版本的发布;filebeat引入了loadbalance特性;在output.logstash中可以配置多个logstash;服务启动后随机选择一个logstash;如果loadbalance设置为ture,会负载写压力到所有配置的logstash节点。因此整体架构图如下:

    1、在应用服务器上,使用filebeat收集应用的日志,根据不同应用的日志,配置不同的document_type;统一发给下游的logstash处理;

    2、logstash拼装filebeat发过来的消息;根据日志总量的大小、存储在不同的日志服务器上等因素;使用logstash的plugins-outputs-kafka,将应用日志分组发给多个topic;

    3、在日志服务器上,使用logstash的plugins-outputs-file,读取topic里面的数据,根据不同的规则将日志拆分写入不同的目录下;

 

filebeats升级到5.*版本简单的配置如下:

filebeat.prospectors:
- input_type: log
paths:
- /*/appname.log    #应用日志的详细目录
document_type: dispatch1    #应用的名字;区别于其他应用;
filebeat.spool_size: 6144     #后台事件计数阈值,超过后强制发送
output.logstash:
hosts: ["172.24.0.29:5043", "172.24.0.30:5043", "172.24.0.31:5043"]
loadbalance: true
bulk_max_size: 2048    #单个批量API索引请求的最大事件数

通过loadbalance特性,既解决了logstash的负载均衡问题,也解决了logstash的高可用问题;

由于我司目前的kafka版本是0.8.2;logstash只能使用2.x版本。官方说明如下:

中转logstash配置如下:

input {
beats {
port => 5049

codec => multiline {
pattern => "^%{TIMESTAMP_ISO8601}"
negate => true
what => "previous"
}


}
}

output {
kafka {
bootstrap_servers => "172.24.16.133:9092,172.24.16.134:9092,172.24.16.135:9092"
topic_id => "applog5"
codec => plain {
format => "%{type} %{host} %{message}"
}
}
}

1、由于应用太多,我们初期规划了6条线;申请了6台服务器,三台一组,安装中转logstash。

2、安装了3台服务器的kafka集群;

3、使用%{type} %{host} %{message}格式,将日志存入kafka的topic中;

落地的kafka配置:

input{
kafka {
codec => "plain"
group_id => "kafkatofile1"
auto_offset_reset => "largest"
# reset_beginning => true
topic_id => "applog1"
zk_connect => "172.24.16.133:2181,172.24.16.134:2181,172.24.16.135:2181"
}

}
filter {
grok {
match => { "message" => "%{DATA:type} %{DATA:host} %{TIMESTAMP_ISO8601:logtime} (?<content>.+)" }
}
grok { match => [ "logtime", "(?<logdate>%{YEAR}-%{MONTHNUM}-%{MONTHDAY})[T ]%{HOUR:loghour}"]
remove_field => ["YEAR","MONTHNUM","MONTHDAY"]
}

}

output {
file {
path => "/data1/logs/%{type}/%{host}/%{logdate}/%{type}.log-%{logdate}-%{loghour}"
message_format => "%{logtime} %{content}"
}
}

    1、根据topic里面的日志存储格式拆分;

    2、使用path => "/data1/logs/%{type}/%{host}/%{logdate}/%{type}.log-%{logdate}-%{loghour}"确定日志的存储目录;

    3、使用message_format => "%{logtime} %{content}"确定日志的内容;

 

总结:

    使用上面的架构,基本上能够解决在日志高峰期,logstash收集日志丢失的问题。在订单量不大;日志量相对较小的情况下;基本能满足日志收集需求。

    随着我司数据量的日趋增加;应用的总数、单个应用的日志量也越来越多;使用logstash2.*版本的grok来拆分日志,遇到了严重的性能瓶颈;日志积压越来越严重。起初我们是想着通过增加中转logstash的服务器数量来解决;在增加完3台服务器后,随着日志量的增加;很快又遇到了性能瓶颈。

    另一个致命的问题是,由于日志是负载给3台中转logstash处理的,由于3台服务器处理日志的性能有差异;另外,由于无法指定kafka的partition;日志在消费落地时,无法保证消息的有序性。所以我们在落日的日志服务器上查看应用的日志时,会有小幅度的乱序现象。这对于开发人员分析日志是很麻烦的。

    综合上面两个问题,我们查阅logstash5.*的文档,发现可以通过dissect代替grok解决性能问题;使用新的plugins-outputs-kafka里面的message_key特性并结合kafka0.10,来指定同一个日志写入同一个partition。来保证日志落地的消息有序性。

2.1阶段

    我们仍采用2.0阶段的架构图,不过做了如下的微调:

    1、取消了filebeat的loadbalance,让同一个日志的filebeat,一直往同一个logstash写;

    2、中转、落地的logstash升级到5.*版本;

    3、kafka升级到0.10版本;

中转的logstash配置如下:

input {
beats {
port => 5045
codec => multiline {
pattern => "^%{TIMESTAMP_ISO8601}"
negate => true
what => "previous"
}
}
}

output {
kafka {
bootstrap_servers => "172.24.16.133:19092,172.24.16.134:19092,172.24.16.135:19092"
topic_id => "applog1"
codec => plain {
format => "%{type} %{host} %{message}"
}
# workers => 16
batch_size => 131072
buffer_memory => 268435456
compression_type => "lz4"
linger_ms => 1000
receive_buffer_bytes => 262144
send_buffer_bytes => 524288
reconnect_backoff_ms => 100
message_key => "%{type} %{host}"

}
}

其中通过message_key => "%{type} %{host}",来指定同一个host中的相同type的日志,发往同一个topic的partition。

 

落地的logstash配置如下:

input{
kafka {
codec => "plain"
group_id => "kafkatofile2"
bootstrap_servers => "172.24.16.133:19092,172.24.16.134:19092,172.24.16.135:19092"
auto_offset_reset => "latest"
consumer_threads => 6
topics => "applog2"
}

}
filter {
dissect {
mapping => {
"message" => "%{type} %{host} %{logdate} %{loghour}:%{logms} %{content}"
}
}
if "_dissectfailure" in [tags] {
drop { }
}
if [logdate] !~ "^[0-9]" {
drop {}
}

}

output {
file {
path => "/data1/logs/%{type}/%{host}/%{logdate}/%{type}.log-%{logdate}-%{loghour}"
codec => line {
format => "%{logdate} %{loghour}:%{logms} %{content}"
}
}
}

使用dissect代替grok。性能比对如下图:


前两条线是使用grok方式,来拆分日志,处理的速度大约是4w/s。后两条线是用dissect方式来拆分日志;处理的速度约8w/s。CPU占用grok方式约70%,dissect方式约60%。

对比发现,dissect方式处理速度是grok方式的2倍;同时CPU占用有小幅的降低;

总结:

    使用新的logstash后,基本上能满足日志的收集流程。大部分日志都能做到实时的收集、落地。

原创粉丝点击