05 ‐ Procedimentos ILM - BktechBrazil/elastic-eck GitHub Wiki

5.1 - O que é o 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.

Curiosidade: como o ILM surgiu

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.

Por que usar ILM?

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.

Fases do ciclo de vida

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:

ILM Phases Diagram

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.

Actions disponíveis por fase

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 nas fases warm e cold, a menos que a policy defina a alocação por atributos com allocate.

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.

Licença: ILM, rollover, data streams e as fases hot, warm, cold e delete estão disponíveis na licença Basic (gratuita). A action searchable_snapshot, usada nas fases cold e frozen, exige licença Enterprise.

Rollover

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

ILM Rollover Flow

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_age tenha sido atingido. Isso evita a criação de índices vazios em série.

Como o ILM se encaixa na arquitetura

O fluxo completo de uma implementação com ILM tem três peças:

ILM Policy → Index Template → Índice (gerenciado automaticamente)
  1. ILM Policy: define as regras de ciclo de vida
  2. Index Template: associa a policy a todos os índices que correspondem a um padrão de nome
  3. Índice inicial: criado com um alias de escrita — a partir daí, o ILM cuida do resto

ILM no ECK: tiers como nodeSets

No ECK, cada tier de dados é um nodeSet com o papel correspondente (data_hot, data_warm, data_cold). Quando um índice entra na fase warm ou cold, a action migrate altera o _tier_preference do índice e o Elasticsearch move fisicamente os shards para os pods daquele tier. O operator não participa dessa movimentação: ele só mantém os pods de cada tier como o manifesto descreve.

Neste capítulo, o cluster lab-es tem um node em cada tier:

Pod (node) Tier Papéis
lab-es-es-default-0 Hot master, data_hot, data_content, ingest, ml, remote_cluster_client, transform
lab-es-es-node2-0 Warm data_warm
lab-es-es-node3-0 Cold data_cold

Como cada tier tem um único node, os índices dos laboratórios usam number_of_replicas: 0. Com réplica, a cópia não teria outro node do mesmo tier para ser alocada e o índice ficaria yellow. Em produção, com dois ou mais nodes por tier, mantenha pelo menos 1 réplica nas fases hot e warm.


5.2 - Data streams

Nos laboratórios do rollover clássico, você escreve em um alias e o ILM cria os índices -000001, -000002 e assim por diante. Hoje, para dados de série temporal (logs, métricas, eventos), o mecanismo recomendado é o data stream.

Um data stream expõe um nome único para escrita e leitura (por exemplo, lab-logs-app) e, por baixo, mantém uma série de índices de apoio (backing indices), com nomes como .ds-lab-logs-app-2026.09.23-000001, -000002... O rollover cria um novo índice de apoio e passa a escrever nele; os antigos continuam disponíveis para leitura até o ILM deletá-los.

Alias de rollover Data stream
Nome para escrita Alias com is_write_index: true O próprio nome do data stream
Índice inicial Criado à mão (-000001) com o alias Criado automaticamente no primeiro documento
Configuração no template index.lifecycle.rollover_alias "data_stream": {}
Campo @timestamp Opcional Obrigatório em todo documento
Atualizar ou apagar documentos Direto pelo alias Só com _update_by_query / _delete_by_query ou no índice de apoio

O index template é quem amarra tudo: padrão de nome + data_stream + policy de ILM + mapping.

Os coletores da Elastic (Elastic Agent, Beats, Logstash) gravam em data streams com nomes no formato logs-*-* e metrics-*-*. Por isso o Elasticsearch já traz index templates prontos para esses padrões, e os índices dos laboratórios usam outros nomes para não colidir com eles.


5.3 - Preparando o ambiente

Passo 1 — Conferir a arquitetura de tiers

No terminal da VM:

kubectl -n elastic get elasticsearch lab-es
kubectl -n elastic get pods | grep lab-es

Resultado esperado: NODES 3, HEALTH green e os pods lab-es-es-default-0, lab-es-es-node2-0 e lab-es-es-node3-0 com 1/1 Running.

Se o lab-es estiver com um único node, crie e aplique o manifesto com os tiers:

mkdir -p ~/eck-lab/manifests
cd ~/eck-lab

cat <<'EOF' > manifests/elasticsearch-tiers.yaml
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: lab-es
spec:
  version: 9.4.2
  nodeSets:
    - name: default
      count: 1
      config:
        node.roles: ["master", "data_hot", "data_content", "ingest", "ml", "remote_cluster_client", "transform"]
        node.store.allow_mmap: true
        node.attr.datacenter: datacenter1
      podTemplate:
        spec:
          containers:
            - name: elasticsearch
              env:
                - name: ES_JAVA_OPTS
                  value: -Xms2g -Xmx2g
              resources:
                requests:
                  memory: 4Gi
                  cpu: "1"
                limits:
                  memory: 4Gi
      volumeClaimTemplates:
        - metadata:
            name: elasticsearch-data
          spec:
            accessModes: [ReadWriteOnce]
            resources:
              requests:
                storage: 20Gi
            storageClassName: local-path
    - name: node2
      count: 1
      config:
        node.roles: ["data_warm"]
        node.store.allow_mmap: true
        node.attr.datacenter: datacenter1
      podTemplate:
        spec:
          containers:
            - name: elasticsearch
              env:
                - name: ES_JAVA_OPTS
                  value: -Xms1g -Xmx1g
              resources:
                requests:
                  memory: 2Gi
                  cpu: 500m
                limits:
                  memory: 2Gi
      volumeClaimTemplates:
        - metadata:
            name: elasticsearch-data
          spec:
            accessModes: [ReadWriteOnce]
            resources:
              requests:
                storage: 10Gi
            storageClassName: local-path
    - name: node3
      count: 1
      config:
        node.roles: ["data_cold"]
        node.store.allow_mmap: true
        node.attr.datacenter: datacenter2
      podTemplate:
        spec:
          containers:
            - name: elasticsearch
              env:
                - name: ES_JAVA_OPTS
                  value: -Xms1g -Xmx1g
              resources:
                requests:
                  memory: 2Gi
                  cpu: 500m
                limits:
                  memory: 2Gi
      volumeClaimTemplates:
        - metadata:
            name: elasticsearch-data
          spec:
            accessModes: [ReadWriteOnce]
            resources:
              requests:
                storage: 10Gi
            storageClassName: local-path
EOF

kubectl apply -n elastic -f manifests/elasticsearch-tiers.yaml
kubectl -n elastic get pods -w

Resultado esperado: os pods lab-es-es-node2-0 e lab-es-es-node3-0 são criados e o lab-es-es-default-0 é reiniciado. Aguarde os três ficarem 1/1 Running e pressione Ctrl+C. Durante o reinício do lab-es-es-default-0, o Kibana fica indisponível por um ou dois minutos.

Passo 2 — Acessar o Kibana

PASSWORD=$(kubectl -n elastic get secret lab-es-es-elastic-user \
  -o go-template='{{.data.elastic | base64decode}}')
echo "Senha do elastic: $PASSWORD"

pkill -f "port-forward.*5601" || true
nohup kubectl -n elastic port-forward --address 0.0.0.0 service/lab-kb-kb-http 5601 > /tmp/pf-kibana.log 2>&1 &
sleep 2
cat /tmp/pf-kibana.log

Resultado esperado: a senha do usuário elastic e a mensagem Forwarding from 0.0.0.0:5601 -> 5601.

O túnel fica ligado ao pod do Kibana que estava rodando quando ele foi aberto. Se esse pod for reiniciado ou recriado, o túnel cai e o Kibana deixa de responder no navegador: execute este bloco novamente.

No navegador, abra https://<IP_DA_VM>:5601, faça login com o usuário elastic e abra o Console (Dev Tools). Confira os papéis dos nodes:

GET _cat/nodes?v&h=name,node.role

O lab-es-es-default-0 deve aparecer com os papéis hilmrst, o lab-es-es-node2-0 com w (warm) e o lab-es-es-node3-0 com c (cold).

Passo 3 — Ajustar o intervalo de verificação do ILM

Por padrão, o ILM verifica as condições das policies a cada 10 minutos. Para os laboratórios, vamos reduzir para 10 segundos:

PUT /_cluster/settings
{
  "persistent": {
    "indices.lifecycle.poll_interval": "10s"
  }
}

Esse valor vale para todos os laboratórios deste capítulo e é restaurado no final, na seção de volta para um node.

Nota sobre os timers dos laboratórios: os tempos configurados (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.


5.4 - Laboratório 1: ILM com dados de vendas

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, migração para o tier warm e deleção após um período configurado.

Todos os comandos devem ser executados no Console do Kibana.

Passo 1 — Criar a ILM Policy

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 ILM injeta a action migrate, que move o índice para o node warm, e o forcemerge compacta todos os segments em um único, otimizando leitura
  • Delete: o índice é deletado 5 minutos após o rollover

Passo 2 — Criar o Index Template

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": 0,
      "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" }
      }
    }
  }
}

Passo 3 — Criar o índice inicial com o alias de escrita

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.).

Passo 4 — Verificar que o ILM foi aplicado ao índice

GET /vendas-000001/_ilm/explain

O 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.

Veja também em qual node o índice está:

GET /_cat/shards/vendas-*?v&h=index,prirep,state,node

O shard fica no lab-es-es-default-0, o node hot.

Passo 5 — Inserir documentos

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.

Passo 6 — Acompanhar o rollover

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=index

Apó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?v

O is_write_index deve ter migrado para vendas-000002.

Acompanhe o estado do ILM em cada índice:

GET /vendas-*/_ilm/explain

Passo 7 — Acompanhar as transições de fase e a mudança de tier

Aguarde os tempos configurados e execute os comandos abaixo repetidamente para observar as transições:

GET /vendas-000001/_ilm/explain
GET /_cat/shards/vendas-*?v&h=index,prirep,state,node

Os 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

Quando o vendas-000001 entra na fase warm, o _cat/shards passa a mostrar o shard dele no lab-es-es-node2-0: a action migrate moveu o índice para o tier warm. Na fase delete, o índice desaparece.

Para ver a preferência de tier que o ILM aplicou:

GET /vendas-000001/_settings?filter_path=**._tier_preference

Passo 8 — Forçar uma transição manualmente (opcional)

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.

Passo 9 — Limpeza ao final do lab

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/sales

Remover a policy:

DELETE /_ilm/policy/policy-vendas

5.5 - Laboratório 2: policy completa com hot, warm e cold

Neste laboratório vamos criar uma policy mais próxima de produção, com três fases ativas, readonly e forcemerge na fase warm, e acompanhar o índice passando pelos três nodes do cluster: hot, warm e cold.

Passo 1 — Criar a policy completa

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": 0 }
        }
      },
      "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:

  • readonly na fase warm: após o rollover, o índice nunca mais vai receber escritas — torná-lo readonly explicita isso e protege contra escritas acidentais
  • forcemerge com max_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 no warm e no cold: controla o número de réplicas em cada fase. No laboratório, com um node por tier, as duas fases usam 0. Em produção, o comum é manter 1 réplica no warm (leitura paralela e proteção contra falha) e usar 0 no cold, para economizar disco — os dados raramente são consultados nessa fase
  • migrate (injetada automaticamente) no warm e no cold: move o índice para o node warm e, depois, para o node cold

Por que não usar 1 réplica no warm do laboratório? A action allocate espera todas as cópias do índice serem alocadas antes de seguir para as próximas actions. Com um único node warm, a réplica nunca seria alocada e o ILM ficaria parado nessa etapa.

Passo 2 — Criar o index template

PUT /_index_template/template-logs-completa
{
  "index_patterns": ["app-completa-*"],
  "template": {
    "settings": {
      "number_of_shards": 1,
      "number_of_replicas": 0,
      "index.lifecycle.name": "policy-logs-completa",
      "index.lifecycle.rollover_alias": "app-completa"
    },
    "mappings": {
      "properties": {
        "@timestamp": { "type": "date" },
        "mensagem":   { "type": "text" },
        "nivel":      { "type": "keyword" },
        "servico":    { "type": "keyword" }
      }
    }
  }
}

O padrão de nome é app-completa-*, e não logs-completa-*: nomes no formato logs-*-* são reservados pelo template interno do Elasticsearch para data streams de logs, que tem prioridade maior e impediria a criação de um índice comum com esse nome.

Passo 3 — Criar o índice inicial

PUT /app-completa-000001
{
  "aliases": {
    "app-completa": {
      "is_write_index": true
    }
  }
}

Passo 4 — Inserir documentos e acompanhar o ciclo completo

POST /_bulk
{ "index": { "_index": "app-completa" } }
{ "@timestamp": "2026-05-01T10:00:00Z", "mensagem": "Serviço iniciado", "nivel": "INFO", "servico": "api-gateway" }
{ "index": { "_index": "app-completa" } }
{ "@timestamp": "2026-05-01T10:01:00Z", "mensagem": "Conexão estabelecida", "nivel": "INFO", "servico": "auth-service" }
{ "index": { "_index": "app-completa" } }
{ "@timestamp": "2026-05-01T10:02:00Z", "mensagem": "Timeout na chamada ao banco", "nivel": "ERROR", "servico": "api-gateway" }
{ "index": { "_index": "app-completa" } }
{ "@timestamp": "2026-05-01T10:03:00Z", "mensagem": "Retry bem-sucedido", "nivel": "WARN", "servico": "api-gateway" }
{ "index": { "_index": "app-completa" } }
{ "@timestamp": "2026-05-01T10:04:00Z", "mensagem": "Cache invalidado", "nivel": "DEBUG", "servico": "cache-service" }

Acompanhe as transições com:

GET /app-completa-*/_ilm/explain

Observe como o índice transita pelo hot → warm → cold → delete ao longo dos minutos configurados. Em cada fase, verifique também em qual node o índice está e o número de réplicas:

GET /_cat/shards/app-completa-*?v&h=index,prirep,state,node
GET /_cat/indices/app-completa-*?v&h=index,pri,rep,status,health

O app-completa-000001 começa no lab-es-es-default-0 (hot), passa para o lab-es-es-node2-0 (warm), depois para o lab-es-es-node3-0 (cold) e, por fim, é deletado.

Passo 5 — Limpeza

PUT /_cluster/settings
{
  "persistent": {
    "action.destructive_requires_name": false
  }
}
DELETE /app-completa-*
DELETE /_index_template/template-logs-completa
DELETE /_ilm/policy/policy-logs-completa
PUT /_cluster/settings
{
  "persistent": {
    "action.destructive_requires_name": null
  }
}

5.6 - Laboratório 3: simulando e resolvendo erros de ILM

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.

Por que o ILM pode travar?

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 ou migrate não avança Não há nodes com o tier configurado, ou as réplicas não cabem nos nodes do tier Configurar os papéis dos nodes (no ECK, os nodeSets de cada tier) ou reduzir as réplicas
Fase não avança min_age não foi atingido Aguardar ou usar _ilm/move para forçar a transição

Simulando um erro — alias ausente

Vamos criar um índice gerenciado pelo ILM, mas sem o alias correto, e observar o que o ILM reporta. Primeiro crie uma policy e o template:

PUT /_ilm/policy/policy-erro
{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": { "max_docs": 1 }
        }
      }
    }
  }
}
PUT /_index_template/template-logs-erro
{
  "index_patterns": ["app-erro-*"],
  "template": {
    "settings": {
      "number_of_shards": 1,
      "number_of_replicas": 0,
      "index.lifecycle.name": "policy-erro",
      "index.lifecycle.rollover_alias": "app-erro"
    }
  }
}

Agora crie o índice sem o alias:

PUT /app-erro-000001

O índice foi criado com a policy, mas o alias app-erro com is_write_index: true não existe. Insira um documento e aguarde o ILM tentar o rollover:

POST /app-erro-000001/_doc
{
  "@timestamp": "2026-05-01T10:00:00Z",
  "mensagem": "Teste de erro ILM"
}

Após alguns segundos, consulte o estado do ILM:

GET /app-erro-000001/_ilm/explain

Você verá o campo step como ERROR e o step_info com um erro descrevendo que o alias não aponta para o índice. Para ver só índices com erro em todo o cluster:

GET /*/_ilm/explain?only_errors=true

Resolvendo o erro

Adicione o alias correto ao índice:

POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "app-erro-000001",
        "alias": "app-erro",
        "is_write_index": true
      }
    }
  ]
}

Agora instrua o ILM a tentar novamente:

POST /app-erro-000001/_ilm/retry

Verifique que o erro foi resolvido:

GET /app-erro-000001/_ilm/explain

O índice sai do estado de erro, faz o rollover e o alias passa a apontar para o app-erro-000002.

Limpeza

PUT /_cluster/settings
{ "persistent": { "action.destructive_requires_name": false } }
DELETE /app-erro-*
DELETE /_index_template/template-logs-erro
DELETE /_ilm/policy/policy-erro
PUT /_cluster/settings
{ "persistent": { "action.destructive_requires_name": null } }

5.7 - Laboratório 4: ILM com data streams

Neste laboratório o ciclo de vida é aplicado a um data stream. Não há alias nem índice inicial para criar: o primeiro documento cria o data stream e o primeiro índice de apoio.

Passo 1 — Criar a policy de ILM (hot → warm → delete)

PUT _ilm/policy/lab-logs-policy
{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": { "max_primary_shard_size": "1gb", "max_age": "5m" },
          "set_priority": { "priority": 100 }
        }
      },
      "warm": {
        "min_age": "2m",
        "actions": {
          "forcemerge": { "max_num_segments": 1 },
          "set_priority": { "priority": 50 },
          "allocate": { "number_of_replicas": 0 }
        }
      },
      "delete": {
        "min_age": "10m",
        "actions": { "delete": {} }
      }
    }
  }
}

Passo 2 — Criar o index template com data stream

PUT _index_template/lab-logs-template
{
  "index_patterns": ["lab-logs-*"],
  "data_stream": {},
  "priority": 200,
  "template": {
    "settings": {
      "number_of_shards": 1,
      "number_of_replicas": 0,
      "index.lifecycle.name": "lab-logs-policy"
    },
    "mappings": {
      "properties": {
        "@timestamp": { "type": "date" },
        "mensagem":   { "type": "text" },
        "nivel":      { "type": "keyword" }
      }
    }
  }
}

O "data_stream": {} faz o template criar data streams em vez de índices comuns. O "priority": 200 garante que este template tenha precedência sobre outros templates que também casem com o nome (como um template genérico para lab-*): dois templates com padrões sobrepostos não podem ter a mesma prioridade.

Passo 3 — Criar o data stream indexando o primeiro documento

POST lab-logs-app/_doc
{ "@timestamp": "2026-08-04T12:00:00Z", "nivel": "INFO", "mensagem": "app iniciada" }

Como existe um template com data_stream para lab-logs-*, o Elasticsearch cria o data stream lab-logs-app e o primeiro índice de apoio .ds-lab-logs-app-<data>-000001.

Passo 4 — Ver os bastidores do data stream

GET _data_stream/lab-logs-app

A resposta mostra o data stream, a policy de ILM e a lista de índices de apoio (indices). Você escreve e consulta o nome único; o ILM gerencia os índices por baixo.

Passo 5 — Forçar um rollover manual

POST lab-logs-app/_rollover
GET _data_stream/lab-logs-app

Agora o data stream tem dois índices de apoio: o ...-000001, que saiu da fase hot, e o ...-000002, que passou a receber as escritas.

Passo 6 — Acompanhar as fases e os tiers

GET lab-logs-app/_ilm/explain
GET _cat/shards/.ds-lab-logs-app-*?v&h=index,prirep,state,node

Indexe mais um documento e repita os dois comandos a cada 1 ou 2 minutos:

POST lab-logs-app/_doc
{ "@timestamp": "2026-08-04T12:05:00Z", "nivel": "ERROR", "mensagem": "falha X" }

O índice de apoio mais antigo passa da fase hot para a warm depois de 2 minutos (e o shard dele muda do lab-es-es-default-0 para o lab-es-es-node2-0) e é deletado depois de 10 minutos.

Passo 7 — Ver a policy e onde ela está aplicada

GET _ilm/policy/lab-logs-policy

O campo in_use_by mostra os índices, data streams e templates que usam a policy.

Passo 8 — Limpeza

DELETE _data_stream/lab-logs-app
DELETE _index_template/lab-logs-template
DELETE _ilm/policy/lab-logs-policy

Apagar o data stream apaga também todos os índices de apoio dele.


5.8 - Gerenciando policies via Kibana

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"
}

5.9 - Boas práticas de ILM

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=true

Em 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.

Dimensione os tiers junto com a policy: no ECK, cada tier é um nodeSet. Os nodes warm e cold precisam ter disco suficiente para receber os índices que o ILM vai mover para eles, e o número de nodes de cada tier precisa comportar as réplicas definidas na policy.


5.10 - Operações úteis de ILM no dia a dia

Listar todas as policies:

GET /_ilm/policy

Ver o estado do ILM em todos os índices de um padrão:

GET /vendas-*/_ilm/explain

Ver apenas índices com erro no ILM:

GET /vendas-*/_ilm/explain?only_errors=true

Retentar um step com erro:

POST /vendas-000001/_ilm/retry

Ver o status geral do ILM no cluster:

GET /_ilm/status

Pausar o ILM globalmente (útil durante manutenções):

POST /_ilm/stop

Retomar o ILM:

POST /_ilm/start

5.11 - Curator: alternativa open source ao ILM

O 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


5.12 - Voltando o lab-es para um node

Com os laboratórios de ILM concluídos, o lab-es volta a ter um único node. Essa etapa é obrigatória: os próximos laboratórios usam a memória da VM para outros componentes.

Voltar para um node é reaplicar o manifesto original do lab-es: o operator migra os dados dos nodes node2 e node3 para o lab-es-es-default-0 e só depois remove os pods e os PVCs deles. A migração só acontece se todos os shards couberem no node que fica.

Passo 1 — Restaurar o intervalo do ILM e conferir os índices

No Console, restaure o intervalo de verificação do ILM para o valor padrão:

PUT /_cluster/settings
{
  "persistent": {
    "indices.lifecycle.poll_interval": null
  }
}

Confira se sobrou algum índice ou data stream dos laboratórios e se algum índice tem réplica:

GET _cat/indices/vendas-*,app-*,.ds-lab-logs-*?v&h=index,health,pri,rep
GET _cat/indices?v&h=index,health,pri,rep&s=rep:desc

Apague o que sobrou dos laboratórios usando as seções de limpeza de cada um. Se algum índice seu tiver rep maior que 0, zere as réplicas dele (os índices de sistema, que começam com ., ajustam as réplicas sozinhos):

PUT <nome-do-indice>/_settings
{
  "index": { "number_of_replicas": 0 }
}

Passo 2 — Reaplicar o manifesto original

No terminal da VM, recrie o manifesto original do lab-es (se ele já existir, o conteúdo é o mesmo) e aplique:

mkdir -p ~/eck-lab/manifests
cd ~/eck-lab

cat <<'EOF' > manifests/elasticsearch.yaml
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: lab-es
spec:
  version: 9.4.2
  nodeSets:
    - name: default
      count: 1
      config:
        node.store.allow_mmap: true
      podTemplate:
        spec:
          containers:
            - name: elasticsearch
              env:
                - name: ES_JAVA_OPTS
                  value: -Xms2g -Xmx2g
              resources:
                requests:
                  memory: 4Gi
                  cpu: "1"
                limits:
                  memory: 4Gi
      volumeClaimTemplates:
        - metadata:
            name: elasticsearch-data
          spec:
            accessModes: [ReadWriteOnce]
            resources:
              requests:
                storage: 20Gi
            storageClassName: local-path
EOF

kubectl apply -n elastic -f manifests/elasticsearch.yaml
kubectl -n elastic get pods -w

Resultado esperado: o operator reinicia o lab-es-es-default-0 (que volta a ter todos os papéis) e, depois de migrar os dados, remove os pods lab-es-es-node2-0 e lab-es-es-node3-0. Pressione Ctrl+C quando só restar o lab-es-es-default-0 com 1/1 Running.

Passo 3 — Verificar

kubectl -n elastic get elasticsearch lab-es
kubectl -n elastic get pvc | grep lab-es

Resultado esperado: NODES 1 e HEALTH green, e só o PVC elasticsearch-data-lab-es-es-default-0.

Se a redução não avançar (os pods node2 e node3 continuam Running depois de vários minutos), algum shard não consegue ir para o node que fica. Veja os eventos e o log do operator e confira o Passo 1:

kubectl -n elastic describe elasticsearch lab-es | sed -n '/Events/,$p'
kubectl -n elastic-system logs statefulset/elastic-operator | tail -20

Resumo do capítulo

  • O ILM automatiza o ciclo de vida dos índices com policies divididas em fases (hot, warm, cold, frozen, delete), cada uma com suas actions.
  • O rollover cria um novo índice quando o atual atinge um limite de tamanho, idade ou documentos; a escrita segue por um alias ou por um data stream.
  • A policy, o index template e o índice inicial (ou o data stream) formam o fluxo completo do ILM.
  • No ECK, cada tier é um nodeSet; a action migrate move os shards para os pods do tier da fase, sem participação do operator.
  • O _ilm/explain mostra fase, action e step de cada índice; only_errors=true lista os índices travados e o _ilm/retry retoma um step com erro.
  • Nomes logs-*-* e metrics-*-* são reservados pelos templates internos de data streams; use outros padrões nos seus índices.

Origem do conteúdo

5.1 - O que é o ILM

5.2 - Data streams

5.3 a 5.7 - Laboratórios

5.8 a 5.11 - Kibana, boas práticas, operações e Curator

5.12 - Voltando o lab-es para um node

⚠️ **GitHub.com Fallback** ⚠️