Elasticsearch ILM生命周期管理

Implementing a Hot-Warm-Cold Architecture with Index Lifecycle Management
https://www.elastic.co/blog/implementing-hot-warm-cold-in-elasticsearch-with-index-lifecycle-management

ILM lifecycle phases:
https://www.elastic.co/docs/manage-data/lifecycle/index-lifecycle-management/index-lifecycle

ILM is a feature that was first introduced in Elasticsearch 6.6 (beta) and made generally available in 6.7. ILM is part of Elasticsearch and is designed to help you manage your indexes.

image

在ES早期版本中,用户一般通过手动或外部的自动化脚本/工具(Curator等)管理index的生命周期,如每日新建索引并通过alias重定向,旧索引自行删除等手段。
在ES 6.7版本中引入了ILM policy进行自动化的index lifetime management,并在7.10+版本中逐步成熟。
本文介绍一下如何并应用ILM policy实现索引数据的自动过期和删除。

1. 设置es node属性

https://www.elastic.co/docs/reference/elasticsearch/configuration-reference/node-settings

# 7.9 之前你可以自定义node的标签,但是在ILM策略中你必须通过allocate主动设置标签识别实现数据冷热分离
# 6.7-7.9版本期间es配置关于node.roles/node role相关配置改动比较频繁,演进比较快速,建议保持使用node.attr.box_type
node.attr.box_type: hot
node.attr.box_type: warm
node.attr.box_type: cold
# 这些值本身没有强制语义,只是打了个标签,ES 内部并不会自动识别,需要在ILM policy中手动设置action
# 7.9 开始推荐 `node.roles`, 取代了之前通过node.attr.box_type自定义的标签,这些是内置角色,系统直接识别,影响内部调度和 API,但是与node.attr.box_type没有本质区别,配置难度也相似
# vi elasticsearch.yml
node.roles: [ master, data_hot ]

关于node.roles设置:

#### 核心角色
- `master` → 参与集群管理(选主、元数据)。
- `data` → 通用数据节点(7.9 后被细分,不推荐单独用)。
- `data_content` → 一般用于存放静态数据集,不参与冷热分离的那些数据
集群至少要有master、data_content、data_hot三种角色(如果用了data角色,那么至少要有master、data两种角色)。
#### 数据子角色(冷热分离相关)
- `data_hot` → 热节点,处理写入和实时查询。
- `data_warm` → 温节点,存储历史索引。
- `data_cold` → 冷节点,存储长期不活跃数据。
- `data_frozen` → 冻结节点,用于搜索快照(S3 等对象存储)。
#### 其他角色
- `ingest` → 数据预处理(ingest pipelines)。
- `ml` → 机器学习节点。
- `remote_cluster_client` → 跨集群查询。
- `transform` → 批处理和持续数据转换。
- `voting_only` → 只参与投票的 master 节点。

如果node.roles未出现在配置文件中,那么节点默认拥有所有角色,如果node.roles被设置为空列表:node.roles: [],那么此节点被认为是Coordinating only node,将其理解为仅接收客户端请求的节点即可,小型集群没有必要设置此类节点。
所有节点默认都具有coordinate功能,即默认都可以接收客户端请求。

2. 设置ILM policy(通过kibana也能设置ILM)

https://www.elastic.co/docs/manage-data/lifecycle/index-lifecycle-management/configure-lifecycle-policy

PUT /_ilm/policy/my_policy
{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": { "max_age": "7d", "max_size": "50gb" }  # 注意不同版本支持的rollover options有演进,参考本文第二个链接中的Hot phase部分rollover链接中的Options即可
        }
      },
      "warm": {
        "actions": {
          "allocate": { "include": { "box_type": "warm" } },  # 7.10+改为"allocate": {"require":{"data":"warm"}}即可,下述同理
          "forcemerge": { "max_num_segments": 1 }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "allocate": { "include": { "box_type": "cold" } },
          "searchable_snapshot": { "snapshot_repository": "my_repo" }
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": { "delete": {} }
      }
    }
  }
}
# rollover的含义为:当index创建超过30天或者达到50GB后,写入切换到一个新的索引
# warm未设置min_age/max_age,那么hot数据rollover后立即进入warm状态
# cold设置了min_age=30d,那么第37天开始warm数据变为cold,同理第127天后数据删除
# 在7.10+之前我们可以为cold标签设置searchable_snapshot实现冷数据的低成本搜索,7.10+之后可以直接定义data_frozen角色
# 挂载一个本地盘用于searchable_snapshot,也就是说cold节点本身的data目录只存很少的热点数据,没有的从my_repo拉取,因此cold节点本地磁盘可以很小,大部分数据依赖挂载的nfs等远程存储即可
PUT _snapshot/my_frozen_repo
{
  "type": "fs",
  "settings": {
    "location": "/mnt/my_frozen_repo", 
    "compress": true
  }
}
# 生产上没必要设置很多phase(层),根据需求来即可,一般只需要hot/warm/cold三层,给cold设置searchable_snapshot即可
# 不同的phase支持的action列表不一样,例如searchable_snapshot只支持在cold/frozen层设置,rollover只支持在hot层设置,具体差异参考本文第二个链接中的Phase actions部分即可

3. 应用ILM策略

ILM策略建好了,我们需要把他应用到索引或索引模板才能生效,一般来说我们肯定是应用到索引模板的,因为单个索引应用ILM策略无意义,除非每次创建索引你都绑定一次ILM,这很麻烦不如使用模板。
https://www.elastic.co/docs/manage-data/lifecycle/index-lifecycle-management/policy-apply

需要特别注意的是:包含rollover动作的ILM策略必须应用于索引模板,不能直接应用于索引,且索引模板必须包含index alias配置, 原因如下:

  1. rollover会生成符合index_patterns的新索引名,因此对于使用了rollover的ILM策略,必须应用于index template而非index本身
  2. rollover是基于index alias实现的,当current索引过期后,新索引创建时index alias会自动将别名指向新index,客户端因此只通过别名即可读写

应用ILM到模板的方式很简单,只要新增模板时设置好ILM策略即可(index.lifecycle.name)。

PUT _index_template/my_template
{
  "index_patterns": ["test-*"],
  "template": {
    "settings": {
      "number_of_shards": 1,   # 生产上如果单个索引数据量超过了1GB,那么就可以开始考虑设置number_of_shards>1啦,确保单个分片数据量在<30GB为宜
      "number_of_replicas": 1,
      "index.lifecycle.name": "my_policy",
      "index.lifecycle.rollover_alias": "test-alias"
    }
  }
}

在7.10+的新版本中当你向首个符合pattern(必须是数字后缀)的index写入数据时,会自动为其设置alias和"is_write_index": true
但是在老版本中,你还必须手动创建第一个index,指定其alias和ILM策略,以后ILM才会自动递增创建新索引并重定向别名至新索引:

PUT test-000001
{
  "settings": {
    "index.lifecycle.name": "my_policy",
    "index.lifecycle.rollover_alias": "test-alias"
  },
  "aliases": {
    "test-alias": {
      "is_write_index": true
    }
  }
}

上边讲述的其实是一种理想场景,即客户端只需要关注alias即可,至于alias指向的具体索引则完全交由ES自动管理,所有索引本身则依赖ILM自动进行过期清理。
但实际使用中一种很常见的场景是,客户端自己通过时间滚动策略直接写入yyyy/mm/dd格式的索引,那么事情就简单了,我们只需要应用ILM策略到模板即可,此时有两种设置方式:

  1. PUT _index_template/my_template绑定ILM时不指定rollover_alias,此时ILM策略中也不能包含rollover关键字
  2. PUT _index_template/my_template时依然指定rollover_alias,同时ILM策略中也必须包含rollover关键字,但是这个rollover操作因为索引名不符合自动管理的要求,因此总是失败,导致数据无法转阶段。

两种设置方式都不会报错,但只有第一种设置方式才是有效的,说白了就是ILM策略中的rollover action与template中的rollover_alias必须同步,要么都没有要么都有,而且当都有时必须是自动管理的index名称而不能是自定义的index name,否则即便你的template能绑定ilm策略成功,rollover也会失败导致你的数据无法过期(通过<index_name>/_ilm/explain查看ilm策略未生效的原因)。

在7.11+的版本中,你可以通过<索引名>/_settings接口得到此索引遵循的ILM策略,然后通过_ilm/policy/<ilm策略名>查看ILM策略内容以及应用了此策略的所有模板(composable_templates字段),之后可以通过_index_template/<模板名>?pretty查看此模板的具体配置,其中有个composed_of字段需注意,此配置表示当前模板是由多个组件模板构成的,是ES新推出的组件模板,即复用一些提前建好的组件模板构成新模板(组件模板本身不能用于索引绑定),可以通过_component_template/<组件模板名>查看具体配置。

补充

一个关键词较为齐全的ILM policy:

PUT _ilm/policy/hot-warm-cold-delete-60days {
    "policy": {
        "phases": {
            "hot": {
                "actions": {
                    "rollover": {
                        "max_size": "50gb",  # 注意不同版本支持的rollover options有演进,参考本文第二个链接中的Hot phase部分rollover链接中的Options即可
                        "max_age": "30d"
                    },
                    "set_priority": {
                        "priority": 50  # priority越大的policy,ILM处理的优先级越高
                    }
                }
            },
            "warm": {
                "min_age": "7d",
                "actions": {
                    "forcemerge": {
                        "max_num_segments": 1   # 合并底层段,可以不设置或设置的高一些,根据实际负载抉择
                    },
                    "shrink": {
                        "number_of_shards": 1   # 一般不建议设置,因为会将数据集中到某个节点;其设计目的是考虑到warm阶段的数据几乎无写入需求,读取需求也相对较低,所以分片没有太大意义,但是会造成数据集中,需权衡
                    },
                    "allocate": {
                        "require": {
                            "data": "warm"
                        }
                    },
                    "set_priority": {
                        "priority": 25
                    }
                }
            },
            "cold": {
                "min_age": "30d",
                "actions": {
                    "set_priority": {
                        "priority": 0
                    },
                    "freeze": {},
                    "allocate": {
                        "require": {
                            "data": "cold"
                        }
                    }
                }
            },
            "delete": {
                "min_age": "60d",
                "actions": {
                    "delete": {}
                }
            }
        }
    }
}

最后,推荐使用kibana进行索引、索引模板、ILM策略的管理,目前kibana这方面的UI操作已经很完善了。

posted @ 2025-09-10 12:06  realcp1018  阅读(56)  评论(0)    收藏  举报