
1. 项目概述与整体方案设计1.1 为什么要在Rocky 9.4上搭ELK日志分析这件事只要是跑业务的服务器基本都绕不开。服务器一多靠着tail -f逐个翻日志的日子就过不下去了。ELK这套组合——Elasticsearch负责存储和检索Logstash负责采集和清洗Kibana负责可视化展示十多年来一直是日志管理领域的事实标准。Rocky 9.4作为RHEL 9系的衍生版本因为和上游完全二进制兼容又是社区维护免费使用这两年在中型互联网公司和传统企业的服务器环境里普及率相当高。用Rocky 9.4作为ELK的运行环境一方面能享受到RHEL系生态的稳定性和长期维护周期另一方面9.x内核和glibc版本对Java系的Elasticsearch支持也很好不用像在老版本系统上那样为了兼容性折腾一堆依赖库。这套环境适配的是这类需求你手上有若干台服务器日志分散在各处想要一个集中式的日志查询入口同时希望保留原始日志的完整现场保证排查问题时能翻到第一手资料。1.2 整套方案的组件角色分工ELK不是一个单一的软件它是一套组合管线每个环节各司其职。Elasticsearch是整个系统的核心所有日志最终都会变成JSON文档存储在它的索引里它负责倒排索引的构建和分布式检索相当于整个系统的“数据库”。Logstash是数据管道负责从各个来源把日志捞进来做过滤、解析、格式化再写入Elasticsearch相当于“数据搬运工清洗工”。Kibana是前端的可视化层提供Web界面让你能搜索日志、画图表、配告警相当于“驾驶舱”。在实际部署中还有一个组件几乎成了标配——Filebeat或Beats家族。Logstash本身比较吃内存每起一个实例都占不少资源不适合直接跑在每台业务服务器上。Filebeat是一个轻量级的日志采集器用Go写的内存占用通常只有几十MB它会监听日志文件的变化把新增内容转发给Logstash做集中处理。这套经典架构里Filebeat在业务端采集Logstash做解析Elasticsearch做存储检索Kibana做展示四层各司其职。你也可能会问到Kafka在整套架构里的位置。当业务量极大时比如每秒几十万条日志Logstash的处理能力会成为瓶颈此时会在Filebeat和Logstash之间加一层Kafka消息队列做削峰填谷和数据缓冲。对于日志量在中小规模的环境Kafka可以先不引入单靠Filebeat Logstash足够扛住日常负载了。1.3 版本选型与依赖关系版本搭配是这套部署里最容易踩坑的地方。我这次选择的是当前比较稳的组合Elasticsearch 8.10.x Logstash 8.10.x Kibana 8.10.x Filebeat 8.10.x。四个组件统一大版本号这是Elastic官方给出的兼容性要求混用不匹配的版本经常会出现字段映射错误或者数据写入失败。8.x版本自带安全认证功能默认开启HTTPS和用户认证这对现在的网络安全环境来说是个刚需不像7.x时代需要额外装X-Pack插件。依赖方面Elasticsearch 8.x内置了JDK不需要在系统里再装一套Java环境。但Logstash和Kibana还是会依赖系统中的Java运行环境。在Rocky 9.4上用dnf install java-11-openjdk安装OpenJDK 11即可这个版本搭配ELK 8.10全家桶是经过验证的。有一点需要提醒Rocky 9.4自带的JDK可能是17甚至21虽然高版本JDK在某些情况下也能跑但为了避免兼容问题最好显式安装JDK 11。环境规划也要提前考虑好这套方案中Elasticsearch节点至少需要4GB可用内存如果条件允许8GB更稳妥Logstash分配2GB足够Kibana 1GB即可Filebeat内存占用极低512MB都绰绰有余。如果是单机部署在同一个节点上建议整机内存不低于8GB这是底线。2. 环境准备与基础配置2.1 Rocky 9.4系统初始化装ELK之前系统的初始状态整理一定要做扎实。Rocky 9.4安装完成后第一件事是更新系统补丁dnf update -y把内核和基础软件全部升到最新避免后续装软件时遇到依赖冲突。然后关闭SELinux或者至少把SELinux设为permissive模式。ELK各组件的端口监听和数据读写方式比较多SELinux的默认策略会对文件访问和端口监听做限制如果不懂怎么为每个组件写SELinux策略直接setenforce 0并修改/etc/selinux/config里有SELinuxdisabled能省掉大量排查时间。生产环境里如果你有安全合规要求那就要花时间学一学如何为ELK写SELinux自定义策略但对大多数场景来说关闭SELinux是最务实的做法。防火墙的放行规则也要提前想清楚。ELK这套组件涉及的端口主要有Elasticsearch的9200端口REST API、9300端口节点间通信、Kibana的5601端口、Logstash的5044端口接收Filebeat数据。在Rocky 9.4上做实验时如果只有一台服务器也可以直接停掉firewalld但如果是规范一点的环境建议按需放行端口用firewall-cmd --add-port9200/tcp --permanent这种方式逐项加规则然后把对公网暴露的端口限制在内网网段。文件描述符的调整也是Elasticsearch启动前的必备项。ES在运行时会打开大量文件句柄索引分片文件、日志文件、内存映射文件系统默认的1024远远不够直接改/etc/security/limits.conf增大nofile和nproc的软硬限制。还有一点容易被忽略ES对vm.max_map_count参数有硬性要求默认值65530在ES启动时会被检查低于262144会直接报错。用sysctl -w vm.max_map_count262144临时修改同时写入/etc/sysctl.conf让它永久生效。2.2 JDK安装与相关系统参数JDK的安装看似简单但版本选择很关键。我之前在CentOS 7上用过OpenJDK 8跑ES 8.x结果启动时报了UnsupportedClassVersionError折腾了很久才反应过来是JDK版本不匹配。这次在Rocky 9.4上方案是安装OpenJDK 11命令如下dnf install -y java-11-openjdk java -version安装完成后用alternatives --config java确认默认Java版本已经切到11。有些服务器上可能已经装了新版JDKalternatives命令可以让你在多版本之间切换默认值。这一步做完顺便把JAVA_HOME写进/etc/profile文件后面Logstash和Kibana启动时会用到。内存参数方面除了前面调整过的内核参数还要注意swap的使用倾向。ES官方建议尽可能禁用swap或者在系统层面降低swappiness值。修改/etc/sysctl.conf设置vm.swappiness1让内存在压力大时尽量不要用交换分区因为swap的磁盘IO会成为性能瓶颈。这个参数在日志量大的场景下感受非常明显开了swap和关掉swapES的查询响应时间能差一个数量级。在8.x版本里ELK安装包是不需要再单独去配置用户创建和目录权限的。无论是用rpm方式安装还是tar包方式解压安装官方安装包都会自动创建elasticsearch用户和对应的数据目录。如果你是纯手工从官网下载tar包解压部署那就要自己创建用户并授权目录。这里推荐直接用rpm方式安装后续用systemd管理服务会省心很多。3. ELK核心组件安装与配置3.1 Elasticsearch的安装与调优Elasticsearch的安装过程如果通过rpm包来做非常直接。先导入Elastic官方的签名密钥再配置yum源。在Rocky 9.4上创建一个/etc/yum.repos.d/elastic.repo文件写入[elastic-8.x] nameElastic repository for 8.x packages baseurlhttps://artifacts.elastic.co/packages/8.x/yum gpgcheck1 gpgkeyhttps://artifacts.elastic.co/GPG-KEY-elasticsearch enabled1 autorefresh1 typerpm-md接着执行dnf install -y elasticsearch安装完成后主配置文件在/etc/elasticsearch/elasticsearch.yml。在这里设置几个关键参数。cluster.name: elk-prod node.name: node-1 network.host: 0.0.0.0 http.port: 9200 discovery.seed_hosts: [127.0.0.1] cluster.initial_master_nodes: [node-1] xpack.security.enabled: true xpack.security.enrollment.enabled: truenetwork.host如果设置成0.0.0.0表示监听所有网卡接口这样才能让其他服务器上的Kibana和Logstash连过来。如果是纯本机测试环境也可以用127.0.0.1。8.x版本默认启用了安全认证首次启动时会自动生成elastic用户的初始密码这个密码会打印在启动日志中需要保存好。启动服务后第一件事就是用这个初始密码登录并修改密码或者用elasticsearch-reset-password -u elastic重新生成一个。内存调整是ES调优的核心。编辑/etc/elasticsearch/jvm.options文件把-Xms和-Xmx都设为物理内存的一半左右但注意最大不要超过32GB。ES使用堆内存和堆外内存的比例有个经验值堆内存设4GB堆外内存用于Lucene的segment文件缓存大概需要翻倍的空间所以给ES节点的物理内存最好在堆内存的3倍左右。如果机器是16GB内存堆内存给8GB是比较合的比例剩下的留给系统页缓存和其他进程。ES启动完成后用curl -k https://localhost:9200加上用户名密码访问能看到集群状态为green说明ES核心已就绪。这里有个小细节8.x默认启用了TLS加密通信curl访问时要加-k参数跳过证书校验。3.2 Logstash的管道配置Logstash安装同样走yum源dnf install -y logstash。它的配置文件放在/etc/logstash/conf.d/目录下一个典型的管道由input、filter、output三个区块组成。以最常见的Filebeat输入、Elasticsearch输出为例input { beats { port 5044 } } filter { grok { match { message %{COMBINEDAPACHELOG} } } date { match [ timestamp, dd/MMM/yyyy:HH:mm:ss Z ] target timestamp } } output { elasticsearch { hosts [https://localhost:9200] user elastic password yourpassword index nginx-access-%{YYYY.MM.dd} ssl_certificate_verification false } }这里面的设计思路值得展开说说。input区块用beats插件监听5044端口接收Filebeat转发过来的数据。filter区块用grok插件做日志解析COMBINEDAPACHELOG是Logstash内置的正则模板能直接从一行Nginx或Apache访问日志里切出客户端IP、请求时间、请求方法、状态码、响应字节数这些字段。date插件把日志里原有的时间字符串转换成标准的timestamp字段这样做的好处是日志入库后你可以按日志实际发生时间检索而不是按ES接收到日志的时间。output区块指定ES的地址和索引名规则按天分割索引的好处后面再细说。启动方式和ES类似用systemd管理systemctl start logstash systemctl enable logstash这里有一个我在实际配置过程中反复踩的坑Logstash的配置文件如果写错启动不会马上暴露问题因为Logstash会等有数据流经管道时才报错。所以配置完先做语法检查用/usr/share/logstash/bin/logstash --config.test_and_exit -f /etc/logstash/conf.d/这条命令能帮你验证配置文件语法是否正确不会真正启动服务。跑通了再正式启动。3.3 Kibana的界面配置Kibana安装和上面两个组件方式一致。关键配置在/etc/kibana/kibana.ymlserver.port: 5601 server.host: 0.0.0.0 elasticsearch.hosts: [https://localhost:9200] elasticsearch.username: kibana_system elasticsearch.password: yourpassword这里有个容易忽略的细节Kibana连接ES用的是一个专用账号kibana_system不是elastic超级用户。这个账号在ES启动初始化时已经创建好了密码需要自己重置。如果你用elastic用户来连接ES的安全审计日志里会给出警告而且从最小权限原则来看也不应该用超级账号跑前端连接。启动Kibana后用浏览器访问http://服务器IP:5601看到登录页输入之前设置的账号密码即可进入。3.4 Filebeat轻量采集端部署Filebeat的部署模式决定了整个方案的适用范围。我们要在每台需要采集日志的业务服务器上装Filebeat它负责读日志文件然后转发到Logstash。安装方式还是rpm但注意Filebeat的yum源和ELK其他组件是同一个源版本要严格匹配。Filebeat的核心配置在/etc/filebeat/filebeat.yml这里以采集Nginx访问日志为例filebeat.inputs: - type: filestream id: nginx-access paths: - /var/log/nginx/access.log parsers: - ndjson: target: output.logstash: hosts: [logstash服务器IP:5044]用filestream类型替代旧的log类型是8.x版本的新特性。filestream类型的管理状态是独立的它维护了每个文件的读取位置即使Filebeat重启或日志文件被rotate也能从上次的位置继续读取不会重复也不会漏。这个机制底层是记录每个文件的唯一标识符和当前偏移量到注册表文件里比老版的log类型在文件轮转场景下可靠得多。Filebeat配置完后需要filebeat test output测试一下到Logstash的网络连通性filebeat test config验证配置文件语法。两个测试都通过后再启动服务。到这里整套链路已经形成Filebeat读文件 → 转发到Logstash → 解析处理后写入ES → Kibana查询展示。下面进入实战验证和进阶部分。4. 数据链路打通与实战验证4.1 从启动到定位问题的全流程验证整套环境装完验证数据链路是否通畅是重中之重。我习惯的验证顺序是由后往前先确认ES里有数据再看Kibana能不能查到最后才是前端写日志测试采集。第一步在ES里查看索引是否存在curl -k -u elastic:yourpassword https://localhost:9200/_cat/indices?v如果能看到类似nginx-access-2025.01.15这样的索引说明Logstash已经成功写入数据了。接下来去Kibana操作打开Management → Stack Management → Index Patterns创建一个索引模式匹配nginx-access-*然后在Discover页面查询。这里有件事必须讲清楚ES里数据虽然已经存在但Kibana查询时如果找不到索引模式什么都看不到。第二步验证时间字段。ES中存储的数据默认有一个timestamp字段这是Kibana做时间筛选的依据。如果你在Logstash的filter中用date插件把日志里的原始时间字符串转换成了timestamp那么Kibana的时间轴就会按日志实际发生时间来展示如果没有做这个转换timestamp会以ES接收到数据的时刻为准。这两者在日志延迟传输场景下差别很大比如网络抖动导致Filebeat的数据延迟发送日志原始时间和采集时间的差值可能就是几十分钟排查问题时如果时间轴错位会让你误解事件顺序。第三步是端到端测试。在客户端上手动输出一条测试日志到日志文件里比如echo test log entry /var/log/nginx/access.log然后在Kibana里立刻刷新如果配置正确这条日志会在几秒钟内出现在查询结果里。整个链路的延迟通常在1到3秒之间如果超过5秒还没看到就要逐步排查哪个环节出问题了。4.2 Grok正则解析的进阶玩法Grok是整个ELK里最有技术含量也最折磨人的部分。内置的COMBINEDAPACHELOG模板能覆盖大多数Nginx/Apache日志格式但实际业务里的日志格式千奇百怪——应用的JSON日志、防火墙的syslog、各种中间件的自定义格式都需要自己写Grok表达式。Grok的原理本质上就是把多个命名的正则表达式组合起来。比如要解析下面这行日志2025-01-15T10:30:00Z ERROR UserService getUserInfo failed: userId10023, errdatabase timeout可以写这样的Grok表达式filter { grok { match { message %{TIMESTAMP_ISO8601:log_time} %{LOGLEVEL:level} %{WORD:service} %{NOTSPACE:method} failed: %{NOTSPACE:param}, %{GREEDYDATA:error_msg} } } }拆分出来会得到log_time、level、service、method、param、error_msg这几个字段。写Grok表达式时有几个容易忽略的地方GREEDYDATA匹配任意字符它是贪婪的会把剩余的所有内容都吞掉所以一般情况下要放在表达式的最后面NOTSPACE匹配不含空格的字符串适合用来抓user id这种值LOGLEVEL已经内置了debug/info/warn/error/fatal这些日志级别的正则不用自己写。调试Grok表达式不用一遍遍重启Logstash直接在Kibana的Dev Tools里就能验证或者用Elastic官网的Grok Debugger在线工具离线调试效果一样。真实场景里我都是先在调试器里把表达式跑通确认能提取出想要的字段再贴回Logstash配置里能省去大量重启服务的时间。4.3 索引生命周期管理与性能优化日志数据和业务数据不一样它有很强的时效性。一周前的日志基本只有合规审计时才会去翻半年后的日志几乎永远不会被查询。如果让索引无限增长ES的查询性能和磁盘空间都会失控。所以生产环境的ELK一定需要配上索引生命周期管理ILM。ILM的配置分为两个部分。一部分在ES侧定义策略比如索引创建后在30天时进入删除阶段或者在热阶段保留7天滚动到温阶段7天最后冷阶段再存16天。另一部分在Logstash侧output块里指定ilm_enabled true和ilm_policy logs_30day_policy写完索引后ES会自动按策略执行rollover和删除。PUT _ilm/policy/logs_30day_policy { policy: { phases: { hot: { actions: { rollover: { max_size: 50GB, max_age: 7d } } }, delete: { min_age: 30d, actions: { delete: {} } } } } }这个策略的含义是索引在写入数据达到50GB或7天时触发rollover新建一个索引承接后续写入索引年龄达到30天后自动删除。这就能保证ES里只保留最近30天的日志磁盘占用始终可控。5. 常见问题排查与实战避坑5.1 组件启动失败的高频原因整套ELK部署下来我遇到过的问题能列出一长串最典型的是这几类。端口被占用或未监听。ES启动后如果发现连接不上先用ss -lntp | grep 9200看清端口是否在监听。Rocky 9.4的firewalld默认对很多端口是放行的但如果你改过端口或者自定义过zone规则很容易出现外部连不进来的情况。排查思路是先在本机用curl -k https://localhost:9200测通了说明ES没问题再测远程不通就是防火墙或网络层面的问题。内存不足导致的两个组件互相挤压。如果你在同一台机器上跑ES和Logstash它们默认都会申请较大内存。ES默认堆内存为物理内存的一半Logstash默认是1GB。物理机内存16GB时没问题如果你用虚拟机跑且分配内存只有8GBES就要4GBLogstash 1GB再算上Kibana和系统本身内存压力非常大系统会频繁使用swap反应到现象上就是服务启动很慢、查询卡顿。所以单机部署ELK时一定要在jvm.options里主动调小ES的堆内存给其他组件留出余地。5.2 数据采集断流与重复Filebeat和Logstash之间的断流问题也经常遇到。表现是日志文件里有新内容但Kibana里长时间看不到。排查顺序是这样的先看Filebeat的日志/var/log/filebeat/filebeat如果出现connection refused多半是Logstash没有监听5044端口或者防火墙挡了再测试Logstash的pipeline配置/usr/share/logstash/bin/logstash --config.test_and_exit确认语法没问题最后看LS的日志/var/log/logstash/logstash-plain.log有没有写入ES时报错。重复采集的情况过去在老版本使用log类型时会遇到8.x换成filestream类型之后基本绝迹了。但如果你是从老版本升级上来的注意清理旧的注册表文件/var/lib/filebeat/registry否则可能出现重复读取的问题。5.3 时区问题导致的时间错乱时区问题是我见过最多人踩坑的地方。EFK/ELK体系默认使用UTC时间存储日志。如果你在业务日志的原始时间里带的是中国标准时间UTC8但Logstash解析时没有做时区转换那么Kibana里看到的时间会比真实时间早8个小时。解决方式有两种。第一种是在Logstash的date插件中显式指定时区date { match [ timestamp, dd/MMM/yyyy:HH:mm:ss Z ] timezone Asia/Shanghai target timestamp }第二种比较取巧在Kibana的Advanced Settings里修改dateFormat:tz为本地时区但这只是改变了显示层的时间底层存储仍是UTC。我建议做法是在Logstash层处理好时区让timestamp字段就是日志发生时的本地时间这样后续做聚合分析时不会乱。5.4 磁盘空间告警与容量规划日志系统的磁盘消耗速度远超预期。按照经验一个每秒产生100条日志的服务每条日志按1KB计算带完整字段的JSON格式很容易到这个体量一天的日志量约8.6GBES存储需要加上倒排索引和分片副本的膨胀系数一般是原始日志的1.2到1.5倍也就是十几GB一天。一个月下来就是300到500GB。所以部署ELK之前容量规划非常重要。容量规划的两个关键决定副本数的设置和保留天数。副本能保证数据安全但副本会翻倍消耗磁盘。单机环境建议副本数设为0写入性能还能提升。保留天数通过ILM策略来控制30天是大多数业务场景的合理值。磁盘监控也建议提前配置好。ES自带的cat API可以快速查看节点磁盘状态curl -k -u elastic:yourpassword https://localhost:9200/_cat/allocation?v如果发现磁盘超过85%使用率ES会自动把索引设为只读模式来防止写入失败这是8.x的自我保护机制。此时必须马上清理旧索引或扩容磁盘否则整个日志采集链路会直接停摆。6. 进阶优化引入Kafka后的高吞吐架构在日志量大、并发高的生产环境里Filebeat直接对接Logstash的架构有一个天然的瓶颈Logstash的处理能力决定了整个管道的吞吐上限一旦Logstash出现GC卡顿或宕机Filebeat本地的数据就会积压严重时连业务服务写日志都会被拖慢。解决方案是在Filebeat和Logstash之间加一层Kafka。架构变为Filebeat → Kafka → Logstash → ES → Kibana。Kafka的引入主要解决三个问题削峰填谷日志产生的峰值流量可以暂存在Kafka里Logstash按自己的处理能力消费不会因为瞬时流量过大而崩溃解耦Logstash升级或重启不影响Filebeat的正常采集扩展性多个Logstash实例可以并行消费Kafka的多个分区水平扩展吞吐。如果系统内存低于8GB不建议在单机上强行加入Kafka。Kafka本身依赖ZooKeeper或KRaft模式又会占用1GB到2GB内存单机资源容易捉襟见肘。但对于日志量达到日均百GB级别的场景Kafka这一层基本是必须的。配置上Filebeat的output需要改成Kafkaoutput.kafka: hosts: [kafka服务器IP:9092] topic: nginx-access partition.round_robin: reachable_only: trueLogstash的input则改成kafka消费模式input { kafka { bootstrap_servers kafka服务器IP:9092 topics [nginx-access] group_id logstash-nginx codec json } }加完这一层之后整套系统的吞吐量就不再受限于单一组件了。Filebeat采集能力、Kafka的磁盘吞吐、Logstash的消费能力、ES的索引吞吐每一层都可以独立扩容。这也是为什么现在互联网公司的日志系统基本都是这种多层解耦架构。7. 运维经验与日常保养ELK接入业务之后真正的挑战才开始。系统要稳定运行日常运维方面有几个习惯我是坚持做的。定期检查集群健康状态是基本功。用curl -k -u elastic:password https://localhost:9200/_cluster/health?pretty看status字段green是正常yellow说明有副本分片没分配red说明有主分片丢失后两种都需要立刻处理。ES集群的健康状态不像MySQL那样启动后就能长期稳定它和磁盘空间、节点状态、分片数量都强相关任何一环出问题都可能导致状态降级。Kibana的告警功能也建议配置起来。在Stack Management → Alerting里可以设置索引写入速率低于某个值时触发告警也能设置磁盘使用率超过阈值时通知。这样不用人肉盯监控面板ES自己就能报警。还有一个我踩过的坑Elasticsearch的默认配置里字段映射是动态创建的。如果日志格式变化了比如加了一个新字段新字段会自动加入索引映射但如果日志里出现了类型不一致比如以前user_id是数字后来变成了字符串ES会拒绝写入该文档。这个问题在8.x里可以通过配置dynamic: false或者strict来避免把索引映射固定住。建议在索引模板里把动态字段设为false只显示你已经明确映射过的字段这样日志格式变化产生的影响是可控的。最后再分享一个小技巧Kibana的Discover页面在应对千万级数据量时查询响应变慢是很常见的事。如果确认不是ES节点性能问题可以检查一下查询的filter里是否有对未映射字段的term查询。ES对keyword类型的字段做精确匹配要快很多如果用text类型的字段做精确匹配会走全文检索链路性能差距能有十倍。日志业务里凡是后续要用于筛选的字段如日志级别、服务名、用户ID在创建索引模板时统一用keyword或integer类型几秒钟的响应时间能压到几百毫秒。ELK这套系统本质上是给你一个重新审视自身业务日志的机会。日志采集、清洗、展示这条链路打通之后你要做的不是守着Kibana看曲线而是能从海量日志中快速提取出业务问题的根因。部署只是第一步把索引策略、字段映射、解析规则沉淀成一套标准才算真正把ELK用好了。