Centralized Logging with EFK›02 · Elasticsearch fundamentals
Learning Hub / Observability & Reliability / Centralized Logging with EFK

Lesson 02 of 8 · Modules

Elasticsearch fundamentals

The Elasticsearch concepts that decide whether a logging cluster is fast or on fire: documents and indices, primary and replica shards, mappings (text vs keyword), data streams for logs, node roles, heap and memory, and reading cluster health.

Practitioner
Key wordsindexdocumentshardsreplicasmappingsdynamic mappingkeyword vs textdata streamsnode rolesJVM heapcluster health

Documents, indices and shards

  • A document is one JSON record: here, one log line with its fields.
  • An index is a collection of documents with the same mapping (schema).
  • An index is split into primary shards; each primary has replica copies on other nodes.
  • Shards are the unit of distribution: they move between nodes, recover, and are searched in parallel.

A library (Elasticsearch) keeps books (documents) on shelves (indices). A big shelf is split into boxes (shards) so several librarians can search at once, and each box has a photocopy kept in another room (replica). If one room floods, the photocopy is still there.

Cluster health

Colour Meaning
green All primary and replica shards assigned
yellow All primaries assigned, some replicas not (reduced redundancy)
red At least one primary unassigned: some data isn't searchable or writable

GET _cluster/allocation/explain tells you why a shard isn't assigned (disk watermark, no eligible node, allocation rules…).

Mappings: text vs keyword

{
  "mappings": {
    "properties": {
      "@timestamp":  { "type": "date" },
      "message":     { "type": "text" },
      "log.level":   { "type": "keyword" },
      "kubernetes":  { "properties": {
        "namespace_name": { "type": "keyword" },
        "pod_name":       { "type": "keyword" } } },
      "http.status": { "type": "short" }
    }
  }
}
  • text: analyzed for full-text search (message).
  • keyword: exact values for filters, sorting, aggregations (namespace, level, service).
  • Dynamic mapping creates fields automatically, which is convenient but risky: a field that's a number in one log and a string in another causes mapping conflicts, and free-form keys cause mapping explosion (thousands of fields). Control it with index templates and limits (index.mapping.total_fields.limit).

Data streams for logs

Logs are append-only and time-based. A data stream (e.g. logs-shop-prod) is written to by name, while Elasticsearch creates backing indices (.ds-logs-shop-prod-2026.09.27-000001) and rolls over to a new one by size or age (lesson 08). An index template with "data_stream": {} defines settings and mappings for them. Documents need an @timestamp field, and writes use the create operation.

Nodes, heap and memory

Role Job
master Cluster state, shard allocation (run 3 dedicated masters in production)
data_hot / data_warm / data_cold / data_content Hold shards for each tier
ingest Run ingest pipelines
(coordinating only) Route requests and merge results
  • Recent Elasticsearch versions size the JVM heap automatically from the container's memory limit (about half). Keep the heap below ~31 GB so the JVM can use compressed object pointers.
  • The rest of the memory is filesystem cache, which Elasticsearch relies on heavily for search speed.
  • Set memory requests = limits for Elasticsearch pods and use fast disks (SSD) for hot data.

Shard sizing rules of thumb

  • Aim for shards of roughly 10–50 GB; fewer, larger shards beat many small ones.
  • Use 1 primary shard for small daily log volumes; increase only when a single shard would exceed the target size.
  • Watch the total: there's a default limit of 1,000 shards per non-frozen data node (cluster.max_shards_per_node), and you should stay far below it.

Try it: a single-node playground (Docker)

  1. Run a single-node Elasticsearch 8.x/9.x in Docker following Elastic's "run locally" instructions (security enabled; note the generated password), or use the ECK setup from lesson 03.
  2. Index three documents into test-logs with different fields; GET test-logs/_mapping and look at the dynamic mapping (note text + .keyword multi-fields for strings).
  3. Index a document where status is a string after it was a number, and read the mapping error.
  4. Create an index template with data_stream: {} for logs-lab-*, write to logs-lab-default, then run GET _data_stream.
  5. On a single node, create an index with 1 replica and explain why health is yellow.

Going deeper: cluster internals

  • Master stability matters most: dedicated masters, odd count (3), low load. Large cluster states (many shards and fields) slow everything.
  • Refresh interval (default 1s) trades freshness for indexing throughput; for heavy log ingestion, 30s is often fine.
  • Use _cat/thread_pool/write?v and rejected counts to spot indexing overload; it shows up as back-pressure in Fluent Bit.
  • Mapping changes need new indices (rollover, or reindex); plan templates before production.

Recap

  • Documents in indices, split into primary and replica shards.
  • green/yellow/red is about shard assignment; allocation/explain tells you why.
  • text for full-text, keyword for filters and aggregations; control dynamic mapping with templates.
  • Logs go into data streams with rollover.
  • Dedicated masters, heap ≤ ~31 GB (about half of memory), 10–50 GB shards, and not too many of them.

This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.