第133篇:ElasticSearch导入TB级日志实战:Java API调用与踩坑记录

Part1 前言
大家好,我是ABC_123。最近要对一批总大小达TB级别的日志进行分析,常规工具几乎无法进行高效处理,于是我想到了使用开源的ELK。不过Logstash的配置规则我一直用着不顺手,更习惯用Java代码直接调用ElasticSearch的API,按照自己的需求通过编程实现日志分析。查找旧资料时发现,上一次编写的代码已经是2017年的,这次重新动手运行一下,没想到了在新版的ElasticSearch上踩了不少坑。今天把解决问题过程记录下来,让大家少走弯路(文末有加群方式)。

Part2 技术研究过程
-
往年代码解析与回顾
大概在2017年左右,我当时使用的还是ElasticSearch是7.x版本。这次我重新查阅了ELK官方文档,发现7.x系列的ES还在持续更新。于是我重新下载了对应版本的 ES,并打算在之前写过的代码基础上做些修改,继续使用 Elasticsearch 官方提供的 Java High Level REST Client API 来导入体量较大的日志文件。不断调整代码中一些参数配置,代码最终逐渐趋于稳定,日志也没有再出现导入丢失的情况。
如下图所示,这是自己曾经写的旧代码,直接通过Java客户端调用 Elasticsearch 的 HTTP/REST API 来实现数据导入,在老版本的ES上运行没有问题。首先通过RestHighLevelClient 作为官方提供的 Java 高级客户端,与 Elasticsearch 集群建立通信,接着使用 CreateIndexRequest 来创建索引。

然后使用BulkProcessor与IndexRequest进行批量日志导入工作。然后调整这些参数的组合,综合考虑写入性能与系统稳定性,我们将单条日志构造成IndexRequest,再交由BulkProcessor 管理和批量提交,完成日志的导入工作(以下这些参数是2017年的,还是存在一些问题,我们必须进行调优改进)。

-
代码初步修改与Security错误提示
经过查询ES官网文档与介绍时发现,ElasticSearch数据库的7.x版本这些年一直在更新和维护,所以索性把项目中的pom.xml里ES的依赖版本号统一配置为较新的7.17.18,如下图所示。

对往期代码稍微调整之后,使用上述java代码导入日志时,遇到如下报错信息:
[2025-09-07T15:22:21,513][INFO ][o.e.x.s.s.SecurityStatusChangeListener] [DESKTOP-9FQQLLS] Active license is now [BASIC]; Security is disabled
这是因为新版的ES默认启用了安全机制,不配置账号密码,就会提示各种错误。往期代码我的ES环境是没有配置账号密码的,于是我想着可否屏蔽这些错误,不设置账号密码使得代码能正常运行,后期踩了一堆坑之后,发现新版对安全性要求很高,此路行不通。于是修改elasticsearch.yml文件,开启安全功能:
xpack.security.enabled: truexpack.security.transport.ssl.enabled: true
在启用xpack.security.enabled后,会要求所有客户端连接都进行身份验证。接下来通过交互式命令行来设置es的账号密码:
.\elasticsearch-setup-passwords.bat interactive

接下来修改RestHighLevelClient的连接代码,用户名输入elastic,密码也写上。

图中create方法代码如下,提供账号密码,构建RestHighLevelClient对象。

-
内存不足与ES控制台乱码问题
在日志导入过程中,遇到了ES控制台中文乱码问题,还遇到偶尔内存不足的问题。由于我这里的环境是windows系统,所以系统默认中文编码是GBK,导致乱码问题。于是修改jvm.options的内容,增加-Dfile.encoding=GBK。
另一方面,为了解决超大日志导入产生的内存不足问题,对JVM内存配置进行了如下调整:将JVM堆内存初始大小和最大大小都设置为 6GB(-Xms6g 和 -Xmx6g),当然-Xmx可以设置得再大一些;同时指定了 2GB 的堆外直接内存(-XX:MaxDirectMemorySize=2g),这2G内存可用于Lucence索引读写操作,它不会计入-Xmx限制,但仍然占用物理内存。

-
寻找Kibana的替代品
一般来说,ELK中的K指的是Kibana,与Elasticsearch配合使用。但是我写代码的时候,个人不太喜欢用kibana,因为其启动速度太慢,所以ABC_123在研发阶段就尝试寻找Kibana的替代品。首先发现了kaizen,这个软件是java写的,双击可以直接使用,比较轻便;但使用了一段时间后,发现它的几个功能点需要收费,虽然免费版功能足够用,但是如果想查看1万条之后的索引数据,就必须注册license才能继续查看。


后来ABC_123经过进一步搜索,终于找到了一个小巧、简单且非常容易上手的工具elasticvue.exe,它无需复杂的配置,双击即可运行,非常适合在开发和测试阶段快速查看 Elasticsearch 中的数据。我就用它来验证日志导入效果,作为研发阶段测试使用。

如下图所示,elasticvue提供了一个简洁直观的界面,可以方便查看索引的基本信息,例如名称、健康状态、分片、副本、文档数量以及存储空间等,点开可以看到json格式的索引日志数据。

-
BulkProcessor配置参数调优
经过ABC_123的反复测试,最终形成了如下代码:使Java代码在处理海量日志导入时,既能保持较高的吞吐量,又能避免因内存或磁盘压力过大而导致的失败。这些代码是对BulkProcessor的优化配置,这里的关键优化点包括:
批量触发条件:每当累计20000条请求或24MB数据就会触发一次批量提交,减少提交频率,降低 ES 集群的写入开销。
强制提交间隔:即使批量条数和大小未达到阈值,也会在100秒后强制提交一次,保证数据不会长时间滞留在内存中。
失败重试策略:第一次导入失败后等待30秒,如果仍然失败,下次间隔翻倍,最多重试 3 次。

-
内存监控与告警机制
在后台运行过程中,发现偶尔一段时间会出现内存会不断增长的情况,于是我加入了一段内存检测与处理代码。如下图所示,通过Runtime.getRuntime()方法获取 JVM 的堆内存使用情况,并设定了内存告警阈值(85%)和严重告警阈值(100%)。这段代码会定期检查当前内存占用率,当使用量接近或超过阈值时,输出日志进行告警,避免系统因内存不足而崩溃。

当检测到内存占用超过严重阈值时,代码会立即打印严重警告日志,并调用System.gc() 进行强制垃圾回收,然后再次检查GC后的内存占用情况,确保内存得到了释放。

之前还写过一个performGCIfNeeded方法,当内存使用率超过70%时才会触发垃圾回收。执行GC前后会分别记录内存使用量,方便定位和分析问题。

-
批量导入经常失败报错
代码完成之后,导致日志过程中遇到了新问题:当数据量达到20多个G时,总是出现批量导入失败的报错。

后期意识到,代码对于所有的异常报错没有完全抓取到,于是我修改代码,对批量导入的报错和异常做了更详细的记录和日志输出。

此时报错日志如下,提示ES变成只读状态了,具体原因当时不得而知。

起初我认为可能是原始日志文件某一行文本太长,导致ES报错。于是为了验证是否是日志问题,重新修改代码:遍历BulkResponse的每条响应,判断是否isFailed(),如果失败则提取对应的 IndexRequest,获取原始文档内容以及错误详情(文档 ID、错误类型、错误信息等),并写入到失败文件中。

在批次导入失败时,创建一个本地文件,并在文件头写入批次ID、原文件路径、导出时间、总请求数、失败数量等基本信息。

最后终于看到了具体的报错原因:disk usage exceeded。这表明硬盘剩余空间已经不足,Elasticsearch 在检测到磁盘使用率超过阈值时,会自动将索引标记为只读状态,从而导致所有写入操作失败,接下来我们清理一下硬盘,就可以解决问题了。

此时才知道,先前日志里的“free: 45.3gb[1.2%]”其实说明你的磁盘使用率已经很高了,只剩1.2% 空间,必须先释放空间。
[2025-09-07T15:22:51,222][WARN ][o.e.c.r.a.DiskThresholdMonitor] [DESKTOP-9FQQLLS] flood stage disk watermark [95%] exceeded on [fd0tXSOWTqubIY2KtNKxqg][DESKTOP-9FQQLLS][F:\xxxxxx\2_elasticsearch-7.3.1-9201\data\nodes\0] free: 45.3gb[1.2%], all indices on this node will be marked read-only
-
Elasticsearch日志数据迁移
Elasticsearch不同版本之间的数据迁移很麻烦,它不像Mysql、SQLServer那样可以直接备份数据然后还原数据。综合考量,最终通过elasticdump先把数据导出到json文件中,然后再重新导入。
导出索引结构(mapping)
elasticdump --input=http://localhost:9201/iis_log2025 --output=./iis_log2025_mapping.json --type=mapping
导出索引数据(data)
elasticdump --input=http://localhost:9201/iis_log2025 --output=./iis_log2025_data.json --limit=10000 --type=data > iis_log2025.log
Part3 总结
1. JVM 的配置要结合实际场景,堆内存及直接内存的配置需要根据实际情况调整。
2. 批量导入日志时,要注意根据自己的硬盘和内存、cpu占用率调整BulkProcessor的参数。
3. 内存及磁盘监控必须做到位,尤其是磁盘超过 95% 使用率时,ES 会直接把索引置为只读状态,导致所有写入失败
4. 为了便于技术交流,现已建立微信群"希水涵-信安技术交流群",欢迎您的加入。


公众号专注于网络安全技术分享,包括APT事件分析、红队攻防、蓝队分析、渗透测试、代码审计等,每周一篇,99%原创,敬请关注。
Contact me: 0day123abc#gmail.com
OR 2332887682#qq.com
(replace # with @)
更多推荐
所有评论(0)