Index Lifecycle Management(ILM)实战
Index Lifecycle Management(ILM)实战
运维 ES 最烦的事情之一:每天建新索引、删旧索引、处理数据迁移。如果是日志类数据,每天一个 log-2025-06-01、log-2025-06-02……手动管理会死人的。
ILM(索引生命周期管理)就是 ES 内置的自动化引擎:定义规则 → 自动执行 hot → warm → cold → delete,全程不需要人介入。
ILM 的四个阶段
Hot(热) → Warm(温) → Cold(冷) → Delete(删)
↑ ↑ ↑ ↑
写入活跃 只读 很少查询 滚蛋
SSD/高性能 普通磁盘 廉价存储
Hot 阶段
索引正在被频繁写入。注重写入性能和快速可见。
- 通常 SSD,高 IOPS
refresh_interval: 1s- 副本数 1+
Warm 阶段
索引不再写入,但仍经常被查询。
- 可以迁到普通磁盘
forcemerge到较少 segment,优化查询- 缩减副本(如果查询负载不高)
Cold 阶段
数据几乎不查了,但出于合规需要保留。
- 迁到最便宜的存储
- 甚至可以
searchable snapshot挂载快照(不占本地磁盘)
Delete 阶段
数据超过保留期限,自动删除。
实战:日志索引 30 天生命周期
假设日志每天产生一个新索引,保留 30 天。规则:
- Hot:写入 7 天
- Warm:第 8 天转为只读,forcemerge
- Cold:第 15 天迁到冷节点
- Delete:第 30 天自动删除
第一步:配置 ILM Policy
PUT /_ilm/policy/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": {
"forcemerge": { "max_num_segments": 1 },
"shrink": { "number_of_shards": 1 },
"allocate": {
"require": { "data": "warm" }
},
"set_priority": { "priority": 50 }
}
},
"cold": {
"min_age": "15d",
"actions": {
"allocate": {
"require": { "data": "cold" }
},
"set_priority": { "priority": 0 }
}
},
"delete": {
"min_age": "30d",
"actions": {
"delete": {}
}
}
}
}
}
关键字段解释:
| 动作 | 作用 |
|---|---|
rollover |
索引达到 50GB 或 1 天后自动滚动(创建新索引) |
forcemerge |
合并为 1 个 segment,搜索性能最优 |
shrink |
缩减分片数,减少集群资源占用 |
allocate |
把索引迁到指定标签的节点 |
delete |
删除索引 |
第二步:创建 Index Template 绑定 Policy
PUT /_index_template/logs_template
{
"index_patterns": ["logs-*"],
"data_stream": {},
"template": {
"settings": {
"index.lifecycle.name": "logs_policy",
"number_of_shards": 1,
"number_of_replicas": 1
}
}
}
第三步:创建初始索引
PUT /logs-000001
{
"aliases": {
"logs_write": { "is_write_index": true }
}
}
之后 ILM 会自动创建 logs-000002、logs-000003……并按时执行各阶段操作。
第四步:查 ILM 状态
# 查看某个索引当前在哪个阶段
GET /logs-000001/_ilm/explain
# 查看 policy 的当前运行情况
GET /_ilm/policy/logs_policy
# 手动触发迁移到下一阶段
POST /logs-000001/_ilm/retry
Rollover:滚动索引的核心
Rollover 是 ILM 最关键的一环——它在索引达到一定条件时自动创建一个新索引,并把写入流量切过去。
PUT /logs_write/_rollover
{
"conditions": {
"max_size": "50GB",
"max_age": "1d",
"max_docs": 10000000
}
}
满足任一条件即触发滚动。三种条件建议同时使用:
max_size:索引太大,搜索变慢 → 滚动max_age:按天分割,方便管理 → 滚动max_docs:文档数太多,segment 碎片化 → 滚动
与其他索引方案对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| ILM | 全自动,策略统一 | 需要 7.x+ 版本 |
| Curator(老方案) | 灵活,可自定义脚本 | 需要外部 cron,已不再维护 |
| 手动脚本 | 完全可控 | 运维负担重,易出错 |
| Data Stream(ES 7.9+) | 与 ILM 深度集成,自动管理后备索引 | 只适合时序数据 |
Data Stream:ILM 的最佳搭档
如果你所有数据都是时序追加的(日志、指标、事件),Data Stream 比直接管理索引更省心:
// 创建 data stream(自动绑定 ILM + rollover)
PUT /_index_template/logs_template
{
"index_patterns": ["logs-*"],
"data_stream": {}, // 标记为 data stream
"template": {
"settings": { "index.lifecycle.name": "logs_policy" }
}
}
// 写入时直接往 data stream 写
POST /logs/_doc
{ "@timestamp": "2025-06-01T10:00:00Z", "message": "server started" }
// ES 自动路由到 logs 的下一个后备索引
Data Stream 特点:
- 只能追加,不能 update(完美匹配日志场景)
- 自动管理后备索引的创建、滚动和删除
@timestamp字段必须存在
常见坑
坑 1:shrink 后节点磁盘空间不够
Shrink 需要先把所有分片迁到一个节点再合并为一个。如果 8 个分片共 40GB,shrink 目标节点需要 40GB 可用空间。
解决:shrink 前确保目标节点有足够盘,或者用 number_of_shards: 1 从根源上避免。
坑 2:rollover 条件配得太大
max_size: 500GB + max_age: 90d——等你哪天想删数据时,一个 500GB 的索引删了集群半天喘不过气。
解决:控制单索引大小在 30-50GB 以内。用小索引容易管理,删除也快。
坑 3:忘了配 delete 阶段
热 → 温 → 冷都配好了,唯独没配 delete。磁盘满了才想起来。
解决:ILM Policy 上线前必须四个阶段都确认一遍。
总结
ILM 解决的是"人管索引太累"的问题。规划三原则:
- 小步快滚——索引达到 30-50GB 就滚动,别让它长成巨无霸
- 四个阶段都要配——特别是 delete,磁盘满了救不回来
- Data Stream 优先——如果是时序数据,用 Data Stream 比手动管理索引省一个数量级的工作量