先聊点实际的。做运维干了这么多年,我最怕听到的一句话就是“日志丢哪儿去了?”线上应用一抖动,开发、DBA、业务方全来找你。早期我也靠ssh到每台机器上敲tail -f,机器少点还能硬扛,等规模上来之后,几十台服务器、几个T的日志,别说分析,光翻文件都能把人翻崩溃。这也是为什么ELK这套日志分析系统能火这么多年,Elasticsearch负责存储和检索,Logstash负责清洗和转换,Kibana负责可视化和交互,再套一个Filebeat做轻量采集,一套链路就直接把“日志”变成了“可搜索的资产”。
这篇文章不是给你念官方文档,是基于我在Rocky Linux 9.4系统上的完整落地过程写的。从环境准备、组件安装、Kafka缓冲层接入,到Nginx日志真实跑通,再到索引生命周期管理和高频故障排查,一条线走下来。涉及的所有配置我都自己敲过、验证过,你照着操作就能复现。不管是刚接触日志平台的运维新手,还是想从单机ELK往生产架构过渡的老手,这篇都应该能给你省点时间。
1. 整体设计与架构思路
1.1 为什么选ELK,而不是Loki或ClickHouse
先别急着装组件,得先搞清楚为什么是ELK。很多朋友问过我,现在Loki和ClickHouse也都能做日志,新项目是不是该直接上新的?我的答案很直接:看场景,但大多数场景下ELK依然是最稳的选择。
拿Loki来说,它主打轻量、省资源,因为它不建全文索引,只存压缩日志块和标签。优点是便宜、部署快,适合Kubernetes里的容器日志场景。但缺点也明显:日志内容检索靠近似匹配,想做复杂聚合分析、跨索引多维度下钻,使用体验明显不如Elasticsearch。ClickHouse更适合做结构化数据的OLAP分析,查询性能极其强悍,但你要想用Kibana那种开箱即用的可视化看板,或者让非技术人员在页面上拖拖拽拽,ClickHouse还得额外配Grafana之类的组件,链路长,对操作者的SQL能力要求也不低。
ELK的优势是生态完整。Elasticsearch是真正的倒排索引,文本搜索速度快;Logstash和Filebeat提供了大量现成插件,解析Nginx日志、应用JSON日志,甚至数据库增量数据,都有成熟方案;Kibana从数据探索、可视化管理到告警规则,全图形化操作。也就是说,一套ELK能覆盖日志采集、清洗、存储、检索、可视化、告警的完整闭环,团队里任何人都能上手查日志。对大多数中小团队来说,这是学习成本和时间成本最低的方案。
ELK最大的问题就是吃资源。Elasticsearch是JVM应用,堆内存、文件句柄、磁盘IO一样都不能缺。但资源问题可以通过合理的分片策略、索引生命周期管理来缓解,并不影响它作为团队日志中枢的地位。我见过很多大厂从ELK迁移到自研平台的案例,但最终架构里依然保留了Elasticsearch作为查询引擎。这足以说明它在日志分析领域的位置。
1.2 引入Kafka做缓冲:日志链路的关键一步
很多入门教程只教你Filebeat直接输出到Elasticsearch,Logstash直接从Filebeat接收数据。这种极简链路在小规模场景下当然能跑,但当日志量涨到一定规模,或者Logstash短暂宕机时,日志就会直接丢失。因为Filebeat的吞吐能力远高于Logstash的处理速度,中间没有任何蓄水池,一旦下游来不及消费,数据要么堆积在Filebeat内存里,要么直接丢弃。
我在生产架构里会加一层Kafka,这也是热词里反复出现Kafka的原因。Kafka在这条链路里的角色是缓冲和削峰填谷。Filebeat采集到的日志先发到Kafka的Topic里,Kafka的磁盘顺序写性能极高,能瞬时接收海量消息。Logstash再根据自己的处理能力,按需从Kafka拉取数据。这样做有两个直接好处:第一,下游Logstash挂了,Kafka会保留消息,等它恢复后继续消费,日志一条不丢;第二,高峰期的日志洪峰不会直接冲击Elasticsearch,避免写入瓶颈。
完整的生产链路是:Filebeat -> Kafka -> Logstash -> Elasticsearch -> Kibana。Filebeat负责采集文件日志,Kafka负责缓冲,Logstash负责解析和清洗,Elasticsearch负责存储和检索,Kibana负责展示和告警。这套架构比我早期用的Filebeat -> Logstash -> ES要稳得多。如果你只是自己学习或者日志量很小,可以暂时跳过Kafka;但如果你想按生产标准来搭建日志平台,Kafka这一层从第一天就该加上,后面省事。
1.3 适用Rocky 9.4的注意事项
为什么标题要强调Rocky 9.4?因为ELK的安装和系统环境强相关,尤其是RHEL系发行版,坑特别多。Rocky Linux 9.4是RHEL 9系的衍生版本,默认自带的是systemd、firewalld、SELinux,这套组合在新手眼里就是三座大山。
网上大量ELK教程都是基于Ubuntu 18.04或者CentOS 7写的。CentOS 7默认的systemd版本老,firewalld规则用法跟Rocky 9.4有明显差异;Ubuntu走的是apt源,服务管理和防火墙逻辑跟RHEL系完全不同。要是你拿着Ubuntu教程在Rocky 9.4上操作,大概率会卡在服务启动失败、端口不通、Filebeat读不了日志这些问题上。Rocky 9.4的dnf源和Elastic官方yum源兼容性很好,安装路径和配置路径也有明确规定,只要跟着本文走,整个过程能顺畅很多。
还有一点要特别提醒,Rocky 9.4默认开启了SELinux,而且是在Enforcing模式下。Elasticsearch安装包对SELinux做了适配,一般不会出问题;但Filebeat采集/var/log目录下的日志时,很容易被SELinux的权限模型拦截。很多人遇到Filebeat明明配好了路径却读不到内容,第一反应是防火墙,实际上SELinux才是罪魁祸首。后文我会给出具体的处理建议。
2. 核心细节:安装前不可跳过的系统准备
2.1 调整内核参数与文件描述符
在安装任何ELK组件之前,先调整系统参数,这一步省不得。很多新手一上来就敲dnf install,装完后Elasticsearch启动直接崩溃,报错里写着“bootstrap checks failed”,其实就是内核参数没调整。
第一个是vm.max_map_count。Elasticsearch底层依赖Lucene,Lucene运行时会映射大量的匿名内存区域,默认的65530上限根本不够用,Elasticsearch要求至少262144。调整方法很简单:
sysctl -w vm.max_map_count=262144 echo 'vm.max_map_count=262144' >> /etc/sysctl.conf sysctl -p第二个是文件描述符和进程数限制。Elasticsearch官网明确要求nofile至少65535,nproc至少4096。用systemd管理的服务,默认限制不满足要求,需要额外配置。在/etc/security/limits.conf里追加:
elasticsearch soft nofile 65535 elasticsearch hard nofile 65535 elasticsearch soft nproc 4096 elasticsearch hard nproc 4096修改完后最好重启一下系统,或者至少重新登录终端,让ulimit生效。安装完Elasticsearch后,可以用systemctl show elasticsearch -p LimitNOFILE确认限制值是否生效。
第三个是内存和swap。这个经常被忽略,生产环境踩坑概率极高。Elasticsearch的JVM堆内存默认是物理内存的一半,如果你的机器只有8GB内存,它会默认分4GB给堆,再加上堆外内存和系统其他进程,运气不好直接把机器卡死。建议给ES分配堆内存不要超过物理内存的一半,同时绝不超过31GB(JVM对象指针压缩的临界点)。调整堆内存的路径在/etc/elasticsearch/jvm.options.d/下,推荐单独建一个heap.options文件:
-Xms4g -Xmx4g这里特别强调,Xms和Xmx必须设置成相同值,避免JVM动态伸缩堆大小带来的性能损耗。同时建议把vm.swappiness调低到10,让ES进程尽量避免被swapping。如果你的机器内存充足,甚至可以关掉swap:
echo 'vm.swappiness=10' >> /etc/sysctl.conf sysctl -p2.2 防火墙、SELinux与端口规划
安装之前先把端口规划好。最简单的单机环境至少涉及下列端口:
| 组件 | 默认端口 | 用途说明 |
|---|---|---|
| Elasticsearch | 9200 | HTTP接口,Kibana、Logstash、REST客户端通过这个端口访问 |
| Elasticsearch | 9300 | 节点间集群通信端口,单机联调也必须保留 |
| Kibana | 5601 | 浏览器访问Web界面的端口 |
| Logstash | 5044 | 如果直接从Beats接收日志,需要开放(本文用Kafka则不需要) |
| Logstash | 9600 | Logstash监控API端口 |
| Kafka | 9092 | Filebeat和Logstash的数据传输端口 |
如果机器开了firewalld,先永久放行这些端口:
firewall-cmd --permanent --add-port=9200/tcp firewall-cmd --permanent --add-port=9300/tcp firewall-cmd --permanent --add-port=5601/tcp firewall-cmd --permanent --add-port=9092/tcp firewall-cmd --reload注意,如果你打算在不同机器上分别部署组件,就要根据实际规划只放行对应端口。比如Elasticsearch节点之间必须开放9300,Kafka客户端要能访问9092,而Logstash和Filebeat不需要对外开放9200,否则安全风险很大。
接下来是SELinux。最省事的临时方案是setenforce 0,把SELinux切到Permissive模式。但这不是负责任的做法,生产环境重启后SELinux还是会回到Enforcing。Elasticsearch自带的一些二进制文件已经打了SELinux策略,而Filebeat在Enforcing模式下读取/var/log/nginx/access.log之类的高风险路径,经常被SELinux拦截。遇到这种情况,先不要急着关SELinux,用ausearch -m avc -ts recent看懂拦截日志,或者直接查一下Filebeat具体的SELinux布尔值:
getsebool -a | grep filebeat如果确实需要立即跑通链路,临时切到Permissive验证问题,确认是SELinux后,再针对具体服务配置allow规则,或者用semanage放宽特定目录的访问权限。我在测试环境通常先Permissive跑通,生产环境再正经处理SELinux策略,这个顺序能帮你快速分清问题到底在配置还是安全模块。
2.3 配置Elastic官方Yum源与JDK
Rocky 9.4默认的dnf源里没有ELK组件,需要单独配置Elastic官方仓库。在/etc/yum.repos.d/elastic.repo写入以下内容:
[elastic-8.x] name=Elastic repository for 8.x packages baseurl=https://artifacts.elastic.co/packages/8.x/yum gpgcheck=1 gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch enabled=1 autorefresh=1 type=rpm-md配置好后执行dnf clean all && dnf makecache,然后就能搜索到elasticsearch、kibana、logstash、filebeat这几个包了。这里顺便说一下JDK的事。Elasticsearch 8.x和Logstash 8.x都自带捆绑的JDK,系统上不装JDK也能正常跑,别画蛇添足手动配JAVA_HOME,反而可能出现版本不兼容。Kafka是独立组件,需要系统里安装JDK,建议统一装OpenJDK 17:
dnf install -y java-17-openjdk java-17-openjdk-devel java -version另外强烈建议把chrony时钟同步配好。ELK全家桶对时间非常敏感,日志会按天分索引,客户端时间错了,数据就写到“昨天”的索引里,排查起来极其痛苦。
3. 从零到通:整套ELK安装实操记录
3.1 安装并初始化Elasticsearch 8
先安装核心组件:
dnf install -y elasticsearch安装完成后,修改/etc/elasticsearch/elasticsearch.yml,单机学习环境最精简的配置是:
cluster.name: my-elk node.name: node-1 path.data: /var/lib/elasticsearch path.logs: /var/log/elasticsearch network.host: 0.0.0.0 http.port: 9200 discovery.type: single-node xpack.security.enabled: true重点解释几个配置。network.host如果只写127.0.0.1,Kibana和Logstash在同一台机器上访问没问题,但如果你计划让其他机器上的组件连接ES,必须改成0.0.0.0或具体的内网IP。discovery.type: single-node是单机学习环境的关键配置,如果不设置,ES默认走集群发现,单节点会一直等待其他节点加入,日志里刷master not discovered yet。xpack.security.enabled: true是8.x的默认值,意味着安全认证默认开启,后面设置密码时需要用到。
启动服务并设置开机自启:
systemctl daemon-reload systemctl enable --now elasticsearch验证一下端口和服务状态:
ss -tlnp | grep 9200 curl http://127.0.0.1:9200如果没有配置证书和密码,这里的curl请求会返回401。这就是8.x和7.x最大的区别,8.x默认开启安全认证。接下来设置内置账号密码,执行:
/usr/share/elasticsearch/bin/elasticsearch-setup-passwords interactive命令会让你逐一设置elastic、apm_system、kibana_system、logstash_system、beats_system等账号的密码。这里记住两个最关键的用户:elastic是超级管理员,Kibana登录时使用;kibana_system是Kibana连接ES时使用的服务账号,密码必须记住,后面配置Kibana要用。
设置完成后再次验证:
curl -u elastic:你的密码 http://127.0.0.1:9200能正常返回集群信息,ES就准备好了。
3.2 配置Kibana
安装Kibana:
dnf install -y kibana修改/etc/kibana/kibana.yml:
server.port: 5601 server.host: "0.0.0.0" elasticsearch.hosts: ["http://127.0.0.1:9200"] elasticsearch.username: "kibana_system" elasticsearch.password: "你在上一步设置的kibana_system密码"如果Kibana跟ES不在同一台机器上,elasticsearch.hosts需要换成ES所在机器的内网IP。启动服务:
systemctl daemon-reload systemctl enable --now kibana启动后访问http://服务器IP:5601,用elastic账号登录Kibana。首次进入会提示让你创建Index Pattern,先跳过,等Logstash写完数据再建也不迟。有时候Kibana启动后页面迟迟不出现,报Kibana server is not ready yet,多半是ES的连接认证出问题,优先看/var/log/kibana/kibana.log,里面会直接告诉你原因。只要ES的kibana_system账号密码正确,Kibana会在几十秒内完成初始化。
3.3 部署Logstash管道
安装Logstash:
dnf install -y logstashLogstash的配置核心是管道,每一条管道由input、filter、output三部分组成。配置文件放在/etc/logstash/conf.d/下,文件后缀必须是.conf。我这里给一个从Kafka消费日志、解析后写入ES的完整配置:
input { kafka { bootstrap_servers => "127.0.0.1:9092" topics => ["nginx-log"] group_id => "logstash-nginx" consumer_threads => 4 codec => json } } filter { if [fields][log_type] == "nginx-access" { grok { match => { "message" => "%{IPORHOST:client_ip} - - \[%{HTTPDATE:timestamp}\] \"%{WORD:http_method} %{URIPATHPARAM:request}\" %{NUMBER:http_status} %{NUMBER:body_bytes_sent} \"%{DATA:http_referer}\" \"%{DATA:user_agent}\"" } } date { match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ] target => "@timestamp" } } } output { elasticsearch { hosts => ["http://127.0.0.1:9200"] user => "elastic" password => "你的elastic密码" index => "nginx-access-%{+YYYY.MM.dd}" } }这里要解释几个容易踩坑的地方。codec => json表示从Kafka读到的消息按JSON解析,Filebeat默认输出的是带message字段的JSON结构,这么配没问题。filter里用grok解析Nginx的access日志,正则表达式写错了会解析失败,建议用Kibana自带的Grok Debugger调试正则,别自己硬想。最后一段date插件很关键,它是把日志里的时间字符串转成@timestamp字段,否则默认会用Logstash处理消息的当前时间,日志时间跟处理时间一旦有偏差,索引归属就会错乱。
配置写完后校验语法:
/usr/share/logstash/bin/logstash -t -f /etc/logstash/conf.d/nginx.conf看到Configuration OK就说明没问题。由于Logstash配置文件相对复杂,校验通过后再启动服务:
systemctl start logstash systemctl status logstash启动日志在/var/log/logstash/logstash-plain.log。如果这里能用logstash_system账号替代elastic更好?实测内置的logstash_system账号只能上报监控信息,没有索引写入权限,所以初学阶段直接用elastic跑通,生产环境应该在Kibana里创建独立角色和用户,授予monitor、manage_index_templates和write权限,再完成线上接入,这个思路要记住。
3.4 部署Filebeat采集器
安装Filebeat:
dnf install -y filebeatFilebeat的配置文件是/etc/filebeat/filebeat.yml。给一个采集Nginx访问日志并发送到Kafka的配置:
filebeat.inputs: - type: filestream enabled: true paths: - /var/log/nginx/access.log fields: log_type: nginx-access output.kafka: hosts: ["127.0.0.1:9092"] topic: "nginx-log" partition.hash: reachable_only: true required_acks: 1有几点要单独说明。input类型从7.x开始建议用filestream替代log,filestream更稳定,支持续读,配合paths指定的文件,能实现断点续传。fields里的log_type是自定义字段,Logstash的filter里就是根据这个字段区分日志类型的,所以两边必须一致。output.kafka配置里,required_acks: 1表示Kafka的Leader写入即确认,兼顾速度和可靠性。如果你的Filebeat之前配置过其他输出,一定要删除或注释掉多余的output.elasticsearch段,YAML配置里多个output不会自动合并,只会采用最后一个。
启动前先验证配置:
filebeat test config filebeat test outputtest config检查语法,test output会尝试连接Kafka,能显示连接成功就不需要再纠结网络问题了。然后启动服务:
systemctl enable --now filebeat这里有一个新手特别容易忽略的细节:Filebeat默认从文件末尾开始读取日志,也就是说,启动Filebeat之前Nginx写入的日志内容不会上传。所以建议先启动Filebeat,再主动刷新一下Nginx页面产生新日志,验证链路时会舒服很多。
3.5 Kafka做缓冲层:单节点快速部署
Kafka的部署用官方二进制包。这里我特意选用KRaft模式,从Kafka 3.3起ZooKeeper模式已经不再推荐,3.7版本直接用KRaft就能跑单节点,省掉一堆ZooKeeper的额外配置。下载并解压:
cd /opt wget https://archive.apache.org/dist/kafka/3.7.0/kafka_2.13-3.7.0.tgz tar -zxvf kafka_2.13-3.7.0.tgz mv kafka_2.13-3.7.0 kafkaKRaft模式需要先格式化存储目录。首先生成集群ID:
/opt/kafka/bin/kafka-storage.sh random-uuid然后格式化:
/opt/kafka/bin/kafka-storage.sh format -t <上面生成的UUID> -c /opt/kafka/config/kraft/server.properties格式化之前,建议先修改/opt/kafka/config/kraft/server.properties里的几个关键配置:
process.roles=broker,controller node.id=1 controller.quorum.voters=1@127.0.0.1:9093 listeners=PLAINTEXT://127.0.0.1:9092 advertised.listeners=PLAINTEXT://127.0.0.1:9092 controller.listener.names=CONTROLLER log.dirs=/opt/kafka/dataadvertised.listeners这个参数非常关键。如果Filebeat或Logstash在远程机器上,这里必须填Kafka所在机器的内网IP,而不是127.0.0.1,否则客户端会拿着127.0.0.1去连Kafka,直接超时。这是我见过最多人踩的坑。启动Kafka:
/opt/kafka/bin/kafka-server-start.sh -daemon /opt/kafka/config/kraft/server.properties验证端口和进程:
ss -tlnp | grep 9092 jpsjps能看到Kafka进程说明启动成功。最后创建日志Topic,建议显示创建而不是依赖自动创建,因为你可以自主控制副本数和分区数:
/opt/kafka/bin/kafka-topics.sh --create --topic nginx-log --bootstrap-server 127.0.0.1:9092 --partitions 3 --replication-factor 1单节点环境下replication-factor只能设为1,多节点再提高副本数。到这里,ELK + Kafka的完整骨架已经拉起来了。下面进入验证阶段,做成一条真实的数据看板。
4. 日志真实跑通:从Nginx日志到Kibana看板
4.1 设计一条完整的Nginx日志链路
理论讲再多,不如亲手把数据从日志文件送到Kibana页面。我这里的实验环境是同一台Rocky 9.4机器,Nginx、Filebeat、Kafka、Logstash、Elasticsearch、Kibana全装在同一台。生产环境你可以拆到多台机器,原理完全一样。
先在Rocky 9.4上安装Nginx并确保能产生访问日志:
dnf install -y nginx systemctl enable --now nginx curl http://127.0.0.1/ > /dev/null此时/var/log/nginx/access.log里应该有日志了。因为Filebeat默认从文件末尾开始读,所以这里先不用纠结旧日志,后面多刷新几次页面产生新日志就行。
链路数据流是这样的:Nginx写日志 -> Filebeat采集文件内容 -> 发送到Kafka的nginx-logTopic -> Logstash消费Topic -> 解析Nginx日志格式 -> 写入nginx-access-YYYY.MM.dd索引 -> Kibana读取ES数据并生成看板。任何一个环节断了,你都能通过下面的验证手段快速定位。
4.2 数据流验证的四个关键卡点
跑通链路之后,验证环节相当于给整条管道做体检。我总结了四个必须检查的卡点,按照从上游到下游的顺序来排查,效率最高。
第一卡点:Filebeat到底有没有读到文件内容。执行journalctl -u filebeat -f或者tail -f /var/log/filebeat/filebeat,看日志里有没有类似Publishing events或Successfully published的记录。如果什么都没有,很有可能是Filebeat没权限读Nginx日志,或者SELinux拦截了,此时用ausearch -m avc -ts recent查SELinux拦截记录。
第二卡点:Kafka里有没有收到消息。用Kafka自带的消费命令直接看:
/opt/kafka/bin/kafka-console-consumer.sh --bootstrap-server 127.0.0.1:9092 --topic nginx-log --from-beginning能看到滚动输出的JSON日志,说明Filebeat到Kafka这段没问题。如果这里没有数据,重点排查Filebeat的output.kafka配置和网络连通性,用filebeat test output能定位大部分问题。
第三卡点:Logstash有没有成功消费Kafka消息并写入ES。看Logstash日志:
tail -f /var/log/logstash/logstash-plain.log如果出现Pipeline aborted due to error或者401认证错误,重点检查output段的ES地址、用户名密码。如果Logstash正常工作,它会静默处理消息,通常不会刷明显日志,这时候直接进入第四卡点。
第四卡点:ES索引里有没有文档。执行:
curl -u elastic:你的密码 "http://127.0.0.1:9200/_cat/indices?v"能看到nginx-access-2025.xx.xx索引,并且docs.count大于0,说明ES写入成功。此时去Kibana的Stack Management里创建Index Pattern,索引名填nginx-access-*,时间字段选@timestamp,然后到Discover页面选好索引模式,就能看到Nginx访问日志一条条显示出来,再拖几个字段生成柱状图、饼图,一个日志看板就算跑通了。
5. 常见问题与排查技巧实录
5.1 六个高频问题与解决方案
ELK这套链路组件太多,任何一段出错都会导致数据不通。我把自己运维过程中频繁遇到的问题整理成一张表,都是实操中真实出现过的,照着排查能省不少时间。
| 问题现象 | 可能原因 | 解决思路 |
|---|---|---|
| Elasticsearch启动失败,报bootstrap checks failed | vm.max_map_count不够 | 执行sysctl -w vm.max_map_count=262144并写入/etc/sysctl.conf |
| Kibana页面提示Kibana server is not ready yet | kibana_system账号密码错误,或者ES内存不足 | 查看/var/log/kibana/kibana.log,重置密码并确认ES堆内存设置合理 |
| Filebeat已经启动,但Kafka Topic里看不到消息 | Filebeat配置文件语法错误,或output.kafka未生效 | 先filebeat test output,再查看Filebeat日志里的Publish记录 |
| Logstash启动失败,报401 Unauthorized | ES账号密码错误,或内置logstash_system账号无写权限 | 测试环境改用elastic账号,生产环境创建专用写角色 |
| Kibana能看到索引,但Discover里没有数据 | Index Pattern时间字段选错,或索引选了_all | 确认时间字段为@timestamp,索引模式为nginx-access-* |
| Filebeat读取不到新写入的日志 | SELinux拦截,或Filebeat启动时文件已经是旧位置 | 临时setenforce 0验证是否SELinux问题,再针对性配置权限 |
这里要额外多讲一句SELinux。很多书上说RHEL系要关SELinux,其实在生产环境关掉不是一个好习惯。正确的做法是观察拦截日志,用ausearch -m avc -ts recent找出具体是哪个进程访问哪个文件被拒了,再用semanage fcontext -a -t httpd_log_t "/var/log/nginx(/.*)?"之类的命令修正上下文。如果暂时不想深究SELinux策略,先把Filebeat跑通,后面再补策略,这是当前成本最低的路线。
5.2 排查链路问题的方法论:从“假成功”到“真成功”
发现数据不见以后,很多人第一反应是把所有组件日志翻一遍,结果越翻越乱。我个人的经验是:永远不要同时改多个组件配置,每次只改一个点,从下游往上游逐段验证。最核心的方法论是“先证明每一段有数据流动,再谈数据格式和展示”。
举个实际例子。有一次用户反馈Kibana看板数据延迟一个小时。我先看ES的_cat/indices,索引数据量正常,说明Logstash写入没问题;再看Logstash日志,确实在处理消息;继续查Kafka,发现消息正常;最后定位到Filebeat,原来是因为Nginx日志文件被logrotate切割后,Filebeat的filestream状态没有更新,停止读取新文件。这种问题不按照链路上游到下游逐层排查,光看一个组件的日志是发现不了的。
还有一点值得提醒:Filebeat的日志里出现“Publishing events”并不等于数据已经落到ES,它只代表数据去到了Kafka。Kafka的“消息已接收”也不等于Logstash消费成功,Logstash消费成功也不等于ES写入完成。每一层都有自己的确认机制,务必走到ES的索引计数确认,才算整个链路真正跑通。我用这套思路排查问题,基本没有超过十分钟搞不定的。
6. 生产化扩展:从“能跑”到“能扛”
6.1 索引生命周期管理ILM
Elasticsearch一个常见事故是磁盘被日志索引打满。如果没有人定期删除旧索引,ES就会无限膨胀,最终导致节点无响应。手工删除索引太原始,正确姿势是使用索引生命周期管理(ILM)。
ILM的核心思想是把索引按阶段管理:Hot阶段是热数据,持续写入,达到一定大小或时间后滚动到新索引;Delete阶段是到期删除,直接清掉历史数据。下面给一个简化的策略,日志保留7天:
curl -u elastic:你的密码 -X PUT "http://127.0.0.1:9200/_ilm/policy/nginx-ilm-policy" -H 'Content-Type: application/json' -d' { "policy": { "phases": { "hot": { "actions": { "rollover": { "max_size": "5GB", "max_age": "1d" } } }, "delete": { "min_age": "7d", "actions": { "delete": {} } } } } }'使用ILM需要在Logstash输出索引时设置ilm_enabled和ilm_policy参数,或者通过ES的Index Template来绑定策略。最简单的做法是在Kibana的Stack Management -> Index Lifecycle Policies里可视化创建策略,再到Index Templates里配置模板绑定。这样新创建的索引会自动套用策略,到期自动删除,再也不用半夜爬起来清磁盘。
6.2 集群化、权限与告警
单机ELK跑通之后,如果日志量持续增长,第一件要升级的事就是把ES从单节点变成多节点集群。三节点是最常见的架构:master节点负责集群管理,data节点负责存储,client节点负责接收请求。Elasticsearch天然支持水平扩容,只要在elasticsearch.yml里配置相同的cluster.name,并指定discovery.seed_hosts指向其他节点IP,节点之间就能自动发现并组成集群。注意集群节点之间要开放9300端口,并保持每个节点内存配置合理,避免出现集群脑裂。
权限方面,不要长期用elastic超级管理员账号连接Logstash和Filebeat。生产环境建议在Kibana里创建专用角色,只授予对应索引的read、write权限,以及monitor权限。比如给Logstash创建一个logstash-writer角色,只允许访问nginx-access-*索引,这样即使密码泄露,攻击面也被控制在最小范围。
告警是日志平台下一步价值所在。Kibana自带的Alerting功能可以基于查询结果设置阈值告警,比如“最近5分钟500错误数量超过100条”,触发后通过邮件、Webhook等方式通知。对于自定义的复杂告警,社区开源方案ElastAlert 2依然活跃,它能把ES查询结果跟规则引擎结合,做更细粒度的告警。我目前的做法是Kibana Alerting处理常规监控,ElastAlert处理跨索引的复杂规则,双管齐下。
最后分享一个实用小工具。日常巡检我习惯写一组curl命令别名,随时看集群状态和索引占用:
alias es-health='curl -s -u elastic:你的密码 http://127.0.0.1:9200/_cluster/health?pretty' alias es-indices='curl -s -u elastic:你的密码 "http://127.0.0.1:9200/_cat/indices?v&s=index"'每次改完配置,先检查语法,再观察日志,最后验证数据端到端一致,这个习惯帮我躲过了很多次深夜事故。ELK从入门到精通,其实不难,难的是把每个组件的边界、每一条数据的流向都理清楚。你把这套链路亲手搭一遍,再遇到任何日志问题,就不会再慌了。