-
Notifications
You must be signed in to change notification settings - Fork 3
05 ‐ Procedimentos ILM
O ILM (Index Lifecycle Management) é o mecanismo nativo do Elasticsearch para automatizar o gerenciamento do ciclo de vida dos índices. Em vez de gerenciar manualmente quando um índice deve ser movido para um hardware mais barato ou quando deve ser deletado, o ILM faz isso de forma automática com base em regras chamadas policies.
O ILM é especialmente útil em cenários de dados temporais — logs, métricas, eventos de segurança — onde os dados perdem valor com o tempo e precisam ser retidos por um período determinado antes de serem descartados.
Antes do ILM existir, o problema de gerenciar o ciclo de vida de índices era resolvido de três formas:
- Scripts cron manuais: alguém escrevia um script shell que rodava toda madrugada, verificava o tamanho ou a idade dos índices e executava operações via API. Frágil, difícil de auditar, sem visibilidade.
- Curator: uma ferramenta open source mantida pela própria Elastic que funcionava como um agendador de tarefas para índices. Melhor que scripts manuais, mas ainda era uma ferramenta externa que precisava ser instalada, configurada e monitorada separadamente.
- Rollover manual: equipes criavam novos índices manualmente toda semana ou todo mês, atualizavam aliases e deletavam os antigos. Com volume alto, isso virava um processo operacional relevante.
O ILM foi introduzido na versão 6.6 como feature X-Pack paga, e passou a ser gratuito na versão 7.x. A motivação era clara: a Elastic observava que uma grande parte dos clusters em produção tinha problemas de gestão de disco causados justamente pela falta de automação. O ILM resolveu isso de forma nativa, com visibilidade pelo Kibana e sem dependência de ferramentas externas.
Sem o ILM, é comum que clusters acumulem índices antigos indefinidamente, consumindo disco e degradando o desempenho. Gerenciar isso manualmente é trabalhoso e propenso a erros. O ILM resolve isso com automação baseada em políticas declarativas.
Uma ILM policy é composta por fases, cada uma com suas próprias condições de entrada e ações a executar. As fases disponíveis são:
Fonte: Madhusudhan Konda — Elasticsearch in Action, 2nd Edition
| Fase | Quando ocorre | Ações típicas |
|---|---|---|
| Hot | Fase inicial — o índice está sendo ativamente escrito e lido | Rollover por tamanho, idade ou número de documentos |
| Warm | O índice não recebe mais escritas, mas ainda é consultado com frequência | Force merge, shrink, readonly, realocação para nodes warm |
| Cold | Dados raramente consultados | Realocação para nodes cold, searchable snapshots |
| Frozen | Dados muito raramente acessados | Searchable snapshots em object storage (S3, GCS) |
| Delete | Dados que não precisam mais ser retidos | Deleção do índice |
Não é obrigatório usar todas as fases — uma policy pode ter apenas hot e delete, por exemplo.
Cada fase suporta um conjunto específico de actions. Conhecer o que está disponível em cada fase é essencial para desenhar uma policy adequada ao seu caso de uso.
Fase Hot:
| Action | O que faz |
|---|---|
rollover |
Cria um novo índice quando o atual atinge o critério configurado (tamanho, idade ou número de documentos). É a action central da fase hot. |
set_priority |
Define a prioridade de recuperação do índice após uma falha de node. Índices com prioridade maior são recuperados primeiro. |
readonly |
Bloqueia novas escritas no índice — útil quando o rollover já aconteceu mas você quer garantir imutabilidade. |
downsample |
Reduz a granularidade de dados de série temporal, agregando múltiplos pontos em um único ponto. Útil para métricas — mantém o dado mas ocupa muito menos espaço. |
Fase Warm:
| Action | O que faz |
|---|---|
forcemerge |
Compacta todos os segments do índice em um único segment. Melhora a performance de leitura e libera o espaço de documentos deletados. |
shrink |
Reduz o número de primary shards do índice. Exige que o índice esteja em read-only e que todos os shards estejam no mesmo node. |
readonly |
Bloqueia novas escritas. |
set_priority |
Define prioridade de recuperação — geralmente menor que na fase hot. |
allocate |
Controla em quais nodes o índice pode ser alocado e permite reduzir o número de réplicas. |
migrate |
Move o índice para o tier warm automaticamente, atualizando o _tier_preference. Injetada automaticamente pelo ILM quando o cluster tem nodes warm. |
Fase Cold:
| Action | O que faz |
|---|---|
allocate |
Reduz réplicas para zero ou move para nodes cold. |
migrate |
Move para o tier cold automaticamente. |
readonly |
Bloqueia escritas. |
searchable_snapshot |
Monta o índice como um searchable snapshot — os dados são movidos para um repositório de snapshots e acessados sob demanda sem ocupar disco local. |
Fase Delete:
| Action | O que faz |
|---|---|
delete |
Deleta o índice. |
wait_for_snapshot |
Aguarda que um SLM (Snapshot Lifecycle Management) tenha executado um snapshot do índice antes de deletar. Garante que os dados foram preservados antes da remoção. |
O rollover é a action mais importante da fase hot. Ele cria um novo índice quando o índice atual atinge uma condição configurada:
-
max_size: tamanho total dos shards primários -
max_age: tempo desde a criação do índice -
max_docs: número de documentos -
max_primary_shard_size: tamanho do maior shard primário individualmente -
max_primary_shard_docs: número de documentos no maior shard primário
Fonte: Opster — ILM Hot-Warm-Cold Architecture
Quando o rollover acontece, o alias de escrita é movido automaticamente para o novo índice, sem nenhuma interrupção para quem está indexando.
Detalhe importante: o rollover só acontece se o ILM verificar as condições e o índice já tiver pelo menos um documento. Um índice vazio nunca sofre rollover, mesmo que o
max_agetenha sido atingido. Isso evita a criação de índices vazios em série.
O fluxo completo de uma implementação com ILM tem três peças:
ILM Policy → Index Template → Índice (gerenciado automaticamente)
- ILM Policy: define as regras de ciclo de vida
- Index Template: associa a policy a todos os índices que correspondem a um padrão de nome
- Índice inicial: criado com um alias de escrita — a partir daí, o ILM cuida do resto
Neste laboratório vamos configurar um ciclo de vida completo para um conjunto de dados de vendas. O índice será gerenciado automaticamente pelo ILM, com rollover na fase hot e deleção após um período configurado.
Todos os comandos devem ser executados no Kibana Dev Tools.
Nota sobre os timers do lab: os tempos configurados neste lab (segundos e minutos) são propositalmente curtos para que as transições entre fases sejam observáveis durante a aula. Em produção, os valores típicos são dias ou semanas.
Por padrão, o ILM verifica as condições das policies a cada 10 minutos. Para o lab, vamos reduzir para 10 segundos:
PUT /_cluster/settings
{
"persistent": {
"indices.lifecycle.poll_interval": "10s"
}
}Lembre-se de restaurar o valor padrão ao final do lab: defina
indices.lifecycle.poll_intervalcomonull.
PUT /_ilm/policy/policy-vendas
{
"policy": {
"phases": {
"hot": {
"min_age": "0ms",
"actions": {
"rollover": {
"max_age": "1m",
"max_docs": 5
},
"set_priority": {
"priority": 100
}
}
},
"warm": {
"min_age": "2m",
"actions": {
"forcemerge": {
"max_num_segments": 1
},
"set_priority": {
"priority": 50
}
}
},
"delete": {
"min_age": "5m",
"actions": {
"delete": {}
}
}
}
}
}Explicação das condições:
- Hot → Rollover: acontece quando o índice tiver mais de 1 minuto ou mais de 5 documentos — o que ocorrer primeiro
-
Warm: o índice entra nessa fase 2 minutos após o rollover. O
forcemergecompacta todos os segments em um único, otimizando leitura - Delete: o índice é deletado 5 minutos após entrar na fase de deleção
O template associa a ILM policy e o alias de rollover a todos os índices com o padrão vendas-*:
PUT /_index_template/sales
{
"index_patterns": ["vendas-*"],
"template": {
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1,
"index.lifecycle.name": "policy-vendas",
"index.lifecycle.rollover_alias": "vendas"
},
"mappings": {
"properties": {
"@timestamp": { "type": "date" },
"Category": { "type": "keyword" },
"City": { "type": "keyword" },
"Country": { "type": "keyword" },
"CustomerName": { "type": "keyword" },
"DaystoShipActual": { "type": "long" },
"DaystoShipScheduled": { "type": "long" },
"Discount": { "type": "double" },
"OrderDate": { "type": "date", "format": "iso8601" },
"OrderID": { "type": "keyword" },
"OrderProfitable": { "type": "keyword" },
"PostalCode": { "type": "long" },
"ProductName": { "type": "text" },
"Profit": { "type": "long" },
"ProfitRatio": { "type": "double" },
"Quantity": { "type": "long" },
"Region": { "type": "keyword" },
"Sales": { "type": "long" },
"SalesForecast": { "type": "long" },
"SalesaboveTarget": { "type": "keyword" },
"SalesperCustomer": { "type": "double" },
"Segment": { "type": "keyword" },
"ShipDate": { "type": "date", "format": "iso8601" },
"ShipMode": { "type": "keyword" },
"ShipStatus": { "type": "keyword" },
"State": { "type": "keyword" },
"Sub_Category": { "type": "keyword" },
"latitude": { "type": "double" },
"location": { "type": "geo_point" },
"longitude": { "type": "double" }
}
}
}
}O primeiro índice precisa ser criado manualmente com o alias configurado como is_write_index: true. Esse alias é o que o ILM usa para saber para onde direcionar as novas escritas após cada rollover.
PUT /vendas-000001
{
"aliases": {
"vendas": {
"is_write_index": true
}
}
}A convenção de nomear o índice inicial com sufixo numérico (
-000001) é exigida pelo mecanismo de rollover — o Elasticsearch incrementa esse número a cada rollover (-000002,-000003, etc.).
GET /vendas-000001/_ilm/explainO campo phase deve indicar hot e o campo action deve mostrar rollover. O campo age mostra quanto tempo o índice tem desde sua criação.
Insira alguns documentos via alias. Note que usamos o alias vendas como destino — não o nome do índice diretamente. Isso garante que as escritas sempre vão para o índice ativo de escrita, independentemente de quantos rollovers já aconteceram.
POST /vendas/_doc
{
"Category": "Office Supplies",
"Discount": 0,
"ProductName": "Xerox 214",
"latitude": 41.6347,
"PostalCode": 2740,
"ShipStatus": "Shipped Early",
"OrderID": "CA-2014-148355",
"Segment": "Corporate",
"SalesaboveTarget": "No",
"Sales": 19,
"SalesperCustomer": 19.44,
"Profit": 9,
"ShipMode": "Standard Class",
"CustomerName": "Nick Crebassa",
"DaystoShipScheduled": 6,
"longitude": -70.9372,
"Sub_Category": "Paper",
"ProfitRatio": 48,
"Quantity": 3,
"City": "New Bedford",
"ShipDate": "2014-12-29T00:00:00.000Z",
"SalesForecast": 25,
"OrderDate": "2014-12-25T00:00:00.000Z",
"OrderProfitable": "No",
"@timestamp": "2014-12-25T00:00:00.000Z",
"State": "Massachusetts",
"Country": "United States",
"Region": "East",
"location": "41.6347,-70.9372",
"DaystoShipActual": 4
}Insira mais 4 documentos para atingir a condição de rollover por max_docs: 5. Pode repetir o mesmo documento alterando o OrderID para simular registros diferentes.
Após inserir 5 documentos ou aguardar 1 minuto (o que ocorrer primeiro), o ILM deve executar o rollover automaticamente. Verifique:
GET /_cat/indices/vendas-*?v&s=indexApós o rollover, você verá dois índices: vendas-000001 (que saiu da fase hot) e vendas-000002 (novo índice ativo de escrita).
Verifique o alias:
GET /_cat/aliases/vendas?vO is_write_index deve ter migrado para vendas-000002.
Acompanhe o estado do ILM em cada índice:
GET /vendas-*/_ilm/explainAguarde os tempos configurados e execute o comando abaixo repetidamente para observar as transições:
GET /vendas-000001/_ilm/explainOs campos a observar:
| Campo | O que indica |
|---|---|
phase |
Fase atual do índice (hot, warm, delete) |
action |
Ação sendo executada na fase atual |
step |
Etapa dentro da ação |
age |
Tempo de vida do índice |
phase_time |
Quando o índice entrou na fase atual |
Se quiser acelerar a transição para testes sem esperar o timer, use:
POST /_ilm/move/vendas-000001
{
"current_step": {
"phase": "hot",
"action": "complete",
"name": "complete"
},
"next_step": {
"phase": "warm",
"action": "forcemerge",
"name": "forcemerge"
}
}Use com cautela — forçar transições em produção pode ter efeitos colaterais dependendo das ações configuradas na policy.
Restaurar o intervalo de verificação do ILM para o valor padrão:
PUT /_cluster/settings
{
"persistent": {
"indices.lifecycle.poll_interval": null
}
}Por padrão, o Elasticsearch bloqueia deleções usando wildcard (*) para evitar remoção acidental de dados. Antes de deletar os índices do lab, é necessário desabilitar essa proteção temporariamente:
PUT /_cluster/settings
{
"persistent": {
"action.destructive_requires_name": false
}
}Remover os índices do lab:
DELETE /vendas-*Após a limpeza, restaure a proteção:
PUT /_cluster/settings
{
"persistent": {
"action.destructive_requires_name": null
}
}Remover o template:
DELETE /_index_template/salesRemover a policy:
DELETE /_ilm/policy/policy-vendasNeste laboratório vamos criar uma policy mais próxima de produção, com três fases ativas, shrink na fase warm e redução de réplicas na fase cold. O objetivo é ver o comportamento completo do ciclo de vida antes da deleção.
Pré-requisito: os nodes do cluster precisam ter os roles de tier configurados (data_hot, data_warm, data_cold) conforme o Lab 4.4. Se o cluster estiver com a configuração padrão (todos os roles em todos os nodes), as actions de
migratedo ILM ainda funcionam — o Elasticsearch simplesmente não vai mover os shards entre nodes diferentes porque todos têm todos os roles. O comportamento do ciclo de vida é o mesmo.
PUT /_ilm/policy/policy-logs-completa
{
"policy": {
"phases": {
"hot": {
"min_age": "0ms",
"actions": {
"rollover": {
"max_age": "2m",
"max_docs": 10,
"max_primary_shard_size": "50gb"
},
"set_priority": { "priority": 100 }
}
},
"warm": {
"min_age": "3m",
"actions": {
"set_priority": { "priority": 50 },
"readonly": {},
"forcemerge": { "max_num_segments": 1 },
"allocate": { "number_of_replicas": 1 }
}
},
"cold": {
"min_age": "6m",
"actions": {
"set_priority": { "priority": 0 },
"allocate": { "number_of_replicas": 0 }
}
},
"delete": {
"min_age": "10m",
"actions": {
"delete": {}
}
}
}
}
}Explicação das ações adicionais em relação ao Lab 1:
-
readonlyna fase warm: após o rollover, o índice nunca mais vai receber escritas — torná-lo readonly explicita isso e protege contra escritas acidentais -
forcemergecommax_num_segments: 1: compacta todos os segments em um único, o que melhora a performance de leitura e elimina o overhead de múltiplos segments pequenos -
allocate: number_of_replicas: 1no warm: mantém a réplica ainda disponível para leitura paralela -
allocate: number_of_replicas: 0no cold: remove a réplica para economizar disco — os dados raramente são consultados nessa fase, então não faz sentido manter a cópia
PUT /_index_template/template-logs-completa
{
"index_patterns": ["logs-completa-*"],
"template": {
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1,
"index.lifecycle.name": "policy-logs-completa",
"index.lifecycle.rollover_alias": "logs-completa"
},
"mappings": {
"properties": {
"@timestamp": { "type": "date" },
"mensagem": { "type": "text" },
"nivel": { "type": "keyword" },
"servico": { "type": "keyword" }
}
}
}
}PUT /logs-completa-000001
{
"aliases": {
"logs-completa": {
"is_write_index": true
}
}
}POST /_bulk
{ "index": { "_index": "logs-completa" } }
{ "@timestamp": "2026-05-01T10:00:00Z", "mensagem": "Serviço iniciado", "nivel": "INFO", "servico": "api-gateway" }
{ "index": { "_index": "logs-completa" } }
{ "@timestamp": "2026-05-01T10:01:00Z", "mensagem": "Conexão estabelecida", "nivel": "INFO", "servico": "auth-service" }
{ "index": { "_index": "logs-completa" } }
{ "@timestamp": "2026-05-01T10:02:00Z", "mensagem": "Timeout na chamada ao banco", "nivel": "ERROR", "servico": "api-gateway" }
{ "index": { "_index": "logs-completa" } }
{ "@timestamp": "2026-05-01T10:03:00Z", "mensagem": "Retry bem-sucedido", "nivel": "WARN", "servico": "api-gateway" }
{ "index": { "_index": "logs-completa" } }
{ "@timestamp": "2026-05-01T10:04:00Z", "mensagem": "Cache invalidado", "nivel": "DEBUG", "servico": "cache-service" }Acompanhe as transições com:
GET /logs-completa-*/_ilm/explainObserve como o índice transita pelo hot → warm → cold → delete ao longo dos minutos configurados. Em cada fase, verifique também o número de réplicas:
GET /_cat/indices/logs-completa-*?v&h=index,pri,rep,status,healthPUT /_cluster/settings
{
"persistent": {
"action.destructive_requires_name": false
}
}DELETE /logs-completa-*
DELETE /_index_template/template-logs-completa
DELETE /_ilm/policy/policy-logs-completaPUT /_cluster/settings
{
"persistent": {
"action.destructive_requires_name": null
}
}Um dos cenários mais comuns em produção é o ILM travar em um step com erro. Vamos simular essa situação e ver como diagnosticar e resolver.
Os erros mais frequentes de ILM em produção são:
| Situação | Causa | Solução |
|---|---|---|
| Rollover não acontece | Índice não tem o alias configurado como is_write_index
|
Recriar o alias corretamente |
| Rollover não acontece | Index template não tem o rollover_alias configurado |
Corrigir o template e recriar o índice |
forcemerge trava |
Node com disco insuficiente | Liberar espaço ou mover o índice para outro node |
shrink falha |
Índice não está em read-only | Adicionar a action readonly antes do shrink na policy |
allocate falha |
Não há nodes com o tier configurado | Configurar os roles nos nodes conforme a arquitetura Hot-Warm-Cold |
| Fase não avança |
min_age não foi atingido |
Aguardar ou usar _ilm/move para forçar a transição |
Vamos criar um índice sem o alias correto e observar o que o ILM reporta:
PUT /logs-erro-000001PUT /_index_template/template-logs-erro
{
"index_patterns": ["logs-erro-*"],
"template": {
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1,
"index.lifecycle.name": "policy-logs-completa",
"index.lifecycle.rollover_alias": "logs-erro"
}
}
}O índice foi criado, o template está configurado, mas o alias logs-erro com is_write_index: true não existe. Insira um documento e aguarde o ILM tentar o rollover:
POST /logs-erro-000001/_doc
{
"@timestamp": "2026-05-01T10:00:00Z",
"mensagem": "Teste de erro ILM"
}Após alguns segundos, consulte o estado do ILM:
GET /logs-erro-000001/_ilm/explainVocê verá o campo step_info com um erro descrevendo que o alias não está configurado. Para ver só índices com erro em todo o cluster:
GET /*/_ilm/explain?only_errors=trueAdicione o alias correto ao índice:
POST /_aliases
{
"actions": [
{
"add": {
"index": "logs-erro-000001",
"alias": "logs-erro",
"is_write_index": true
}
}
]
}Agora instrua o ILM a tentar novamente:
POST /logs-erro-000001/_ilm/retryVerifique que o erro foi resolvido:
GET /logs-erro-000001/_ilm/explainPUT /_cluster/settings
{ "persistent": { "action.destructive_requires_name": false } }DELETE /logs-erro-*
DELETE /_index_template/template-logs-erroPUT /_cluster/settings
{ "persistent": { "action.destructive_requires_name": null } }Além da API, é possível criar e gerenciar ILM policies diretamente pelo Kibana, sem escrever JSON. O caminho é:
Menu → Stack Management → Index Lifecycle Policies
A interface permite:
- Criar e editar policies com um formulário visual
- Visualizar todos os índices gerenciados por cada policy
- Ver o estado atual de cada índice (fase, ação, possíveis erros)
- Retentar steps com erro sem precisar usar a API
Para associar uma policy a um index template existente, vá em:
Menu → Stack Management → Index Management → Index Templates
Selecione o template desejado e edite a seção Index settings adicionando:
{
"index.lifecycle.name": "nome-da-policy",
"index.lifecycle.rollover_alias": "nome-do-alias"
}Nomeie policies de forma descritiva: policy-logs-nginx-30d é mais claro do que policy1. O nome deve comunicar o tipo de dado e o período de retenção.
Não reutilize a mesma policy para dados com requisitos diferentes: logs de aplicação e eventos de segurança podem ter retenções e tiers diferentes. Crie uma policy para cada caso de uso.
Sempre teste com timers curtos antes de ir para produção: configure max_age em minutos e min_age em segundos para validar o comportamento completo do ciclo de vida antes de ajustar para dias e semanas.
Use wait_for_snapshot na fase delete: em ambientes onde compliance é importante, nunca delete sem garantir que o snapshot foi executado. A action wait_for_snapshot integra o ILM com o SLM (Snapshot Lifecycle Management) e só deleta após confirmar que o backup existe.
Monitore índices com erro regularmente:
GET /*/_ilm/explain?only_errors=trueEm produção, esse endpoint deve fazer parte do monitoramento da stack — um índice travado no ILM significa que dados estão acumulando em disco sem passar pelo ciclo esperado.
Cuidado com shrink em produção: o shrink requer que todos os shards do índice estejam no mesmo node antes de executar. Em clusters grandes, isso pode causar movimentação significativa de dados. Avalie se o benefício de reduzir o número de shards justifica o custo operacional.
Listar todas as policies:
GET /_ilm/policyVer o estado do ILM em todos os índices de um padrão:
GET /vendas-*/_ilm/explainVer apenas índices com erro no ILM:
GET /vendas-*/_ilm/explain?only_errors=trueRetentar um step com erro:
POST /vendas-000001/_ilm/retryVer o status geral do ILM no cluster:
GET /_ilm/statusPausar o ILM globalmente (útil durante manutenções):
POST /_ilm/stopRetomar o ILM:
POST /_ilm/startO Curator é uma ferramenta open source mantida pela Elastic para gerenciar o ciclo de vida de índices via linha de comando. Antes do ILM existir (versão 6.6), o Curator era a principal forma de automatizar tarefas como deleção de índices antigos, force merge e snapshot.
Hoje, com o ILM disponível nativamente e gratuito desde a versão 7.x, o Curator perdeu relevância em novas implementações. No entanto, ainda aparece em ambientes legados que migraram de versões antigas do Elasticsearch sem rever a gestão de índices.
Quando você ainda pode encontrar o Curator:
- Clusters que foram instalados antes do ILM existir e nunca foram migrados
- Ambientes com restrições que impedem o uso de features X-Pack (muito raros hoje)
- Scripts de automação externos que gerenciam índices de forma imperativa
Para novas implementações, use o ILM. Ele é mais integrado, mais simples de configurar e não requer ferramentas externas.
Referência: github.com/elastic/curator