在 Elasticsearch 生产环境中,索引生命周期管理(ILM, Index Lifecycle Management) 是实现自动化运维的核心功能。然而,手动编写 ILM 策略 JSON 容易出错、难以标准化。
为此,我们设计一套 Elasticsearch ILM 策略生成器(ILM Policy Generator),支持:
- 可视化配置 ILM 阶段;
- 自动生成标准 JSON;
- 导出为模板或直接应用;
- 支持 Hot-Warm-Cold-Delete 全流程;
- 适配日志、监控、业务数据等场景。
本文提供一个 可落地的 ILM 策略生成器设计方案 + Python 实现模板。
一、目标与核心功能
| 功能 | 说明 |
|---|---|
| ✅ 可视化配置 | 表单式输入,无需手写 JSON |
| ✅ 自动生成策略 | 输出标准 ILM JSON |
| ✅ 多场景模板 | 预设日志、指标、商品等模板 |
| ✅ 参数校验 | 检查 min_age 顺序、单位合法性 |
| ✅ 导出与应用 | 支持导出 JSON / 直接调用 ES API |
| ✅ 命令行 + Web 两种模式 | 满足不同使用习惯 |
二、ILM 策略结构回顾
{
"policy": {
"phases": {
"hot": { ... },
"warm": { ... },
"cold": { ... },
"delete": { ... }
}
}
}
每个阶段可包含多个 Actions,如 rollover、forcemerge、shrink、migrate、delete 等。
三、ILM 策略生成器设计
1. 输入参数模型
| 参数 | 类型 | 说明 |
|---|---|---|
name | string | 策略名称(如 logs-policy) |
hot.max_size | string | 滚动条件:最大大小(如 50gb) |
hot.max_age | string | 滚动条件:最大年龄(如 1d) |
warm.min_age | string | 进入 warm 阶段的最小年龄 |
warm.forcemerge | bool | 是否执行 forcemerge |
warm.shrink | int | 是否 shrink 分片数 |
warm.migrate | string | 迁移到指定 node.attr.zone |
cold.min_age | string | 进入 cold 阶段的最小年龄 |
cold.migrate | string | 迁移到冷节点 |
delete.min_age | string | 删除前最小年龄 |
delete.delete | bool | 是否删除 |
四、Python 实现:ILM 策略生成器
文件结构
ilm-generator/
├── generator.py # 核心生成逻辑
├── templates/ # 预设模板
│ ├── logs.json
│ ├── metrics.json
│ └── products.json
├── cli.py # 命令行接口
└── web.py # Web 接口(可选)
1. 核心生成逻辑 generator.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Elasticsearch ILM 策略生成器
"""
import json
from typing import Dict, Optional
class ILMGenerator:
def __init__(self):
self.policy = {"policy": {"phases": {}}}
def validate_unit(self, value: str) -> bool:
"""验证时间/大小单位"""
if not value:
return True
units = ['s', 'm', 'h', 'd', 'w', 'kb', 'mb', 'gb', 'tb']
return any(value.endswith(u) for u in units)
def add_hot_phase(self, max_size: str = None, max_age: str = None):
"""添加 Hot 阶段"""
if not max_size and not max_age:
raise ValueError("Hot 阶段必须设置 max_size 或 max_age")
actions = {}
if max_size:
if not self.validate_unit(max_size):
raise ValueError(f"无效大小单位: {max_size}")
actions['rollover'] = {'max_size': max_size}
if max_age:
if not self.validate_unit(max_age):
raise ValueError(f"无效时间单位: {max_age}")
actions['rollover'] = actions.get('rollover', {})
actions['rollover']['max_age'] = max_age
self.policy["policy"]["phases"]["hot"] = {"actions": actions}
return self
def add_warm_phase(
self,
min_age: str,
forcemerge: bool = True,
shrink: int = None,
migrate_to: str = None
):
"""添加 Warm 阶段"""
if not self.validate_unit(min_age):
raise ValueError(f"无效时间单位: {min_age}")
actions = {}
if forcemerge:
actions['forcemerge'] = {'max_num_segments': 1}
if shrink:
actions['shrink'] = {'number_of_shards': shrink}
if migrate_to:
actions['migrate'] = {'destination': migrate_to}
self.policy["policy"]["phases"]["warm"] = {
"min_age": min_age,
"actions": actions
}
return self
def add_cold_phase(self, min_age: str, migrate_to: str = None):
"""添加 Cold 阶段"""
if not self.validate_unit(min_age):
raise ValueError(f"无效时间单位: {min_age}")
actions = {}
if migrate_to:
actions['migrate'] = {'destination': migrate_to}
self.policy["policy"]["phases"]["cold"] = {
"min_age": min_age,
"actions": actions
}
return self
def add_delete_phase(self, min_age: str):
"""添加 Delete 阶段"""
if not self.validate_unit(min_age):
raise ValueError(f"无效时间单位: {min_age}")
self.policy["policy"]["phases"]["delete"] = {
"min_age": min_age,
"actions": {"delete": {}}
}
return self
def build(self) -> Dict:
"""生成最终策略"""
return self.policy
def to_json(self, indent=2) -> str:
"""输出 JSON 字符串"""
return json.dumps(self.policy, ensure_ascii=False, indent=indent)
def save(self, filepath: str):
"""保存到文件"""
with open(filepath, 'w', encoding='utf-8') as f:
f.write(self.to_json())
print(f"✅ 策略已保存到: {filepath}")
def apply(self, es_client, policy_name: str):
"""直接应用到 Elasticsearch"""
from elasticsearch import TransportError
try:
es_client.ilm.put_lifecycle(name=policy_name, **self.policy)
print(f"✅ ILM 策略 '{policy_name}' 已创建")
except TransportError as e:
print(f"❌ 创建失败: {e}")
2. 命令行接口 cli.py
#!/usr/bin/env python3
import argparse
from generator import ILMGenerator
def main():
parser = argparse.ArgumentParser(description="Elasticsearch ILM 策略生成器")
parser.add_argument("--name", required=True, help="策略名称")
parser.add_argument("--output", help="输出文件路径")
parser.add_argument("--apply", action="store_true", help="直接应用到 ES")
# Hot 阶段
parser.add_argument("--hot-max-size", help="Hot: max_size (e.g., 50gb)")
parser.add_argument("--hot-max-age", help="Hot: max_age (e.g., 1d)")
# Warm 阶段
parser.add_argument("--warm-min-age", help="Warm: min_age (e.g., 1d)")
parser.add_argument("--no-forcemerge", action="store_true", help="禁用 forcemerge")
parser.add_argument("--shrink", type=int, help="shrink 分片数")
parser.add_argument("--migrate-to", help="迁移到指定 zone")
# Cold 阶段
parser.add_argument("--cold-min-age", help="Cold: min_age (e.g., 7d)")
parser.add_argument("--cold-migrate-to", help="Cold 节点 zone")
# Delete 阶段
parser.add_argument("--delete-min-age", help="Delete: min_age (e.g., 30d)")
args = parser.parse_args()
# 构建策略
gen = ILMGenerator()
if args.hot_max_size or args.hot_max_age:
gen.add_hot_phase(args.hot_max_size, args.hot_max_age)
if args.warm_min_age:
gen.add_warm_phase(
min_age=args.warm_min_age,
forcemerge=not args.no_forcemerge,
shrink=args.shrink,
migrate_to=args.migrate_to
)
if args.cold_min_age:
gen.add_cold_phase(args.cold_min_age, args.cold_migrate_to)
if args.delete_min_age:
gen.add_delete_phase(args.delete_min_age)
# 输出
policy = gen.build()
print("📊 生成的 ILM 策略:")
print(gen.to_json())
if args.output:
gen.save(args.output)
if args.apply:
from elasticsearch import Elasticsearch
es = Elasticsearch(["http://localhost:9200"])
gen.apply(es, args.name)
if __name__ == "__main__":
main()
3. 使用示例
生成日志策略
python cli.py \
--name logs-policy \
--hot-max-size 50gb \
--hot-max-age 1d \
--warm-min-age 1d \
--shrink 1 \
--migrate-to warm_nodes \
--cold-min-age 7d \
--cold-migrate-to cold_nodes \
--delete-min-age 30d \
--output logs-policy.json \
--apply
生成指标策略
python cli.py \
--name metrics-policy \
--hot-max-size 100gb \
--hot-max-age 7d \
--warm-min-age 7d \
--forcemerge \
--delete-min-age 90d \
--output metrics-policy.json
五、预设模板 templates/logs.json
{
"name": "logs-policy",
"hot": {
"max_size": "50gb",
"max_age": "1d"
},
"warm": {
"min_age": "1d",
"forcemerge": true,
"shrink": 1,
"migrate_to": "warm_nodes"
},
"cold": {
"min_age": "7d",
"migrate_to": "cold_nodes"
},
"delete": {
"min_age": "30d"
}
}
加载模板:
def load_template(name):
with open(f"templates/{name}.json", "r") as f:
return json.load(f)
六、Web 版本(可选)
使用 Flask 提供 Web 界面:
from flask import Flask, request, jsonify, render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('form.html') # HTML 表单
@app.route('/generate', methods=['POST'])
def generate():
data = request.json
gen = ILMGenerator()
# 根据 data 构建策略
return jsonify(gen.build())
可集成到 Kibana 或运维平台。
七、最佳实践 ✅
| 项目 | 建议 |
|---|---|
| 策略命名 | 业务-场景-policy(如 logs-policy) |
| min_age 顺序 | hot < warm < cold < delete |
| shrink 条件 | 源索引主分片数 > 1 |
| forcemerge | 仅用于只读索引 |
| 迁移属性 | 配合 node.attr.zone 使用 |
| 定期审查 | 检查策略是否仍适用 |
1774

被折叠的 条评论
为什么被折叠?



