1. 为什么我们绕不开ELK这套组合日志这东西平时没人搭理一旦线上出问题它就成了救命稻草。我最早做运维那会儿排查一个接口超时靠的是登录十几台机器挨个tail -f眼睛都快看瞎了最后发现是某台机器的磁盘写满了导致日志根本没落盘。那次之后我就下定决心必须把日志集中起来。后来试过不少方案兜兜转转还是回到了Elasticsearch Logstash Kibana这套组合也就是大家常说的ELK。这套东西到底解决什么问题简单说三件事收集、存储与检索、可视化。Logstash 负责把散落在各处的日志抓过来、洗干净、送出去Elasticsearch 负责把它们存起来并且建好索引让你能秒级检索Kibana 则给你一个网页界面画图、做看板、点几下就能查到想要的东西。它适合谁我觉得只要你的服务超过三台机器或者你已经开始被“日志在哪台机器上”这个问题折磨那就该考虑上了。哪怕你是刚接触运维的新手跟着走一遍也能搭起来因为这套生态的文档和社区实在太厚了。不过我得先泼盆冷水ELK 不是装完就完事的玩具它吃内存、吃磁盘、配置项多到让人头大。很多人第一次部署完发现 Kibana 连不上 ES或者 Logstash 启动就报错其实都是些很基础的坑。这篇我就按自己实际落地的顺序把整套流程拆开讲包括我踩过的那些坑和后来总结出来的参数调法。2. 部署前的整体设计与选型考量2.1 组件版本怎么选才不闹心版本这块我吃过亏。早期我图省事ES 装了 7.xLogstash 拿了 6.x结果启动直接报协议不兼容。三个组件的版本号必须保持一致这是铁律。比如你要用 7.17.x那就 ES、Logstash、Kibana 全部 7.17.x。为什么强调这个因为它们之间走的是内部 REST 协议小版本之间偶尔都会有字段差异大版本跨着用基本等于给自己找事。那选哪个大版本我的建议是新项目直接上 7.x 的最后一个稳定版或者 8.x。7.x 生态最成熟网上资料多遇到问题一搜就有8.x 默认开启了安全认证安全性好但配置稍复杂。如果你只是内网测试环境7.x 上手最快。另外注意ES 从 7.11 开始自带了 JDK不用你单独装 Java 了这省了不少事。2.2 单机还是集群先想清楚我见过太多人一上来就搭三节点集群结果资源不够集群状态一直是 yellow 甚至 red。部署形态要跟着你的数据量和可用性要求走。场景推荐形态说明个人学习/测试单节点把 ES 同时当 master 和 data 节点够用小团队生产日志量 10GB/天单节点 定期快照省钱但要做好备份中等规模生产3 节点集群一主两数据避免单点大规模独立 master 多 data 节点需要专门规划单节点模式下ES 默认会因为discovery.type的问题起不来必须显式设置成单节点发现模式这个后面实操会讲。集群模式则要注意脑裂问题discovery.seed_hosts和cluster.initial_master_nodes这两个参数配错集群根本组不起来。2.3 数据流架构怎么设计ELK 的数据流不是只有一条路。最常见的两种Logstash 直连应用服务器装 Logstash或者 Filebeat日志直接推给 ES。简单但 Logstash 比较重装在每个应用节点上吃资源。Filebeat Logstash ES应用节点只装轻量的 Filebeat 采集统一发给一台 Logstash 做清洗再进 ES。这是生产环境的主流做法。我推荐第二种。Filebeat 是 Go 写的内存占用小几十兆就能跑Logstash 是 JVM 的动辄几百兆起步集中部署一台就够了。这样应用节点压力小清洗规则也集中管理改起来方便。下面实操我就按Filebeat - Logstash - Elasticsearch - Kibana这条链路来讲这也是最贴近真实生产的架构。3. 核心组件安装与关键配置实操3.1 Elasticsearch 安装与单节点启动先说 ES。我习惯用 tar 包部署不依赖包管理器迁移也方便。假设装在/opt/elk下。# 创建目录并解压 mkdir -p /opt/elk cd /opt/elk tar -zxvf elasticsearch-7.17.9-linux-x86_64.tar.gz mv elasticsearch-7.17.9 es接下来改配置文件es/config/elasticsearch.yml这是核心cluster.name: my-elk-cluster node.name: node-1 path.data: /opt/elk/es/data path.logs: /opt/elk/es/logs network.host: 0.0.0.0 http.port: 9200 discovery.type: single-node这里几个点必须说清楚。network.host设成0.0.0.0是为了让其他机器能访问但生产环境一定要配合防火墙规则别裸奔。discovery.type: single-node是单节点模式的关键不写这个 ES 会尝试找其他节点然后启动失败。path.data和path.logs建议单独指定别用默认的方便后面挂载独立磁盘。还有一个绕不开的问题ES 不能用 root 用户启动。这是硬性安全限制。所以得建个普通用户useradd esuser chown -R esuser:esuser /opt/elk/es su - esuser /opt/elk/es/bin/elasticsearch -d-d是后台运行。启动后验证一下curl http://localhost:9200返回一段带cluster_name和version的 JSON 就说明起来了。如果报max virtual memory areas vm.max_map_count [65530] is too low那是系统参数没调执行sysctl -w vm.max_map_count262144想永久生效就写进/etc/sysctl.conf。这个坑几乎人人都会踩一次记住就行。3.2 Logstash 管道配置的门道Logstash 的核心是管道pipeline由 input、filter、output 三段组成。配置文件放在logstash/config/下我一般新建一个logstash.conf。input { beats { port 5044 } } filter { grok { match { message %{TIMESTAMP_ISO8601:log_time} %{LOGLEVEL:level} %{GREEDYDATA:msg} } } date { match [ log_time, ISO8601 ] target timestamp } } output { elasticsearch { hosts [http://192.168.1.10:9200] index app-logs-%{YYYY.MM.dd} } }input用beats插件监听 5044 端口专门收 Filebeat 发来的数据。filter里的grok是重点它用正则把一行日志拆成结构化字段。上面这个模式假设你的日志长这样2024-01-15T10:23:45 INFO 用户登录成功。%{LOGLEVEL:level}会把 INFO 提取成level字段后面在 Kibana 里就能按级别过滤了。date插件的作用是把日志里的时间替换掉 Logstash 默认的采集时间这样时间轴才准确。很多人忽略这一步结果 Kibana 里看到的时间全是采集时间排查问题时对不上。output里index用了日期变量这样每天生成一个索引方便按天清理。ES 的索引名必须全小写别用大写字母否则会报错。启动 Logstash/opt/elk/logstash/bin/logstash -f /opt/elk/logstash/config/logstash.conf想后台跑就加--config.reload.automatic配合 nohup或者用 systemd 托管。启动日志里看到Pipeline started就成功了。3.3 Filebeat 轻量采集端配置Filebeat 装在应用服务器上配置比 Logstash 简单得多。核心是filebeat.ymlfilebeat.inputs: - type: log enabled: true paths: - /var/log/myapp/*.log fields: app: myapp fields_under_root: true output.logstash: hosts: [192.168.1.10:5044]paths指定要采集的日志路径支持通配符。fields加自定义字段比如标记这个日志来自哪个应用后面在 Kibana 里就能按应用筛选。fields_under_root: true让这些字段直接放在顶层不然会嵌套在fields下面查询时得多写一层。启动/opt/elk/filebeat/filebeat -c /opt/elk/filebeat/filebeat.yml -e-e是输出到标准错误方便看日志。确认没问题后去掉-e后台运行。注意Filebeat 默认会记录文件读取位置到 registry 文件重启后不会重复采集。但如果你手动删了日志文件又重建同名文件可能会漏采这时候需要清理 registry。3.4 Kibana 连接与界面初始化Kibana 配置更简单改kibana/config/kibana.ymlserver.port: 5601 server.host: 0.0.0.0 elasticsearch.hosts: [http://192.168.1.10:9200]启动/opt/elk/kibana/bin/kibana浏览器打开http://192.168.1.10:5601能看到界面就成功了。第一次进去要创建索引模式Index Pattern填app-logs-*Kibana 会自动匹配所有日期索引。然后选timestamp作为时间字段保存后就能在 Discover 里看到日志了。4. 常见问题排查与避坑经验实录4.1 启动类问题速查表现象可能原因解决方向ES 启动报 vm.max_map_count 过低系统参数未调sysctl -w vm.max_map_count262144ES 用 root 启动被拒安全限制建普通用户启动Kibana 连不上 ES地址写错或 ES 未起先 curl ES 确认再查 kibana.ymlLogstash 启动报端口占用5044 被占netstat -tlnp查占用进程Filebeat 采集不到日志路径不对或权限不足检查 paths 和文件读权限4.2 那些文档里不会写的坑第一个坑磁盘水位线。ES 默认磁盘使用超过 85% 就不再分配新分片超过 90% 直接转只读。我有次测试环境磁盘满了日志写不进去查了半天才发现是这个机制。解决办法是清理旧索引或者临时调高水位线curl -XPUT http://localhost:9200/_cluster/settings -H Content-Type: application/json -d { transient: { cluster.routing.allocation.disk.watermark.low: 90%, cluster.routing.allocation.disk.watermark.high: 95% } }但这只是应急根本还是要做好索引生命周期管理定期删除老数据。第二个坑grok 正则写错导致日志丢失。grok 匹配失败时默认行为是给这条日志打上_grokparsefailure标签但日志本身还是会进 ES。如果你在 filter 里加了drop或者条件判断可能就把日志丢了。我的习惯是先在 Kibana 的 Dev Tools 里用_grokparsefailure搜一下确认没有解析失败的日志再上线。第三个坑时区问题。Logstash 和 ES 默认用 UTC 时间Kibana 显示时会按浏览器时区转换。如果你日志里的时间是本地时间又没做 date 转换那 Kibana 里看到的时间会差 8 小时。解决办法就是在 filter 里用 date 插件正确解析或者统一让应用输出 ISO8601 带时区的时间。第四个坑内存给太少。ES 默认堆内存是 1GB生产环境根本不够。改jvm.options-Xms4g -Xmx4gXms和Xmx设成一样大避免运行时动态调整带来的性能抖动。但别超过物理内存的 50%因为 ES 还要用文件系统缓存来加速检索。4.3 性能调优的几个实用参数索引刷新间隔默认是 1 秒意味着每秒都会生成一个新 segment写入压力大。如果对实时性要求没那么高可以调大curl -XPUT http://localhost:9200/app-logs-*/_settings -H Content-Type: application/json -d { index.refresh_interval: 30s }这样写入吞吐能提升不少。另外日志类数据一般不需要副本单节点环境下副本分片只会让集群状态变 yellow可以设成 0curl -XPUT http://localhost:9200/app-logs-*/_settings -H Content-Type: application/json -d { index.number_of_replicas: 0 }5. 从能用到好用索引管理与看板搭建5.1 索引生命周期管理入门日志数据的特点是“新数据查得勤老数据基本不看”。如果所有数据都堆在一个索引里检索会越来越慢。ES 提供了ILM索引生命周期管理来解决这个问题。基本思路是热阶段最近 7 天保持高资源温阶段7-30 天降低副本冷阶段30 天以上压缩存储最后删除。配置 ILM 策略curl -XPUT http://localhost:9200/_ilm/policy/logs_policy -H Content-Type: application/json -d { policy: { phases: { hot: { actions: { rollover: { max_size: 10gb, max_age: 1d } } }, delete: { min_age: 30d, actions: { delete: {} } } } } }然后在 Logstash 的 output 里指定ilm_enabled true和ilm_policy logs_policy让索引自动滚动和清理。这样就不用每天手动删索引了。5.2 Kibana 看板搭建思路Kibana 的 Visualize 功能很强大但别一上来就堆一堆图。我的经验是围绕三个问题来搭有没有错、错在哪、什么时候错得多。用Level 字段的饼图看各级别日志占比ERROR 占比高就说明有问题。用时间序列折线图看日志量趋势突增突降都是信号。用Data Table按应用名分组统计错误数快速定位是哪个服务出问题。把这些图拼到一个 Dashboard 上设好自动刷新往大屏上一挂值班的时候一眼就能看出异常。这比盯着滚动日志高效多了。5.3 告警怎么接Kibana 7.x 之后内置了告警功能Alerting可以基于查询条件触发。比如“5 分钟内 ERROR 日志超过 100 条就发通知”。配置入口在 Stack Management 里的 Rules and Connectors。连接器支持邮件、Webhook 等。我一般用 Webhook 推到一个内部通知服务这样告警能直接进群响应快。提示告警规则别设太敏感否则半夜被叫醒发现只是正常的业务波动。建议先观察一周日志基线再定阈值。6. 我踩过的那些坑和最后想说的整套 ELK 跑通其实不难难的是稳定运行。我印象最深的一次是 Logstash 进程莫名其妙挂了查日志发现是 JVM 内存溢出。原因是 grok 正则写得太复杂遇到超长日志行时回溯爆炸。后来我把正则拆简单了又加了timeout_millis限制才彻底解决。所以filter 里的正则一定要做性能测试别拿生产日志当小白鼠。还有一次是 ES 集群突然变红所有分片不可用。排查发现是某台节点磁盘满了而副本分片又没地方分配。那次之后我养成了两个习惯一是给磁盘加监控告警二是定期检查集群健康状态curl -XGET http://localhost:9200/_cluster/health?pretty看到status: green才安心。最后分享一个小技巧如果你只是临时想看某台机器的日志不想大动干戈部署整套可以用 Filebeat 的consoleoutput 先测试采集是否正常确认路径和格式没问题了再改成 Logstash 或 ES 输出。这样能省掉很多来回调试的时间。ELK 这套东西装一次可能只要半天但调优和排错是个长期活儿慢慢来别急。