Showing posts with label Observability. Show all posts
Showing posts with label Observability. Show all posts

Monday, 3 August 2026

elasticsearch-exporter



Elasticsearch doesn't speak Prometheus. It exposes its internals over its own REST API — GET /_nodes/stats, /_cluster/health, /_cat/indices — as JSON, in Elastic's own shape. Prometheus can't scrape that.

The elasticsearch-exporter is a small sidecar-or-Deployment-shaped translator that sits between the two:
  • it polls those ES REST endpoints on an interval,
  • flattens the JSON into Prometheus text-format metrics,
  • and serves them on /metrics (conventionally :9114) for Prometheus to scrape.
The canonical implementation is prometheus-community/elasticsearch_exporter (formerly justwatchcom/elasticsearch_exporter), packaged as the prometheus-elasticsearch-exporter Helm chart. Elastic also ships a first-party alternative path — Metricbeat's elasticsearch module, or the newer Elastic Agent integration — but those ship into Elasticsearch/Kibana's own monitoring cluster, not into Prometheus, so they don't help a Grafana-alerting-on-Prometheus setup.

The metrics it produces are for example:

  • metric
    • what it gives you 
  • search_jvm_memory_used_bytes{area="heap"}
    • actual JVM heap in use — the thing that actually predicts an ES OOM
  • elasticsearch_jvm_memory_max_bytes{area="heap"}
    • configured heap ceiling (-Xmx), so you can take a real ratio
  • elasticsearch_jvm_gc_collection_seconds_*
    • GC pressure; sustained old-gen GC is the pre-OOM tell
  • elasticsearch_breakers_tripped
    • circuit breakers firing — ES rejecting work to avoid OOM
  • elasticsearch_cluster_health_status
    • green/yellow/red, unassigned shards                                 │

The exporter is a separate deployable — ECK does not install it.

Prometheus Metrics



The 4 Core Prometheus Metric Types


Prometheus defines four primary metric types. Choosing the right type depends on how the value behaves over time.


                              ┌───────────────────────────┐
                              │ Prometheus Metric Types   │
                              └─────────────┬─────────────┘
                                            │
         ┌───────────────────┬──────────────┴───────────────┬────────────────────┐
         ▼                   ▼                              ▼                    ▼
  ┌──────────────┐    ┌──────────────┐              ┌──────────────┐     ┌──────────────┐
  │    Gauge     │    │   Counter    │              │  Histogram   │     │   Summary    │
  ├──────────────┤    ├──────────────┤              ├──────────────┤     ├──────────────┤
  │ Value goes   │    │ Value ONLY   │              │ Groups values│     │ Calculates   │
  │ UP and DOWN  │    │ increases    │              │ into buckets │     │ quantiles    │
  │ (Snapshot)   │    │ (Cumulative) │              │ (Client-side)│     │ (Client-side)│
  └──────────────┘    └──────────────┘              └──────────────┘     └──────────────┘

1. Gauge


  • Behavior: Can increase, decrease, or stay the same.
  • Use Case: Current state, temperatures, memory usage, concurrent connections, replica counts.
  • PromQL Functions: avg_over_time(), max_over_time(), direct evaluation.

In Prometheus (and OpenMetrics standards), a Gauge is a metric that represents a single numerical value that can arbitrarily go up and down.

It acts like a digital display or a vehicle's speedometer—it gives you a point-in-time snapshot of a current state.

Understanding the Gauge Type


Because a Gauge can fluctuate freely, it is used to measure snapshot values, current state levels, and instantaneous statuses.

  • Example Real-World Analogy: A car's speedometer (can go from 0 to 70 mph, back to 30 mph), ambient temperature, or fuel tank level.
  • Kubernetes/KSM Examples:
    • kube_cronjob_status_last_successful_time: Holds a Unix timestamp (moves forward or stays fixed).
    • kube_deployment_status_replicas_available: Number of ready pods (e.g., 3 → 5 → 2).
    • node_memory_MemAvailable_bytes: Available system memory in bytes.

Key Feature: Instantaneous Operations


Because gauges fluctuate, you do not run rate or increase functions like rate() or increase() on them in PromQL. Instead, you query their current value directly or perform operations like running averages (avg_over_time()), min/max checks, or scalar math (e.g., time() - gauge).



2. Counter


  • Behavior: A cumulative metric that only increases (or resets to 0 upon process restart). It never decreases naturally.
  • Use Case: Counting total occurrences of events (e.g., total HTTP requests served, total pipeline errors, network bytes transmitted).
  • PromQL Functions: rate(), irate(), increase().

Why use a Counter instead of a Gauge? A counter allows PromQL's rate() function to accurately calculate "per-second rates" while automatically handling process restarts (resets).

3. Histogram


  • Behavior: Samples observations (usually things like request durations or payload sizes) and counts them in configurable buckets. It also provides a sum of all observed values.
  • Exposed Data:
    • <basename>_bucket{le="<upper_bound>"}: Counter of observations with value <= upper bound.
    • <basename>_count: Total number of observations (Counter).
    • <basename>_sum: Sum of all observed values (Counter).
  • Use Case: Measuring latencies, response times, or request sizes where you want to calculate percentiles (e.g., p95, p99) on the server side using PromQL (histogram_quantile()).

4. Summary


  • Behavior: Similar to a histogram, but calculates configurable quantiles (e.g., 0.50, 0.90, 0.99) directly on the client application side over a sliding time window.
  • Exposed Data:
    • <basename>{quantile="0.95"}: The 95th percentile value.
    • <basename>_count: Total observations.
    • <basename>_sum: Sum of observations.
  • Use Case: When client-side percentile calculation is required and you don't need to aggregate quantiles across multiple instances in Prometheus.


Summary Comparison Matrix


Metric Type
  • Can Decrease?
  • Typical PromQL Functions
  • Best Used For

Gauge
  • Yes
  • Direct value, avg_over_time()
  • Current state, memory usage, counts of current objects

Counter
  • No (only on restart)
  • rate(), increase()
  • Total count of events over time, request volume

Histogram
  • No (bucket counts increase)
  • histogram_quantile(), rate()
  • Request latencies, payload sizes (aggregatable)

Summary
  • No (counts/sums increase)
  • Direct quantile query
  • Request latencies (pre-calculated on client)




Container Metrics


container_memory_rss



container_memory_rss is a Prometheus metric (exposed by cAdvisor) that measures a container's Resident Set Size—specifically, the amount of physical RAM allocated to non-reclaimable, non-file-backed memory.

Key Technical Breakdown


Under the hood in Linux cgroups, container_memory_rss tracks:
  • Anonymous Memory: Process heap allocations, execution stack, and memory allocated via malloc or mmap(MAP_ANONYMOUS).  
  • Swap Cache: Memory swapped out to disk that is being brought back into RAM.

What it EXCLUDES:

Unlike standard Linux host RSS metrics, container_memory_rss in cAdvisor excludes file-backed page caches (memory used to cache files read from disk). Because of this, it only measures memory that cannot be automatically freed by the Linux kernel under memory pressure.  


container_memory_rss vs. Other Container Metrics


To understand its role, it helps to see how it fits into cAdvisor's other core memory metrics:

Metric
  • Includes
  • Purpose / Key Characteristic

container_memory_rss
  • Heap + Stack (Anonymous memory)
  • Stable indicator of process memory footprint. Does not fluctuate with disk I/O.

container_memory_working_set_bytes
  • Heap + Stack + Active Page Cache
  • What Kubernetes actually monitors. Fluctuates with file reads/writes.

container_memory_usage_bytes
  • Heap + Stack + Active Cache + Inactive Cache
  • Raw total RAM usage. Can be misleading because inactive cache is easily reclaimed by the OS.

Why is container_memory_rss Important?

  • Memory Leak Detection: Because it excludes cached file reads, container_memory_rss provides a much cleaner signal for application-level memory leaks. If this metric steadily climbs over time without dropping, your application (e.g., Go heap, JVM heap, Node process) is holding onto unmanaged memory. 
  • Debugging OOM Kills: While Kubernetes triggers OOMKills based on container_memory_working_set_bytes hitting resource limits, container_memory_rss helps you determine why it happened:  
    • High RSS + High Working Set --> Application memory leak or underestimated heap limit.
    • Low RSS + High Working Set --> Heavy disk I/O / file caching (e.g., reading massive log files or database indexes into memory).  

When container_memory_rss is NOT a relevant metric?


Example:

WiredTiger is the default storage engine that MongoDB (here, the Percona Server for MongoDB / PSMDB cluster) uses to actually read and write data to disk. In <ticketID> it matters specifically because of how it uses memory, which is what breaks those four Grafana alert rules.

The relevant behavior:

WiredTiger keeps a large in-memory cache. It maintains its own cache of frequently-accessed data and indexes, and it deliberately sizes that cache to roughly half the container's memory limit (the issue cites ~10 GiB against a 21Gi limit on rs0/rs2, and it's tunable per <ticketID>). WiredTiger runs its own eviction, targeting about 80% cache fill under normal operation and triggering aggressive eviction around 95%.

That cached data shows up as page cache, not RSS. This is the crux of the ticket. container_memory_rss counts anonymous/resident process memory but excludes the OS page cache — and for a WiredTiger workload the file-backed pages (the cache) are the bulk of the real footprint. So rss / limit sits structurally pinned around 50-58% no matter how much memory pressure the pod is actually under. An alert keyed on container_memory_rss > 0.90 can therefore never fire for a WiredTiger container. It's dead. (alert is defined as: container_memory_rss{container="mongod"} / limit > 0.90)

But you can't just switch to working-set either. container_memory_working_set_bytes does include those active file pages, so on the busy replicas it reads 95-96%. That looks alarming but is expected and healthy: it's the WiredTiger cache sitting at its designed ~half-of-limit size and WT's own eviction watermark. The kernel reclaims those pages under real pressure, and the evidence is that nothing in the mongodb namespace has ever been OOMKilled. Flipping the metric would just move the rule from never-firing to always-firing.

The signal that actually matters is WiredTiger cache fill. Because WT stalls user operations when its cache can't evict fast enough, the meaningful early-warning metric is cache utilization (the existing MongoDB WiredTiger cache fill — above 95% for 30m rule from <ticketID>), not container RSS or working set. That's why the ticket argues the RSS rules are redundant, not just broken, and leans toward deleting them.

So in this issue's context, "WiredTiger" is essentially shorthand for "a workload whose memory lives mostly in a large, self-managed, page-cache-backed database cache" — which is exactly the profile that makes RSS-based memory alerting meaningless and makes cache-fill the correct signal instead. (The same logic applies to the Elasticsearch rules, where the JVM heap plays the analogous role.)


Example PromQL Query


To track application heap growth without the noise of filesystem caching, query RSS like this:

# Rate of container RSS growth over 5-minute intervals
rate(container_memory_rss{namespace="production", container!=""}[5m])


container_memory_working_set_bytes


...

container_memory_usage_bytes


...

Introduction to cAdvisor (Container Advisor)




cAdvisor (short for Container Advisor) is an open-source tool created by Google to collect, aggregate, process, and export resource usage and performance metrics for running containers.

It acts as a daemon that monitors resource isolation parameters, historical resource usage, and network statistics directly from the host node.



How It Works


cAdvisor doesn't require instrumenting code inside containers. Instead, it inspects the node environment where containers run:
  • Queries Linux Kernel Structures: It pulls raw performance data directly from kernel mechanisms—primarily cgroups (control groups) for CPU, memory, and disk utilization, and network interfaces for throughput metrics.
  • Discovers Running Containers: It automatically detects running containers across multiple runtimes (Docker, containerd, CRI-O, systemd containers).
  • Exposes Metrics: It formats gathered data and exposes it over a /metrics HTTP endpoint (primarily in Prometheus format) for scrapers to ingest.


Role in Kubernetes


In Kubernetes, cAdvisor is not deployed as a standalone pod. Instead, it is built directly into the kubelet binary that runs on every node.
  • The kubelet uses cAdvisor internally to monitor local container resource usage.
  • It exposes cAdvisor metrics under the /metrics/cadvisor endpoint on the kubelet API port (typically 10250).
  • Tools like Prometheus scrape this endpoint to collect system-wide container metrics (container_cpu_usage_seconds_total, container_memory_rss, container_network_transmit_bytes_total, etc.).

Core Capabilities


  • Resource Usage Monitoring: Tracks real-time CPU utilization, memory breakdown (RSS, cache, swap), network I/O, and disk space usage per container.
  • Historical Trend Aggregation: Keeps a small buffer of historical telemetry in memory for local inspection.
  • Multi-Runtime Support: Works out of the box with containerd, Docker, and CRI-compliant container engines.
  • Built-in Web UI: When run as a standalone binary or Docker container outside Kubernetes, it provides a lightweight built-in dashboard for quick visual inspection of container stats.


Summary of Metric Flow


Linux Kernel / cgroups --> cAdvisor --> Prometheus --> Grafana

cAdvisor sits right at the boundary between the underlying host/kernel layer and the monitoring stack, turning low-level kernel counters into structured metrics for alerting and visualization.


Grafana Alerting

 


How it works

  • Grafana alerting periodically queries data sources and evaluates the condition defined in the alert rule
  • If the condition is breached, an alert instance fires
  • Firing instances are routed to notification policies based on matching labels
  • Notifications are sent out to the contact points specified in the notification policy

How to set alerts

  • Alert rules: Create an alert rule to query a data source and evaluate the condition defined in the alert rule. 
    • There are two types of alerts rules:
      • Grafana-managed. Examples:
        • APM
        • AWS
        • Data Quality
        • Kubernetes
        • MongoDB
        • Storage
        • Synthetics
      • Data source-managed. Data sources containing configured alerts rules are for example Mimir or Loki data sources where alert rules are stored and evaluated in the data sources itself. In these data sources you can select Manage alerts via Alerting UI to be able to manage these alerts rules in the Grafana UI as well as in the data source where they were configured.
        • Prometheus
        • Mimir
        • Loki
    • Define the condition that must be met before an alert rule fires
  • Route alert notifications either directly to a contact point or through notification policies for more flexibility
    • Contact points: Configure who receives notifications and how they are sent
    • Notification policies: Configure how firing alert instances are routed to contact points
  • Monitor your alert rules using dashboards and visualizations





Thursday, 19 March 2026

Monitoring and Observability

 

Monitoring vs Observability

In the world of IT and DevOps, monitoring and observability are two related but distinct concepts used to manage system health and performance. 

Core Difference


The simplest way to distinguish them is:
  • Monitoring tells you what is happening (and when). It is reactive and focuses on known problems using predefined metrics.
  • Observability tells you why it is happening. It is proactive and uses the system's outputs to understand its internal state, especially for "unknown unknowns". 

Key Comparison Table


Feature         Monitoring                 Observability
----------           ---------------                    -----------------    
Purpose         Detect known issues Diagnose root causes
Perspective External (symptoms) Internal (system state)
Question         "Is the system healthy?" "Why is it behaving this way?"
Approach Reactive                         Proactive
Focus         "Known knowns"         "Unknown unknowns"
Data Types Metrics, logs                 Metrics, logs, and traces


Analogy: The Car

  • Monitoring is your dashboard. It has dials for speed and fuel, and a "check engine" light. It tells you if you are speeding or if something is broken.
  • Observability is the mechanic’s diagnostic tool. When the "check engine" light comes on, the mechanic plugs in a tool to see exactly which sensor failed and why, without having to take the entire engine apart. 

Common Tools

  • Monitoring Tools: Nagios, Zabbix, Prometheus.
  • Observability Platforms: Datadog, New Relic, Honeycomb, Dynatrace.





Three Pillars of Observability


The three pillars of observability—metrics, logs, and traces—are essential telemetry data types used to understand the internal state of complex, distributed systems. They enable teams to detect, investigate, and resolve performance issues by providing high-level trends, granular event details, and full request-flow paths. 

Metrics


Quantitative measurements over time (e.g., CPU usage, error rates).

Numerical measurements that describe the health, performance, and behavior of a system over time (e.g., CPU usage, error rates, throughput). They are ideal for alerting, capacity planning, and spotting trends or symptoms.


Logs


Granular, timestamped records of discrete events.

Timestamped, granular records of discrete events. They provide the detailed context (text or structured data) necessary to understand exactly what happened within an application or service.


Traces


End-to-end journeys of a single request through a distributed system, showing how different 

Records showing the journey of a single request as it travels through a distributed system, encompassing multiple services. They are critical for pinpointing bottlenecks, latency, or failures in microservices architectures. 

Why They Are Used Together


While metrics indicate that a problem exists, logs provide the context of why it happened, and traces show where it is occurring. Correlating these three data types provides actionable insights rather than just raw data.


Are Logs concern of Monitoring or Observabilty?

Both monitoring and observability deal with logs, but they do so in fundamentally different ways, representing a shift from simply knowing something is broken to understanding why. 

Monitoring is generally used to detect known issues using logs. It is reactive and focuses on pre-defined metrics or alert thresholds, such as alerting when error logs spike or when a specific error code appears.

Observability is used to investigate and understand the "why" behind issues by exploring logs, metrics, and traces together. It is proactive, allowing you to debug complex, distributed systems without needing to know every question ahead of time. 

Comparison: Logs in Monitoring vs. Observability


Feature                 Log Monitoring                                   Log Observability
----------                  ---------------------                                      -------------------------
Primary Question   What went wrong?                                   Why did it go wrong?
Approach        Reactive: Alerts when logs meet criteria   Proactive: Explores data to find root causes
Log Handling        Searchable, indexed logs for active alerts  Contextualized, correlated logs (with traces)
Data Usage       Simple monitoring and basic dashboards   Deep, ad-hoc, and exploratory analysis
Typical Usage       "Error rate > 5%"                                   "Why did this transaction fail?"


How They Work Together

Logs are one of the "three pillars" of observability—alongside metrics and traces—that provide the detailed, granular context necessary for troubleshooting, notes Grafana. 

Monitoring tells you the system is unhealthy (e.g., an alert fires because of high error rates in log files).
Observability allows you to use tools like Splunk or Datadog to dive into the logs and traces to find the specific line of code or database failure causing the issue

In short, monitoring is a component of observability—you cannot have true observability without comprehensive logging. 


Monitoring vs Observability on the example of AWS Lambda


For AWS Lambda, monitoring identifies what is wrong (e.g., an execution failed), while observability reveals why it happened by connecting logs, metrics, and traces across your entire serverless architecture. 

Monitoring AWS Lambda: Detecting the Known


Monitoring focuses on pre-defined health indicators. You use it to track "known-knowns" and trigger reactive alerts when thresholds are breached. 

  • Key Tool: Amazon CloudWatch collects standard metrics automatically.
  • Monitored Metrics:
    • Invocations: The total number of times your function runs.
    • Errors: The count of failed executions.
    • Duration: How long your function takes to run.
    • Throttles: Occurrences where invocations are blocked due to concurrency limits.
  • Example Scenario: You set a CloudWatch Alarm to notify you if your Lambda's error rate exceeds 5%. Monitoring tells you there is a problem, but not the specific line of code that caused it. 

Observability in AWS Lambda: Investigating the Unknown


Observability is a property of the system that allows you to understand its internal state from external outputs. It uses high-cardinality telemetry to investigate complex, distributed issues. 

  • Key Tool: AWS X-Ray provides distributed tracing to visualize the request path across multiple services.
  • Key Elements:
    • Distributed Traces: Seeing a "waterfall" view of a request as it moves from API Gateway to Lambda, then to DynamoDB.
    • Log Insights: Using CloudWatch Logs Insights to run ad-hoc queries across massive log volumes to find specific patterns.
    • Enhanced Instrumentation: Using libraries like AWS Lambda Powertools to add structured logging and context to your telemetry.
  • Example Scenario: You notice high latency in a specific user's request. Using X-Ray, you see that the Lambda function itself is fast, but it is waiting 2 seconds for a downstream third-party API call. Observability provided the "why". 

Comparison Summary for Lambda


Feature   Monitoring (CloudWatch)           Observability (X-Ray + Logs + Metrics)
----------     ---------------------------------            ---------------------------------------------------
Goal   Track health against thresholds.   Understand root causes and behavior.
Questions  "Is my function failing?"           "Why is this specific request slow?"
Visibility   Isolated metrics for one function.   Request paths across multiple services.
Action   Reactive (Alarms/Notifications).   Proactive (Debugging/Optimisation).


---

Friday, 20 February 2026

Grafana Observability Stack

 




Grafana uses these components together as an observability stack, but each has a clear role:


Loki – log database. It stores and indexes logs (especially from Kubernetes) in a cost‑efficient, label‑based way, similar to Prometheus but for logs.

Tempo – distributed tracing backend. It stores distributed traces (spans) from OpenTelemetry, Jaeger, Zipkin, etc., so you can see call flows across microservices and where latency comes from.

Mimir – Prometheus‑compatible metrics backend. It is a horizontally scalable, long‑term storage and query engine for Prometheus‑style metrics (time series).

Alloy – telemetry pipeline (collector). It is Grafana’s distribution of the OpenTelemetry Collector / Prometheus agent / Promtail ideas, used to collect, process, and forward metrics, logs, traces, profiles into Loki/Tempo/Mimir (or other backends).

How Grafana UI relates to them


Grafana UI itself is “just” the visualization and alerting layer:

  • It connects to Loki, Tempo, Mimir (and many others) as data sources.
  • For each backend you configure:
    • A Loki data source for logs.
    • A Tempo data source for traces.
    • A Prometheus/Mimir data source for metrics (Mimir exposes a Prometheus‑compatible API).
  • Grafana then lets you:
    • Build dashboards and alerts from Mimir metrics.
    • Explore logs from Loki.
    • Explore traces from Tempo and cross‑link them with logs/metrics (e.g., click from a log line to a trace, or from a metrics graph into logs/traces).

A useful mental model: Loki/Tempo/Mimir are databases, Alloy is the collector/router, and Grafana is the UI on top.


Are they deployed in the same Kubernetes cluster?


Common patterns:

  • Very common: deploy Loki, Tempo, Mimir, Alloy, and Grafana in the same Kubernetes cluster as your apps. This is the typical “in‑cluster LGTM” setup; all telemetry stays inside the cluster and traffic is simple.
  • Also common: run them in a separate observability cluster (or use Grafana Cloud backends), while Alloy/agents run in each workload cluster and ship data over the network. This improves isolation and makes it easier to share one observability stack across many clusters.
  • In smaller setups or dev environments, everything (apps + LGTM + Grafana) often lives in one cluster; in larger/regulated setups, people tend to separate “workload clusters” and an “observability cluster”.

So: they don’t have to be on the same cluster, but it’s perfectly normal (and often simplest) to run Grafana + Loki + Tempo + Mimir + Alloy together in a single Kubernetes cluster and point your apps’ telemetry to Alloy.



Mimir


  • Open-source, horizontally scalable, and highly available Time Series Database (TSDB)
  • Designed by Grafana Labs as an extension for Prometheus
  • Solves the scaling and storage limits of standalone Prometheus servers, allowing enterprises to ingest, store, and query over a billion active metrics

Why is it used?
  • Unlimited Scalability & Long-Term Storage: While Prometheus is typically a single-node application with limited local disk storage, Mimir decouples storage from ingestion. It offloads long-term metric data to cheap object storage like Amazon S3, Google Cloud Storage (GCS), or Azure Blob Storage.
  • Prometheus Compatibility: It uses the same data format as Prometheus and is 100% compatible with PromQL (Prometheus Query Language), making it a drop-in replacement or upgrade for existing Prometheus setups.
  • Multi-Tenancy: It allows different teams or departments to use a single, shared Mimir cluster while keeping their data and queries entirely isolated.
  • High Availability (HA): Mimir uses a microservices-based architecture (distributors, ingesters, queriers, and compactors). This means if a node fails, your incoming metrics are safely replicated elsewhere without data loss

How it Works in Your Stack?
  • Collection: Systems like Prometheus, Grafana Alloy, or the OpenTelemetry Collector scrape and collect system/application metrics.
  • Ingestion: These metrics are sent to Grafana Mimir via the remote_write protocol. Mimir then shards, replicates, and safely stores the data.
  • Visualization: You connect Grafana to Mimir as a data source to build interactive dashboards and set up alerting rules.



Why not using elasticsearch instead of loki, tempo and mimir?


Elasticsearch can replace part of what Loki, Tempo, and Mimir do, but not all of it, and usually with higher cost/complexity for cloud‑native observability.

1. Scope: logs vs full observability


Elasticsearch is a general search and analytics engine that’s great at full‑text search, aggregations, and analytics over documents (including logs).

The LGTM stack is explicitly split by signal:
  • Loki → logs
  • Tempo → traces
  • Mimir → metrics

Each is optimized only for its signal type and integrates tightly with Grafana and modern telemetry standards.

You could plausibly replace Loki with Elasticsearch for logs, but Elasticsearch does not natively replace Tempo (distributed tracing backend) or Mimir (Prometheus‑compatible metrics backend).

2. Logs: Loki vs Elasticsearch


Elasticsearch strengths:
  • Very powerful full‑text search, fuzzy matching, relevance scoring, complex aggregations.
  • Good when you need deep forensic search and advanced analytics on log text.

Loki strengths:
  • Stores logs as compressed chunks plus a small label index, so storage and compute are much cheaper than Elasticsearch for typical Kubernetes logs.
  • Very tight integration with Grafana and the rest of LGTM, and simple, label‑based querying.

Trade‑off: Elasticsearch gives richer search at a high infra + ops cost, Loki gives “good enough” search for operational troubleshooting with much lower cost and operational burden.

3. Traces and metrics: Tempo & Mimir vs “just ES”


Tempo:
  • Implements distributed tracing concepts (spans, traces, service graphs) and OpenTelemetry/Jaeger/Zipkin protocols; the data model and APIs are specialized for traces.
  • Elasticsearch can store trace‑like JSON documents, but you’d have to build/maintain all the trace stitching, UI navigation, and integrations yourself.

Mimir:
  • Is a horizontally scalable, Prometheus‑compatible time‑series database, with native remote‑write/read and PromQL semantics.
  • Elasticsearch can store time‑stamped metrics, but you lose Prometheus compatibility, PromQL semantics, and the whole ecosystem that expects a Prometheus‑style API.

So using only Elasticsearch means you’re giving up the standard metrics and tracing ecosystems and rebuilding a lot of tooling on top of a generic search engine.

4. Cost, complexity, and operational burden


Elasticsearch clusters generally need:
  • More RAM/CPU per node, careful shard and index management, and capacity planning.
  • Storage overhead from full‑text indexes (often 1.5–3× raw log size plus replicas).
Loki/Tempo/Mimir:

  • Are designed for object storage, compression, and label‑only indexing, which dramatically lowers storage and compute requirements for logs and metrics.
  • Have simpler, well‑documented reference architectures specifically for observability.

For a modern Kubernetes‑centric environment, that usually makes LGTM cheaper and easier to run than a single big Elasticsearch cluster for everything.

5. When Elasticsearch still makes sense


You might still choose Elasticsearch (often with Kibana/APM) if:
  • You already have a strong ELK stack and team expertise.
  • Your primary need is deep, flexible text search and analytics over logs, with less emphasis on Prometheus/OTel ecosystems.
  • You want Elasticsearch’s ML/anomaly‑detection features and are willing to pay the operational cost.

But if your goal is a Grafana‑centric, standards‑based (Prometheus + OpenTelemetry) observability platform, LGTM (Loki+Tempo+Mimir, plus Alloy as collector) is a better fit than trying to push everything into Elasticsearch.

---