04 ‐ Entendendo Índice, Shards e Segments no Elasticsearch - BktechBrazil/elastic-eck GitHub Wiki
Índice (coleção lógica de documentos: "pedidos")
└─ Shard (uma fatia do índice = um índice Lucene completo)
└─ Segment (arquivos imutáveis dentro do shard; nascem no refresh e são fundidos no merge)
Um índice no Elasticsearch é uma coleção de documentos com características semelhantes. Cada índice é identificado por um nome único e é a unidade principal de armazenamento e consulta. Em um cluster, é possível ter quantos índices forem necessários — por exemplo, um para logs de aplicação, outro para dados de clientes, outro para eventos de segurança.
Para distribuir os dados horizontalmente entre os nodes do cluster, o Elasticsearch divide cada índice em partes menores chamadas shards. Cada shard é, na prática, uma instância independente do Lucene — com sua própria estrutura de dados e capacidade de executar buscas de forma autônoma.
Essa divisão permite que o Elasticsearch processe operações em paralelo e distribua a carga entre múltiplos nodes, o que melhora tanto o desempenho quanto a disponibilidade.
Existem dois tipos de shard:
- Primary shards: o número de primary shards é definido no momento da criação do índice e não pode ser alterado depois. Escolher esse número com cuidado é importante — shards demais geram overhead de gerenciamento, shards de menos limitam a capacidade de escalar.
- Replica shards: cópias dos primary shards, armazenadas em nodes diferentes. Servem para alta disponibilidade (se um node falhar, as réplicas garantem que os dados continuem acessíveis) e para aumentar o throughput de leitura, já que as buscas podem ser distribuídas entre primários e réplicas.
Regra de ouro: uma réplica nunca é alocada no mesmo node do seu primário — senão ela não protegeria contra a queda daquele node. Por isso, em um cluster de um único node, as réplicas ficam unassigned e o índice fica yellow.
Primários × réplicas depois da criação:
number_of_replicasé dinâmico e pode ser alterado a qualquer momento.number_of_shardsé fixo: para mudar o número de primários é preciso criar um novo índice (por exemplo, com as APIs_split,_shrinkou_reindex).
Dentro de cada shard, os dados são organizados em estruturas chamadas segments. Um segment é um índice invertido imutável — a estrutura central usada pelo Lucene para armazenar e recuperar documentos.
Quando novos documentos são indexados, eles passam por três etapas:
- Buffer de memória: o documento é escrito primeiro em um buffer em memória (o translog garante durabilidade em caso de falha antes da escrita em disco).
- Novo segment: periodicamente, o buffer é descarregado para um novo segment em disco. Esse segment é imutável — para atualizar ou deletar um documento, o Elasticsearch marca o documento antigo como deletado e escreve uma nova versão em um segment diferente.
- Merge: com o tempo, o Elasticsearch mescla segments pequenos em segmentos maiores. Durante esse processo, documentos marcados como deletados são descartados definitivamente, liberando espaço em disco.
Fonte: Medium — Luis Sena
Entender esse ciclo é essencial para ajustar o comportamento de indexação, controlar o uso de memória e disco, e otimizar o desempenho de buscas em diferentes cargas de trabalho. É ele que explica por que "otimizar" um índice que não recebe mais escrita é fazer um force merge: juntar os segments em poucos segments maiores e eliminar de vez os documentos deletados.
Quando um documento é indexado, o Elasticsearch precisa decidir em qual shard primário ele será armazenado. Essa decisão é feita por uma fórmula de roteamento baseada no _id do documento:
shard = hash(_id) % number_of_primary_shards
O Elasticsearch calcula o hash do _id e divide pelo número de primary shards — o resto da divisão determina o shard destino. Isso garante uma distribuição uniforme dos documentos entre os shards sem necessidade de coordenação central.
Consequências práticas:
- O número de primary shards não pode ser alterado depois da criação do índice, porque mudar esse número alteraria o resultado da fórmula — os documentos já indexados nunca seriam encontrados
- É possível definir um roteamento customizado usando um campo específico no lugar do
_id— útil para garantir que documentos relacionados fiquem no mesmo shard e otimizar buscas que sempre filtram por aquele campo
Escolher o número correto de shards é uma das decisões mais importantes ao criar um índice. Tanto poucos quanto muitos shards causam problemas:
Problemas com poucos shards:
- Shards muito grandes ficam lentos para buscar e demoram muito para se recuperar após uma falha de node
- Limita a capacidade de paralelizar operações de busca
Problemas com muitos shards:
- Cada shard consome memória do cluster (heap do master node e metadados)
- Um número excessivo de shards pequenos gera mais overhead de coordenação do que ganho de performance
Referências práticas recomendadas pela Elastic:
| Parâmetro | Referência |
|---|---|
| Tamanho ideal por shard | Entre 10 GB e 50 GB para a maioria dos casos de uso |
| Tamanho máximo recomendado por shard | 50 GB — acima disso, recovery e merge ficam lentos |
| Número máximo de documentos por shard | Até 200 milhões de documentos |
| Heap dos master-eligible nodes | Pelo menos 1 GB de heap para cada 3.000 índices do cluster |
| Limite de shards por node | Por padrão, o cluster não permite mais de 1.000 shards (não congelados) por node (cluster.max_shards_per_node) |
Exemplo prático: se você espera ter um índice de 100 GB, o ideal é criar entre 2 e 10 shards primários. Se o índice vai crescer ao longo do tempo via rollover (como em logs), mantenha cada índice gerado pelo rollover entre 10 GB e 50 GB ajustando as condições de rollover no ILM.
Um node pode acumular papéis (node.roles). Em clusters pequenos, um node faz tudo; em clusters grandes, separar os papéis traz estabilidade:
| Papel | Função |
|---|---|
master |
Coordena o cluster (estado, alocação de shards). Poucos e dedicados em produção |
data (data_hot, data_warm, data_cold, data_frozen, data_content) |
Guardam os shards. data genérico atende a todos os tiers |
ingest |
Executam ingest pipelines (pré-processamento) |
ml |
Rodam jobs de Machine Learning |
| coordinating | Um node sem nenhum papel (node.roles: []) só recebe requisições, distribui e consolida os resultados |
No ECK, cada conceito de arquitetura do Elasticsearch vira um objeto do Kubernetes:
| Conceito Elasticsearch | Objeto no ECK |
|---|---|
| Um node | Um pod (gerenciado por um StatefulSet) |
| Um grupo de nodes com a mesma configuração | Um nodeSet (com config.node.roles) |
| Escalar o número de nodes | Mudar count no nodeSet
|
| Disco de um node | Um PVC (via volumeClaimTemplates) |
| Topologia com papéis ou tiers separados |
Vários nodeSets com papéis e recursos diferentes |
Ou seja: desenhar a arquitetura de um cluster no ECK é desenhar a lista de nodeSets. Um cluster com 3 masters dedicados e 3 nodes de dados e ingestão são dois nodeSets:
nodeSets:
# 3 masters dedicados
- name: master
count: 3
config:
node.roles: ["master"]
# 3 nodes de dados e ingestão
- name: ingest-data
count: 3
config:
node.roles: ["data", "ingest"]O nome do pod segue o padrão <cluster>-es-<nodeSet>-<número>: o nodeSet ingest-data de um cluster quickstart gera os pods quickstart-es-ingest-data-0, -1 e -2. O nome do node no Elasticsearch é o mesmo nome do pod.
Quando o count aumenta, o operator cria os novos pods. Quando diminui, o operator primeiro migra os dados dos nodes que vão sair e só depois remove os pods e os seus PVCs. A migração só acontece se o número de réplicas dos índices couber nos nodes que sobram: um índice com 1 réplica não pode ficar em um único node de dados, e nesse caso a redução fica parada até você diminuir as réplicas.
A alocação de shards define como o Elasticsearch distribui os shards entre os nodes do cluster. O Elasticsearch faz isso automaticamente, mas oferece controles granulares para situações específicas.
Controla o comportamento geral de alocação de shards em todo o cluster. É útil durante janelas de manutenção para evitar movimentação desnecessária de shards.
E no ECK? Nos rolling upgrades e reinícios, o operator desliga cada node usando a API de desligamento de nodes (node shutdown) com um
allocation_delay: o Elasticsearch espera um pouco antes de realocar os shards de um node que vai voltar logo, evitando movimentação desnecessária. Esse tempo pode ser ajustado com a anotaçãoeck.k8s.elastic.co/restart-allocation-delayno recursoElasticsearch. Você não precisa alterar a alocação à mão para upgrades.
O Elasticsearch monitora o uso de disco de cada node e toma decisões de alocação com base em três marcas d'água (watermarks):
- Low watermark: quando atingida, o node para de receber novos shards por alocação. Não afeta shards já existentes.
- High watermark: quando atingida, o Elasticsearch começa a mover shards existentes para outros nodes com mais espaço disponível.
- Flood stage: situação crítica — o Elasticsearch bloqueia a indexação nos índices alocados nesse node para evitar corrupção de dados.
E no ECK? O disco de cada node é o PVC criado a partir do
volumeClaimTemplates. Com a StorageClasslocal-pathdo laboratório, o volume é um diretório do próprio host e o tamanho pedido no PVC não é imposto: o Elasticsearch enxerga o espaço livre do disco da VM.
Permite que o Elasticsearch considere atributos personalizados dos nodes na hora de distribuir shards. O caso de uso mais comum é rack awareness — garantir que primary e replica shards de um mesmo índice fiquem em racks físicos diferentes, de forma que uma falha de rack não derrube todos os dados ao mesmo tempo.
E no ECK? Os atributos são definidos no
configdonodeSet(por exemplo,node.attr.rack_id). O ECK cria automaticamente em cada pod o atributok8s_node_name, com o nome do node do Kubernetes em que o pod está rodando, e o usa na alocação para evitar que primário e réplica fiquem no mesmo host. A partir do ECK 3.4, a forma recomendada de espalhar o cluster por zonas de disponibilidade é o campozoneAwarenessdonodeSet: o operator distribui os pods entre as zonas e configura sozinho onode.attr.zonee ocluster.routing.allocation.awareness.attributes:nodeSets: - name: default count: 6 zoneAwareness: {}
Permite incluir ou excluir nodes específicos da alocação usando atributos como nome do node, endereço IP ou atributos personalizados. Útil para retirar um node de produção gradualmente ou direcionar índices específicos para hardware determinado.
Uma estratégia de gerenciamento de dados que categoriza nodes e índices em "temperaturas" com base na frequência de acesso:
| Tier | Hardware típico | Para que serve |
|---|---|---|
| Hot | SSDs de alto desempenho | Dados ativos: indexação constante e buscas frequentes |
| Warm | HDDs ou SSDs de médio desempenho | Dados recentes mas menos acessados, principalmente leitura |
| Cold | Storage mais barato | Dados raramente consultados, recuperação mais lenta aceitável |
| Frozen | Object storage (ex: S3, GCS) | Dados históricos via searchable snapshots, acesso muito esporádico |
Essa arquitetura é a base do ILM (Index Lifecycle Management), que automatiza a transição dos dados entre os tiers ao longo do tempo.
Fonte: Opster — ILM: Manage the index lifecycle
E no ECK? Cada tier é um
nodeSetcom o papel correspondente (data_hot,data_warm,data_cold), seus próprios recursos e sua própria StorageClass. Em produção, osnodeSetsde cada tier costumam ter também umanodeAffinitypara rodar em hosts do Kubernetes com o hardware adequado (SSD para hot, discos maiores para warm).
As APIs _cat retornam informações do cluster em formato de tabela, fácil de ler no terminal ou no Console. O parâmetro ?v adiciona o cabeçalho das colunas e ?help lista as colunas disponíveis.
| Endpoint | O que mostra |
|---|---|
GET _cat/health?v |
Saúde do cluster (green, yellow, red) |
GET _cat/nodes?v |
Nodes, papéis (node.role), heap, CPU e qual é o master (*) |
GET _cat/nodeattrs?v |
Atributos personalizados de cada node |
GET _cat/indices?v |
Índices, saúde, número de primários e réplicas, documentos e tamanho |
GET _cat/shards/<indice>?v |
Onde cada shard está (prirep: p = primário, r = réplica) e o estado (STARTED, INITIALIZING, UNASSIGNED) |
GET _cat/segments/<indice>?v |
Segments de cada shard: quantidade, documentos, deletados e tamanho |
GET _cat/allocation?v |
Shards e uso de disco por node |
GET _cluster/health/<indice>?pretty |
Saúde de um índice específico |
GET _cluster/allocation/explain |
Por que um shard não está alocado |
Na coluna node.role do _cat/nodes, cada letra é um papel: m = master, d = data, i = ingest, l = ml, h = data_hot, w = data_warm, c = data_cold, s = data_content, e assim por diante.
Os laboratórios deste capítulo usam o cluster lab-es e o Console do Kibana. Os comandos de terminal (kubectl) rodam na VM.
No terminal da VM, obtenha a senha do usuário elastic e abra o túnel até 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.logO 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 a senha exibida e abra o Console (Dev Tools no menu de navegação ou Console na busca global).
Caso a instalação tenha feito seguindo o Laboratório do repositório (00-preparacao-ambiente), seguir os passos abaixo para que os laboratórios da wiki não ocorra algum erro:
# 1) Remover o quickstart — libera ~5Gi de memória reservada
kubectl -n elastic delete kibana quickstart --ignore-not-found
kubectl -n elastic delete elasticsearch quickstart --ignore-not-found
kubectl -n elastic get elasticsearch,kibana # deve restar só lab-es e lab-kb
kubectl -n elastic get pvc # deve restar só o do lab-es
# 2) Criar a pasta que a wiki usa, com os manifestos que estão rodando
mkdir -p ~/eck-lab/manifests
cp /opt/elastic-eck/02-deploy-elastic-stack-eck/manifests/elasticsearch.yaml \
/opt/elastic-eck/02-deploy-elastic-stack-eck/manifests/kibana.yaml ~/eck-lab/manifests/
cd ~/eck-lab
kubectl diff -n elastic -f manifests/elasticsearch.yaml # sem saída = idêntico ao cluster
kubectl diff -n elastic -f manifests/kibana.yaml
# 3) Conferir a versão do operator
helm list -n elastic-system # CHART eck-operator-3.4.1Limpamos o ambiente quickstart da máquina para liberação de recursos, então copiamos os manifestos para o caminho usado por essa wiki e então comparamos com o manifesto vigente atualmente.
GET _cat/nodes?v&h=name,node.role,heap.percent,cpu,disk.used_percent,masterGET _cat/shards/kibana_sample_data_ecommerce?vPUT loja
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}GET _cat/shards/loja?vConfira a saúde do índice:
GET _cluster/health/lojaPara ver o motivo de a réplica não ser alocada:
GET _cluster/allocation/explain
{
"index": "loja",
"shard": 0,
"primary": false
}Não corrija o índice agora: no próximo laboratório, as réplicas serão alocadas quando o cluster ganhar novos nodes.
No ECK, aumentar o cluster é acrescentar nodeSets ao manifesto e reaplicá-lo. Vamos transformar o lab-es em um cluster de 3 nodes, com o atributo datacenter em cada um, na mesma distribuição de um ambiente com dois datacenters:
| Pod (node) | nodeSet |
Papéis | Atributo datacenter
|
Memória / heap |
|---|---|---|---|---|
lab-es-es-default-0 |
default |
todos (master, data, ingest...) | datacenter1 |
4Gi / 2 GB |
lab-es-es-node2-0 |
node2 |
data, ingest
|
datacenter1 |
2Gi / 1 GB |
lab-es-es-node3-0 |
node3 |
data, ingest
|
datacenter2 |
2Gi / 1 GB |
O lab-es, os dados, a senha do usuário elastic e o Kibana continuam os mesmos. O lab-es-es-default-0 segue como o único master.
Os dois nodes novos pedem 2Gi de memória cada. Confira quanto já está reservado no node do Kubernetes:
kubectl describe node eck-lab | grep -A 8 "Allocated resources"Caso o hostname não tenha sido configurado na instalação:
kubectl describe node $(kubectl get nodes -o name) | grep -A 8 "Allocated resources"
# ou
kubectl describe node vmi3605242 | grep -A 8 "Allocated resources"Resultado esperado: a linha memory mostra os requests já reservados (cerca de 5 a 6 GiB com o lab-es e o Kibana). Os 4Gi dos nodes novos precisam caber no que falta para a memória total da VM.
mkdir -p ~/eck-lab/manifests
cd ~/eck-lab
cat <<'EOF' > manifests/elasticsearch-3-nodes.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
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", "ingest"]
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", "ingest"]
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
EOFO que mudou em relação ao manifesto original do lab-es:
| Trecho | Mudança |
|---|---|
nodeSets[default].config |
Ganhou node.attr.datacenter: datacenter1. Como a configuração do node mudou, o operator reinicia o pod lab-es-es-default-0
|
nodeSets[node2] |
Novo grupo com 1 node de dados e ingestão no datacenter1
|
nodeSets[node3] |
Novo grupo com 1 node de dados e ingestão no datacenter2
|
O node.attr.<nome> cria um atributo personalizado no node. É o equivalente a acrescentar a mesma linha no elasticsearch.yml de cada servidor em uma instalação tradicional.
kubectl apply -n elastic -f manifests/elasticsearch-3-nodes.yaml
kubectl -n elastic get pods -wResultado 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.
Enquanto o
lab-es-es-default-0reinicia, o cluster fica sem master e o Kibana mostra erros por um ou dois minutos. Isso é esperado: em produção, o cluster teria 3 masters e continuaria no ar.
Confira o recurso:
kubectl -n elastic get elasticsearch lab-es
kubectl -n elastic get statefulset,pvc | grep lab-esResultado esperado: HEALTH green e NODES 3; três StatefulSets (lab-es-es-default, lab-es-es-node2, lab-es-es-node3) e um PVC para cada pod.
NAME HEALTH NODES VERSION PHASE AGE
lab-es green 3 9.4.2 Ready 2d
No Console:
GET _cat/nodes?v&h=name,node.role,masterGET _cat/nodeattrs?v&h=node,attr,valueGET _cat/shards/loja?v
GET _cluster/health/lojaObserve também que o Elasticsearch passou a distribuir os shards dos outros índices entre os três nodes:
GET _cat/allocation?vTodos os comandos desta seção devem ser executados no Console do Kibana.
Desativar a alocação de shards (útil durante manutenção):
PUT /_cluster/settings
{
"persistent": {
"cluster.routing.allocation.enable": "none"
}
}Com a alocação desativada, nenhum shard novo é alocado. Crie um índice e observe:
PUT teste-alocacao
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1
}
}GET _cat/shards/teste-alocacao?vReativar a alocação de shards:
PUT /_cluster/settings
{
"persistent": {
"cluster.routing.allocation.enable": "all"
}
}GET _cat/shards/teste-alocacao?vSempre use
persistentem vez detransientao alterar configurações de cluster. Settings transient são fortemente desaconselhados a partir do Elasticsearch 8.x — se o cluster se tornar instável, eles podem ser perdidos, deixando o cluster em um estado inesperado.
PUT /_cluster/settings
{
"persistent": {
"cluster.routing.allocation.disk.watermark.low": "85%",
"cluster.routing.allocation.disk.watermark.high": "90%",
"cluster.routing.allocation.disk.watermark.flood_stage": "95%"
}
}Significado de cada watermark:
-
low(85%): o node para de receber novos shards por realocação automática quando o uso de disco atinge esse valor. -
high(90%): o Elasticsearch começa a mover shards existentes para nodes com mais espaço. -
flood_stage(95%): a indexação é bloqueada nos índices alocados nesse node. O índice fica em modo read-only automaticamente.
Confira o uso de disco de cada node:
GET _cat/allocation?vPara restaurar os valores padrão:
PUT /_cluster/settings
{
"persistent": {
"cluster.routing.allocation.disk.watermark.low": null,
"cluster.routing.allocation.disk.watermark.high": null,
"cluster.routing.allocation.disk.watermark.flood_stage": null
}
}Passo 1 — Verificar os atributos aplicados:
O atributo datacenter já foi definido no manifesto (Laboratório 2), no config de cada nodeSet:
GET /_cat/nodeattrs?vPasso 2 — Ativar a consciência de alocação via API:
PUT /_cluster/settings
{
"persistent": {
"cluster.routing.allocation.awareness.attributes": "datacenter"
}
}O Elasticsearch passa a considerar o atributo datacenter na distribuição de shards — tentando garantir que primary e replica de um mesmo índice fiquem em datacenters diferentes.
Passo 3 — Ver a awareness funcionando:
GET _cat/shards/loja?v&h=index,shard,prirep,state,nodePasso 4 — Forçar a consciência de alocação:
Na awareness comum, se um datacenter inteiro cair, o Elasticsearch recria as cópias perdidas nos datacenters que sobraram. A awareness forçada reserva lugar para os datacenters informados: quando não é possível equilibrar, o Elasticsearch prefere deixar a réplica unassigned a colocá-la no mesmo datacenter que o primário. Isso evita que o datacenter que sobrou fique sobrecarregado com todas as cópias.
Para ver a diferença, crie um índice com 2 réplicas (3 cópias de cada shard):
PUT loja-force
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 2
}
}GET _cat/shards/loja-force?v&h=index,shard,prirep,state,nodeAgora force a awareness informando três datacenters, sendo que o datacenter3 não existe (é como se ele tivesse caído):
PUT /_cluster/settings
{
"persistent": {
"cluster.routing.allocation.awareness.force.datacenter.values": "datacenter1,datacenter2,datacenter3"
}
}GET _cat/shards/loja-force?v&h=index,shard,prirep,state,node
GET _cluster/health/loja-forcePasso 5 — Desfazer as configurações de awareness:
PUT /_cluster/settings
{
"persistent": {
"cluster.routing.allocation.awareness.attributes": null,
"cluster.routing.allocation.awareness.force.datacenter.values": null
}
}DELETE loja-forceE em produção? Com zonas de disponibilidade reais, a forma recomendada no ECK é o campo
zoneAwarenessdonodeSet, que distribui os pods entre as zonas e configura a awareness automaticamente.
Vamos retirar os shards do índice loja do node lab-es-es-node3-0:
PUT loja/_settings
{
"index.routing.allocation.exclude._name": "lab-es-es-node3-0"
}GET _cat/shards/loja?v&h=index,shard,prirep,state,nodeAqui usamos a filtragem no nível do índice (
index.routing.allocation.exclude._name). A mesma configuração existe no nível do cluster (cluster.routing.allocation.exclude._name), mas no ECK é o operator que a usa para migrar os dados antes de remover nodes; alterá-la à mão pode conflitar com o operator.
Verificar a distribuição atual de shards:
GET /_cat/allocation?vRemover a restrição após o exercício:
PUT loja/_settings
{
"index.routing.allocation.exclude._name": null
}PUT segments-lab
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1
}
}Indexe 5 documentos com refresh=true, para forçar um refresh a cada documento. No Console, selecione as cinco linhas e execute todas de uma vez:
PUT segments-lab/_doc/1?refresh=true
{ "n": 1 }
PUT segments-lab/_doc/2?refresh=true
{ "n": 2 }
PUT segments-lab/_doc/3?refresh=true
{ "n": 3 }
PUT segments-lab/_doc/4?refresh=true
{ "n": 4 }
PUT segments-lab/_doc/5?refresh=true
{ "n": 5 }GET _cat/segments/segments-lab?v&h=shard,prirep,segment,docs.count,docs.deleted,sizeDELETE segments-lab/_doc/1?refresh=trueGET _cat/segments/segments-lab?v&h=shard,prirep,segment,docs.count,docs.deleted,sizePOST segments-lab/_forcemerge?max_num_segments=1GET _cat/segments/segments-lab?v&h=shard,prirep,segment,docs.count,docs.deleted,sizeCuidado: force merge é caro em I/O. Em produção, faça só em índices que não recebem mais escrita, como índices antigos que já passaram para o tier warm ou cold.
Neste laboratório vamos configurar cada node do cluster com um tier diferente e verificar como o Elasticsearch direciona a alocação de shards com base nessa configuração. É a arquitetura usada pelo ILM:
| Pod (node) | nodeSet |
Tier | Papéis |
|---|---|---|---|
lab-es-es-default-0 |
default |
Hot |
master, data_hot, data_content, ingest, ml, remote_cluster_client, transform
|
lab-es-es-node2-0 |
node2 |
Warm | data_warm |
lab-es-es-node3-0 |
node3 |
Cold | data_cold |
O node hot precisa deixar de ter o papel genérico
data. Esse papel vale para todos os tiers: com ele, o Elasticsearch poderia manter no hot um índice que deveria estar no warm ou no cold.
Com um único node hot, índices com réplica não conseguiriam alocar a réplica em outro node do mesmo tier e ficariam yellow. Remova os índices de teste deste capítulo:
DELETE loja,teste-alocacao,segments-labcd ~/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
EOFA única diferença em relação ao elasticsearch-3-nodes.yaml é a linha node.roles de cada nodeSet. Em uma instalação tradicional, isso seria editar o node.roles no elasticsearch.yml de cada servidor e reiniciar os nodes um a um; no ECK, o operator faz o rolling restart sozinho.
kubectl apply -n elastic -f manifests/elasticsearch-tiers.yaml
kubectl -n elastic get pods -wResultado esperado: o operator reinicia os três pods, um de cada vez. Aguarde os três ficarem 1/1 Running e pressione Ctrl+C. Depois confira:
kubectl -n elastic get elasticsearch lab-esResultado esperado: HEALTH green e NODES 3.
GET /_cat/nodes?v&h=name,node.role,ipOs índices que já existiam (dados de exemplo e índices dos capítulos anteriores) usam por padrão o tier data_content e passam a ficar todos no lab-es-es-default-0:
GET _cat/allocation?vCriar um índice alocado no tier hot:
PUT /hot_index
{
"settings": {
"index.routing.allocation.include._tier_preference": "data_hot",
"number_of_replicas": 0
}
}O índice é criado sem réplica porque cada tier do laboratório tem um único node.
Verificar onde os shards foram alocados:
GET /_cat/shards/hot_index?vMover o índice para o tier warm:
PUT /hot_index/_settings
{
"index.routing.allocation.include._tier_preference": "data_warm"
}GET /_cat/shards/hot_index?vMover o índice para o tier cold:
PUT /hot_index/_settings
{
"index.routing.allocation.include._tier_preference": "data_cold"
}GET /_cat/shards/hot_index?vO
lab-esfica com essa arquitetura de três tiers, que é a base do ILM. Se você não for continuar com o ILM, volte o cluster para um node seguindo a seção 4.10.
Faça esta seção só se precisar liberar memória na VM. 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. Por isso, antes de reaplicar o manifesto, os índices não podem ter réplicas nem estar presos aos tiers warm ou cold.
Remova o índice de teste do tier cold e confira se sobrou algum índice com réplica:
DELETE hot_indexGET _cat/indices?v&h=index,health,pri,rep&s=rep:descSe 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 }
}Confira também que não ficou nenhuma configuração de awareness ativa:
GET _cluster/settings?filter_path=persistent.cluster.routingNão pode aparecer nenhuma configuração de awareness nem de exclude. O "allocation": { "enable": "all" } é o valor padrão e pode aparecer.
cd ~/eck-lab
kubectl apply -n elastic -f manifests/elasticsearch.yaml
kubectl -n elastic get pods -wResultado 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.
kubectl -n elastic get elasticsearch lab-es
kubectl -n elastic get pvc | grep lab-esResultado 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
node2enode3continuamRunningdepois 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
- Um índice é dividido em shards (cada um é um índice Lucene), e cada shard guarda os dados em segments imutáveis que são fundidos pelo merge.
- O número de primários é fixo na criação do índice; o número de réplicas pode mudar a qualquer momento. Uma réplica nunca fica no mesmo node do seu primário, por isso um cluster de um node fica
yellowcom réplicas. - No ECK, node = pod, grupo de nodes =
nodeSet, escalar = mudarcountou acrescentarnodeSetse disco = PVC. Atributos e papéis dos nodes ficam noconfigde cadanodeSet, e o operator aplica as mudanças com rolling restart. - A alocação de shards é controlada por configurações de cluster e de índice: habilitação, watermarks de disco, awareness por atributos (comum e forçada) e filtros.
- O force merge reduz o número de segments e elimina documentos deletados, mas deve ser usado só em índices que não recebem mais escrita.
- A arquitetura hot-warm-cold no ECK é um
nodeSetpor tier, e o_tier_preferencedo índice decide em qual tier os shards ficam.
4.1 - Índices, shards e segments
- Clusters, nodes, and shards
- Near real-time search (refresh e segments)
- Merge settings
- _routing field
- Size your shards
- Split index API
- Shrink index API
4.2 - Nodes, papéis e a tradução para o ECK
- Node roles
- Nodes orchestration (ECK)
- Node configuration (ECK)
- Advanced Elasticsearch node scheduling (ECK)
4.3 - Alocação e roteamento de shards
- Nodes orchestration: restart allocation delay (ECK)
- Shard allocation, relocation, and recovery
- Cluster-level shard allocation and routing settings
- Shard allocation awareness
- Index-level shard allocation filtering
- Data tiers
- Storage recommendations (ECK)
- Sizing and capacity planning
- Miscellaneous cluster settings (cluster.max_shards_per_node)
4.4 - APIs _cat
4.5 a 4.10 - Laboratórios
