日志采集与存储:ELK/EFK 架构与实战配置
日志采集与存储,是整个性能分析体系的基石。你想想看,如果连日志都收不全、存不好,后面再牛的分析工具也是白搭。这一章,我就带你看看生产环境中常用的 ELK 和 EFK 架构,以及我在实际项目中踩过的那些坑。
ELK vs EFK:选哪个?
ELK 是 Elasticsearch、Logstash、Kibana 的缩写。EFK 则是把 Logstash 换成了 Fluentd。说白了,核心区别就是采集器的选择。
| 对比维度 | Logstash | Fluentd |
|---|---|---|
| 资源消耗 | 较高,Java 进程,默认占用 1GB+ 内存 | 较低,Ruby/C 混合,内存占用约 50-200MB |
| 插件生态 | 丰富,200+ 插件,社区活跃 | 也很丰富,1000+ 插件,Kubernetes 原生支持好 |
| 配置复杂度 | 配置语法灵活,但容易写复杂 | 配置简洁,基于 JSON 或 YAML |
| 性能 | 吞吐量高,但需要调优 JVM | 吞吐量中等,但稳定,适合容器化部署 |
| 适用场景 | 传统架构、需要复杂数据处理的场景 | 云原生、Kubernetes、边缘采集场景 |
我个人习惯是:如果团队对 Java 栈熟悉,且日志量在每天 TB 级以下,用 Logstash 没问题。但如果跑在 Kubernetes 上,或者对资源敏感,我会优先选 Fluentd。记得有一次,我在一个 16GB 内存的节点上同时跑了 Logstash 和业务应用,结果 OOM 了……后来换成 Fluentd,内存直接降到 150MB。
Logstash 配置实战
Logstash 的配置分三段:input、filter、output。我直接给一个生产级的例子。
input {
beats {
port => 5044
ssl => true
ssl_certificate => "/etc/logstash/certs/logstash.crt"
ssl_key => "/etc/logstash/certs/logstash.key"
}
}
filter {
grok {
match => { "message" => "%{COMBINEDAPACHELOG}" }
}
date {
match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
target => "@timestamp"
}
mutate {
remove_field => [ "message", "original" ]
}
}
output {
elasticsearch {
hosts => ["https://es-node1:9200", "https://es-node2:9200"]
index => "nginx-access-%{+YYYY.MM.dd}"
user => "logstash_writer"
password => "${ES_PASSWORD}"
ssl => true
cacert => "/etc/logstash/certs/ca.crt"
}
}
小技巧:grok 正则调试很痛苦。我建议先用 Kibana 自带的 Grok Debugger 测试,或者用
stdout { codec => rubydebug } 输出到控制台看解析结果。别问我怎么知道的——我曾经花了一下午调一个时间戳格式,最后发现是时区没设对。
Fluentd 配置实战
Fluentd 的配置更简洁,适合容器化环境。下面是一个采集 Docker 容器日志的例子。
<source>
@type tail
path /var/log/containers/*.log
pos_file /var/log/fluentd-containers.log.pos
tag kubernetes.*
format json
read_from_head true
</source>
<filter kubernetes.**>
@type kubernetes_metadata
@id filter_kube_metadata
</filter>
<match kubernetes.**>
@type elasticsearch
host "#{ENV['ES_HOST']}"
port "#{ENV['ES_PORT']}"
user "#{ENV['ES_USER']}"
password "#{ENV['ES_PASSWORD']}"
logstash_format true
logstash_prefix k8s-logs
flush_interval 5s
</match>
注意:Fluentd 的
pos_file 很重要。它记录了文件读取位置,防止重启后重复采集。我曾经因为忘记配这个,导致 ES 集群被重复数据撑爆。嗯,血的教训。
日志分片与索引策略
日志存进 Elasticsearch 后,怎么管理?核心就是分片和索引策略。分片决定了数据的并行处理能力,索引策略决定了查询效率和存储成本。
分片设计原则
- 分片数 = 节点数 × 1-2:太多分片会导致集群管理开销大,太少则无法利用多节点并行。
- 每个分片大小控制在 20-50GB:超过 50GB 后,查询和恢复都会变慢。
- 主分片数在创建索引时确定,无法修改:所以一开始就要规划好。我一般按未来 6 个月的数据量估算。
索引生命周期管理(ILM)
手动管理索引太累了。Elasticsearch 提供了 ILM,可以自动完成索引的创建、滚动、冷热迁移和删除。
PUT _ilm/policy/logs_policy
{
"policy": {
"phases": {
"hot": {
"min_age": "0ms",
"actions": {
"rollover": {
"max_size": "50GB",
"max_age": "1d"
},
"set_priority": {
"priority": 100
}
}
},
"warm": {
"min_age": "7d",
"actions": {
"allocate": {
"require": {
"data_type": "warm"
}
},
"forcemerge": {
"max_num_segments": 1
},
"shrink": {
"number_of_shards": 1
}
}
},
"delete": {
"min_age": "30d",
"actions": {
"delete": {}
}
}
}
}
}
核心思路:热节点写入,7 天后移到温节点合并段,30 天后自动删除。这样既保证了写入性能,又控制了存储成本。我在一个日增 500GB 日志的项目中,靠这套策略把存储成本降了 60%。
架构总览:一张图看懂
下面这张图展示了完整的日志采集与存储链路。从应用产生日志,到最终在 Kibana 上可视化,每一步都有讲究。
避坑指南
- 分片数别拍脑袋定:我曾经给一个索引设了 30 个分片,结果集群只有 3 个节点,每个节点 10 个分片,查询慢得要死。后来改成 6 个分片,性能翻倍。
- ILM 的 rollover 条件别设太激进:比如
max_size: 10GB且max_age: 1h,会导致每小时生成一个新索引,分片数爆炸。我一般设 50GB 或 1 天。 - Logstash 的 JVM 堆大小要调:默认 1GB 不够,但也不要超过物理内存的 50%。我习惯设
-Xms4g -Xmx4g,配合pipeline.workers参数调整并发。 - Fluentd 的 buffer 要配:不加 buffer 的话,ES 挂了数据就丢了。用
@type file做持久化 buffer,保证数据不丢。
我的经验:日志采集链路中,最脆弱的往往是网络。我建议在采集器和 ES 之间加一层 Kafka,既能削峰,又能解耦。即使 ES 挂了,数据也不会丢。等 ES 恢复后,Kafka 自动回放,完美。