3. 日志存储与索引:Elasticsearch 集群搭建、索引生命周期管理、分片与副本策略、冷热数据分层存储

日志存进来,怎么存得稳、查得快,是审计日志分析绕不开的核心问题。我见过不少团队,日志一股脑全塞进 ES,结果集群一个月就撑爆了。说白了,存储和索引不是「能存就行」,而是得从一开始就规划好。

这一章,我们聚焦四个实战点:集群怎么搭、索引怎么管生命周期、分片副本怎么配、冷热数据怎么分层。嗯,都是我在生产环境里踩过坑之后总结出来的。

3.1 Elasticsearch 集群搭建:别小看基础配置

搭建 ES 集群,很多人觉得就是装个包、改个配置文件。其实不然。我刚开始带团队时,就因为在 elasticsearch.yml 里漏了 discovery.seed_hosts,结果节点互相找不到,折腾了一下午。

这里给出一份我常用的最小化生产配置(3 节点集群示例):

# elasticsearch.yml - 节点 1
cluster.name: audit-logs-prod
node.name: node-1
path.data: /data/elasticsearch/data
path.logs: /data/elasticsearch/logs
network.host: 192.168.1.10
http.port: 9200
transport.port: 9300
discovery.seed_hosts: ["192.168.1.10:9300", "192.168.1.11:9300", "192.168.1.12:9300"]
cluster.initial_master_nodes: ["node-1", "node-2", "node-3"]
gateway.recover_after_nodes: 2
action.destructive_requires_name: true
我的习惯: 永远把 action.destructive_requires_name 设为 true。否则一个 DELETE * 就能让你哭。我曾经在测试环境手滑过,嗯,就一次。

节点角色分配也很关键。我建议这样分:

节点角色 职责 推荐配置
Master 集群元数据管理、节点心跳 3 台,4C8G,SSD
Data 数据存储、查询、聚合 按需扩展,16C64G+,SSD
Ingest 数据预处理管道 2 台,8C16G
Coordinating 请求分发、结果合并 2 台,8C16G

你想想看,如果让一个节点既当 master 又扛大量写入,集群稳定性就会打折扣。我见过一个案例,就是因为 master 节点压力过大导致脑裂,日志丢了整整 2 小时。

3.2 索引生命周期管理:让日志自动「退休」

审计日志有个特点:越老的日志查得越少。如果所有索引都保持同样的副本数和分片数,存储成本会直线上升。索引生命周期管理(ILM)就是解决这个问题的。

ILM 有四个阶段:Hot → Warm → Cold → Delete。我一般这样配置:

PUT _ilm/policy/audit_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_tier": "warm"
            }
          },
          "forcemerge": {
            "max_num_segments": 1
          },
          "shrink": {
            "number_of_shards": 1
          },
          "set_priority": {
            "priority": 50
          }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "allocate": {
            "require": {
              "data_tier": "cold"
            }
          },
          "freeze": {},
          "set_priority": {
            "priority": 0
          }
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}
关键点: Hot 阶段用 rollover 控制索引大小,避免单个索引过大。我习惯设 50GB 或 1 天,哪个先到就切。Warm 阶段做 forcemerge 和 shrink,能省 60% 以上的存储。

为什么 warm 阶段要 shrink 到 1 个分片?因为老索引查询频率低,不需要那么多分片并行。我曾在项目中做过测试,shrink 后查询性能几乎没下降,但磁盘占用少了将近一半。

3.3 分片与副本策略:多一分则肥,少一分则瘦

分片和副本,是 ES 性能调优的重头戏。很多人上来就设 10 个分片、2 个副本,结果集群负载不均。我个人的经验是:

  • 分片数 = (数据总量 / 每个分片期望大小) × 节点数因子
  • 每个分片大小:建议 20GB ~ 50GB
  • 副本数:生产环境至少 1,关键数据 2

举个例子:每天产生 500GB 审计日志,保留 7 天热数据。总数据量 3.5TB。按每个分片 40GB 算,大约需要 90 个分片。如果集群有 6 个数据节点,每个节点分 15 个分片,压力比较均衡。

避坑指南: 我曾经把分片数设得过大(200+),结果集群的 master 节点因为管理太多分片元数据,GC 频繁,查询响应时间从 200ms 飙升到 5s。后来我强制限制了每个节点的分片数上限:cluster.max_shards_per_node: 50。

副本策略也有讲究。写入密集的场景,副本数不宜过多,否则写入放大效应明显。我一般这样权衡:

场景 副本数 说明
热数据(写入频繁) 1 保证高可用,同时控制写入开销
温数据(查询为主) 1 查询负载均衡,节省存储
冷数据(极少查询) 0 只保留一份,用快照备份
关键审计数据 2 防止节点故障导致数据丢失

3.4 冷热数据分层存储:把钱花在刀刃上

审计日志的访问模式很规律:最近 7 天的日志查得最多,30 天前的几乎没人看。如果所有数据都用 SSD 存,成本太高。冷热分层就是让热数据跑在 SSD 上,冷数据放在 HDD 或对象存储里。

实现方式很简单:给节点打标签。

# 热节点 elasticsearch.yml
node.attr.data_tier: hot

# 温节点 elasticsearch.yml
node.attr.data_tier: warm

# 冷节点 elasticsearch.yml
node.attr.data_tier: cold

然后在 ILM 策略里,通过 allocate action 指定数据迁移到对应层级的节点。前面 ILM 配置里已经展示了这部分。

这里我画了一张流程图,帮你理清整个数据流转:

审计日志冷热分层存储与 ILM 流程 日志采集端 Hot 阶段 (SSD 节点) rollover: 50GB 或 1天 Warm 阶段 (HDD 节点) forcemerge + shrink 到 1 分片 Cold 阶段 (对象存储) freeze + 快照备份 说明 • 热数据:SSD + 2副本 • 温数据:HDD + 1副本 • 冷数据:对象存储 + 0副本 • 90天后自动删除

这套架构我在一个日处理 2TB 日志的项目里跑了一年多,存储成本降低了 70%,查询热数据依然在 200ms 以内。嗯,效果很理想。

我的建议: 冷数据层不要只依赖 ES 自身的副本。我习惯配合快照备份到 S3 或 HDFS,这样即使集群出问题,也能从快照恢复。毕竟审计日志丢了,合规检查就过不了。

最后提一句,分片和副本策略不是一成不变的。我建议每季度 review 一次集群的健康状况,根据实际写入量和查询模式做调整。别怕改,ES 支持动态调整大部分参数。