<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[AI - Elastic Observability Labs]]></title>
    <description><![CDATA[Trusted security news & research from the team at Elastic.]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[AI - Elastic Observability Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltad972c1c27dbefc6/6a88d9782904ea5e8511d473/observability-labs-thumbnail.png</url>
      <link>https://www.elastic.co/observability-labs/blog/category/ai</link>
    </image>
    <link>https://www.elastic.co/observability-labs/blog/category/ai</link>
    <atom:link href="https://www.elastic.co/observability-labs/rss/category/ai.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[en]]></language>
    <lastBuildDate>Wed, 16 Sep 2026 02:07:58 GMT</lastBuildDate>
  <item>
    <title><![CDATA[LLM tracing in Elastic APM: prompts, responses, and token counts in the span view]]></title>
    <description><![CDATA[In a twenty-call agentic trace, you can see which span is using the most tokens and read the prompt that caused it. Both live in Elastic APM, so there is no second tool to run.]]></description>
    <content:encoded><![CDATA[<p>Elastic APM now does LLM tracing in the trace view. The GenAI tab in the span flyout has the whole conversation, so you can read the system prompt, the user messages and the model response, and copy any of them. Every GenAI span row in the waterfall shows input and output token counts, so in an agentic trace with twenty LLM calls you can find the span using the most tokens without opening any of them. Your LLM calls are now in the same waterfall as your database queries and HTTP spans.</p>
<p>Both features follow the <a href="https://github.com/open-telemetry/semantic-conventions-genai/tree/main/docs/gen-ai">OTel GenAI semantic conventions</a> and work with any OTel-instrumented provider. If your framework already emits OTel GenAI span attributes, there is nothing to change.</p>
<h2 id="howotelgenaispansarestructured">How OTel GenAI spans are structured</h2>
<p>A GenAI span stores everything as span attributes. A typical chat span includes:</p>
<ul>
<li><code>gen_ai.provider.name</code>: the provider (<code>openai</code>, <code>anthropic</code>, <code>aws.bedrock</code>, etc.); <code>gen_ai.system</code> is supported as a fallback for older instrumentation.</li>
<li><code>gen_ai.operation.name</code>: the operation type (<code>chat</code>, <code>embeddings</code>, etc.).</li>
<li><code>gen_ai.request.model</code>: the model being called.</li>
<li><code>gen_ai.usage.input_tokens</code>: tokens consumed by the prompt.</li>
<li><code>gen_ai.usage.output_tokens</code>: tokens generated in the response.</li>
<li><code>gen_ai.input.messages</code>, <code>gen_ai.output.messages</code>: conversation messages.</li>
<li><code>gen_ai.system_instructions</code>: the system prompt.</li>
</ul>
<p>Both features read from these attributes:</p>
<p>| Feature | What it shows | Where it appears | Attributes it reads |
| --- | --- | --- | --- |
| <strong>GenAI tab</strong> | Details (operation type, request model, provider, input and output token counts, response model, response ID) and Conversation (system prompt, user messages, model response) | Span flyout in the APM trace view, and the span flyout in Discover | Appears with any <code>gen_ai.*</code> attribute. Conversation needs <code>gen_ai.system_instructions</code>, <code>gen_ai.input.messages</code>, and <code>gen_ai.output.messages</code> |
| <strong>Token count badges</strong> | Input and output token counts for each GenAI span | Every GenAI span row in the trace waterfall | <code>gen_ai.usage.input_tokens</code>, <code>gen_ai.usage.output_tokens</code> |</p>
<h2 id="howtoreadllmpromptsandresponsesinthegenaitab">How to read LLM prompts and responses in the GenAI tab</h2>
<p>When any <code>gen_ai.*</code> attribute is present on a span, the span flyout shows a dedicated <strong>GenAI</strong> tab next to <strong>Metadata</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaeedbb89fd9c6871/6a968cd2144a15728fde3efd/genai-tab-span-details.png" alt="GenAI tab in span details flyout" /></p>
<p>The Details section shows model metadata from the span attributes: operation type, request model, provider, input and output token counts, response model, and response ID. The Conversation section shows the full exchange, populated from <code>gen_ai.system_instructions</code> (system prompt), <code>gen_ai.input.messages</code> (user messages), and <code>gen_ai.output.messages</code> (model response), each with a copy button so you can pull the exact prompt or response out of the trace without scraping text from a formatted table.</p>
<p>All raw span attributes remain accessible on the <strong>Metadata</strong> tab.</p>
<p>The <strong>GenAI</strong> tab is also available in the span flyout in <strong>Discover</strong>, so you can inspect LLM prompts and responses directly alongside your log and trace data without switching to the APM view.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd1ccc4e30c48f8ed/6a968ced36a7416fdf27288b/genai-tab-discover.png" alt="GenAI tab in Discover span flyout" /></p>
<h2 id="whatinstrumentationdoesllmtracingrequire">What instrumentation does LLM tracing require?</h2>
<p>No Kibana-side configuration is needed. The GenAI tab appears automatically when any <code>gen_ai.*</code> attribute is present on a span. Full Conversation support requires the <a href="https://github.com/open-telemetry/semantic-conventions-genai/blob/main/docs/gen-ai/gen-ai-spans.md">OTel GenAI v1.37.0 span-attribute model</a>: <code>gen_ai.input.messages</code>, <code>gen_ai.output.messages</code>, and <code>gen_ai.system_instructions</code>.</p>
<p>Frameworks that emit the older span-events model (<code>gen_ai.user.message</code>, <code>gen_ai.assistant.message</code>, <code>gen_ai.choice</code>) will show the Details metadata section but will not populate the Conversation section. For a current list of compatible instrumentations, see the <a href="https://github.com/open-telemetry/opentelemetry-python-genai/#released-instrumentations">OTel GenAI semantic conventions</a>.</p>
<p>To verify, open the span in Discover, check that <code>gen_ai.input.messages</code> and <code>gen_ai.output.messages</code> are present, and confirm the Conversation section renders.</p>
<p>If your application already sends APM data to Elastic from a GenAI workload, open any GenAI span in the trace view and check for the GenAI tab.</p>
<h2 id="llmtokenusageinthetracewaterfall">LLM token usage in the trace waterfall</h2>
<p>Token count badges now appear on each GenAI span row in the waterfall, so you can scan the full trace without drilling in. In agentic traces with ten or twenty LLM calls, this lets you identify which span is driving token consumption before opening any span.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2d47691c65ef8799/6a968d055f9db7338e560d85/genai-waterfall-tokens.png" alt="Trace waterfall with token counts on GenAI spans" /></p>
<p>Each row shows input and output token count badges sourced from <code>gen_ai.usage.input_tokens</code> and <code>gen_ai.usage.output_tokens</code>. The row label is the span name, which instrumentation frameworks typically set to something like <code>chat gpt-4o-mini</code>.</p>
<p>Waterfall-level token counts are most useful in agentic traces where a chain of LLM calls uses different models or the same model with varying context sizes.</p>
<h2 id="whichllmprovidersdoeselasticapmsupportforgenaitracing">Which LLM providers does Elastic APM support for GenAI tracing?</h2>
<p>Elastic APM's LLM tracing works with any OTel-instrumented provider: the GenAI tab and waterfall token counts use the same OTel attribute schema regardless of which provider your application uses. Provider is read from <code>gen_ai.provider.name</code>, falling back to <code>gen_ai.system</code> for older instrumentation.</p>
<p>The <a href="https://github.com/open-telemetry/semantic-conventions-genai/tree/main/docs/gen-ai">OTel GenAI semantic conventions</a> that enable this provider detection are currently in a <code>Development</code> lifecycle. Check the <a href="https://github.com/open-telemetry/semantic-conventions-genai/releases">release notes</a> before upgrading instrumentation.</p>
<h2 id="howtoenablellmtracinginelasticapm">How to enable LLM tracing in Elastic APM</h2>
<blockquote>
  <p><strong>Availability:</strong> Both features are available as a Technical Preview on Elastic Serverless and will be available as a Technical Preview in Elastic Stack 9.6.</p>
</blockquote>
<p>To try these features:</p>
<ol>
<li>Instrument your GenAI application with an OTel SDK that follows the <a href="https://github.com/open-telemetry/semantic-conventions-genai/tree/main/docs/gen-ai">OTel GenAI semantic conventions</a> (v1.37.0 or later for full Conversation support).</li>
<li>Send traces to <a href="https://www.elastic.co/observability">Elastic Observability</a> using OTLP, the Elastic APM agent, or an EDOT SDK.</li>
<li>Open the <strong>APM</strong> section in Kibana, navigate to a service that makes LLM calls, and open the trace waterfall for any transaction.</li>
</ol>
<p>The GenAI tab appears on any span with at least one <code>gen_ai.*</code> attribute set; token count badges appear when <code>gen_ai.usage.input_tokens</code> or <code>gen_ai.usage.output_tokens</code> are present.</p>
<p>If you don't have a GenAI application to test with, the <a href="https://github.com/jennypavlova/otel-genai-chat-app">otel-genai-chat-app</a> repository is a minimal OpenAI chat app pre-instrumented with EDOT. Set <code>OPENAI_API_KEY</code> and follow the EDOT commands in the <a href="https://github.com/jennypavlova/otel-genai-chat-app#otel-genai-chat-app">README</a> to send traces to Elastic and see both features in action.</p>
<h2 id="whatsnextforllmobservabilityinelasticapm">What's next for LLM observability in Elastic APM</h2>
<p>We're exploring cost estimation per span (estimated spend based on model pricing and token counts, surfaced in the waterfall) and tool call rendering (structured display of tool/function call inputs and outputs for agentic spans).</p>
<p>If you are building GenAI applications and want early access or to share feedback, reach out through the <a href="https://discuss.elastic.co/c/observability">Elastic community forums</a> or open an issue in the <a href="https://github.com/elastic/kibana/issues">kibana repository</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/llm-tracing-elastic-apm-genai-spans</link>
    <guid isPermaLink="false">llm-tracing-elastic-apm-genai-spans</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[What's New]]></category>
    <dc:creator><![CDATA[Jenny Pavlova,Miriam Aparicio,Costas Pipilas]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7b5da648bcacf4aa/6a968bfc5c312610fa43eee4/header.png" length="0" type="image/png"/>
    <pubDate>Tue, 01 Sep 2026 15:22:01 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[From alert to root cause in 3 minutes: automated root cause analysis with Elastic Agent Builder]]></title>
    <description><![CDATA[Automated root cause analysis only works if the agent compares the incident window against the last healthy one. Skip that step and you get a summariser. The read-only skill, the scoped role and the Elastic Workflow are all here.]]></description>
    <content:encoded><![CDATA[<p>An alert fires on checkout latency. Three minutes and 22 seconds later there's an Elastic Observability case open with the root cause, the evidence behind it, and how confident the agent was. Nobody moved between Kibana, chat, tickets and a terminal to get there.</p>
<p>In this article, we'll build that loop end to end. We'll use <a href="https://www.elastic.co/docs/solutions/observability/ai/agent-builder-observability">Elastic Agent Builder</a> to feed logs, traces, metrics, alerts, and runbook context into an agent that investigates a problem, and <a href="https://www.elastic.co/docs/explore-analyze/workflows">Elastic Workflows</a> to execute the known next steps: opening a case, sending a notification, running an enrichment query, or triggering a remediation path.</p>
<p>We'll work through a checkout latency regression as our example, but the same pattern applies to any incident class where your team already knows the manual steps.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li><a href="https://www.elastic.co/cloud">Elastic Cloud</a> or <a href="https://www.elastic.co/docs/deploy-manage/deploy/self-managed">self-managed</a> cluster running 9.4+</li>
</ul>
<p>We'll use a checkout latency regression as the running example. If you want to follow along against your own telemetry, point the queries at your service instead. If you'd rather reproduce the exact incident, the <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/observability-labs/automated-root-cause-analysis-agent-builder/automated-root-cause-analysis-agent-builder.ipynb">supporting notebook</a> simulates it and sets up the role, skill, tool, and workflow for you.</p>
<h2 id="whydashboardsarenotenoughforincidentresponse">Why dashboards are not enough for incident response</h2>
<p>Dashboards show the symptom but cannot choose the next query. A dashboard is still one of the best tools for shared situational awareness, and during an incident the hard work starts after the chart turns red and the engineer still needs to answer a sequence of operational questions.</p>
<ul>
<li><strong>What changed?</strong> You need access to related deploys, alerts, logs, traces, and metrics from the same time window.</li>
<li><strong>What is affected?</strong> You need visibility into services, hosts, users, regions, SLOs, and dependency paths.</li>
<li><strong>What is the likely cause?</strong> You need evidence from telemetry combined with runbooks or previous incident cases.</li>
<li><strong>What is safe to do next?</strong> You need a bounded action that includes proper permissions, an audit trail, and a rollback path.</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6114ae1146e77d77/6a8ea04cf65645830854b8a0/02-dashboard-limits.png" alt="Kibana dashboard with KPI, trend, and breakdown panels showing current state but no next step" /></p>
<p>That last question is where a dashboard stops. It can show the symptom, but it cannot decide which query to run next, which runbook applies, or which workflow should run. Engineers provide that judgment today by moving between Kibana, chat, tickets, terminals, and internal docs.</p>
<p>An SRE control plane keeps the judgment with the engineer while moving more context and more action into the same operational surface.</p>
<h2 id="howautomatedrootcauseanalysisworksstatepolicyandaction">How automated root cause analysis works: state, policy and action</h2>
<p>Automated root cause analysis needs three things in one place: the telemetry, the permissions that bound it, and the actions it can trigger.</p>
<ul>
<li><strong>State:</strong> For SRE work, that state is telemetry in Elasticsearch: logs, traces, metrics, alerts, SLOs, and related operational records.</li>
<li><strong>Policy:</strong> Policy defines who can query which data, which tools an agent can call, which workflows can run, and where a human decision is required.</li>
<li><strong>Action:</strong> Action is a set of known tools and workflows that run with explicit inputs, permissions, and outputs.</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb6dd104b9aa23ca4/6a8ea04fb0dddebce0929d43/03-control-plane.png" alt="Elastic as an SRE control plane: investigate with Agent Builder and tools, decide behind guardrails, act through workflows" /></p>
<p>Agent Builder is useful where the system needs reasoning over messy context, and Workflows are useful where the system needs deterministic execution.</p>
<p>The two can work in both directions; a workflow can call an agent with an <code>ai.agent</code> step when it needs analysis before the next step, and an agent can call a workflow through a workflow tool when a conversation needs a repeatable action.</p>
<h2 id="whatelasticagentbuilderaddstoaiincidentresponse">What Elastic Agent Builder adds to AI incident response</h2>
<p><a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/skills">Agent Builder skills</a> are reusable capability packs. A skill can include instructions, tools, and context that guide an agent through a specific task.</p>
<p>Reusable skill packs matter for SRE work because incident response is rarely a single query. A good investigation has a shape, and root cause analysis is a good example. The useful unit is not "ask the model what happened." It's a repeatable investigation path that starts from an alert, scopes the time window, checks the right telemetry, records uncertainty, and hands a case or workflow a structured result. The agent needs to decide which signal to start from, query the right index, compare the right time windows, inspect related services, and explain the evidence without hiding what it doesn't know.</p>
<p>Elastic includes a built-in Elastic AI Agent for this pattern. Built-in skills are scoped by solution, so the one that carries an SRE incident loop is <code>observability.investigation</code>, alongside platform skills such as <code>dashboard-management</code> that any solution can use. The list shows the short name, so look for <code>investigation</code> in the UI.</p>
<p>The skill ships as Markdown instructions, the same format we use for our own skill in the next section.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0fd3e84ed2a01173/6a8ea052cf1e0e1f3e756709/04-skills.png" alt="The observability.investigation skill in Agent Builder, showing its description and its Markdown instructions" /></p>
<p>There are also out-of-the-box <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/tools">tools</a> such as <code>platform.core.search</code>, <code>platform.core.get_document_by_id</code>, <code>platform.core.get_index_mapping</code>, <code>platform.core.list_indices</code>, <code>platform.core.get_workflow_execution_status</code>, and <code>platform.core.resume_workflow_execution</code>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltef9789bfb6591041/6a8ea056af2548467458e3a4/05-tools.png" alt="Agent Builder Tools page listing the built-in platform.core tools, with the search tool description open" /></p>
<p>Skills guide the work, tools perform bounded operations, and the agent chooses what to use based on the task.</p>
<h2 id="thescenarioacheckoutlatencyregression">The scenario: a checkout latency regression</h2>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6294afc4fcd74760/6a8ea0583e4fd5f1131a8a02/06-scenario.png" alt="Alert triggers an Agent Builder investigation across logs, traces, and metrics, ending in a root cause and a case" /></p>
<p>Deployment <code>2026.07.09.1</code> ships a connection pool misconfiguration to <code>checkout-api</code>. Within minutes, p95 latency goes from 180ms to over 2s and HTTP 500s appear for the first time. Nobody knows yet that the pool is the cause.</p>
<p>The evidence is spread across three signals, and no single one answers the question:</p>
<p>| Signal | What it shows |
| :---- | :---- |
| Logs | <code>PoolExhaustedException</code> and HTTP 500s, only on the new version |
| Traces | The <code>payment-gateway</code> span goes from ~180ms to ~2500ms |
| Metrics | Connection pool pinned at 20 of 20 right after the deploy |</p>
<p>Correlating those three is the work we want the agent to do. That gives us the contract for the rest of this article:</p>
<p>| Contract | Detail |
| :---- | :---- |
| <strong>Input</strong> | Service name and the alert summary |
| <strong>Access</strong> | Read-only search over <code>logs-*</code>, <code>traces-*</code>, and <code>metrics-*</code> |
| <strong>Output</strong> | Likely cause, supporting evidence, confidence, and the next safe action |
| <strong>Side effect</strong> | One Observability case with the analysis attached |</p>
<p>Everything after this point builds one piece of that contract: the skill shapes the investigation, the tool and role bound the access, and the workflow turns the output into a case.</p>
<h2 id="buildareadonlyinvestigationskillinelasticagentbuilder">Build a read-only investigation skill in Elastic Agent Builder</h2>
<p>Let's start with a read-only skill that improves investigation quality without touching production:</p>
<pre><code># Checkout latency investigation

Use this skill when an engineer asks why checkout latency, errors, or failed transactions increased.

Work through the investigation in this order:

1. Identify the affected service, environment, and time range.
2. Query traces for the slowest transactions in that window.
3. Query logs for errors from the same service and dependency path.
4. Compare current error and latency rates with the previous healthy window.
5. Return the likely cause, supporting evidence, confidence level, and the next safe action.

Do not recommend a production change unless there is a workflow tool assigned for that action.

If the evidence is incomplete, say what data is missing.
</code></pre>
<p>This kind of skill is a runbook execution guide, and it keeps the agent consistent across incidents. It also helps less experienced engineers ask better follow-up questions, because the agent can show the next query and explain why it matters.</p>
<p>Without step 4, the agent describes what's happening now and stops there. Comparing against the previous healthy window is what makes it an analysis. And the last line lets the agent say the data is missing instead of guessing.</p>
<h2 id="addaiagentobservabilitytoolswithnarrowpermissions">Add AI agent observability tools with narrow permissions</h2>
<p>Each tool should expose the smallest operation the agent needs, with the smallest data access that still supports the task.</p>
<p>For a read-only investigation agent, the required privileges usually start with searching observability data and inspecting index structure. The <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/permissions">Agent Builder permissions documentation</a> calls out that tools run against Elasticsearch data as the current user, and that read-oriented tools need index privileges such as <code>read</code> and <code>view_index_metadata</code>.</p>
<p>Run this in Dev Tools to create an investigation-scoped role:</p>
<pre><code>POST /_security/role/agent-builder-observability-investigator
{
  "cluster": ["monitor_inference"],
  "indices": [
    {
      "names": ["logs-*", "metrics-*", "traces-*"],
      "privileges": ["read", "view_index_metadata"]
    }
  ],
  "applications": [
    {
      "application": "kibana-.kibana",
      "privileges": ["feature_agentBuilder.read", "feature_actions.read"],
      "resources": ["space:default"]
    }
  ]
}
</code></pre>
<p>This role gives the agent enough access to inspect telemetry while keeping production-changing actions out of scope. The <code>monitor_inference</code> cluster privilege is what lets the agent use the inference endpoints behind Agent Builder, and it grants no data access on its own.</p>
<p>When you add a custom tool, describe it in operational language, because the tool description is part of how the agent decides when to call it. Prefer descriptions like this:</p>
<pre><code>Use this tool to search checkout service logs for errors in a bounded time range.

Required inputs:
- service_name
- environment
- start_time
- end_time

Return:
- matching log samples
- error counts by message
- affected host and pod names when present
</code></pre>
<p>A narrow tool description is much safer than a broad tool that says "search all logs for anything relevant." The agent gets a clear contract, and reviewers can reason about what the tool can and cannot do.</p>
<h2 id="useelasticworkflowsforincidentresponseautomation">Use Elastic Workflows for incident response automation</h2>
<p>Once the investigation path is useful, we can add Workflows for the actions that should be repeatable. With Workflows the control plane becomes operational because it can query more context, ask an agent to summarize evidence, open a case, notify a channel, or call a remediation endpoint. The key is that each step is explicit.</p>
<p>The Workflows editor gives you a validation loop before you save or run anything. Use it to catch syntax issues before the workflow writes to Cases or calls any action.</p>
<p>Go to <strong>Workflows &gt; Create workflow</strong> and paste the following:</p>
<pre><code>name: obs-labs-checkout-control-plane
description: Checkout regression investigation with Agent Builder and case creation.
tags: ["sre-control-plane", "agent-builder", "workflows"]

triggers:
  - type: manual

inputs:
  - name: service_name
    type: string
    default: "checkout-api"
  - name: alert_summary
    type: string
    default: "Checkout API p95 latency increased above 2s and HTTP 500s rose in the last 15 minutes after deployment 2026.07.09.1."

steps:
  - name: rca_analysis
    type: ai.agent
    agent-id: elastic-ai-agent
    create-conversation: true
    with:
      message: |
        Investigate this checkout incident as an SRE would.

        Service: {{ inputs.service_name }}
        Alert: {{ inputs.alert_summary }}

        Search the available logs, traces, and metrics for this service.
        Compare the window before and after the most recent deployment.

        Return a concise likely cause, supporting evidence, confidence, and next safe action.
        If the evidence is incomplete, say what data is missing.

  - name: case_title
    type: ai.agent
    agent-id: elastic-ai-agent
    with:
      conversation_id: "{{ steps.rca_analysis.output.conversation_id }}"
      message: "Produce a short case title for this incident. Output only the title."

  - name: case_description
    type: ai.agent
    agent-id: elastic-ai-agent
    with:
      conversation_id: "{{ steps.rca_analysis.output.conversation_id }}"
      message: "Produce a concise case description. Output only the description."

  - name: create_case
    type: cases.createCase
    with:
      title: "{{ steps.case_title.output.message }}"
      description: "{{ steps.case_description.output.message }}"
      owner: "observability"
      severity: "medium"
      tags: ["sre-control-plane", "agent-builder", "workflows"]

  - name: add_agent_analysis
    type: cases.addComment
    with:
      case_id: "{{ steps.create_case.output.case.id }}"
      comment: |
        ## Agent Builder RCA

        {{ steps.rca_analysis.output.message }}

        Agent conversation: {{ kibanaUrl }}/app/agent_builder/conversations/{{ steps.rca_analysis.output.conversation_id }}
</code></pre>
<p>Each step feeds the next one through its output. <code>ai.agent</code> steps emit a <code>message</code> with the model's text and a <code>conversation_id</code>, and <code>cases.createCase</code> emits the new <code>case.id</code>. Those three fields are the whole contract:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd791f884d5887901/6a8ea080f61d6e99f39d4f76/07-workflow-steps.png" alt="Workflow steps: rca_analysis feeds case_title and case_description through a shared conversation, then a case is created and the analysis added as a comment" /></p>
<p>This workflow doesn't restart anything. It asks Agent Builder to investigate, reuses the same conversation to generate the case title and description, creates an Observability case, and writes the agent analysis back as a case comment.</p>
<p>Two details are worth calling out. The <code>create-conversation: true</code> flag on the first step is what makes the next two steps cheap: <code>case_title</code> and <code>case_description</code> pass the same <code>conversation_id</code>, so the agent already has the investigation in context and doesn't repeat the queries. And we use a manual trigger with a default <code>alert_summary</code> so you can run the sequence before attaching it to a live alert rule. In production, you'd switch the trigger to <code>alert</code> and attach the workflow to the rule that owns that incident class.</p>
<p>Run the workflow with the play button. Our run took 3 minutes and 22 seconds, with <code>rca_analysis</code>, <code>case_title</code>, <code>case_description</code>, <code>create_case</code>, and <code>add_agent_analysis</code> all marked as successful. Almost all of that is the investigation itself: <code>rca_analysis</code> alone took 3 minutes and 5 seconds, while the two case writes finished in about a second each.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0de6f806a10502c9/6a8ea083bc5bb390c5f93c4c/08-workflow-execution.png" alt="Workflow execution view with the five steps successful in 3 minutes and 22 seconds" /></p>
<p>The workflow then wrote an Observability case. The case list shows one open case with the generated checkout title, our tags, medium severity, and one comment.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2bac0ad2a9ed3c6b/6a8ea086f656457ab454b8bf/09-cases-list.png" alt="Observability Cases list showing one open case created by the workflow" /></p>
<p>The case detail is the audit artifact for the investigation. It records the evidence considered, the affected hosts and deployment version, the likely error type, the agent's confidence, and any missing signals.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf370e642a67d50e5/6a8ea0eabc5bb3171ff93c5c/10-case-detail.png" alt="Case detail with the generated description and the Agent Builder RCA comment" /></p>
<p>A useful operational control plane surfaces the limits of its evidence instead of turning uncertainty into a confident claim. If your agent never reports missing data or a lower confidence, that's a signal to tighten the skill instructions, not a sign that every investigation went well.</p>
<p>The read-only automated root cause analysis pattern improves response quality without changing the affected service. Add remediation only when the action is well understood, narrowly scoped, and paired with verification and rollback: clearing one cache key, restarting one worker, shifting traffic away from one unhealthy instance, or running a pre-approved maintenance task.</p>
<h3 id="turnelasticworkflowsintoagenttools">Turn Elastic Workflows into agent tools</h3>
<p><a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/tools/workflow-tools">Workflow tools</a> let an Agent Builder conversation trigger an Elastic Workflow and use its output. This is the bridge from "the agent recommended a next step" to "the agent can offer a known action."</p>
<p>A workflow tool should have a narrow description:</p>
<pre><code>Use this tool only when checkout errors are caused by connection pool exhaustion on a single worker.

The workflow drains and recycles the connection pool for one worker, then verifies that the worker resumes successful requests.

Required input:
- host_name

Do not use this tool for database outages, deploy regressions affecting all hosts, or multi-host failures.
</code></pre>
<p>The description matters because it sets the agent's selection boundary. Note how the last line excludes the very scenario we just investigated: our incident hit both hosts and was caused by a deploy, so the agent should not offer this tool. That's the point. A workflow tool that matches every incident is a workflow tool with no boundary.</p>
<p>The workflow still owns execution. The agent doesn't need to know how to recycle the pool. It only needs to recognize when a known workflow may apply, collect the required input, and present the action to the engineer.</p>
<h2 id="howdoyoustopanaiagentfromchangingproduction">How do you stop an AI agent from changing production?</h2>
<p>An SRE control plane should be built around blast-radius control, which means every action path needs a clear boundary. Use these checks before exposing a workflow as an agent tool:</p>
<p>| Check | Importance |
| :---- | :---- |
| Read-only first | Proves the investigation path before adding production action |
| Narrow input schema | Prevents vague prompts from becoming vague actions |
| Explicit permissions | Keeps the agent limited to the current user's allowed data and actions |
| Dry-run or case-only mode | Lets teams review outputs before enabling remediation |
| Human review for risky steps | Keeps judgment in the loop where impact is high |
| Post-action verification | Confirms that the workflow improved the service instead of only executing a command |</p>
<p>For the review boundary itself, Workflows gives you <code>wait</code> steps, timeouts, and execution history, so a risky path can pause for an approval and still leave an audit trail.</p>
<p>An agent can help gather evidence and propose the next step, but production action should stay inside known workflow paths.</p>
<h3 id="validateagainstoneincidentclassfirst">Validate against one incident class first</h3>
<p>For a real rollout, validate the control plane against one recurring incident class. Track whether the agent finds the right evidence, whether the workflow output is complete enough for review, and whether engineers trust the recommended next step.</p>
<p>Use a simple validation plan:</p>
<ol>
<li>Pick one alert type with a known runbook.</li>
<li>Build a read-only investigation skill for that alert.</li>
<li>Add one or two query tools with scoped index permissions.</li>
<li>Run the agent against historical incidents and compare its summary with the actual case notes.</li>
<li>Add a case-creation workflow and review the output with the owning SRE team.</li>
<li>Only then consider a workflow tool that performs a bounded remediation step.</li>
</ol>
<p>The main failure mode is not that the model gives an imperfect summary. It's granting broad action before the investigation path is proven. Keep the first version boring, scoped, and reviewable.</p>
<h2 id="conclusion">Conclusion</h2>
<p>What we covered:</p>
<ul>
<li>An SRE control plane combines state (telemetry in Elasticsearch), policy (permissions and review boundaries), and action (known tools and workflows).</li>
<li>Agent Builder handles reasoning over messy context, while Workflows handles deterministic execution, and the two can call each other.</li>
<li>A read-only investigation skill turns a runbook into a repeatable investigation path that records uncertainty instead of hiding it.</li>
<li>Scoped roles with <code>read</code> and <code>view_index_metadata</code> on <code>logs-*</code>, <code>metrics-*</code>, and <code>traces-*</code> keep the agent useful without letting it change production.</li>
<li>Reusing a <code>conversation_id</code> across <code>ai.agent</code> steps lets later steps build on the investigation instead of repeating it.</li>
<li>A case-only workflow gives you the full audit artifact before you enable any remediation.</li>
<li>Tool descriptions are a security boundary, not documentation, because they decide when the agent offers an action.</li>
</ul>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://www.elastic.co/docs/solutions/observability/ai/agent-builder-observability">Agent Builder for Observability</a></li>
<li><a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/skills">Agent Builder skills</a></li>
<li><a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/tools">Agent Builder tools</a></li>
<li><a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/tools/workflow-tools">Workflow tools</a></li>
<li><a href="https://www.elastic.co/docs/explore-analyze/workflows/use-cases/ai-augmented-workflows">AI-augmented workflows</a></li>
<li><a href="https://www.elastic.co/docs/explore-analyze/workflows/use-cases/observability/root-cause-analysis">Root cause analysis workflow for observability alerts</a></li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/automated-root-cause-analysis-agent-builder</link>
    <guid isPermaLink="false">automated-root-cause-analysis-agent-builder</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[Agentic Observability]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcd0b2a7ebf36b14d/6a8ea0ee73006e52f1d8d9b3/01-header.png" length="0" type="image/png"/>
    <pubDate>Tue, 25 Aug 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Sleep through the 3am page: automated incident response with Elastic on Red Hat OpenShift]]></title>
    <description><![CDATA[Elastic Observability handles three routine incidents on its own: it scales, restarts or rolls back the workload, then confirms the service recovered, all with the reasoning model inside your own cluster.]]></description>
    <content:encoded><![CDATA[<p>Every operations team knows the 3am page. A service slows down, an alert fires, and someone wakes up to start digging through dashboards, logs, and traces to find the one signal that explains the outage. By the time they find it, customers have already felt it. This post is about a different approach: autonomous SRE, where Elastic Observability handles the routine incident from detection through fix, running entirely inside your own Red Hat OpenShift cluster, so the page that wakes someone up is the exception, not the routine. No deep configuration here, just the high-level picture of how it works and what it changes for the people who run the systems.</p>
<h2 id="whymanualincidentresponsecantkeepupatscale">Why manual incident response can't keep up at scale</h2>
<p>Modern platforms run at a scale the human brain was never meant to triage. A single service can handle tens of thousands of transactions a second, and each one leaves a trail of metrics, logs, and traces. When something breaks, the answer is somewhere in that flood of data, but finding it by hand is slow, and slow is expensive. The AI era is making it worse, not better: data volumes are compounding, and the more tools a team adds, the more scattered the answer becomes.</p>
<p>Three things go wrong in the manual model:</p>
<ul>
<li><strong>Exploding cost and volume.</strong> The sheer amount of telemetry is compounding observability spend, and teams often drop data to control the bill, which means the one signal that explains the outage may not even be there when they look.  </li>
<li><strong>Lost and fragmented context.</strong> The real story usually lives across container logs, infrastructure events, and application traces at once. When those sit in different tools, no single platform correlates them at the moment an alert fires, and stitching them together under pressure is slow and easy to get wrong.  </li>
<li><strong>Slow investigations and rushed calls.</strong> Every minute spent searching for the cause is a minute the outage continues, and a 3am restart made on a hunch can make the incident worse instead of better.</li>
</ul>
<p>The result is long outages, stressed teams, and a Mean Time to Resolution (MTTR) that stays stubbornly high no matter how many dashboards you build. Dashboards show you the problem. They do not fix it.</p>
<h2 id="whatautomatedincidentresponsedoesendtoend">What automated incident response does end to end</h2>
<p>Autonomous SRE means the system handles the full incident loop on its own: it detects the problem, investigates the likely cause, resolves it with a corrective action, and verifies the action worked. The same loop a skilled on-call engineer runs in their head, running continuously and at machine speed.</p>
<p>It follows a simple cycle: <strong>detect, investigate, resolve, verify.</strong></p>
<ol>
<li><strong>Detect.</strong> Elastic Observability continuously collects the metrics, logs, and traces from across the environment and maintains a live system model: an always-current map of your services, hosts, and the dependencies between them. It surfaces the events that actually matter instead of flooding the team with raw alerts.  </li>
<li><strong>Investigate.</strong> When something crosses a threshold, the platform pulls together the related evidence and asks an AI model, one that reads the evidence and explains it in plain language, what is happening, how confident it is, and which other services the problem will affect.  </li>
<li><strong>Resolve.</strong> Based on that diagnosis, a remediation step is carried out, for example, scaling a service, restarting it, or rolling back a recent change, either automatically or with a person's approval.  </li>
<li><strong>Verify.</strong> The system then checks whether the fix worked and the service returned to healthy, and records the whole sequence so a human can review exactly what happened and why.</li>
</ol>
<p>The important word is <em>loop</em>. The system does not stop at an alert or a recommendation. It closes the gap between knowing and doing, which is precisely the gap where outages live.</p>
<h3 id="howelasticobservabilitycorrelatestelemetryforairootcauseanalysis">How Elastic Observability correlates telemetry for AI root cause analysis</h3>
<p>Autonomous action is only as good as the context behind it, and context is where Elastic Observability is strong. It brings the metrics, logs, and traces from across your environment into one place, then builds a live model of how those pieces connect, so the system reasons over the full story rather than a single noisy signal.</p>
<p>That unified view matters for three reasons:</p>
<ul>
<li><strong>Better diagnosis.</strong> Grounding the AI model in real, correlated telemetry rather than a single metric means the diagnosis reflects what is actually happening, not a guess.  </li>
<li><strong>Fewer false moves.</strong> When the evidence is complete, the system is far less likely to act on a symptom and miss the cause.  </li>
<li><strong>A record you can trust.</strong> Every observation, decision, and action is captured, so the incident comes with a built-in audit trail instead of a gap in the story.</li>
</ul>
<p>This is the same foundation Elastic already provides for search and security, applied to keeping services healthy.</p>
<h2 id="whyautomatedincidentresponserunsonredhatopenshift">Why automated incident response runs on Red Hat OpenShift</h2>
<p>The reason this approach works for regulated, sovereign, and on-premises environments is that the entire loop runs inside your own Red Hat OpenShift cluster. Nothing about an incident, not the telemetry, not the diagnosis, not the action, has to leave your walls.  </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt57c8143de2338f9a/6a8e9fcdfa5f3ca2d90e3a40/image_1.png" alt="The whole loop inside one cluster. Elastic Observability, the reasoning model on Red Hat OpenShift AI, and the remediation agent all run on Red Hat OpenShift, so telemetry and decisions never leave the cluster." /></p>
<p>Four things make Red Hat OpenShift the right home for it:</p>
<ul>
<li><strong>The full stack is in-cluster.</strong> Elastic Observability is deployed and managed natively on Red Hat OpenShift through the Elastic Cloud on Kubernetes (ECK) operator, so the data foundation lives next to the workloads it watches. That keeps analysis fast and keeps your data under your control. It runs on managed OpenShift (such as Red Hat OpenShift Service on AWS or Azure Red Hat OpenShift) or self-managed Red Hat OpenShift.  </li>
<li><strong>The reasoning model runs locally too.</strong> Red Hat OpenShift AI serves the language model, for example, IBM Granite, inside the same cluster. The AI that diagnoses your incidents never sends your telemetry to an outside service, which is what makes the approach viable for air-gapped and sovereign deployments.  </li>
<li><strong>Remediation speaks to Red Hat OpenShift natively.</strong> When the system acts, it is performing ordinary Red Hat Kubernetes operations: scaling a deployment, restarting a workload, rolling back to the last good version. These are the same actions your platform team already trusts, now triggered automatically and verified.  </li>
<li><strong>It is a packaged, validated starting point.</strong> The whole pattern ships as a quickstart in the Red Hat catalog, built jointly so a team can stand it up on an existing Red Hat OpenShift environment and see the loop work without assembling the pieces from scratch.</li>
</ul>
<p>The payoff is sovereignty without a tradeoff: you get machine-speed, AI-driven incident response and you keep every byte of telemetry and every decision inside infrastructure you already run.</p>
<h2 id="fromalerttoverifiedfix">From alert to verified fix</h2>
<p>Here is the routine incident, told the way the team experiences it once the loop is in place.  </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1913df630fd924ca/6a8e9fd1f59d7c15d3c93817/image_2.png" alt="From alert to root cause. Raw signals are correlated and reasoned over, then consolidated into a single situation with a named root cause, the blast radius, and a confidence score." /></p>
<p>A service starts to slow down. Response times climb past their healthy range and an alert fires, the same trigger that would normally start a human's night. Instead, the platform immediately gathers the relevant evidence: which service, what changed recently, the related errors and events. It hands that package to the model running on Red Hat OpenShift AI, which returns a plain-language diagnosis, a confidence level, and the <em>blast radius</em>, which is the set of other services this incident will affect if it is left alone.</p>
<p>The recommended action, if it is one you have allowed to run automatically, is then carried out as a native Red Hat Kubernetes operation; the service is scaled to absorb the load, and the platform watches the response times settle back to normal. The entire sequence, from the first alert to the confirmed recovery, is written up as a case: what happened, why, what was done, and the proof it worked. In the morning, the team reviews a finished incident report instead of reconstructing a fire drill.</p>
<p>The engineer's job shifts from <em>finding and fixing</em> to <em>reviewing and improving</em>. That is the real change. The work moves from reactive firefighting to oversight.</p>
<h3 id="whichkubernetesincidentscanberemediatedautomatically">Which Kubernetes incidents can be remediated automatically</h3>
<p>Autonomous response is most valuable on the common, well-understood incidents, the ones that are tedious rather than novel. Each maps to a native Red Hat Kubernetes action the system can take and then verify:</p>
<p>| When this happens | The Red Hat Kubernetes action | And confirms it by |
| ---- | ---- | ---- |
| A service slows down under load | Scales the deployment to add capacity | Watching response times return to normal |
| A service runs out of memory and crashes | Restarts the workload cleanly | Checking it comes back healthy and stays up |
| A recent change breaks something | Rolls back to the last good version | Confirming the service passes its health checks again |</p>
<p>These are the incidents that make up most of the pages a team gets, and they are exactly the ones a closed loop is best suited to take off their plate. Novel or high-stakes incidents still rise to a human, which is by design.</p>
<h2 id="howyoustayincontrolofautomatedremediation">How you stay in control of automated remediation</h2>
<p>Autonomous does not mean unaccountable. The principle is simple: the agent recommends, you decide. The system removes the toil, not the oversight, and a few rules keep humans firmly in charge.  </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc6bce05c0ce3a6e5/6a8e9fd43e7b81f2223189ea/image_3.png" alt="The agent recommends, you decide. A diagnosis is ranked by confidence, then either executed automatically on high confidence or routed to a person for review, and every path ends in verification and a recorded case." /></p>
<ul>
<li><strong>Every action is recorded.</strong> The full sequence, from diagnosis to action to verification, is captured as a reviewable case. Nothing happens off the record.  </li>
<li><strong>Confidence is part of the decision.</strong> Remediation options are ranked by confidence, so high-confidence fixes can run automatically while low-confidence situations are routed to a person instead of acted on.  </li>
<li><strong>Humans set the boundaries.</strong> You decide which actions the system is allowed to take on its own and which require a human to approve. You can keep a person in the loop wherever it matters.  </li>
<li><strong>Your model, in your cluster.</strong> The reasoning runs on Red Hat OpenShift AI inside your own environment, so sensitive telemetry never has to leave your walls. For regulated and sovereign environments, that keeps the whole loop, data and decisions alike, under your control.</li>
</ul>
<p>The goal is a system that earns trust the way a good junior engineer does: it shows its work, it knows when to ask, and it never hides what it did.</p>
<h2 id="howautomatedincidentresponsereducesmttr">How automated incident response reduces MTTR</h2>
<p>The headline outcome is a lower MTTR, because the slow part of an incident, the time before anyone understands it, is largely removed. Teams using Elastic Observability have put real numbers on that shift:</p>
<ul>
<li><a href="https://www.elastic.co/customers/wepay"><strong>WePay</strong></a><strong>, a Chase company, cut the time to find customer impact during incidents by 90%,</strong> improving app performance and releasing product faster.  </li>
<li><a href="https://www.elastic.co/customers/dish-media"><strong>DISH Media</strong></a> <strong>reached 100% visibility, a 10x increase in coverage,</strong> and reduced problem-resolution time for its developers.  </li>
<li><a href="https://www.elastic.co/customers/accolade"><strong>Accolade</strong></a> <strong>monitors about 400 services on Elastic Observability</strong> and doubled developer productivity.</li>
</ul>
<p>Beyond the numbers, the change is structural:</p>
<ul>
<li><strong>Consistency.</strong> The loop responds the same correct way at 3am as it does at 3pm. No fatigue, no improvised fixes.  </li>
<li><strong>Capacity.</strong> Engineers stop spending nights on routine incidents and get that time back for the work only humans can do.  </li>
<li><strong>Resilience.</strong> Faster, more consistent recovery means outages stay small, and small outages are the ones customers never notice.</li>
</ul>
<p>In other words, autonomous SRE does not just make incident response faster. It changes what your best people spend their time on. That direction is also where the market is heading: Elastic was named a Leader in the 2025 Gartner Magic Quadrant for Observability Platforms.</p>
<h2 id="howtogetstartedonyourexistingopenshiftcluster">How to get started on your existing OpenShift cluster</h2>
<p>You do not need to rebuild your stack to begin. Because the pattern ships as a quickstart in the Red Hat catalog, the natural first step is to deploy it on an existing Red Hat OpenShift environment, with Elastic Observability as the unified view across your services, so the context for good decisions already exists. From there you can let the loop run in an advisory mode, where it diagnoses and recommends while a human approves each action, and widen its autonomy as it earns trust on the routine incidents.</p>
<p>Standing it up on Red Hat OpenShift means the observability stack, the reasoning model on Red Hat OpenShift AI, and the remediation agent all come up together inside one cluster, so a team can see the full detect-investigate-resolve-verify loop work end to end before committing to it broadly.</p>
<p>Start by letting the system watch and explain. Let it recommend. Then, incident by incident, let it act. The path to zero-touch operations is incremental, and every step along the way buys back time your team is spending on the 3am page today.</p>
<h2 id="frequentlyaskedquestions">Frequently asked questions</h2>
<p><strong>What is autonomous SRE?</strong> Autonomous SRE is an approach where the observability platform handles the full incident loop on its own: detecting the problem, investigating the likely cause, resolving it with a corrective action, and verifying the service recovered. It automates the routine incident so engineers can focus on the novel and the high-stakes.</p>
<p><strong>Why run autonomous SRE on Red Hat OpenShift?</strong> Running the loop on Red Hat OpenShift keeps the entire stack in-cluster: Elastic Observability (deployed through the ECK operator), the reasoning model on Red Hat OpenShift AI, and the remediation agent all run inside your own environment. No telemetry or decisions leave the cluster, which is what makes the approach a fit for regulated, sovereign, air-gapped, and on-premises deployments. It runs on Red Hat OpenShift Service on AWS, Azure Red Hat OpenShift, or self-managed Red Hat OpenShift.</p>
<p><strong>Does autonomous SRE replace my engineers?</strong> No. It removes the repetitive toil, the routine 3am pages, and shifts engineers from finding-and-fixing to reviewing-and-improving. Humans set which actions are allowed to run automatically, approve anything sensitive, and review every action after the fact.</p>
<p><strong>How does Elastic Observability decide what to do?</strong> It grounds an AI model in real, correlated telemetry, the metrics, logs, and traces from across your environment, plus a live model of how your services depend on each other. The model returns a cause, a confidence level, and the blast radius, and the platform acts only within the boundaries you set.</p>
<p><strong>Is it safe to let a system take action automatically?</strong> Every action is recorded as a reviewable case, remediation options are ranked by confidence, low-confidence situations are routed to a human, and you choose which actions run automatically versus require approval. The remediation steps are ordinary Red Hat OpenShift operations, the same ones your platform team already trusts.</p>
<p><strong>Can this run without sending my data to an outside service?</strong> Yes. With the reasoning model served by Red Hat OpenShift AI inside your cluster, sensitive telemetry never leaves your infrastructure. That makes the approach a fit for regulated, sovereign, and on-premises settings.</p>
<hr />
<p><em>To go deeper on the architecture behind this, see the companion technical walkthrough of the in-cluster autonomous SRE stack on Red Hat OpenShift (coming soon). For the broader picture of how Elastic supports agentic and AI-driven workflows, see <a href="https://www.elastic.co/platform">Elastic's Search AI Platform</a>.</em></p>
<p><em>Gartner, Magic Quadrant for Observability Platforms, 7 July 2025. Gartner does not endorse any vendor, product, or service depicted in its research publications. GARTNER and Magic Quadrant are registered trademarks of Gartner, Inc. and/or its affiliates and are used herein with permission. All rights reserved.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/automated-incident-response-red-hat-openshift</link>
    <guid isPermaLink="false">automated-incident-response-red-hat-openshift</guid>
    <category><![CDATA[Kubernetes]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Matt Isset]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0924018b8ab9b3ab/6a8e9fd773006e0102d8d975/header.png" length="0" type="image/png"/>
    <pubDate>Fri, 21 Aug 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[vLLM Prometheus metrics for self-hosted LLM tuning: TTFT, KV Cache, and GPU Utilization]]></title>
    <description><![CDATA[Tuning a self-hosted vLLM inference using its Prometheus metrics in Elastic Observability — TTFT, KV cache, prefix caching and DCGM GPU counters]]></description>
    <content:encoded><![CDATA[<p>Somewhere in your company there is a team that cannot use Claude, GPT, or Gemini — not because they don't want to, but because their data isn't allowed to leave a jurisdiction, a network boundary, or due to a contract. Claims files. Patient notes, source code under an export-control regime, etc.</p>
<p>That team still wants a model. So the request lands on an SRE's desk, and it sounds deceptively small: <em>"Can you stand up an open-weight model for the claims team? Sixty people. It has to run on our hardware."</em> They aren't even allowed to use a neocloud. There is a cost associated with this, but we won't explore that part. Just the part that covers running the model and observing the configuration.</p>
<p>Standing it up is the easy half. Four manifests and an afternoon, and you have a model answering questions. The hard part arrives a week later, when someone says <em>"it feels slow"</em> and you realize you have no idea whether the deployment is configured well, badly, or catastrophically — and no obvious way to find out.</p>
<p>This guide shows you how Elastic Observability can help you analyze the metrics from the configuration. It walks through tuning a real vLLM deployment using the metrics vLLM already emits. vLLM exposes these on a <code>/metrics</code> endpoint in <strong>Prometheus exposition format</strong> — no instrumentation, no sidecar, no code change — which is why every query in this guide starts from a Prometheus scrape. The goal: turn "it feels slow" into a specific, defensible decision.</p>
<h3 id="testenvironmentvllmonakubernetesclusterusingnvidiaa10gwithdcgmexporterandprometheusmetrics">Test environment: vLLM on a Kubernetes cluster using NVIDIA A10G with dcgm-exporter and Prometheus metrics</h3>
<p>Every figure in this guide was measured on the following stack — one replica, one GPU, no autoscaling.</p>
<ul>
<li><strong>Workload</strong> — Kubernetes-native load generator, scaled from 8 to 32 concurrent requests.</li>
<li><strong>Model</strong> — <code>Qwen/Qwen2.5-3B-Instruct</code>, bf16, <code>--max-model-len 4096</code></li>
<li><strong>Engine</strong> — vLLM <code>v0.23.0</code>, OpenAI-compatible server, Prometheus <code>/metrics</code> on <code>:8000</code></li>
<li><strong>GPU</strong> — NVIDIA A10G, 24 GB — an AWS <code>g5.xlarge</code></li>
<li><strong>Cluster</strong> — Amazon EKS 1.30, tainted GPU node pool with <code>minSize: 0</code></li>
<li><strong>Telemetry</strong> — Prometheus scraping every 15s, plus <code>dcgm-exporter</code> on <code>:9400</code>, shipped via <code>remote_write</code></li>
<li><strong>Analysis</strong> — Elastic Observability, queried with ES|QL and PromQL</li>
<li><strong>Measured</strong> — 2026-07-27</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd14cf1e2fc1644c5/6a859ab0d6cf297af1bafe8a/arch-measurement-stack.png" alt="Architecture of the measurement stack: a load generator driving a vLLM pod running Qwen on a tainted NVIDIA A10G node in Amazon EKS, with dcgm-exporter as a DaemonSet on the same node and a Prometheus pod scraping both and remote-writing to Elastic Observability" /></p>
<h2 id="whyisithardforansretoselfhostandtuneanopenweightllm">Why is it hard for an SRE to self-host and tune an open-weight LLM?</h2>
<p><strong>The difficulty is not the deployment, it's the tuning which has no feedback loop.</strong> vLLM starts, serves, and reports success whether it's configured brilliantly or wastefully. Nothing tells you which.</p>
<p>When loading up the model, your manifest would have this configuration:</p>
<pre><code>      containers:
        - name: vllm
          image: vllm/vllm-openai:v0.23.0  # pin an exact release — metric names shift between versions
          args:
            - "--model=Qwen/Qwen2.5-3B-Instruct"
            - "--max-model-len=4096"         # cap context → predictable KV-cache size
            # A10G has native bf16 — do NOT add --dtype=half (T4-only).
          ports:
            - name: http
              containerPort: 8000            # OpenAI API + /metrics
</code></pre>
<p>But you can run into specific issues, such as:
Hugging Face downloads take minutes. If your cluster expects a server to start in 30 seconds, it will assume the app is dead and kill it mid-download, putting you in an infinite crash loop.</p>
<p>Or you could have a hardware mismatch, and potentially degrade your model’s speed or precision because hardware architectures vary</p>
<p>or a bevy of other issues.</p>
<p>Once the model is finally running, optimizing performance is complete guesswork because default metrics don't tell you if you're being efficient.</p>
<p>You could use <code>nvidia-smi</code>, but this only understands raw hardware state, not application software logic.</p>
<p>Now that you have it running, a few hours to maybe even a day in, the team says "it feels slow." You are, functionally, tuning blind.</p>
<p><strong>How do you tune a self-hosted vLLM deployment?</strong></p>
<p>You're not an inference engineer, you own forty other services besides this one, and you don't have a forward-deployed engineer from a model vendor on call. But tuning an LLM server turns out to need exactly one skill you already have: <strong>reading telemetry and reasoning about saturation.</strong> The only missing piece is telemetry that exists and means something.</p>
<p>It does. vLLM emits a rich Prometheus endpoint out of the box — latency decomposed by inference phase, cache hit rates, batch occupancy, token accounting, completion outcomes. Almost nobody looks at it. The rest of this guide is how to read it.</p>
<hr />
<h2 id="definingtheworkloadsixtyusersshortpromptsstreamingresponses">Defining the workload: sixty users, short prompts, streaming responses</h2>
<p>With the slowness detected and reported, you gather the usage profile of the users. Their usage pattern is as follows:</p>
<ul>
<li><strong>~60 users, but not concurrent.</strong> Realistic peak is <strong>8–12 simultaneous in-flight requests</strong>; sustained is lower.</li>
<li><strong>Short prompts, long answers.</strong> The user pastes a paragraph and asks for a structured summary. Prompts run ~50 tokens; useful answers run 500–1,000.</li>
<li><strong>Interactive, streaming UI.</strong> Perceived speed is dominated by <strong>time to first token (TTFT)</strong>, not total time — the same psychology as a chat interface.</li>
<li><strong>Heavy prompt reuse.</strong> Every request carries the same system prompt and the same policy-language boilerplate.</li>
</ul>
<p>From that, you write down actual service objectives — the step most self-hosted LLM projects skip:</p>
<ul>
<li><strong>TTFT p95 &lt; 300 ms</strong> </li>
<li><strong>inter-token latency &lt; 50 ms</strong> (≥ 20 tokens/sec, faster than reading speed) </li>
<li><strong>zero queueing at 12 concurrent</strong> </li>
<li><strong>error + abort rate &lt; 0.5%</strong></li>
</ul>
<p>Those four numbers are the point of everything that follows. Without them, "it feels slow" has no answer. With them, every metric below passes or fails a stated bar.</p>
<hr />
<h2 id="howdoyougetprometheusmetricsoutofvllmandthegpu">How do you get Prometheus metrics out of vLLM and the GPU?</h2>
<p><strong>There are two sources, and you need both.</strong> </p>
<ul>
<li>vLLM reports on itself — latency by phase, cache hit rates, batch occupancy, token counts — on <code>/metrics</code> at its serving port, with no adapter and no instrumentation work. </li>
<li>The GPU reports separately, through NVIDIA's <code>dcgm-exporter</code> on <code>:9400</code> (NVIDIA Data Center GPU Manager (DCGM) is a suite of tools and libraries designed to comprehensively manage, monitor, and diagnose enterprise-grade NVIDIA GPUs in clusters and data centers). vLLM tells you what the <em>engine</em> thinks is happening; DCGM tells you what the <em>card</em> is actually doing. Step 6 is built entirely on the gap between those two answers.</li>
</ul>
<p>On this EKS cluster that means three things running side by side:</p>
<ul>
<li><strong>vLLM</strong> as a plain Deployment on the tainted <code>g5.xlarge</code> GPU node pool. For one model on one card, a Deployment and a Service is the whole architecture.</li>
<li><strong><code>dcgm-exporter</code></strong> as a DaemonSet, pinned to the same GPU nodes.</li>
<li><strong>A Prometheus server</strong> on a CPU node, scraping both endpoints every 15 seconds.</li>
</ul>
<p>Nothing here is AWS-specific. The production version is the same manifests on an on-prem cluster with L40S or H100 nodes — which is the point of doing this on Kubernetes rather than on a vendor's platform.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd5929cba73afdd08/6a859ab3078290bd30320cc1/arch-vllm-kubernetes.png" alt="vLLM and DCGM exporter running on a tainted GPU node pool in Kubernetes, scraped by Prometheus" /></p>
<h3 id="whataboutkserveandllmd">What about KServe and llm-d?</h3>
<p><strong>Neither was run for this guide, and neither changes where the metrics come from.</strong> KServe and llm-d sit <em>on top of</em> vLLM rather than replacing it — vLLM is still the engine, so <code>/metrics</code> is still the source of every number here. Each adds its own layer on top (KServe: autoscaler and revision metrics; llm-d: router and cache-routing metrics), but the inference telemetry underneath is identical.</p>
<p>What they change is <em>when</em> you need them — and each promotion is triggered by a metric you're already collecting:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte3690f26168dfe1f/6a859ab5982926339a582e1a/scaling-ladder-kserve-llmd.png" alt="The scaling ladder from a plain vLLM Deployment to KServe to llm-d, with the metric that triggers each promotion" /></p>
<hr />
<h2 id="howdoyoushipthevllmmetricsanddcgmmetricstoobservability">How do you ship the vLLM metrics and DCGM metrics to Observability</h2>
<p><strong>A Prometheus scraping inside the cluster only holds hours of data — the metrics have to reach a store you can still query next week.</strong> There are two paths for that, and they are not equivalent.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt757fb6a3447dfa4d/6a859ab8f61d6e3bac9c209b/pipeline-metrics-to-backend.png" alt="Two paths for shipping vLLM and DCGM metrics off the cluster: an OpenTelemetry Collector over OTLP, or a Prometheus server using native remote_write" /></p>
<p><strong>Path A — OpenTelemetry Collector.</strong> Puts inference metrics into the same pipeline as your traces and logs. One collector, one auth path, one mental model. The cost is that Prometheus metrics sent through OTLP get normalized: the stored schema ends up neither purely Prometheus nor purely OTel, and metric names shift.</p>
<p><strong>Path B — native Prometheus <code>remote_write</code>.</strong> Stands up a small Prometheus that scrapes both endpoints and pushes to a backend speaking the remote-write protocol. Names and labels land untouched, <code>_sum</code> / <code>_count</code> / <code>_bucket</code> histogram parts stay intact, and existing queries keep working.</p>
<p><strong>For a tuning exercise, choose Path B.</strong> That's what produced every number in this guide. The reason is narrow but decisive: tuning means comparing against the vLLM documentation and the vLLM community, and both speak in exact metric names. When your chart says <code>vllm:kv_cache_usage_perc</code>, you can search for it.</p>
<p>The deployment is two YAML files — a Prometheus Deployment with two scrape jobs and a <code>remote_write</code> block, plus a Secret holding the backend credential. In this build the destination was an Elastic Serverless project, which exposes a Prometheus remote-write endpoint and lands data in a time-series data stream, <code>metrics-vllm.prometheus-inference</code>.</p>
<p>Two things cost me real time. If your backend has a separate ingest host for OTLP versus its main API, remote-write usually lives on the <strong>main API host</strong>, not the ingest one — pointing at the wrong one returns a 404 that looks like a path error. And the credential needs <strong>index-write privileges</strong>, not just ingest authentication; a key that works fine for OTLP can authenticate successfully and then 403 on every sample. Check <code>prometheus_remote_storage_samples_failed_total</code> on the Prometheus itself before looking anywhere else.</p>
<h3 id="howdoyouconfirmvllmmetricslandedinelastic">How do you confirm vLLM metrics landed in Elastic?</h3>
<p>Once the pipeline is up, look at the field list. Roughly <strong>127 metric series</strong> arrive from a single vLLM pod plus DCGM:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc8fad7c5f6812920/6a859abb682666f68a1ea1af/metrics-landed-discover.png" alt="vLLM and DCGM metric fields arriving in the observability backend, with per-metric sparklines" /></p>
<p>This screen is more useful than it looks. Scanning the field list is how you confirm your vLLM version's exact metric names — <strong>they do shift between major vLLM releases</strong>, and a dashboard built against the wrong names fails silently by returning nothing rather than erroring.</p>
<h3 id="twogotchaswhenqueryingvllmmetrics">Two gotchas when querying vLLM metrics</h3>
<p>Both produce results that look like "the metrics aren't working" when the pipeline is perfectly healthy.</p>
<p><strong>vLLM metric names contain a colon</strong> (<code>vllm:num_requests_running</code>), so they need escaping in most query languages. More insidiously, if you filter by metric <em>name</em> across several metrics and then aggregate only one of them, you get rows back — full of nulls, with no error. Each Prometheus metric lands in its own field, so <strong>naming the field is the filter</strong>; you don't need the name predicate at all.</p>
<p><strong>Counters need rate functions, gauges don't.</strong> <code>vllm:generation_tokens_total</code> is cumulative and monotonic — taking a max of it gives the pod's lifetime total, not its throughput. Gauges like <code>vllm:num_requests_running</code>, <code>vllm:num_requests_waiting</code> and <code>vllm:kv_cache_usage_perc</code> are instantaneous and want max or average. Mixing these up produces charts that are wrong but plausible, which is considerably worse than charts that are empty.</p>
<hr />
<h2 id="whichvllmprometheusmetricsactuallymatter">Which vLLM Prometheus metrics actually matter?</h2>
<p>A reference for the metrics used in this guide, what each tells you, and the condition worth watching. Names are as vLLM emits them; the <code>DCGM_FI_*</code> series come from <code>dcgm-exporter</code>.</p>
<p>| Metric | Type | What it tells you | Watch for |
|---|---|---|---|
| <code>vllm:time_to_first_token_seconds</code> | Histogram | TTFT — how long before the first token streams | p95 above your interactive bar (300 ms here) |
| <code>vllm:inter_token_latency_seconds</code> | Histogram | Streaming speed after the first token | Above ~50 ms is slower than reading speed |
| <code>vllm:e2e_request_latency_seconds</code> | Histogram | Total request time | Rising while TTFT is flat = decode or workload change |
| <code>vllm:request_queue_time_seconds</code> | Histogram | Time waiting for admission | <strong>Earliest saturation signal</strong> — any sustained rise |
| <code>vllm:request_prefill_time_seconds</code> | Histogram | Time processing the prompt | Dominant share = prefill-bound workload |
| <code>vllm:request_decode_time_seconds</code> | Histogram | Time generating tokens | Dominant share = memory-bandwidth-bound |
| <code>vllm:num_requests_running</code> | Gauge | Requests currently being decoded | Batch occupancy |
| <code>vllm:num_requests_waiting</code> | Gauge | Requests queued for admission | Sustained non-zero = add a replica |
| <code>vllm:kv_cache_usage_perc</code> | Gauge | Occupancy of the KV block pool — <strong>not VRAM</strong> | Autoscaling trigger (~60%) |
| <code>vllm:prompt_tokens_total</code> + <code>vllm:prompt_tokens_cached_total</code> | Counters | Prefix-cache hit rate | A drop means routing scattered your prefixes |
| <code>vllm:generation_tokens_total</code> | Counter | Output throughput in tokens/sec | Headline throughput number |
| <code>vllm:request_prompt_tokens</code> + <code>vllm:request_generation_tokens</code> | Histograms | Per-request token sizes; their ratio is the workload's shape | A moving ratio means the workload changed character |
| <code>vllm:iteration_tokens_total</code> | Histogram | Tokens advanced per forward pass | Near 1.0 with concurrency = batching broken |
| <code>vllm:request_success_total{finished_reason}</code> | Counter | Completion outcomes | <code>error</code>/<code>abort</code> = SLO; <code>length</code> share = truncation |
| <code>http_requests_total{status}</code> | Counter | Server-level requests | Catches 4xx and malformed requests <code>vllm:*</code> never sees |
| <code>DCGM_FI_DEV_FB_USED</code> / <code>_FB_FREE</code> | Gauge | Physical VRAM | Capacity planning only — never alert on it |
| <code>DCGM_FI_DEV_GPU_UTIL</code> | Gauge | "A kernel is resident" | <strong>Not</strong> a measure of useful work |
| <code>DCGM_FI_PROF_PIPE_TENSOR_ACTIVE</code> | Gauge | Tensor-core activity | Low here + high DRAM = memory-bound |
| <code>DCGM_FI_PROF_DRAM_ACTIVE</code> | Gauge | Memory-bandwidth activity | High = the bottleneck is bandwidth |
| <code>DCGM_FI_DEV_POWER_USAGE</code> | Gauge | Watts drawn | Pairs with throughput for tokens-per-watt |</p>
<hr />
<h2 id="step1wheredoesvllmlatencygottftprefillanddecodedecomposed">Step 1: Where does vLLM latency go? TTFT, prefill, and decode decomposed</h2>
<p><strong>Decompose total latency into queue, prefill, and decode before optimizing anything.</strong> vLLM reports all three separately, and they have completely different fixes. This is the single most valuable chart in the setup.</p>
<p>The query averages each phase's cumulative time by request count in the same window — in Prometheus terms, <code>rate(vllm:request_prefill_time_seconds_sum) / rate(vllm:e2e_request_latency_seconds_count)</code>, and the same for queue and decode:</p>
<pre><code>TS metrics-vllm.prometheus-inference
| WHERE @timestamp &gt; NOW() - 30 minutes
| STATS reqs = SUM(RATE(`metrics.vllm:e2e_request_latency_seconds_count`)),
        q_s  = SUM(RATE(`metrics.vllm:request_queue_time_seconds_sum`)),
        pf_s = SUM(RATE(`metrics.vllm:request_prefill_time_seconds_sum`)),
        dc_s = SUM(RATE(`metrics.vllm:request_decode_time_seconds_sum`))
    BY minute = BUCKET(@timestamp, 1 minute)
| EVAL queue_ms   = ROUND(q_s  / reqs * 1000, 2),
       prefill_ms = ROUND(pf_s / reqs * 1000, 1),
       decode_ms  = ROUND(dc_s / reqs * 1000, 1)
| KEEP minute, queue_ms, prefill_ms, decode_ms
| SORT minute ASC
</code></pre>
<p>At 8 concurrent requests on the A10G:</p>
<pre><code>minute    | queue_ms | prefill_ms | decode_ms | e2e_ms  | ttft_ms | inter_token_ms
22:55:00  | 0.01     | 37.98      | 1664.69   | 1715.18 | 50.81   | 16.23
22:56:00  | 0.01     | 40.99      | 1670.44   | 1723.78 | 53.50   | 16.20
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9369d6f7665848b2/6a859abe9bf994282509fb28/latency-decomposition.png" alt="Latency decomposition over time — queue, prefill, decode, e2e, TTFT and inter-token latency" /></p>
<p><strong>Read it against the objectives:</strong></p>
<ul>
<li><strong>TTFT is 51 ms against a 300 ms target.</strong> Passing, with almost 6× headroom. Perceived responsiveness is not the problem, whatever was said in the meeting.</li>
<li><strong>Inter-token latency is 16 ms — about 62 tokens/sec</strong> against a 50 ms / 20 tok-s bar. Text arrives roughly three times faster than a person reads it.</li>
<li><strong>Queue time is 0.01 ms.</strong> Nothing is waiting for admission; the engine has capacity to spare at this concurrency.</li>
<li><strong>Decode is 1,665 ms against 38 ms of prefill — 97% of the time is decode.</strong></li>
</ul>
<p>That last line is the finding. <strong>Every optimization aimed at prefill is worthless for this workload.</strong> Chunked prefill, prompt compression, a faster attention kernel for long contexts — all real techniques, all irrelevant when prefill is 2% of the time. Decode is memory-bandwidth-bound, so the levers that would actually move it are <strong>quantization, tensor parallelism across two cards, or a smaller model</strong>. A single chart eliminated the wrong shopping list.</p>
<p><strong>Watch <code>vllm:request_queue_time_seconds</code> specifically.</strong> It is the earliest saturation signal in the entire vLLM metric set — queue time climbs <em>before</em> <code>vllm:num_requests_waiting</code> becomes visibly non-zero, because a request can wait milliseconds for admission without ever registering as queued at scrape time. If you alert on one thing from this section, alert on queue time crossing a small absolute threshold.</p>
<hr />
<h2 id="step2isvllmusingthegpuefficientlykvcacheprefixcachingandbatchoccupancy">Step 2: Is vLLM using the GPU efficiently? KV cache, prefix caching, and batch occupancy</h2>
<p><strong>Four metrics answer this: prefix-cache hit rate, tokens per iteration, KV-cache occupancy, and running-vs-waiting requests.</strong> Latency tells you the experience is good; these tell you whether you're overpaying for it.</p>
<pre><code>TS metrics-vllm.prometheus-inference
| WHERE @timestamp &gt; NOW() - 30 minutes
| STATS ptok    = SUM(RATE(`metrics.vllm:prompt_tokens_total`)),
        cached  = SUM(RATE(`metrics.vllm:prompt_tokens_cached_total`)),
        gen     = SUM(RATE(`metrics.vllm:generation_tokens_total`)),
        it_s    = SUM(RATE(`metrics.vllm:iteration_tokens_total_sum`)),
        it_c    = SUM(RATE(`metrics.vllm:iteration_tokens_total_count`)),
        running = MAX(`metrics.vllm:num_requests_running`),
        waiting = MAX(`metrics.vllm:num_requests_waiting`),
        kv      = MAX(`metrics.vllm:kv_cache_usage_perc`)
    BY minute = BUCKET(@timestamp, 1 minute)
| EVAL prefix_cache_hit_pct = ROUND(cached / ptok * 100, 1),
       tokens_per_iteration = ROUND(it_s / it_c, 2),
       gen_tokens_per_sec   = ROUND(gen, 1),
       kv_cache_pct         = ROUND(kv * 100, 3)
| KEEP minute, prefix_cache_hit_pct, tokens_per_iteration,
       gen_tokens_per_sec, running, waiting, kv_cache_pct
| SORT minute ASC
</code></pre>
<pre><code>minute   | prefix_cache_hit_pct | tokens_per_iteration | gen_tok/s | running | waiting | kv_cache_pct
22:55:00 | 32.5                 | 10.36                | 479.1     | 8.0     | 0.0     | 0.255
22:59:00 | 32.3                 | 10.34                | 480.0     | 8.0     | 0.0     | 0.121
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9c70c464d265fbcd/6a859ac127c5cd723f5f68b4/efficiency-kv-cache.png" alt="Efficiency panel — prefix cache hit rate, tokens per iteration, throughput, running/waiting, KV cache occupancy" /></p>
<p><strong>A 32% prefix-cache hit rate is a third of all prefill work simply not done.</strong> The claims team's requests share a system prompt and policy boilerplate, and vLLM's automatic prefix caching recognizes that. This is a direct argument for <em>raising</em> prompt standardization: the more the application puts shared context in a consistent leading position, the higher this climbs and the cheaper every request gets. It is also the number that will crater the day a naive round-robin load balancer sits in front of two replicas — precisely the condition that justifies llm-d's cache-aware routing.</p>
<p><strong><code>tokens_per_iteration ≈ 10.3</code> with 8 concurrent requests is continuous batching working correctly.</strong> Each forward pass through the model advances about ten sequences at once. If this sat near 1.0 with multiple requests in flight, batching would be broken and you'd be paying full model-forward cost per token per user. This metric proves you're getting vLLM's core value.</p>
<h3 id="whatdoesvllmkv_cache_usage_percactuallymeasure">What does <code>vllm:kv_cache_usage_perc</code> actually measure?</h3>
<p><strong><code>vllm:kv_cache_usage_perc</code> reports occupancy of vLLM's pre-allocated KV block pool — not physical GPU memory.</strong> At startup, vLLM reserves a fraction of VRAM (governed by <code>--gpu-memory-utilization</code>, default 0.9) and carves a KV block pool out of that reservation. This gauge reports how full <em>that pool</em> is.</p>
<p>That's why it read <strong>0.25%</strong> here. Eight concurrent requests holding ~150 tokens each barely touch an A10G's block budget. The pool is large, and correctly so. Push the same server to 32 concurrent requests with 512–1,024 token generations and it moves — to about <strong>2.8%</strong>. Still small.</p>
<p>The instinct is to read a number that low as "the cache is broken" or "I've massively over-provisioned." Both are wrong. <strong>Treating this gauge as a VRAM proxy is the most common self-hosted vLLM configuration error I see.</strong> Step 6 shows exactly how far apart the two are.</p>
<hr />
<h2 id="step3whatshapeisyourvllminferenceworkload">Step 3: What shape is your vLLM inference workload?</h2>
<p><strong>Confirm the workload is what you think it is before tuning anything.</strong> This is the panel that explains a latency "regression" that isn't your fault.</p>
<pre><code>minute   | requests_per_min | avg_prompt_tokens | avg_generated_tokens | avg_max_tokens | gen_to_prompt_ratio
22:55:00 | 282.2            | 49.6              | 103.6                | 103.6          | 2.09
23:01:00 | 276.0            | 50.6              | 103.0                | 103.0          | 2.04
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt23956fea686f8579/6a859ac59829261f8f582e20/workload-shape.png" alt="Workload shape — request rate, prompt and generation token averages, generation-to-prompt ratio" /></p>
<p><strong>The generation-to-prompt ratio is 2.04 — this workload writes twice as much as it reads.</strong> That single ratio <em>is</em> the explanation for Step 1's 97%-decode finding, and it holds for any summarize-and-draft use case. If the team later adds a long-document RAG feature, prompts jump to thousands of tokens, the ratio inverts, the workload becomes prefill-bound, and the correct tuning changes completely. <strong>Watching this ratio is how you learn your workload changed character before someone files a ticket.</strong></p>
<p>Now the column that should bother you: <strong><code>avg_generated_tokens</code> equals <code>avg_max_tokens</code> exactly.</strong> Every request is stopping because it hit its token ceiling, not because the model finished its thought. The screenshot shows the same pattern holding as generation lengths scale to ~685 tokens against a ~777 ceiling.</p>
<p>In a load test that's an artifact of the generator. <strong>In production, that number is users getting cut off mid-sentence</strong> — and it is invisible in every latency metric you have. Which brings us to the metric that catches it.</p>
<hr />
<h2 id="step4arevllmrequestsactuallysucceedingcheckingfinished_reason">Step 4: Are vLLM requests actually succeeding? Checking finished_reason</h2>
<p><strong>Break <code>vllm:request_success_total</code> down by its <code>finished_reason</code> label.</strong> This is the closest thing self-hosted inference has to an application-level SLI, and it catches a failure mode no latency chart can.</p>
<pre><code>TS metrics-vllm.prometheus-inference
| WHERE @timestamp &gt; NOW() - 30 minutes
| STATS completions_per_min = ROUND(SUM(RATE(`metrics.vllm:request_success_total`)) * 60, 2)
    BY minute = BUCKET(@timestamp, 1 minute), finish_reason = labels.finished_reason
| SORT minute ASC, finish_reason
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd26d2ce94404b523/6a859ac818249ce7a418ecaa/health-finish-reason.png" alt="Completion outcomes broken down by finish reason — stop, length, abort, error, repetition" /></p>
<p>Five outcomes, each meaning something different operationally:</p>
<p>| <code>finished_reason</code> | What it means | What to do about it |
|---|---|---|
| <code>stop</code> | The model finished naturally | This is the number you want large |
| <code>length</code> | Truncated at <code>max_tokens</code> | High share means users are cut off — raise the ceiling, or shorten the ask |
| <code>abort</code> | The client disconnected first | Users giving up, or a proxy timeout shorter than your generations |
| <code>error</code> | The engine failed | Your hard SLO signal. Should be flat zero |
| <code>repetition</code> | Degenerate looping output | A sampling-parameter problem, not an infrastructure one |</p>
<p>Under the small load generator the split was <strong>100% <code>length</code></strong> — expected, since it requested a fixed ceiling. In the screenshot, at a mixed load, <code>stop</code> and <code>length</code> run side by side at roughly 51 and 32 completions/min. That mix is the healthy shape: most requests finishing on their own, a minority hitting the ceiling.</p>
<p><strong>The lesson generalizes.</strong> <code>error</code> and <code>abort</code> are what you page on. But the <strong><code>stop</code>-to-<code>length</code> ratio is what you review weekly</strong>, because drift toward <code>length</code> means answers are being truncated and no latency dashboard on earth will tell you.</p>
<p>One blind spot to close: <strong><code>vllm:*</code> metrics only count requests the engine accepted.</strong> Malformed JSON, 4xx, auth failures and dropped connections never reach it. Those live in <code>http_requests_total</code> with <code>status</code> and <code>handler</code> labels — worth a panel beside this one, because "the model is broken" reports frequently turn out to be the gateway in front of it.</p>
<hr />
<h2 id="step5whatnvidiadcgmmetricssaythegpuisactuallydoing">Step 5: What NVIDIA DCGM metrics say the GPU is actually doing</h2>
<p><strong>Use NVIDIA DCGM as an independent witness to vLLM's own account.</strong> Everything so far is the engine describing itself; DCGM describes the silicon.</p>
<pre><code>FROM metrics-vllm.prometheus-inference
| WHERE @timestamp &gt; NOW() - 30 minutes
| STATS gpu_util_pct    = MAX(`metrics.DCGM_FI_DEV_GPU_UTIL`),
        mem_bw_util_pct = MAX(`metrics.DCGM_FI_DEV_MEM_COPY_UTIL`),
        vram_used_mib   = MAX(`metrics.DCGM_FI_DEV_FB_USED`),
        vram_free_mib   = MIN(`metrics.DCGM_FI_DEV_FB_FREE`),
        power_w         = ROUND(MAX(`metrics.DCGM_FI_DEV_POWER_USAGE`), 1),
        temp_c          = MAX(`metrics.DCGM_FI_DEV_GPU_TEMP`)
    BY minute = BUCKET(@timestamp, 1 minute)
| SORT minute ASC
</code></pre>
<p>→ <strong>100% GPU util · 21,483 MiB used / 1,352 MiB free · 240 W · 73 °C</strong></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6aa395617cbe369d/6a859acb43c0b731cd2efb39/dcgm-hardware.png" alt="DCGM GPU hardware panel — utilization, VRAM used and free, power draw, temperature, SM clock" /></p>
<p>100% utilization and 94% VRAM. Under the conventional reading, this card is maxed out and it's time to ask for more hardware. That reading is wrong.</p>
<h3 id="whyisgpuutilizationamisleadingmetricforllminference">Why is GPU utilization a misleading metric for LLM inference?</h3>
<p><strong><code>DCGM_FI_DEV_GPU_UTIL</code> means "a kernel is resident on the device," not "the device is doing useful work."</strong> It reads 100% for a perfectly-tuned server and 100% for a badly-tuned one, so it cannot distinguish them. The DCGM profiling counters can:</p>
<pre><code>gr_engine_active 99.8%  ·  tensor_active 16.6%  ·  dram_active 80.4%
</code></pre>
<p><strong>Read those three together.</strong> The GPU's compute engine is busy essentially all the time — but its <strong>tensor cores, the units that do the actual matrix math, are active only 16.6%</strong>, while <strong>DRAM is active 80.4%</strong>. The card is not computing. It is <strong>waiting on memory.</strong></p>
<p>This is independent, hardware-level confirmation of what Step 1 inferred purely from timings: decode is memory-bandwidth-bound. Two entirely different instruments, two different layers of the stack, one conclusion — the difference between a hypothesis and a finding.</p>
<p>It also permanently retires GPU utilization as a capacity metric for LLM inference. <strong>If your GPU capacity planning rests on <code>DCGM_FI_DEV_GPU_UTIL</code> — and most does — it rests on nothing.</strong></p>
<hr />
<h2 id="step6vllmkvcachevsgpuvramandwhytheydisagree">Step 6: vLLM KV cache vs GPU VRAM, and why they disagree</h2>
<p><strong>Put <code>vllm:kv_cache_usage_perc</code> and physical VRAM usage on one 0–100% axis.</strong> They describe the same GPU memory, they sit at opposite ends of the chart, and both are correct.</p>
<pre><code>minute   | running | gen_tok_s | kv_cache_pct | vram_used_pct | gpu_util | dram_active_pct | tensor_active_pct | tokens_per_watt
00:05:00 | 31      | 1627.5    | 2.82         | 94.1          | 100.0    | 80.4            | 16.6              | 6.80
</code></pre>
<p><strong>KV cache at 2.8%. VRAM at 94.1%.</strong></p>
<p>vLLM pre-allocates a large fraction of VRAM at startup — governed by <code>--gpu-memory-utilization</code>, default 0.9 — and carves its KV block pool out of that reservation. <code>vllm:kv_cache_usage_perc</code> reports occupancy <em>of the pool</em>. DCGM reports what the <strong>driver</strong> sees, which is the whole reservation, whether or not it's holding anything.</p>
<p>The operational consequences are precise, and they're the practical payoff of the entire exercise:</p>
<ul>
<li><strong>Autoscale on <code>vllm:kv_cache_usage_perc</code> and <code>vllm:num_requests_waiting</code>.</strong> These describe admission capacity — whether the engine can take another request right now.</li>
<li><strong>Capacity-plan on VRAM.</strong> This describes physical space — whether a second model could ever fit on this card. (It can't. 1.3 GB free.)</li>
<li><strong>Never alert on VRAM.</strong> It will page you at 3 a.m. for a healthy, mostly-idle server, every single night, forever.</li>
</ul>
<p>And <strong><code>tokens_per_watt</code> — generated tokens divided by power draw, 6.8 here — is a genuine cost-efficiency metric.</strong> It's comparable across GPU models, batch settings and quantization levels in a way that neither latency nor utilization is. When you go back to Finance for card number two, this is the number that makes the argument: <em>at 32 concurrent we sustain 1,627 tokens/sec at 240 watts, and here's what that becomes on an L40S.</em></p>
<hr />
<h2 id="vllmtuningdecisionswhatthesredoeswiththeseprometheusmetrics">vLLM tuning decisions: what the SRE does with these Prometheus metrics</h2>
<p>Six steps, thirty minutes, one server. The verdict against the stated objectives:</p>
<p>| Objective | Measured | Verdict |
|---|---|---|
| TTFT p95 &lt; 300 ms | <strong>51 ms</strong> | Pass, 6× headroom |
| Inter-token &lt; 50 ms | <strong>16 ms</strong> (≈62 tok/s) | Pass |
| Zero queueing at 12 concurrent | <strong><code>queue_ms</code> 0.01, <code>waiting</code> 0</strong> at 8; still 0 at 32 | Pass, large margin |
| Error + abort &lt; 0.5% | <strong>0%</strong> | Pass |</p>
<p><strong>The configuration is correct for this department, and the department is over-provisioned rather than under-provisioned.</strong> That's a defensible, evidence-backed answer to "it feels slow" — and it redirects the investigation to the app, the gateway, or the prompt, which is where the problem actually is.</p>
<p>The concrete follow-ups, each tied to a metric rather than a hunch:</p>
<ol>
<li><strong>Stop optimizing prefill.</strong> — <code>vllm:request_decode_time_seconds</code> vs <code>vllm:request_prefill_time_seconds</code>. Decode is 97% of the time against prefill's 2%, confirmed twice. Chunked prefill and prompt compression are off the table for this workload.</li>
<li><strong>If more throughput is needed, quantize before buying hardware.</strong> — <code>DCGM_FI_PROF_PIPE_TENSOR_ACTIVE</code> (16.6%) vs <code>DCGM_FI_PROF_DRAM_ACTIVE</code> (80.4%). The bottleneck is memory bandwidth, not compute, so an FP8 or AWQ build of the same model is the highest-leverage single change: it moves fewer bytes per token, which is exactly the constrained resource.</li>
<li><strong>Raise the client-side <code>max_tokens</code> ceiling.</strong> — <code>vllm:request_success_total{finished_reason}</code>. Every request finishing on <code>length</code> rather than <code>stop</code> is a user getting cut off mid-answer. This is the one finding that's a live user-experience defect. You need to increase the prompt max_token limit.</li>
<li><strong>Standardize the prompt prefix.</strong> — <code>vllm:prompt_tokens_cached_total</code> over <code>vllm:prompt_tokens_total</code>, 32% today. But it should be better (more like 70%) More shared boilerplate in a consistent leading position raises it, and it's free.</li>
<li><strong>Set the autoscaling trigger now, before it's needed.</strong> — <code>vllm:kv_cache_usage_perc</code> and <code>vllm:num_requests_waiting</code>. Scale when the first crosses ~60% or the second stays above zero. Do <em>not</em> scale on <code>DCGM_FI_DEV_GPU_UTIL</code> — it's pinned at 100% regardless.</li>
<li><strong>Alert on queue time, not on VRAM.</strong> — <code>vllm:request_queue_time_seconds</code> is the earliest true saturation signal; <code>DCGM_FI_DEV_FB_USED</code> is a constant that looks like an emergency.</li>
<li><strong>Revisit when the workload changes shape.</strong> — <code>vllm:request_generation_tokens</code> over <code>vllm:request_prompt_tokens</code>, 2.04 today. When the RAG feature ships that ratio inverts, the workload becomes prefill-bound, and half of this analysis needs redoing. The chart tells you the day it happens.</li>
</ol>
<p>Notice that most of the metrics are looking at the vLLM metrics not the GPU metrics in helping optimize. These are still within the limit of a single service, but when you get <code>vllm:num_requests_waiting</code> to persistently non-zero, then you need to run KServe or you can use KEDA and HPA autoscaling. But you get the metrics to help you determine or allow KServe to scale. So you can see that understanding these metrics are crucial to tuning the inference service. </p>
<p>Elastic Observability can provide this to you.</p>
<hr />
<h2 id="whyselfhostedllmtuningisansreproblemnotanmlproblem">Why self-hosted LLM tuning is an SRE problem, not an ML problem</h2>
<p><strong>Self-hosting an open-weight model is not primarily an ML problem. It is a capacity and saturation problem</strong> — something SREs have been extremely good at for twenty years. The blocker was never skill. It was that the telemetry sat unexamined on a <code>/metrics</code> endpoint nobody scraped, in a schema nobody had mapped to the questions they actually had.</p>
<p>Once it's collected, the reasoning is familiar work in unfamiliar clothes:</p>
<ul>
<li>Decompose latency by phase before optimizing anything (queue / prefill / decode).</li>
<li>Distinguish the logical resource from the physical one (KV block pool ≠ VRAM), and know which each metric describes.</li>
<li>Never trust a single-source utilization number — corroborate the engine's account with the hardware's.</li>
<li>Tie every knob to a metric and every metric to a stated objective, so tuning converges instead of wandering.</li>
</ul>
<p>For a team that isn't allowed to send its data anywhere, that difference — between running a model and <em>operating</em> one — is the whole ballgame. The department gets a capability it's otherwise locked out of, and the SRE gets to answer questions about it with numbers.</p>
<hr />
<h2 id="frequentlyaskedquestions">Frequently asked questions</h2>
<p><strong>What does <code>vllm:kv_cache_usage_perc</code> measure?</strong>
It measures occupancy of vLLM's pre-allocated KV block pool, not physical GPU memory. vLLM reserves a fraction of VRAM at startup (<code>--gpu-memory-utilization</code>, default 0.9) and carves the KV pool from that reservation. In this deployment it read 2.8% while DCGM reported 94.1% VRAM used on the same card at the same moment. Use it as an autoscaling signal; use VRAM for capacity planning.</p>
<p><strong>Why is my vLLM deployment decode-bound?</strong>
Because the workload generates more tokens than it reads. Compare <code>vllm:request_decode_time_seconds</code> against <code>vllm:request_prefill_time_seconds</code>, and check the generation-to-prompt token ratio. In this deployment the ratio was 2.04 — twice as many output tokens as input — which produced 1,665 ms of decode against 38 ms of prefill. Decode is memory-bandwidth-bound, so quantization, tensor parallelism, or a smaller model help; prefill optimizations do not.</p>
<p><strong>Should I autoscale vLLM on GPU utilization?</strong>
No. <code>DCGM_FI_DEV_GPU_UTIL</code> means a kernel is resident on the device, not that the device is doing useful work — it reads 100% for both a well-tuned and a badly-tuned server. Autoscale on <code>vllm:kv_cache_usage_perc</code> (around 60%) or on <code>vllm:num_requests_waiting</code> staying above zero, since those describe whether the engine can admit another request.</p>
<p><strong>Why does my GPU show 100% utilization when it isn't fully used?</strong>
Because GPU utilization only reports kernel residency. Check the DCGM profiling counters instead: in this deployment <code>DCGM_FI_PROF_GR_ENGINE_ACTIVE</code> was 99.8% while <code>DCGM_FI_PROF_PIPE_TENSOR_ACTIVE</code> was only 16.6% and <code>DCGM_FI_PROF_DRAM_ACTIVE</code> was 80.4%. That combination means the GPU is waiting on memory bandwidth rather than computing.</p>
<p><strong>How do I get vLLM metrics into Prometheus?</strong>
vLLM already exposes Prometheus exposition format on <code>/metrics</code> at its serving port — no adapter or instrumentation needed. Point a Prometheus scrape job at the vLLM Service, add a second job for <code>dcgm-exporter</code> on <code>:9400</code>, and use <code>remote_write</code> to ship to long-term storage. Sending through an OpenTelemetry Collector also works but normalizes the metric names, which makes them harder to match against vLLM documentation.</p>
<p><strong>What TTFT should I target for an interactive LLM application?</strong>
For a streaming chat-style interface, a p95 time-to-first-token under 300 ms feels immediate, and inter-token latency under 50 ms (about 20 tokens/sec) outpaces reading speed. This deployment measured 51 ms TTFT and 16 ms inter-token latency on a 3B model on a single NVIDIA A10G, leaving roughly 6× headroom.</p>
<p><strong>Why are all my vLLM requests finishing with <code>length</code>?</strong>
Because they're hitting the <code>max_tokens</code> ceiling instead of the model choosing to stop. Break <code>vllm:request_success_total</code> down by its <code>finished_reason</code> label: a high <code>length</code> share means answers are being truncated mid-sentence. This is invisible in every latency metric, so review the <code>stop</code>-to-<code>length</code> ratio regularly and raise the client-side ceiling if it drifts.</p>
<p><strong>When should I move from a plain vLLM Deployment to KServe or llm-d?</strong>
Move to KServe when <code>vllm:num_requests_waiting</code> is persistently non-zero at peak and you need replicas to appear without human intervention. Move to llm-d when your prefix-cache hit rate collapses across replicas — a sign that load balancing scattered conversations that shared a prefix — or when prefill time starts stealing measurably from decode.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/tune-vllm-prometheus-metrics-elastic</link>
    <guid isPermaLink="false">tune-vllm-prometheus-metrics-elastic</guid>
    <category><![CDATA[Kubernetes]]></category>
    <category><![CDATA[Metrics]]></category>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[What's New]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt33f0247e8d048208/6a859acebc5bb37efef81125/header-vllm-tuning.png" length="0" type="image/png"/>
    <pubDate>Thu, 13 Aug 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[One OTLP endpoint, three teams, zero routing rules: Elasticsearch Streams AI Partitioning]]></title>
    <description><![CDATA[Stop writing log routing rules upfront. See how Streams AI Partitioning reads your data, proposes child streams, and lets you set per-team retention in minutes.]]></description>
    <content:encoded><![CDATA[<p>Ship logs from three teams into one Elastic OTLP endpoint, and <a href="https://www.elastic.co/docs/solutions/observability/streams/management/partitioning">Streams AI Partitioning</a> routes them into per-team child streams, with no routing rules written upfront. In this post, you generate 115 multi-team log records, let the AI analyze what arrived and propose partitions, refine the suggestions in plain English, then set retention independently per team: 90 days for payments, 30 for checkout, 7 for notifications. The entire workflow runs inside Elastic Observability without touching index templates or ILM policies.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc0aa396438b856c5/6a85ce06342d693b6421b14f/image1.png" alt="logs.otel parent stream partitioned into per-team child streams" /></p>
<h2 id="whymultiteamlogroutingneedsstructure">Why multi-team log routing needs structure</h2>
<p>Multi-team Elasticsearch deployments typically converge on a single shared index, which works until teams need different retention periods, sharding settings or processor pipelines.</p>
<p>Before <a href="https://www.elastic.co/docs/solutions/observability/streams/streams">Streams</a>, you had to set up your ingestion scripts to send data to different indices, or data streams, or use the <a href="https://www.elastic.co/docs/reference/enrich-processor/reroute-processor">reroute</a> processor to define the data destination based on some field name.</p>
<p>With AI Partitioning, you let the data arrive first. Then the AI analyzes what showed up, reviews the suggestions it proposes, refines them as needed, and applies them. The result is a set of <a href="https://www.elastic.co/docs/solutions/observability/streams/wired-streams">wired child streams</a> that inherit retention, processors, and schema from the parent, while still allowing you to override any of those per child.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7f1ab44aa0327f17/6a85ce0999083f683140fa31/image2.png" alt="Before and after diagram: single retention for all teams becomes per-team retention with AI Partitioning" /></p>
<h2 id="whatyouneedbeforeusingstreamsaipartitioning">What you need before using Streams AI Partitioning</h2>
<p>Before running the example, three things need to be in place:</p>
<ol>
<li><strong>Wired Streams enabled.</strong> On Elastic Cloud Serverless and Elastic Cloud Hosted 9.4+, wired streams are on by default. If you upgraded from an earlier version, open the Streams app and confirm the toggle is on under Settings.</li>
<li><strong>The Elastic Managed LLM connector.</strong> Go to <strong>Stack Management &gt; Connectors &gt; Create connector &gt; <a href="https://www.elastic.co/docs/reference/kibana/connectors-kibana/elastic-managed-llm">Elastic Managed LLM</a></strong>. Keep in mind the following considerations:</li>
<li>This connector ships preconfigured and does not require an external account or API key.</li>
<li>Any <a href="https://www.elastic.co/docs/reference/kibana/connectors-kibana/gen-ai-connectors">generative AI connector</a> works with the feature.</li>
<li>Note that Elastic Managed LLMs <a href="https://www.elastic.co/pricing/serverless-search">incur a cost per million tokens</a> for input and output.</li>
<li>The account you use needs the <code>manage_inference</code> cluster privilege (the built-in <code>inference_admin</code> role grants it).</li>
<li><strong>The Managed OTLP endpoint URL and an API key.</strong> Open <strong>Cloud Console &gt; Manage &gt; Application endpoints &gt; Ingest</strong>. Copy the <a href="https://www.elastic.co/docs/reference/opentelemetry/motlp">Managed OTLP endpoint URL</a> and generate an API key from that same panel.</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4fa44d988e9a0819/6a85ce0b342d690b0521b153/image3.png" alt="Managed OTLP endpoint URL and Component ID in Elastic Cloud Console" /></p>
<p>Once these are ready, open <strong>Observability &gt; Streams</strong> and confirm a <code>logs.otel</code> wired stream is listed. That stream is the parent we will partition.</p>
<p><em>If <strong>Streams</strong> does not appear in the sidebar, your Kibana space may be using a solution view other than Observability. You can change it in Stack Management &gt; <a href="https://www.elastic.co/docs/deploy-manage/manage-spaces">Spaces</a> &gt; edit your space &gt; set Solution view to <strong>Observability</strong>.</em></p>
<h2 id="generatingmultiteamlogdata">Generating multi-team log data</h2>
<p>Our example uses three apps produced by teams from the same fictional company:</p>
<ul>
<li><code>payments-api</code>: structured JSON with <code>transaction_id</code> and <code>amount_cents</code>. Sensitive data, long retention needs.</li>
<li><code>checkout-web</code>: JSON with <code>cart_id</code> and <code>customer_id</code>. Mostly INFO and ERROR.</li>
<li><code>notifications-worker</code>: less structured, with <code>recipient</code> and <code>channel</code>. High volume.</li>
</ul>
<p>We use a Python script with the <a href="https://opentelemetry.io/docs/languages/python/">OpenTelemetry Python SDK</a> to emit logs for all three teams over OTLP directly to the <a href="https://www.elastic.co/observability-labs/blog/elastic-managed-otlp-endpoint-for-opentelemetry">Managed OTLP endpoint</a>. The full code, including setup and execution, is in the <a href="https://github.com/Delacrobix/Taming-the-Log-Chaos-with-Streams-AI-Partitioning/blob/main/notebook.ipynb">companion notebook</a>.</p>
<p>Each team is defined with a service name, a set of message templates, and a function that generates team-specific attributes:</p>
<pre><code>TEAMS = {
    "payments": {
        "service": "payments-api",
        "messages": [
            ("INFO", "charge captured tx={tx} amount_cents={amt}"),
            ("ERROR", "charge declined tx={tx} reason=insufficient_funds"),
            ("INFO", "refund issued tx={tx} amount_cents={amt}"),
        ],
        "extra": lambda: {
            "transaction_id": f"tx_{random.randint(10000, 99999)}",
            "amount_cents": random.randint(100, 50000),
        },
    },
    "checkout": {
        "service": "checkout-web",
        "messages": [
            ("INFO", "cart updated cart={cart} customer={cust}"),
            ("INFO", "checkout started cart={cart} customer={cust}"),
            ("ERROR", "checkout failed cart={cart} stage=address_validation"),
        ],
        "extra": lambda: {
            "cart_id": f"c_{random.randint(1000, 9999)}",
            "customer_id": f"u_{random.randint(100, 999)}",
        },
    },
    "notifications": {
        "service": "notifications-worker",
        "messages": [
            ("INFO", "email queued recipient={rcp} channel=email"),
            ("INFO", "sms queued recipient={rcp} channel=sms"),
            ("ERROR", "webhook failed recipient={rcp} channel=webhook status=503"),
        ],
        "extra": lambda: {
            "recipient": f"+1555{random.randint(1000000, 9999999)}",
            "channel": random.choice(["email", "sms", "webhook"]),
        },
    },
}
</code></pre>
<p>Setting <code>elasticsearch.index</code> to <code>logs.otel</code> as a resource attribute routes the data into the wired streams root instead of the default OTLP data stream.</p>
<pre><code>def setup_provider():
    resource = Resource.create({"elasticsearch.index": "logs.otel"})
    provider = LoggerProvider(resource=resource)
    provider.add_log_record_processor(BatchLogRecordProcessor(OTLPLogExporter()))
    set_logger_provider(provider)
    handler = LoggingHandler(level=logging.INFO, logger_provider=provider)
    root = logging.getLogger()
    root.setLevel(logging.INFO)
    root.addHandler(handler)
    return provider
</code></pre>
<p>Run the notebook to emit 115 records with an uneven split across teams.</p>
<p>Open <strong>Observability &gt; Streams &gt; <code>logs.otel</code></strong> and switch to the <strong>Partitioning</strong> tab. You should see the ingested data in the preview panel, with attributes like <code>team</code>, <code>service.name</code>, and the team-specific fields visible in the columns.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltae43e2a923d42095/6a85ce0f5c2790885df59b6f/image4.png" alt="Partitioning tab on logs.otel with the Get partitions suggestions button and data preview" /></p>
<h2 id="howstreamsaipartitioningproposeschildstreams">How Streams AI Partitioning proposes child streams</h2>
<p>Streams AI Partitioning analyzes up to 1,000 documents from the parent stream, identifies attribute clustering and cardinality distribution, then proposes child streams, keyed on whichever field best separates the data logically (the ML approach behind this analysis is detailed in <a href="https://www.elastic.co/observability-labs/blog/automated-log-parsing-ml-streams">automated log parsing in Streams</a>).</p>
<p>For the data we just emitted, the AI proposed three child streams keyed on <code>attributes.service.name</code>:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt54fb99bf7450bb1a/6a85ce11d6cf29559dbb093b/image5.png" alt="Review partitioning suggestions keyed on attributes.service.name" /></p>
<p>Each suggestion shows a <a href="https://www.elastic.co/docs/solutions/observability/streams/management/streamlang">Streamlang</a> condition and the percentage of sampled documents that would match. The AI picked <code>service.name</code> because it is a standard OpenTelemetry attribute and a natural identifier for any single workload.</p>
<p>This is a reasonable first proposal, but it is worth thinking about what happens as the deployment grows. Right now there are three services because there are three teams. Tomorrow, Payments might add a <code>refunds-api</code> and a <code>fraud-detector</code>. Each new service would mechanically create another child stream, and over time you would end up with dozens of partitions for what is really just three organizational boundaries.</p>
<p>Elastic's <a href="https://www.elastic.co/docs/solutions/observability/streams/management/partitioning#streams-partitioning-recommendations">partitioning recommendations</a> prefer logical groupings like team or technology type, and aim for tens of partitions rather than hundreds. A <code>team</code>-keyed partitioning is more stable because Payments stays one child stream no matter how many services that team operates.</p>
<h2 id="refiningstreamsaipartitioningsuggestionsinnaturallanguage">Refining Streams AI Partitioning suggestions in natural language</h2>
<p>After reviewing the AI's initial suggestions in Streams AI Partitioning, click <strong>Modify suggestions</strong> to open a free-text prompt.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4c4c5bfe6a47ec17/6a85ce14342d6955af21b159/image6.png" alt="Modify suggestions modal with a plain-English prompt to partition by attributes.team" /></p>
<p>After submitting, the AI regenerates the suggestions. Now the three cards are keyed on <code>attributes.team</code> instead of <code>service.name</code>:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt64002e7c2459f0b9/6a85ce17f5f1a028cc2ec969/image7.png" alt="Regenerated suggestions keyed on attributes.team, all three selected" /></p>
<p>Select all three and click <strong>Accept selected</strong>. A confirmation dialog shows the streams that will be created, each with its <code>WHERE attributes.team equals &lt;team&gt;</code> condition.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt67702e77c93a7775/6a85ce1a9a32f1e5eda7e042/image8.png" alt="Create 3 streams confirmation dialog listing payments, checkout, and notifications" /></p>
<p>Click <strong>Create all streams</strong>. The Partitioning tab now shows the three child streams as part of the <code>logs.otel</code> parent:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc0aa396438b856c5/6a85ce06342d693b6421b14f/image1.png" alt="Partitioning tab showing logs.otel.payments, logs.otel.checkout, and logs.otel.notifications" /></p>
<p>Every new document arriving at the OTLP endpoint will be routed into the correct child according to these conditions. You can open any child stream to verify its data. For example, <code>logs.otel.checkout</code> shows only checkout logs:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd89be7d1789e0361/6a85ce1dba7acc0afb992192/image9.png" alt="logs.otel.checkout child stream data preview showing only checkout-web events" /></p>
<h2 id="howdoyousetperteamlogretentioninelasticsearchstreams">How do you set per-team log retention in Elasticsearch Streams?</h2>
<p>After Streams AI Partitioning creates child streams, each one can have its own lifecycle configuration independently of the parent. Because wired streams use a parent-child hierarchy, every child inherits retention, processors, and schema from the parent by default. You only need to override the partitions you need to change.</p>
<p>Open the child stream <code>logs.otel.payments</code> and go to the <a href="https://www.elastic.co/docs/solutions/observability/streams/management/retention">Retention</a> tab. Click <strong>Edit retention method</strong>, select <strong>Custom period</strong>, and set it to 90 days.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd4181096276f4ba5/6a85ce2098292622ff583944/image10.png" alt="Edit data retention dialog with Custom period set to 90 days" /></p>
<p>Do the same for the other teams with the retention that fits their needs:</p>
<p>| Stream | Retention | Rationale |
| :---- | :---- | :---- |
| <code>logs.otel.payments</code> | 90 days | Sensitive financial data, compliance requirements |
| <code>logs.otel.checkout</code> | 30 days | Useful for debugging, no long-term need |
| <code>logs.otel.notifications</code> | 7 days | High volume, low value after delivery confirmation |</p>
<h2 id="conclusionfromsharedindextoperteamstreamswithoutroutingrules">Conclusion: from shared index to per-team streams, without routing rules</h2>
<p>A shared Elastic deployment with several teams shipping logs is the normal starting point. Organizing it used to mean writing routing rules upfront or maintaining separate index templates and ILM policies by hand.</p>
<p>With Streams AI Partitioning, the workflow is different: you let the data arrive, let the AI read what showed up, refine the suggestions in natural language when they need adjusting, and accept.</p>
<p>The result is a set of child streams that inherit everything from the parent while giving each team its own retention and processing, without any manual template management.</p>
<h2 id="nextsteps">Next steps</h2>
<ul>
<li>Try the <a href="https://github.com/Delacrobix/Taming-the-Log-Chaos-with-Streams-AI-Partitioning/blob/main/notebook.ipynb">companion notebook</a> to generate your own multi-team data.</li>
<li>Read <a href="https://www.elastic.co/observability-labs/blog/simplifying-retention-management-with-streams">How Streams in Elastic Observability Simplifies Retention Management</a> for a deeper look at the retention model.</li>
<li>Read <a href="https://www.elastic.co/observability-labs/blog/elastic-streams-processing">Streams Processing: Stop Fighting with Grok</a> to explore the parsing side of Streams when teams need different processors.</li>
<li>Read <a href="https://www.elastic.co/observability-labs/blog/elastic-observability-streams-ai-logs-investigations">Introducing Streams for Observability</a> for the broader investigation story Streams is part of.</li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/elasticsearch-streams-ai-partitioning-log-routing</link>
    <guid isPermaLink="false">elasticsearch-streams-ai-partitioning-log-routing</guid>
    <category><![CDATA[Logs Analytics]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Aleksandar Panov]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaf5bc8b7f4699ea7/6a85ce2393ffb97251b9148d/header.png" length="0" type="image/png"/>
    <pubDate>Wed, 01 Jul 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[How to trace MCP server tool calls with OpenTelemetry and Elastic APM]]></title>
    <description><![CDATA[Add OpenTelemetry tracing to an MCP server, visualize tool call performance in Elastic APM, and query the trace data from Claude Desktop using the Agent Builder MCP.]]></description>
    <content:encoded><![CDATA[<p>An MCP server is just a Node process, which means OpenTelemetry instrumentation is one <code>--import</code> flag away. What is new is what happens after the traces land in <a href="https://www.elastic.co/docs/solutions/observability/apm">Elastic APM</a>. The same Claude Desktop session that produced them can query them back through the <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/mcp-server">Elastic Agent Builder MCP</a>. The agent analyzes its own tool-call latency, identifies slow tools, and explains failures without leaving the chat. Observability stops being a dashboard that the human checks after the fact and becomes the context the agent uses while working. This post walks through the OTel semantic conventions for MCP, the wrapper pattern for tool spans, and how the loop closes on the Elastic side.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li><p>Elastic Cloud hosted (9.3+) or <a href="https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud/serverless">serverless</a></p></li>
<li><p><a href="https://claude.com/download">Claude Desktop</a></p></li>
<li><p>An MCP server instrumented with the Elastic Distribution of OpenTelemetry (EDOT). We cover how to instrument one below.</p></li>
</ul>
<h2 id="theobservabilitygapinmcpservers">The observability gap in MCP servers</h2>
<p>MCP servers ship with no built-in observability, which means tool-call latency, errors, and performance baselines are invisible to developers.
<a href="https://modelcontextprotocol.io/docs/getting-started/intro">MCP (Model Context Protocol) servers</a> are increasingly used as infrastructure for AI-powered applications, giving AI models access to databases, APIs, internal <a href="https://modelcontextprotocol.io/specification/2025-06-18/server/tools">tools</a>, and business data.
The MCP SDK does not instrument any of it.</p>
<p>The gap shows up in three concrete ways:</p>
<ul>
<li>When a tool call takes 3 seconds, you don't know if the bottleneck is in your business logic, a downstream API, or the data layer.</li>
<li>When a tool call fails, you get the error message but no context about what the server was doing before it failed.</li>
<li>When you add a new tool, you have no baseline to compare its performance against.</li>
</ul>
<p>These are the same problems any backend service faces.
The answer is the same one backend developers have used for years: distributed tracing with <a href="https://www.elastic.co/docs/solutions/observability/apm/opentelemetry">OpenTelemetry</a>.</p>
<p>MCP servers are standard programmatic processes.
There is nothing special about them from an instrumentation perspective.
You add the <a href="https://opentelemetry.io/docs/languages/js/getting-started/nodejs/">OTel SDK</a>, define spans around your tool handlers, and ship traces to your backend.
The only new part is knowing which span names and attributes to use so your traces are meaningful and consistent.</p>
<h2 id="whatwebuilt">What we built</h2>
<p>For this article, we use the <a href="https://www.npmjs.com/package/@modelcontextprotocol/server-everything"><code>@modelcontextprotocol/server-everything</code></a> package, the official reference MCP server published by Anthropic.
It ships with a set of tools that cover the common patterns you will find in real-world MCP servers: simple request/response, parameterized calls, long-running operations, and calls that return structured data.</p>
<p>The server exposes several tools.
In this article we use three of them:</p>
<ul>
<li><p><code>echo</code>: receives a string and returns it unchanged. A minimal request/response tool, useful for verifying that the instrumentation pipeline works end to end.</p></li>
<li><p><code>get-sum</code>: receives two numbers and returns their sum. Represents a parameterized tool with simple business logic.</p></li>
<li><p><code>trigger-long-running-operation</code>: starts a multi-step operation that takes several seconds to complete. Simulates tools that call downstream APIs or run expensive computations.</p></li>
</ul>
<p>We instrumented it with <a href="https://www.elastic.co/docs/reference/opentelemetry/edot-sdks/node/setup">EDOT Node.js</a> (<code>@elastic/opentelemetry-node</code>), which is Elastic's distribution of the OpenTelemetry SDK.
EDOT replaces the five or six individual OTel packages you would otherwise install and adds the <a href="https://www.elastic.co/docs/reference/opentelemetry/compatibility/edot-vs-upstream"><code>elasticapm</code> connector</a> that the Kibana APM UI needs to build its service maps, transaction groupings, and latency charts.
Without that connector, raw OTLP data arrives in Elasticsearch but the APM views have nothing to build from.</p>
<p>The architecture looks like this:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte33fadb139d4917e/6a7f0d48c2cc0911f82495b0/architecture.png" alt="Architecture diagram showing Claude Desktop connected to two MCP servers: an instrumented MCP server that emits OpenTelemetry traces to Elastic APM, and the Elastic Agent Builder MCP that queries those traces back from Elasticsearch" /></p>
<p>This is the loop: Claude executes tools, generates telemetry, and then uses a second MCP to analyze that telemetry.
The observability data becomes something the AI can reason about, not just something that sits in a dashboard waiting for a human to check it.</p>
<p>Both MCP servers are active simultaneously in Claude Desktop.
The instrumented MCP generates telemetry.
The Agent Builder MCP lets us query it.</p>
<p>To connect Claude Desktop to both servers, the <code>claude_desktop_config.json</code> looks like this:</p>
<pre><code>{
  "mcpServers": {
    "everything": {
      "command": "node",
      "args": [
        "--import",
        "/path/to/node_modules/@elastic/opentelemetry-node/import.mjs",
        "/path/to/everything/dist/index.js",
        "stdio"
      ],
      "env": {
        "OTEL_SERVICE_NAME": "everything-mcp-server",
        "OTEL_EXPORTER_OTLP_ENDPOINT": "https://&lt;your-otlp-endpoint&gt;",
        "OTEL_EXPORTER_OTLP_HEADERS": "Authorization=ApiKey &lt;your-api-key&gt;",
        "OTEL_LOG_LEVEL": "none"
      }
    },
    "elastic-agent-builder": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://&lt;your-kibana-url&gt;/api/agent_builder/mcp",
        "--header",
        "Authorization:ApiKey &lt;your-api-key&gt;"
      ]
    }
  }
}
</code></pre>
<p>The <code>--import /path/to/@elastic/opentelemetry-node/import.mjs</code> flag is all it takes for zero-code auto-instrumentation.
But auto-instrumentation only captures HTTP calls, database queries, and other Node.js instrumented libraries.
MCP tool calls are application logic, and application logic needs manual spans.</p>
<h2 id="whattracesmcptoolcallsgenerate">What traces MCP tool calls generate</h2>
<p>The OpenTelemetry specification includes <a href="https://opentelemetry.io/docs/specs/semconv/gen-ai/mcp/">official semantic conventions for MCP</a>.
Following them means your traces are consistent, searchable by name across tools and teams, and compatible with any OTel-aware backend, including Elastic APM.</p>
<p><strong>Span naming</strong> follows the pattern <code>{mcp.method.name} {target}</code>.
For a tool call, this becomes <code>tools/call echo</code> or <code>tools/call get-sum</code>.
This is what you will see as the transaction name in Kibana APM.</p>
<p><strong>Key attributes</strong> on each span:</p>
<p>| Attribute               | Value            | Purpose                   |
| ----------------------- | ---------------- | ------------------------- |
| <code>mcp.method.name</code>       | <code>tools/call</code>     | The MCP protocol method   |
| <code>gen_ai.tool.name</code>      | <code>echo</code>           | The specific tool invoked |
| <code>gen_ai.operation.name</code> | <code>execute_tool</code>   | GenAI semantic convention |
| <code>error.type</code>            | error class name | Set only on failure       |</p>
<p>The wrapper pattern that creates these spans looks like this:</p>
<pre><code>const tracer = trace.getTracer('everything-mcp-server', '1.0.0');

function withToolSpan(toolName, fn) {
  return tracer.startActiveSpan(`tools/call ${toolName}`, (span) =&gt; {
    span.setAttribute('mcp.method.name', 'tools/call');
    span.setAttribute('gen_ai.tool.name', toolName);
    span.setAttribute('gen_ai.operation.name', 'execute_tool');

    try {
      const result = fn();
      span.end();
      return result;
    } catch (err) {
      span.recordException(err);
      span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
      span.setAttribute('error.type', err.constructor.name);
      span.end();
      throw err;
    }
  });
}
</code></pre>
<p>Each tool handler wraps its logic in <code>withToolSpan</code>.
The result is a named span in Elastic APM for every tool invocation, with duration, status, and error details attached.</p>
<p><strong><em>Security note:</em></strong> <em>The OTel spec defines two optional attributes for tool calls:</em> <code>gen_ai.tool.call.arguments</code> <em>and</em> <code>gen_ai.tool.call.result</code>_. Both are flagged as potentially containing sensitive data. The_ <code>get-env</code> <em>tool in the everything server is a good example of why this matters: it returns all environment variables, which may include API keys and credentials. Capture these attributes only if you have confirmed the data is safe to store in your observability backend, and consider masking or filtering at the SDK level before export.</em></p>
<h2 id="kibanaapmexploringmcpserverperformance">Kibana APM: exploring MCP server performance</h2>
<p>After starting Claude Desktop with both MCPs configured and triggering a few tool calls, the <code>everything-mcp-server</code> service appears in Kibana under <strong>Observability &gt; Applications &gt; Services Inventory</strong>:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdbd9c14fcc24ffb0/6a7f0d4bbd2198f4547580d3/services-inventory.png" alt="Kibana APM Services Inventory view showing the everything-mcp-server entry with latency, throughput, and error rate columns populated" /></p>
<h3 id="transactionsview">Transactions view</h3>
<p>Kibana groups traces by transaction name.
Because we follow the semantic conventions, each tool gets its own row: <code>tools/call echo</code>, <code>tools/call get-sum</code>, <code>tools/call trigger-long-running-operation</code>.
You can immediately see latency, throughput, and error rate per tool without any configuration.</p>
<p>This is where the value of consistent span naming becomes concrete.
If you have five different developers adding tools to an MCP server and everyone follows <code>tools/call {toolName}</code>, the APM UI stays organized automatically.</p>
<h3 id="tracewaterfall">Trace waterfall</h3>
<p>Clicking on a specific trace shows the waterfall view.
For a single tool call, the waterfall is straightforward: one span covering the full execution.
If your tool handler makes downstream HTTP requests or database queries that are auto-instrumented, those appear as child spans.
You can see exactly how much time was spent in business logic versus waiting for external calls.</p>
<h3 id="latencydistribution">Latency distribution</h3>
<p>The latency chart shows p50, p95, and p99 distribution across all executions of a given tool.
This makes it easy to distinguish between tools that are consistently fast and those that have occasional outliers.
The <code>trigger-long-running-operation</code> tool, for example, shows a wide distribution depending on how many steps were requested: a useful baseline for understanding expected execution time ranges before setting alerts.</p>
<h3 id="errortracking">Error tracking</h3>
<p>Failed tool calls appear in the Errors panel with their full stack trace, the span attributes attached at the time of failure, and a count of how many times the error has occurred.
If you record the exception with <code>span.recordException(err)</code>, Kibana links the error directly to the trace that produced it.</p>
<h2 id="closingtheloopwiththeagentbuildermcp">Closing the loop with the Agent Builder MCP</h2>
<p>The <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/mcp-server">Elastic Agent Builder MCP</a> lets Claude query its own trace data from Elasticsearch in the same chat session that produced the traces.
The Agent Builder MCP can query any Elasticsearch index the API key has access to, and APM traces are stored under <code>.ds-traces-apm.otel-default-*</code>.
Granting the Agent Builder API key read access to those indices is what closes the loop: the agent that executed the tool calls can now reason about how they performed.</p>
<p>Here is what this looks like in practice.
To generate traces, let's ask in Claude Desktop: <em>"Use the echo tool to say hello, then use get-sum to add 1337 and 42, then run a long-running operation with 3 steps."</em></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf3f00a45a235deb1/6a7f0d4eeab5be043420a709/claude-tool-execution.png" alt="Claude Desktop conversation showing three tool calls executed in sequence: echo, get-sum, and trigger-long-running-operation, each with its result rendered inline" /></p>
<p>Claude executes three tool calls on the instrumented MCP.
Three spans land in Elastic APM.</p>
<p>Now, without leaving the chat, let's try querying the traces by asking: <em>"Search the APM trace data from the last 10 minutes. What tool calls were made, and how long did each one take?"</em></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt74ef63c895e22bbc/6a7f0d51bd219823847580d9/claude-trace-query.png" alt="Claude Desktop chat where the model uses the Elastic Agent Builder MCP to run an ES|QL query against the APM traces index and reports tool names, durations, and status in natural language" /></p>
<p>We can confirm the information against the services data:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb362dbef6539a49f/6a7f0d5305b7b511c418b998/services-data-confirmation.png" alt="Kibana APM transactions view for everything-mcp-server showing the same three tool call transactions and their durations as returned by Claude" /></p>
<p>Claude uses the Agent Builder MCP to run a query against the traces index.
It returns the tool names, durations, and status from the actual trace data, then synthesizes an answer in natural language.</p>
<p>You can go further by asking:</p>
<ul>
<li><p>"Which of those tool calls had the highest p95 latency?"</p></li>
<li><p>"Did any tool calls fail? If so, what was the error message?"</p></li>
<li><p>"Compare the latency of the echo tool vs get-sum across all calls in the last hour."</p></li>
</ul>
<p>Each of these questions translates into an ES|QL query via the Agent Builder's <code>platform.core.execute_esql</code> tool, run against the APM trace indices.</p>
<h2 id="whyelasticformcpobservability">Why Elastic for MCP observability</h2>
<p><strong>The Agent Builder closes the loop:</strong> this is the part that is specific to the Elastic ecosystem.
Because APM data lives in Elasticsearch, and Elasticsearch is queryable via the Agent Builder MCP, you can bring your AI agent's own observability data back into the conversation.
Your AI can reflect on its own performance and spot anomalies.</p>
<p><strong>APM UI built for <a href="https://www.elastic.co/observability-labs/blog/openai-tracing-elastic-opentelemetry">distributed tracing</a>:</strong> Kibana's APM interface is designed for exactly this kind of data: named transactions, trace waterfalls, latency percentiles, error tracking with stack traces, and service maps.</p>
<p><strong>Managed <a href="https://www.elastic.co/docs/reference/opentelemetry/motlp">OTLP</a> endpoint:</strong> Elastic APM accepts OTLP directly since Elastic 8.x.
You point <code>OTEL_EXPORTER_OTLP_ENDPOINT</code> at your APM server and it works.</p>
<p><strong><a href="https://www.elastic.co/observability-labs/blog/elastic-distributions-opentelemetry-ga">EDOT</a> simplifies the setup:</strong> the <code>elasticapm</code> connector is included, which means the APM UI views work without any additional configuration.</p>
<h2 id="conclusion">Conclusion</h2>
<p>MCP servers do not need special observability tooling.
They are programmatic processes, and OpenTelemetry is the right instrument for processes.
The OTel MCP semantic conventions are stable and give you a consistent naming scheme that scales across tools and teams.</p>
<p>What makes the Elastic setup interesting is not the instrumentation itself.
It is the second MCP.
When your observability data lives in Elasticsearch, you can query it from the same AI session that generated it.
That feedback loop is new, and it opens up use cases that dashboards alone cannot cover: real-time anomaly questions, automated triage, and <a href="https://www.elastic.co/observability-labs/blog/elastic-agent-skills-observability-workflows">AI-assisted incident investigation</a> from the chat interface your team is already using.</p>
<h2 id="nextsteps">Next steps</h2>
<ul>
<li><p><a href="https://www.elastic.co/docs/reference/opentelemetry/edot-sdks/node/setup">Set up EDOT Node.js</a></p></li>
<li><p><a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/mcp-server">Configure the Elastic Agent Builder MCP</a></p></li>
<li><p><a href="https://opentelemetry.io/docs/specs/semconv/gen-ai/mcp/">OTel semantic conventions for MCP</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-managed-otlp-endpoint-ga-elastic-cloud-hosted">Managed OTLP endpoint on Elastic Cloud (now GA)</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-openai">Instrumenting Node.js applications with EDOT</a></p></li>
</ul>
<h2 id="frequentlyaskedquestions">Frequently asked questions</h2>
<p><strong>How do I add tracing to an MCP server?</strong>
Use the OpenTelemetry SDK and follow the official OTel MCP semantic conventions. With the Elastic Distribution of OpenTelemetry (EDOT) for Node.js, a single <code>--import</code> flag enables auto-instrumentation for HTTP and database calls. Tool-call spans need to be added manually using a wrapper that sets <code>mcp.method.name</code>, <code>gen_ai.tool.name</code>, and <code>gen_ai.operation.name</code>.</p>
<p><strong>Why are my MCP tool calls slow and how do I find the bottleneck?</strong>
Without tracing, an MCP tool call is a black box: you see the result but not where the time went. Instrument the server with OpenTelemetry, ship traces to Elastic APM, and use the trace waterfall view in Kibana to see exactly how much time was spent in business logic versus downstream HTTP or database calls.</p>
<p><strong>Can I send MCP server traces to Elastic APM without a custom collector?</strong>
Yes. Elastic APM accepts OTLP directly. Set <code>OTEL_EXPORTER_OTLP_ENDPOINT</code> to your APM endpoint and <code>OTEL_EXPORTER_OTLP_HEADERS</code> with an API key, and traces flow in. EDOT bundles the <code>elasticapm</code> connector that the Kibana APM UI needs for service maps and transaction grouping.</p>
<p><strong>What span names and attributes should I use for MCP tool calls?</strong>
Follow the OpenTelemetry MCP semantic conventions: name spans <code>{mcp.method.name} {target}</code> (for example, <code>tools/call echo</code>), and set <code>mcp.method.name</code>, <code>gen_ai.tool.name</code>, and <code>gen_ai.operation.name=execute_tool</code>. On failure, set <code>error.type</code> to the error class name. Consistent naming means the Elastic APM transactions view groups your tool calls automatically.</p>
<p><strong>How is this different from sending MCP traces to Datadog or Grafana?</strong>
Any OTel-compatible backend can receive the traces. The Elastic-specific part is the Agent Builder MCP, which lets the same AI agent that generated the traces query them back from Elasticsearch in natural language. That feedback loop, where the AI reasons about its own tool-call performance, is not available with backends that do not expose their data through an MCP server.</p>
<p><strong>Should I capture MCP tool call arguments and results in my traces?</strong>
The OTel spec defines <code>gen_ai.tool.call.arguments</code> and <code>gen_ai.tool.call.result</code> as optional and warns they may contain sensitive data. Tools like <code>get-env</code>, which returns environment variables, illustrate the risk: API keys and credentials can land in your observability backend. Capture these only when the data is safe to store, and consider masking at the SDK level before export.</p>
<p><strong>Does this work for MCP servers written in languages other than Node.js?</strong>
The OpenTelemetry MCP semantic conventions are language-agnostic. EDOT is available for Node.js, Java, Python, .NET, and other languages, and any of them can send OTLP to Elastic APM. The wrapper pattern shown in this post translates directly: open a span around the tool handler, set the standard attributes, record exceptions on failure.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/mcp-tracing-opentelemetry-elastic-apm</link>
    <guid isPermaLink="false">mcp-tracing-opentelemetry-elastic-apm</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt01a77347914d1229/6a7f0d57c2e9145f21016c0c/header_image.png" length="0" type="image/png"/>
    <pubDate>Thu, 14 May 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Kubernetes observability: MCP specialist agents for safer EKS triage]]></title>
    <description><![CDATA[Scope a specialist EKS MCP agent for cluster checks while the Elastic AI Agent triages; fix a service misconfiguration using the specialist agent in a few prompts.]]></description>
    <content:encoded><![CDATA[<p>Elastic Observability shows you which services and edges in your service map are failing. You may still need to access details like live kubernetes service specs and containerPort to targetPort mapping, which still reside at the cluster. They can be made available in Elasticsearch via EKS MCP. The fix is to equip your Elastic AI Agent with a focused set of EKS tools, through a specialist agent. The Elastic AI agent keeps its stock tools and remains the only surface your SREs interact with. A specialist K8s Troubleshooter agent carries ~20 EKS MCP tools, scoped to a single IAM identity and Kubernetes RBAC. They hand off through an <a href="https://www.elastic.co/docs/explore-analyze/workflows">Elastic Workflow</a> that calls the <a href="https://www.elastic.co/docs/explore-analyze/ai-features/elastic-agent-builder">Agent Builder</a> <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/kibana-api#chat-and-conversations">converse</a> API, so the boundary between observability reasoning and cluster actions is callable, reviewable, and auditable. To prove it works, we break targetPort on product-catalog in <a href="https://github.com/elastic/opentelemetry-demo">elastic-opentelemetry-demo</a> and recover it in 4 prompts on a single thread.</p>
<h2 id="problemcontext">Problem context</h2>
<p>Outages often show up as correlated errors on multiple services like checkout, frontend, and recommendation in Elasticsearch.
That pattern can mean a shared dependency, or it can mean Kubernetes is misleading callers: wrong targetPort, empty Endpoints, or pods that never become ready.
Observability tools like Elasticsearch tell you <em>that</em> callers fail and <em>which</em> edges look wrong.
They generally do not fetch the live Service spec or compare containerPort to targetPort for you.</p>
<p>The Elastic AI Agent in Agent Builder is built for APM, logs, metrics, dependencies, and service maps.
It is not a full EKS operations console.
You could attach all EKS MCP tools to the same agent, but long tool lists increase wrong-tool calls, slow planning, and widen blast radius if a prompt accidentally asks for mutating actions.</p>
<h2 id="solutionoverview">Solution overview</h2>
<p>Use <strong>Elastic AI Agent</strong> as the only agent your SRE chats with.
It reasons from Elasticsearch first.
When evidence points to cluster config, it calls a workflow tool that invokes the <strong>K8s Troubleshooter agent</strong> over <code>/api/agent_builder/converse</code> with a structured <code>user_prompt</code>.
The <strong>K8s Troubleshooter agent</strong> carries only the EKS MCP tools, and cluster access stays scoped to one specialist identity, IAM, and RBAC. You can audit like any other integration.</p>
<p>Elasticsearch reaches EKS through an in-cluster bridge, exposed to Kibana as an MCP connector with a shared secret.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt00eb659814dcf87b/6a7f05ce05b7b5561e18b681/solution_overview.png" alt="Solution Overview" /></p>
<h2 id="beforeyoustart">Before you start</h2>
<p>You need:</p>
<ul>
<li>An EKS cluster with kubectl configured.</li>
<li>An Elasticsearch 9.3+ deployment, an OTLP endpoint, an Elasticsearch API key, Agent Builder, and rights to create agents, MCP tools, and Workflows.</li>
<li>An AI Connector in Elasticsearch for your chosen LLM.</li>
<li>Budget two to four hours the first time you run these steps.</li>
</ul>
<h2 id="implementationwalkthrough">Implementation walkthrough</h2>
<h3 id="step1deploytheelasticopentelemetrydemoandshiptelemetrytoelasticobservability">Step 1: deploy the Elastic OpenTelemetry Demo and ship telemetry to Elastic Observability</h3>
<p>Follow <a href="https://github.com/elastic/opentelemetry-demo"><strong>elastic/opentelemetry-demo</strong></a> for Kubernetes and deploy elastic-opentelemetry-demo application to your EKS cluster.
Configure your Elasticsearch OTLP endpoint and API key, confirm workloads are running, and note the namespace.
In Kibana (APM, Logs, or Service Map), confirm data for checkout, frontend, recommendation, and product-catalog.</p>
<p>If you see healthy traffic to <code>product-catalog</code>, you are ready for the failure drill.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt92e83f13b69e3bc4/6a7f05d1e3a21975f399f1a4/04-service-map-or-errors.png" alt="Healthy Elastic Observability service map for demo services." /></p>
<h3 id="step2runtheeksmcpbridgeregistertheconnectorandbulkimporteksmcptools">Step 2: run the EKS MCP bridge, register the connector, and bulk import EKS MCP tools</h3>
<p>Complete the steps in <a href="https://github.com/ramp-km/aws-eks-mcp-setup/blob/main/README.md"><strong>aws-eks-mcp-setup</strong></a> end to end.
The flow you would be following is: </p>
<ol>
<li>Build and push the EKS MCP Bridge image</li>
<li>Create IAM policies</li>
<li>Create IRSA Service Account</li>
<li>Map IRSA role in aws-auth and apply Kubernetes RBAC</li>
<li>Deploy the bridge with a strong API_ACCESS_TOKEN to the EKS cluster</li>
<li>Connect Elastic Agent Builder with EKS MCP</li>
</ol>
<p>A green MCP connector proves Kibana can reach the bridge.</p>
<p>For production, restrict LoadBalancer security groups to known Elasticsearch egress, prefer TLS on real paths, store tokens in Kubernetes Secrets, and use read-only MCP modes when you only diagnose.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1bf20bb14fb36532/6a7f05d477b034a7333ff22b/05-eks-mcp.png" alt="MCP connector pointed at the EKS bridge." /></p>
<h3 id="step3createak8stroubleshooteragentwithekstoolsonly">Step 3: create a <strong>K8s Troubleshooter agent</strong> with EKS tools only</h3>
<p>In Agent Builder, create an agent with agent ID <code>k8s_troubleshooter</code>, display name <code>K8s Troubleshooter</code>, and custom instructions from <a href="https://github.com/ramp-km/blogs/blob/main/Custom%20K8s%20Troubleshooter/k8s_troubleshooter_agent.md">k8s_troubleshooter_agent</a>.
Attach only EKS MCP tools to this agent.</p>
<p>Chat directly with <strong>K8s Troubleshooter agent</strong> once and confirm a harmless read (for example list pods in the demo namespace).</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5c03fc63aa35b190/6a7f05d7c2e914cf690168ff/02-k8s-troubleshooter-agent.png" alt="K8s Troubleshooter agent" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb8c1ac20d8b1e65c/6a7f05db6693f8f101663c85/02-k8s-troubleshooter-agent-2.png" alt="K8s Troubleshooter agent with EKS MCP tools attached." /></p>
<h3 id="step4elasticsearch93onlyclonetheobservabilityagentwithoutekstools">Step 4 (<code>Elasticsearch 9.3 only</code>): clone the Observability Agent without EKS tools</h3>
<p>Clone the bundled <code>Observability Agent</code> (Agent Builder → Manage Agents → Observability Agent → Clone) and name it <strong>Elastic AI Agent</strong> so it keeps the stock Observability system instructions and tools.
Do not attach EKS MCP tools to this copy.</p>
<p>The parent <strong>Elastic AI Agent</strong> stays an observability-first interface for whoever chats with it.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7a3f591e0527edc7/6a7f05dfe88c65e1bb00b3b8/01-observability-agent-v2.png" alt="Observability Agent v2 tools and instructions." /></p>
<h3 id="step5createtheworkflowandmakeitacallabletool">Step 5: create the workflow and make it a callable tool</h3>
<p><a href="https://www.elastic.co/docs/explore-analyze/workflows/get-started/build-your-first-workflow">Create</a> a new Elastic Workflow by importing <a href="https://github.com/ramp-km/blogs/blob/main/Custom%20K8s%20Troubleshooter/k8s_troubleshooter_workflow.yaml">k8s_troubleshooter_workflow.yaml</a> and enable it.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt24957065ecb6477b/6a7f05e233fa8a71032023ca/05-workflow.png" alt="Kibana Workflows editor: k8s_troubleshooter workflow YAML enabled." /></p>
<p>Create a new tool in Agent Builder of type <code>Workflow</code>. Select the <code>k8s_troubleshooter</code> workflow, set tool ID <code>custom.k8s_troubleshooter</code>, and set the description to <code>Tool to triage and troubleshoot kubernetes related issues</code> (or equivalent wording your team standardizes on).</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf334d5d0b642e7fd/6a7f05e52f00b2c209efe8ce/05-workflow_tool_k8s_troubleshooter.png" alt="Agent Builder: Workflow tool wired to k8s_troubleshooter with custom tool id and description." /></p>
<p>On <strong>Elastic AI Agent</strong>, attach the <code>custom.k8s_troubleshooter</code> workflow tool that you just created.</p>
<p>The parent’s tool list should show the <code>custom.k8s_troubleshooter</code> workflow tool attached, and <strong>K8s Troubleshooter agent</strong> should still answer when invoked on its own.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9b029c737e738946/6a7f05e873d9bd41d829d874/03-workflow-tool-parent-agent.png" alt="Workflow registered as a tool on the parent agent." /></p>
<h3 id="step6injecttheproductcatalogservicemisconfiguration">Step 6: inject the product-catalog service misconfiguration</h3>
<p>Save the original <code>targetPort</code>, then patch to a wrong value (for example 9999).</p>
<pre><code>kubectl get svc -A | grep product-catalog
kubectl get svc product-catalog -n YOUR_NAMESPACE -o yaml
</code></pre>
<pre><code>kubectl patch svc product-catalog -n YOUR_NAMESPACE --type='json' \
  -p='[{"op": "replace", "path": "/spec/ports/0/targetPort", "value": 9999}]'
</code></pre>
<pre><code>kubectl rollout restart deployment/checkout deployment/recommendation deployment/frontend -n YOUR_NAMESPACE
</code></pre>
<p>Callers still resolve Endpoints, but traffic lands on a port the container does not listen on, so Elasticsearch shows downstream errors on checkout, frontend, and recommendation.</p>
<p>You now have symptoms in Elasticsearch and a clear kubernetes cluster-side fault.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb7371f0ca6cee50d/6a7f05ebb4377063a24d69dd/06-demo-services-service-map-or-errors.png" alt="Elastic Observability service map or error view after the misconfiguration." /></p>
<h3 id="step7runtwopromptsontheparentagent">Step 7: run two prompts on the parent agent</h3>
<p>Use AI Agent chat on <strong>Elastic AI Agent</strong>, not on the specialist.</p>
<p><code>Note:</code> If you are using Elasticsearch 9.3, make sure you use the <strong>Elastic AI Agent</strong> that you created, not the stock agent.</p>
<p>Prompt 1: <em>Why are failure transactions increasing for services like checkout and frontend?</em></p>
<p>Expect <strong>Elastic AI Agent</strong> to narrow the issue to the product-catalog service and note possible configuration issues as one of the probable causes, without yet invoking the <code>custom.k8s_troubleshooter</code> tool.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2bf7243ff1f82610/6a7f05ee42a117b6d895bbe2/07-ai-agent-product-catalog-issues.png" alt="Elastic AI Agent identifying product catalog issues" /></p>
<p>Prompt 2: <em>Why is product-catalog service not servicing any requests in (insert your k8s cluster name) cluster? Is there any misconfiguration in the service?</em></p>
<p>Expect <strong>Elastic AI Agent</strong> to call the <code>custom.k8s_troubleshooter</code> tool, which invokes the <strong>K8s Troubleshooter agent</strong> and reads Service, Endpoints, and pods, compares <code>targetPort</code> to <code>containerPort</code>, and explains the mismatch with evidence. Expect to also see the recommended remediation steps.</p>
<p><code>Note:</code> depending on the LLM you are using, the response from the agents may vary.</p>
<p>You get agent-led triage in Elastic Observability and cluster-grounded confirmation in the same thread, along with recommended remediation steps.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaaa6b9e3ffc40882/6a7f05f1e02fac3bf25d62e0/07-ai-agent-chat-custom-k8s-troubleshooter.png" alt="Agent Builder chat on Observability Agent v2 invoking the K8s Troubleshooter agent workflow." /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4e0151dfb52c819e/6a7f05f405b7b5ead618b6b6/07-ai-agent-chat-port-misconfiguration.png" alt="Agent Builder chat on Observability Agent v2 identifying port misconfiguration." /></p>
<h3 id="step8patchtheproductcatalogservice">Step 8: patch the product-catalog service</h3>
<p>Prompt 3: <em>Patch the product-catalog service in (your EKS cluster name) cluster to have 8080 as the targetPort</em></p>
<p>Expect <strong>Elastic AI Agent</strong> to call the <code>custom.k8s_troubleshooter</code> tool, which invokes the <strong>K8s Troubleshooter agent</strong> and patches the product-catalog service.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaea2e5aa61e128e2/6a7f05f833fa8ab2262023de/08-ai-agent-chat-patch-product-catalog.png" alt="Agent Builder chat on Observability Agent v2 patching product-catalog service." /></p>
<p>Prompt 4: <em>Rollout restart upstream services of product-catalog service</em></p>
<p>Expect <strong>Elastic AI Agent</strong> to identify all upstream services of product-catalog and call the <code>custom.k8s_troubleshooter</code> tool, which invokes the <strong>K8s Troubleshooter agent</strong> to roll out restarts for upstream services such as checkout, frontend, and recommendation.</p>
<p>Confirm product-catalog and upstream services recover in Elasticsearch.</p>
<h2 id="validationandtradeoffs">Validation and trade-offs</h2>
<p>You validated that <strong>Elastic AI Agent</strong> stays the main surface, that ~20 EKS tools live on one specialist <strong>K8s Troubleshooter agent</strong>, and that the Workflow plus Agent Builder <code>converse</code> API keeps a clear boundary for audits and reviews.</p>
<p>Trade-offs: MCP bridges need ongoing token and network hygiene, and you should keep mutating tools off or tightly RBAC-scoped until you accept the risk.</p>
<h2 id="frequentlyaskedquestions">Frequently asked questions</h2>
<h3 id="whyiselasticaiagentnottriagingtheissuesasexplainedinthisarticle">Why is Elastic AI Agent not triaging the issues as explained in this article?</h3>
<p>There could be two primary reasons. (A) If you are on Elasticsearch 9.3, make sure you chat on the Elastic AI Agent that you created, and not on the stock agent. (B) Make sure to use one of the LLM models rated <code>Excellent</code> or <code>Great</code> in <a href="https://www.elastic.co/docs/solutions/observability/ai/llm-performance-matrix">Large language model performance matrix for Observability</a></p>
<h3 id="whydomyserviceslookunhealthyinelasticsearchwhentheappcodedidnotchange">Why do my services look unhealthy in Elasticsearch when the app code did not change?</h3>
<p>Kubernetes can mislead HTTP clients: a bad Service <code>targetPort</code>, empty Endpoints, or pods that never become ready can fan out as errors on multiple edges in traces and service maps. Elastic Observability shows which dependencies fail; confirming the live Service spec usually needs cluster access.</p>
<h3 id="howdoigivekubernetesaccesstoelasticaiagentwithoutputtingeveryekstoolonit">How do I give Kubernetes access to Elastic AI Agent without putting every EKS tool on it?</h3>
<p>Run two Agent Builder agents: keep the stock tools on the parent (Elastic AI Agent), and attach only EKS MCP tools to a specialist agent(K8s Troubleshooter agent). Invoke the specialist through a workflow that calls the Agent Builder converse API so the boundary is explicit and auditable.</p>
<h3 id="whychainagentswithelasticworkflowsinsteadofonelongsystemprompt">Why chain agents with Elastic Workflows instead of one long system prompt?</h3>
<p>Workflows give a callable, reviewable step between observability reasoning and cluster actions, which helps with governance and keeps the parent agent’s tool list short. Long unified tool lists often increase mistaken tool use and broaden blast radius if a prompt requests a mutating operation.</p>
<h3 id="howdoesthiscomparetokubectloracloudconsoleforincidentresponse">How does this compare to kubectl or a cloud console for incident response?</h3>
<p>Consoles and kubectl stay the source of truth for live object state. This pattern automates the handoff from Elastic Observability signals to those checks through MCP, while still relying on IAM and Kubernetes RBAC on the specialist identity.</p>
<h3 id="whatarethemainlimitationsorrisksofaneksmcpbridgewithagentbuilder">What are the main limitations or risks of an EKS MCP bridge with Agent Builder?</h3>
<p>MCP bridges need token rotation, network restrictions, and TLS discipline on real paths. Mutating EKS tools should stay off or tightly RBAC-scoped until you accept operational risk.</p>
<h3 id="whydoweneedaneksmcpbridge">Why do we need an EKS MCP bridge?</h3>
<p>The managed EKS MCP server authenticates via AWS SigV4 through a stdio-based proxy (mcp-proxy-for-aws). Elastic's MCP connector requires an HTTP/SSE endpoint. The bridge pod runs mcp-proxy to expose the stdio proxy as an SSE/HTTP endpoint.</p>
<h3 id="canireusethesamelayoutongkeaksorselfmanagedkubernetes">Can I reuse the same layout on GKE, AKS, or self-managed Kubernetes?</h3>
<p>Yes. The separation principle is the same: observability data in Elasticsearch plus a specialist agent with cluster-scoped tools and a workflow-mediated handoff. Swap the MCP server or bridge, adjust RBAC, and parameterize cluster name or region in workflow inputs where needed.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/eks-agent-builder-mcp-kubernetes-troubleshooting</link>
    <guid isPermaLink="false">eks-agent-builder-mcp-kubernetes-troubleshooting</guid>
    <category><![CDATA[Agentic Observability]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Kubernetes]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Ramprasad KM]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt32b449a412d139b6/6a7f05fbc2cc09008c24922b/header.png" length="0" type="image/png"/>
    <pubDate>Mon, 11 May 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Kubernetes Observability from alert to root cause: Dashboards, Alerts, and Anomaly Detection with Elastic]]></title>
    <description><![CDATA[Kubernetes observability with Elastic includes dashboards, alert rules, and ML anomaly detection for alerts with root-cause context.]]></description>
    <content:encoded><![CDATA[<p>Kubernetes observability with Elastic is built for the operator who gets paged at 3 AM. That operator is often in a terminal, a chat tool, or an IDE. They need an answer that is grounded in what is happening in the cluster right now.</p>
<p>The new <a href="https://www.elastic.co/docs/reference/integrations/kubernetes">Elastic Kubernetes integration</a> is built for that operator. It includes  dashboards with drilldowns, alert rule templates, and ML anomaly detection jobs. Additionally Elastic also offers Agentic Investigations, that drives investigations automatically. </p>
<p>This blog will cover the foundational observability components (dashboards, drilldowns, alert templates, etc), while a part 2 covering the agentic investigations will cover workflows, agent skills, and MCP tools and views</p>
<p>The new Kubernetes integration content in this post is generally available across Elastic Cloud Hosted, Serverless, and self-managed deployments.</p>
<hr />
<h2 id="dashboardsdesignedfordrilldownnotjustdisplay">Dashboards designed for drill-down, not just display</h2>
<p>The new Kubernetes dashboards are organized around a three-tier design: a cluster Overview that surfaces what needs attention at a glance, object summary pages for clusters, nodes, namespaces, workloads, and pods, and object detail pages that give you the full picture for any single entity.</p>
<p>Every layer connects to the next: click any entity in a summary table and choose: apply it as a filter on the current view, or open its dedicated detail page.</p>
<p>Here's what that looks like when something's actually wrong:</p>
<p><strong>Following a restart cascade from overview to container</strong></p>
<p><strong>Overview:</strong> The Overview surfaces what needs attention across your cluster.
You can see top pods by CPU, top namespaces by container restarts, and top nodes by memory utilization in one screen.
When the "container restarts" panel starts climbing, you know where to look.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta96de19280caf42b/6a7f0bc7227b1c58be598548/overview-dashboard.jpg" alt="Kubernetes observability with Elastic, cluster overview dashboard showing top pods by CPU and container restarts by namespace" /></p>
<p><strong>Namespaces Overview:</strong> Click into the flagged namespace with 1232 restarts and CPU limit utilization at 116%.
The detail view plots CPU and memory against requests and limits over time.
This shows both the size and duration of the overage.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb75870f34cf3b08f/6a7f0bca6c6eac5ef2f1409b/namespace-overview.jpg" alt="Kubernetes observability with Elastic, namespace overview showing multiple namespaces" /></p>
<p><strong>Namespace Details:</strong> We can get more info on the various pods in this namespace here.
Click the pod driving the restarts.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb9ec4c28319b6e11/6a7f0bcd3ce8e203e4cf533b/namespace-details.jpg" alt="Kubernetes observability with Elastic, namespace detail view showing CPU limit utilization at 116% and container restart count" /></p>
<p><strong>Pod Details:</strong> The pod detail dashboard is organized into capacity, metrics, and containers sections.
Container restarts are flagged in red at the top of the page.
Most panels are metric-driven, and the dashboard also links to correlated pod logs in Discover.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt62fb8cfe62ca374a/6a7f0bd0bdcff07295c42db7/pod-details.jpg" alt="Kubernetes observability with Elastic, pod detail dashboard with container restart alerts, capacity metrics, and log drilldown links" /></p>
<p>It takes four clicks to move from the Cluster Overview to container logs that explain the failure.
These dashboards are starting points for your team.
You can copy and customize them with ESQL visualizations.</p>
<hr />
<h2 id="alertrulesthatfireondayone">Alert rules that fire on day one</h2>
<p>The integration ships with pre-built alerting rule templates for states that are wrong by definition.
No historical baseline or warmup period is required.
Enable them during setup and they work immediately.</p>
<p>These rules do not ask, "Is this abnormal for this service?"
They ask, "Is this a known bad state in Kubernetes?"
A pod in CrashLoopBackOff is always a problem.
A container killed by the kernel for exceeding its memory limit is always a problem.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt491bfc3539965be3/6a7f0bd49090b015b484e967/alert-list.png" alt="Kubernetes observability with Elastic, list of alerts with the CrashLoopBackOff alert rule selected" /></p>
<p>Like the Kubernetes dashboards, these alerts are built on ES|QL queries.
You can see that in the CrashLoopBackOff definition below.
If you are new to ES|QL, you can start with the <a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/esql">ES|QL docs</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9cb843f4cf381e4a/6a7f0bd72f00b2b067efeaf8/alert-detail.png" alt="Kubernetes observability with Elastic, ES|QL query that defines the CrashLoopBackOff alert rule" /></p>
<p>The alert templates cover:</p>
<ul>
<li><strong>CrashLoopBackOff detection</strong> - Fires when a pod's restart count exceeds a configurable threshold within a rolling window.
The default catches a real restart cycle without triggering on routine restarts during a rolling deployment.</li>
<li><strong>Container OOMKilled</strong> - Surfaces kernel-level container terminations due to memory limits.
These events are easy to miss in dashboards and often precede wider failures.
This rule fires on any occurrence.</li>
<li><strong>Deployment below desired replicas</strong> - Fires when a deployment runs fewer replicas than declared for longer than a grace period.
This catches scaling failures and partially failed rollouts.</li>
<li><strong>Pod stuck in Pending</strong> - Fires when a pod cannot be scheduled past a configurable time threshold.
This surfaces node capacity problems, missing resources, and affinity failures before availability drops.</li>
<li><strong>Node disk pressure</strong> - Fires immediately when the Kubernetes DiskPressure node condition is <code>True</code>.
A node condition is a direct state signal, not a statistical threshold.</li>
<li><strong>Persistent volume near capacity</strong> - Alerts when storage utilization crosses a configurable threshold before writes start failing.</li>
</ul>
<p>Each template is parameterized.
Adjust thresholds in the ES|QL query to match your environment.
Connect notifications to PagerDuty, Slack, or another destination in your runbook.</p>
<hr />
<h2 id="anomalydetectionjobswithmlbaselines">Anomaly detection jobs with ML baselines</h2>
<p>Alert rules catch what is definitively wrong.
ML anomaly detection catches patterns that often precede failures.
If you are new to this area, see the <a href="https://www.elastic.co/guide/en/machine-learning/current/ml-ad-overview.html">Elastic anomaly detection overview</a>.</p>
<p>A pod that always runs at 85% memory utilization might be healthy.
A pod that grew from 40% to 85% over twelve hours is usually not healthy.
A static threshold often catches this only after an OOM kill.
The ML module should catch the trajectory earlier.</p>
<p>The integration ships with ML module configurations that learn workload baselines and flag meaningful deviations.
These jobs need 24 to 48 hours of data before results become useful.
Results become more reliable as jobs continue to run.</p>
<h3 id="theincludedmodules">The included modules</h3>
<p><strong>1. Pod memory growth anomalies</strong></p>
<ul>
<li><strong>What it learns:</strong> per-pod memory consumption pattern over time</li>
<li><strong>What it flags:</strong> Growth trajectories that are inconsistent with baseline behavior, such as a slow leak that never crosses the hard limit.</li>
<li><strong>Why ML (not alert rule):</strong> The alert rule catches the OOMKill after the fact.
The ML job catches the trajectory that leads there.</li>
</ul>
<p><strong>2. Network I/O anomalies</strong></p>
<ul>
<li><strong>What it learns:</strong> per-pod network transmit/receive byte rate patterns</li>
<li><strong>What it flags:</strong> Unusual spikes or drops relative to the pod baseline.
A spike can indicate a runaway process or unexpected load.
A drop can indicate a network partition that causes the pod to go idle.</li>
<li><strong>Why ML (not alert rule):</strong> Normal network traffic varies by time of day and workload type.
A batch job pod at high throughput during its normal window is expected.
The same throughput outside that window can be anomalous.</li>
</ul>
<p><strong>3. Pod restart frequency</strong></p>
<ul>
<li><strong>What it learns:</strong> Per-workload restart rate patterns during deployments, scaling events, and routine operations.</li>
<li><strong>What it flags:</strong> Restart patterns that are anomalous relative to each workload's own history.
This is distinct from the CrashLoopBackOff alert rule, which fires on a fixed threshold regardless of context.</li>
<li><strong>Why ML (not alert rule):</strong> A deployment that restarts twice during every rollout can be healthy.
The same deployment restarting twice on a Tuesday afternoon may be unhealthy.
The alert rule cannot distinguish these cases without workload history.</li>
</ul>
<p>Here's our Single Metric Viewer showing anomalies triggered against a specific pod, for the memory growth job:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6d86eb72e08ac5a1/6a7f0bda77b03484193ff457/single-metric-viewer.png" alt="Kubernetes observability with Elastic, ML Single Metric Viewer showing pod memory growth anomaly detection for one pod" /></p>
<p>And here's the multi-series Anomaly Explorer view of the same job, showing detections firing across a variety of pods:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7a2fe1f9686b6a0e/6a7f0bdd4c4bfbd2c8ccd4cf/anomaly-explorer.png" alt="Kubernetes observability with Elastic, Anomaly Explorer showing pod memory anomaly detections across multiple pods" /></p>
<hr />
<h2 id="tryityourselftheotelastronomyshop">Try it yourself: the OTel Astronomy Shop</h2>
<p>If you do not have a Kubernetes cluster ready, you can use the OpenTelemetry Astronomy Shop demo environment.
It uses the same commands as Getting Started Step 2, Path A, but points to demo services.
Create the namespace and secret, then run the Helm install.
All 16 services, Kafka, and PostgreSQL start flowing into Elastic without instrumentation changes.</p>
<p>The demo ships with a built-in feature flag service, <code>flagd</code>, that lets you activate failure scenarios.
Enable <code>cartServiceFailure</code> and watch the checkout-service restart cascade unfold in real time.
The CrashLoopBackOff alert rule fires.
The ML modules begin establishing baselines.
If you have the investigation workflow enabled, it runs automatically when the alert fires.</p>
<hr />
<h2 id="gettingstarted">Getting started</h2>
<p><strong>Step 1 - Install the Kubernetes integration.</strong>
Dashboards are available immediately.
No additional configuration is required.</p>
<p><strong>Step 2 - Deploy data collection.</strong>
There are two supported paths, both based on Helm.
Choose the one that fits your deployment model.</p>
<p><strong>Path A - OpenTelemetry (EDOT collector):</strong>
This path uses the <code>opentelemetry-kube-stack</code> Helm chart with the Elastic Distribution of OpenTelemetry (EDOT) collector.
Create a namespace and a secret with your endpoint and API key, then install:</p>
<pre><code>kubectl create namespace opentelemetry-operator-system

kubectl create secret generic elastic-secret-otel \
  --namespace opentelemetry-operator-system \
  --from-literal=elastic_otlp_endpoint='https://&lt;your-endpoint&gt;.elastic.cloud:443' \
  --from-literal=elastic_api_key='&lt;your-api-key&gt;'

helm upgrade --install opentelemetry-kube-stack open-telemetry/opentelemetry-kube-stack \
  --namespace opentelemetry-operator-system \
  --values 'https://raw.githubusercontent.com/elastic/elastic-agent/refs/tags/v9.3.2/deploy/helm/edot-collector/kube-stack/managed_otlp/values.yaml' \
  --version '0.12.4'
</code></pre>
<p><strong>Path B - Elastic Agent (standalone):</strong>
This path uses the <code>elastic/elastic-agent</code> Helm chart.
The default manifest includes resource limits that may not be appropriate for production.
Review the <a href="https://www.elastic.co/docs/reference/fleet/scaling-on-kubernetes">Scaling Elastic Agent on Kubernetes guide</a> before deploying.</p>
<pre><code>helm repo add elastic https://helm.elastic.co/ &amp;&amp; \
helm install elastic-agent elastic/elastic-agent \
  --version 9.3.2 \
  -n kube-system \
  --set outputs.default.url=https://&lt;your-endpoint&gt;.es.elastic.cloud:443 \
  --set outputs.default.type=ESPlainAuthAPI \
  --set outputs.default.api_key=$(echo "&lt;your-base64-api-key&gt;" | base64 -d) \
  --set kubernetes.enabled=true
</code></pre>
<p><strong>Step 3 - Enable the alert rule templates.</strong>
Go to Observability &gt; Alerts in Kibana.
The Kubernetes templates are in the rule library.
Enable the templates relevant to your environment, set thresholds, and connect your notification channel.</p>
<p><strong>Step 4 - Let the ML modules warm up.</strong>
After 24 to 48 hours, anomaly detection modules establish baselines and begin surfacing pattern-based deviations.
Longer running jobs usually produce better baselines.
Find results in the ML Anomaly Explorer, linked from the Kubernetes dashboards.</p>
<p><strong>Steps 5, 6, and 7 - Agentic content</strong> will be covered in Part 2 (forthcoming), Kubernetes observability with Elastic: Agentic Investigations.</p>
<hr />
<h2 id="whatsnext">What's next</h2>
<p>The next step is the layer that runs investigation workflows when an alert fires.
That includes skills that encode investigation logic, tools that expose facts like ML state and topology, and MCP apps that render outputs in places like Claude Desktop or VS Code.
These technical preview capabilities are available today and will be covered in Part 2 (forthcoming), Kubernetes observability with Elastic: Agentic Investigations.</p>
<p>If you are running Kubernetes on Elastic today, tell us which investigation steps you repeat manually on every incident.
Tell us which remediations you would trust a workflow to propose.
You can <a href="https://discuss.elastic.co/c/observability">join the Elastic Community Discussion here</a>.</p>
<hr />
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion.</em>
<em>Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/kubernetes-dashboards-alerts-anomaly-detection</link>
    <guid isPermaLink="false">kubernetes-dashboards-alerts-anomaly-detection</guid>
    <category><![CDATA[Kubernetes]]></category>
    <category><![CDATA[Metrics]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Jesse Miller]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt582db3c8608d473c/6a7f0be03cab1c86700e47dc/header.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 21 Apr 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Network monitoring with Elastic: Unifying network observability]]></title>
    <description><![CDATA[Learn how to unify network monitoring using Elastic observability and AI. We'll showcase how to correlate network data, identify root causes and fix issues.]]></description>
    <content:encoded><![CDATA[<h2 id="introductionthenetworkmonitoringfragmentationproblem">Introduction: The Network Monitoring Fragmentation Problem</h2>
<p>In five years working with Enterprise accounts at Elastic, I have heard the same challenge again and again:</p>
<p><strong>"We have several network monitoring tools, and we would love to correlate all of them into one platform."</strong></p>
<p>For many organizations, the barrier to true correlation isn't a lack of data, but where that data lives. Frequently, we see SNMP metrics, flow data, and logs isolated in purpose-built silos or dashboards. Without a unified data store and a proper correlation engine, piecing together the full narrative — from a topology change to a performance degradation — becomes a manual, time-consuming puzzle.</p>
<p>When an incident happens, engineers become <strong>human correlation engines</strong> — manually jumping between systems, copying timestamps, cross-referencing device names, and trying to piece together what actually happened. A simple question like "Did this interface failure impact application performance?" requires querying multiple tools and mentally correlating the results.</p>
<p>The real cost isn't the tool licenses — it's the time lost during critical incidents.</p>
<p>This lab is my answer to a fundamental question: <strong>Can Elastic become the unified foundation that actually correlates network data?</strong></p>
<p>More importantly, it demonstrates that Elastic is fully ready for network operations — capable of ingesting diverse telemetry and using AI to correlate relationships, identify root causes, and resolve issues in seconds instead of hours.</p>
<h2 id="theproblemnetworkobservabilityisbroken">The Problem: Network Observability is Broken</h2>
<p>Let me paint a typical scenario I encounter with enterprise network teams:</p>
<p><strong>The Fragmented Reality:</strong></p>
<ul>
<li>No single source of truth</li>
<li>Manual correlation during incidents (15-30 minutes per event)</li>
<li>Fragmented teams (network vs. platform engineers)</li>
<li>Limited automation capabilities</li>
<li>No AI-powered analysis</li>
</ul>
<p><strong>When a link goes down at 2 AM:</strong></p>
<ul>
<li>Notice the alert - 2 minutes</li>
<li>Log into monitoring tool to see the metric - 3 minutes</li>
<li>Switch to traffic analyzer to check impact - 5 minutes</li>
<li>Open log management to search for related messages - 10 minutes</li>
<li>Manually correlate timestamps across systems - 8 minutes</li>
<li>Create a ticket and copy context from multiple tools - 8 minutes</li>
</ul>
<p><strong>Time to initial diagnosis: 36 minutes</strong></p>
<p>This workflow is expensive, error-prone, and doesn't scale.</p>
<h2 id="thevisionelasticasaunifiednetworkobservabilityplatform">The Vision: Elastic as a Unified Network Observability Platform</h2>
<p>What if you could:</p>
<ul>
<li>Collect SNMP metrics, NetFlow, traps, and topology data in <strong>one platform</strong></li>
<li>Correlate network events with application performance <strong>automatically</strong></li>
<li>Generate executive dashboards without separate BI tools</li>
<li>Use <strong>AI to analyze incidents in seconds</strong>, not hours</li>
<li>Trigger alerting from network events</li>
</ul>
<p>This is what this lab aims to demonstrate.</p>
<h2 id="whatibuiltaproductiongradenetworksimulation">What I Built: A Production-Grade Network Simulation</h2>
<p>To demonstrate how Elastic unifies network data, I needed a realistic environment that generates real-world telemetry. Enter <strong>Containerlab</strong>  —  a Docker-based solution that enables us to create a network simulation framework.</p>
<h3 id="labarchitecture">Lab Architecture</h3>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf2d5eece08d2850c/6a7f0e7796b5a6bf6087b4eb/lab-topology.jpg" alt="Lab Topology" /></p>
<p>I simulated a Service Provider core network with:</p>
<ul>
<li><strong>7 FRR routers</strong> forming an OSPF Area 0 mesh</li>
<li><strong>2 Ubuntu hosts</strong> for additional use cases</li>
<li><strong>2 Layer 2 switches</strong> for access layer segmentation</li>
<li><strong>3 telemetry collectors</strong> feeding Elastic Cloud</li>
</ul>
<p><strong>Total containers:</strong> 14</p>
<p><strong>Deployment time:</strong> 12-15 minutes (fully automated)</p>
<p><strong>Full deployment instructions and topology details are available in the <a href="https://github.com/DeBaker1974/Containerlab-OSPF">GitHub repository README</a>.</strong></p>
<h2 id="thethreetelemetrypipelinesprovingmultisourcecorrelation">The Three Telemetry Pipelines: Proving Multi-Source Correlation</h2>
<p>What makes this lab production-ready is its <strong>hybrid observability approach</strong> — proving that Elastic can unify disparate network data sources.</p>
<p>| Pipeline | Data Type | Collection Method | Collector | Use Case |
| :---- | :---- | :---- | :---- | :---- |
| <strong>SNMP Metrics</strong> | Interface stats, system health, LLDP topology | Active polling  | OTEL Collector | Capacity planning, trend analysis |
| <strong>NetFlow</strong> | Traffic flows | Push-based export | Elastic Agent | Top talkers, security investigation |
| <strong>SNMP Traps</strong> | Interface up/down events | Event-driven | Logstash | Real-time incident detection |</p>
<p>This unified architecture proves Elastic can replace multiple specialized network monitoring tools with a single platform.</p>
<h2 id="thepowerofcorrelationoneplatformonequery">The Power of Correlation: One Platform, One Query</h2>
<p>When a network incident occurs, you need to answer questions like:</p>
<ul>
<li>Which interface failed? <em>(SNMP metrics)</em></li>
<li>What traffic was affected? <em>(NetFlow)</em></li>
<li>What was the sequence of events? <em>(SNMP traps)</em></li>
<li>Which devices are downstream? <em>(LLDP topology)</em></li>
</ul>
<p><strong>The Problem:</strong> modern tools offer separate modules glued together, forcing users to navigate different spaces for different sets of data.</p>
<p><strong>The Reality:</strong> You still have to pivot. You see a spike in the Metrics module, but to see why, you have to open the Logs module and manually align the time picker. The data lives in different tables or backends, making true correlation impossible without human intervention.</p>
<p><strong>The Elastic Difference:</strong> One Store, One Language, One AI</p>
<p>Elastic makes it simple. Whether it's an SNMP counter (metric), a NetFlow record (flow), or a Syslog message (log), it is all stored in a unified datastore powered by the Elasticsearch engine. This allows users to easily search across multiple datasets in a single query.</p>
<pre><code>FROM logs-*
| WHERE host.name == "csr23" AND interface.name == "eth1"
</code></pre>
<p><strong>Time required: 3 seconds</strong></p>
<p>Furthermore, as you will see later, the exact location of the data becomes agnostic to the user when leveraging the AI Assistant.</p>
<h2 id="datatransformationfromcrypticoidstoactionableintelligence">Data Transformation: From Cryptic OIDs to Actionable Intelligence</h2>
<p>Raw SNMP traps are notoriously difficult to interpret at a glance. In our current lab setup, the data arrives looking like this:</p>
<pre><code>OID: 1.3.6.1.6.3.1.1.5.3
ifIndex: 2
ifDescr: eth1
</code></pre>
<p>While traditional Network Management Platforms (NMPs) handle OID translation natively, bringing that clarity into Elastic requires a specific configuration.</p>
<p>In this initial lab, we are intentionally working with this raw data to demonstrate how AI assistants can interpret these events even without pre-existing context.</p>
<p>However, the strategy for the next phase of this project is to implement Elasticsearch Ingest Pipelines. This will allow us to map raw OIDs to human-readable names. This step is crucial for bridging the gap between Network tools and Application Observability platforms, allowing network events to be instantly correlated with application errors and infrastructure logs.</p>
<p><strong>The Target State</strong></p>
<p>Once the pipeline is implemented in the next lab, we will transform that raw trap into searchable, meaningful data:</p>
<pre><code>{
  "event.action": "interface-down",
  "host.name": "csr23",
  "interface.name": "eth1",
  "interface.oper_status_text": "Link Down"
}
</code></pre>
<p><strong>The result:</strong></p>
<ul>
<li>Human-readable fields</li>
<li>Searchable dimensions for filtering</li>
<li>Context for automation rules and dashboards</li>
<li>Correlation keys for joining with metrics and flows</li>
</ul>
<p>In our next blog post, we will walk through building the ingest pipeline that performs this transformation — step by step.</p>
<h2 id="intelligentalertingfromnoisetoactionableintelligence">Intelligent Alerting: From Noise to Actionable Intelligence</h2>
<p>Traditional network monitoring relies on simple threshold alerts — "interface down," "high CPU." These alerts flood your inbox but provide <strong>zero context</strong> about root cause, impact, or remediation.</p>
<h3 id="thelabsapproachesqlaiassistant">The Lab's Approach: ES|QL + AI Assistant</h3>
<p><strong>1. Semantic Detection with ES|QL</strong></p>
<p>Instead of generic threshold alerts, the lab uses ES|QL to detect specific event patterns:</p>
<pre><code>FROM logs-snmp.trap-prod
| WHERE snmp.trap_oid == "1.3.6.1.6.3.1.1.5.3"
| KEEP @timestamp, host.name, interface.name, message
</code></pre>
<p><strong>2. Automatic AI-Powered Investigation</strong></p>
<p>When the alert triggers, it invokes the <strong>Observability AI Assistant</strong> with a structured investigation prompt that:</p>
<ul>
<li>Performs immediate triage (which device, which interface, when)</li>
<li>Assesses OSPF impact and traffic rerouting</li>
<li>Correlates with other recent failures</li>
<li>Generates severity assessment and recommended actions</li>
</ul>
<h3 id="thetransformation">The Transformation</h3>
<p>| Traditional Alerting | Intelligent Alerting (Elastic) |
| :---: | :---: |
| <strong>Email: "Interface down on csr23"</strong> | Structured analysis with device context |
| <strong>Manual investigation: 20-30 min</strong> | AI-automated investigation: 90 seconds |
| <strong>Engineer correlates across tools</strong> | Automatic cross-source correlation |
| <strong>No business impact assessment</strong> | Severity + recommended actions included |</p>
<h2 id="acceleratingincidentresponsewiththeelasticaiassistant">Accelerating Incident Response with the Elastic AI Assistant</h2>
<p>This is where the Elastic AI Assistant demonstrates its operational value — moving beyond passive data collection to actively interpret and explain network events in real-time</p>
<p>When an engineer views a trap document in Discover and asks:</p>
<p><strong><em>"Explain this log message"</em></strong></p>
<p>The AI Assistant provides comprehensive analysis including:</p>
<ul>
<li><strong>What happened:</strong> Plain-language explanation of the SNMP trap</li>
<li><strong>Device context:</strong> Router role, interface purpose, network position</li>
<li><strong>Impact analysis:</strong> OSPF neighbor status, traffic rerouting assessment</li>
<li><strong>Root cause possibilities:</strong> Physical layer, link layer, administrative causes</li>
<li><strong>Recommended actions:</strong> Immediate steps, investigation queries, validation checks</li>
<li><strong>Severity assessment:</strong> Business and technical impact rating</li>
</ul>
<h3 id="manualtriagevsaiassistedinvestigation">Manual Triage vs. AI-Assisted Investigation</h3>
<p>| Before | After (Elastic AI) |
| :---- | :---- |
| <strong>Google the OID → 5 min</strong> | Click "Explain this log" → 20 seconds |
| <strong>Open network diagram → 3 min</strong> | Topology context auto-provided |
| <strong>Query multiple tools → 10 min</strong> | Cross-source correlation instant |
| <strong>Assess business impact → 5 min</strong> | Impact analysis auto-generated |
| <strong>Total: ~28 minutes</strong> | <strong>Total: ~20 seconds</strong> |</p>
<h2 id="thevaluepropositiononeplatformonedatamodeloneai">The Value Proposition: One Platform, One Data Model, One AI</h2>
<h3 id="whatthislabdemonstrates">What This Lab Demonstrates</h3>
<p>Elastic provides:</p>
<ul>
<li><strong>One unified platform</strong> for metrics, logs, flows</li>
<li><strong>One data model</strong> (SemConv) for consistent correlation</li>
<li><strong>One search interface</strong> (Kibana) for all network data</li>
<li><strong>One AI assistant</strong> that understands all your network telemetry</li>
<li><strong>AI-powered alerting</strong> with automated investigation</li>
</ul>
<h3 id="businessimpact">Business Impact</h3>
<p><strong>Efficiency Gains:</strong></p>
<ul>
<li><strong>85% reduction in MTTR</strong> (36 min → 5 min for initial diagnosis)</li>
<li><strong>90% reduction</strong> in manual correlation time</li>
<li>Junior engineers gain access to <strong>AI-powered expert analysis</strong></li>
</ul>
<p><strong>Operational Benefits:</strong></p>
<ul>
<li>Network engineers focus on <strong>strategy, not tool-switching</strong></li>
<li><strong>Cross-functional collaboration</strong> in one platform</li>
<li><strong>Reduced tool sprawl</strong> and management overhead</li>
</ul>
<h2 id="lessonslearned">Lessons Learned</h2>
<p>After building this lab, several key insights emerged regarding how network data fits into the broader observability ecosystem:</p>
<p><strong>1. Extending Observability to the Network</strong></p>
<p>Elastic is already the gold standard for high-volume logs and application traces. This lab demonstrates that the same engine seamlessly handles network telemetry without needing a separate, siloed tool.</p>
<ul>
<li>Scale: The same architecture that ingests petabytes of application logs easily handles millions of interface counters.</li>
<li>Structure: Native support for complex nested documents allows for rich SNMP trap data (variable bindings) without flattening or losing context.</li>
<li>Speed: Real-time search applies equally to network events, enabling sub-second troubleshooting.</li>
</ul>
<p><strong>2. OpenTelemetry Semantic Conventions (SemConv) as the Universal Translator</strong></p>
<p>The power isn't just in storing the data, but in standardizing it. By mapping SNMP and NetFlow to the <strong>OpenTelemetry Semantic Conventions (SemConv)</strong>, network data finally speaks the same language as the rest of the stack.</p>
<ul>
<li><strong>Unified Search:</strong> Query across firewall logs, server metrics, and switch telemetry in a single search bar.</li>
<li><strong>Instant Visualization:</strong> Pre-built dashboards work immediately because the field names are standardized.</li>
<li><strong>Cross-Domain Correlation</strong>: Easily correlates a spike in application latency with a specific interface saturation event.</li>
</ul>
<p><strong>3. AI Assistants Thrive on Context</strong></p>
<p>While the AI in this lab was powerful on its own, the experiment highlighted a critical realization: an AI Assistant becomes exponentially more effective when coupled with a specific Knowledge Base.</p>
<p><strong>Context is King:</strong> The AI delivers better root cause analysis when provided with rich metadata, such as device roles and topology maps. Without it, the advice remains generic.</p>
<p><strong>Pro Tip (and What’s Next):</strong></p>
<p>To get organization-specific advice rather than generic suggestions, you need to feed the AI your documentation.</p>
<ul>
<li><strong>The Goal:</strong> Create a Knowledge Base containing device roles, network topology diagrams, and troubleshooting procedures.</li>
<li><strong>The Next Step:</strong> In my next blog post, I will demonstrate exactly how to do this — connecting a Knowledge Base to the AI Assistant to enable fully context-aware troubleshooting.</li>
</ul>
<h2 id="conclusioncompletingtheobservabilitypicture">Conclusion: Completing the Observability Picture</h2>
<p>Elastic is already widely recognized as the standard for Application and Security observability. The goal of this lab wasn't to ask if Elastic can handle networking, but to demonstrate the immense value of bringing network data into that existing ecosystem.</p>
<p>The verdict is clear: Elastic acts as that unified foundation. It effectively breaks down the silo between Network Engineering and the rest of IT.</p>
<p>This isn't just about consolidating dashboards or replacing legacy tools. It is about establishing the Elasticsearch AI Platform as the single source of truth where network telemetry sits right alongside application and infrastructure data.</p>
<p>By treating network data as a first-class citizen in the observability stack, we unlock automated correlation, AI-assisted investigation, and the speed required to resolve incidents before they impact the business. The capabilities are in place, and the foundation is solid — Elastic is ready to unify your network with the rest of your digital business.</p>
<h2 id="readytotryityourself">Ready to Try It Yourself?</h2>
<p>Check out <a href="https://github.com/DeBaker1974/Containerlab-OSPF">github.com/DeBaker1974/Containerlab-OSPF</a></p>
<p>The repository includes:</p>
<ul>
<li>Complete deployment scripts (12-15 minute automated setup)</li>
<li>Pre-configured telemetry pipelines</li>
<li>Kibana dashboards</li>
<li>Alert rules with AI Assistant integration</li>
<li>Detailed README</li>
</ul>
<p><strong>Not ready to build? Try Elastic Serverless:</strong> <a href="https://cloud.elastic.co/registration">Start a free 14-day trial</a> and explore AI-powered observability with your own data.</p>
<p><strong>Special thanks to the Containerlab and FRRouting communities for their incredible open-source tools, and to Sheriff Lawal (CCIE, CISSP), Sr. Manager, Solutions Architecture at Elastic, for mentoring on this project.</strong></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/network-monitoring-with-elastic-unifying-network-observability</link>
    <guid isPermaLink="false">network-monitoring-with-elastic-unifying-network-observability</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Metrics]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Patrick Boulanger]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt644de3218af4a6a1/6a7f0e7a73d9bd4b1129dbc1/article-image.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 16 Feb 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Automating User Journeys for Synthetic Monitoring with MCP in Elastic]]></title>
    <description><![CDATA[This post explores how you can automatically create user journeys with Synthetic Monitoring in Elastic Observability, TypeScript, and FastMCP, and walks through the app and its workflow.]]></description>
    <content:encoded><![CDATA[<p><a href="https://www.elastic.co/docs/solutions/observability/synthetics">Synthetic Monitoring in Elastic Observability</a> enables you to track user pathways using a global testing infrastructure, emulating the full user path to measure the impact of web applications. It also provides comprehensive insight into your website's performance, functionality, and availability from development to production, allowing you to identify and resolve issues before they affect your customers. </p>
<p>One of the main components of Elastic's Synthetic Monitoring is the ability to create user journeys, which can be done with or without code. There is a <a href="https://github.com/elastic/synthetics">Synthetics agent,</a>, a CLI tool that guides you through the process of creating both heartbeat monitors and user journeys and deploying your code to Elastic Observability. If you are using code to create user journeys, you are using <a href="https://playwright.dev/">Playwright</a> under the hood with some additional configuration to make it easier to work with Elastic Observability. </p>
<p>To automatically create user journeys using TypeScript, you can create Playwright tests based on a prompt using <a href="https://www.warp.dev">Warp</a>, an AI-assisted terminal, <a href="https://deepmind.google/models/gemini/pro/">Gemini 2.5 Pro</a>, and <a href="https://modelcontextprotocol.io/docs/getting-started/intro">MCP</a>. This application was built using Python and <a href="https://gofastmcp.com/getting-started/welcome">FastMCP</a>, which wraps the synthetic agent to deploy browser tests to Elastic automatically. This blog post will guide you through how the application works, how to use it, and its development process. You can find the complete code on <a href="https://github.com/JessicaGarson/MCP-Elastic-Synthetics">GitHub</a>. </p>
<h2 id="solutionoverview">Solution overview</h2>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt52957c4621a51a56/6a7f0d329090b04d0584ea0f/01-diagram.jpg" alt="diagram" /></p>
<p>Currently, this solution is set up to run inside Warp as an <a href="https://docs.warp.dev/knowledge-and-collaboration/mcp">MCP server</a>; however, you can also use another client, such as <a href="https://claude.ai/download">Claude Desktop</a> or <a href="https://cursormcp.com/en">Cursor</a>. From there, you create a Python script using <a href="https://gofastmcp.com/getting-started/welcome">FastMCP</a>, which allows you to create functions that are callable by an LLM. Within Warp, you can make a configuration file in JSON that enables you to point to your Python script and pass in all the environment variables you are working with. From there, you'll want to toggle agent mode and ask a question about creating synthetic testing or call the MCP function directly. There are many options for which LLM you can select, be sure to check out <a href="https://docs.warp.dev/agents/using-agents">Warp's documentation</a> to learn more about the options available.</p>
<p>After that, you should ask a question about creating synthetic testing or call the MCP function you are looking for. The following three functions can be used: </p>
<ul>
<li><p><code>diagnose_warp_mcp_config</code> 
Used for debugging environment variable issues that may arise. This function likely won't be needed unless there is an issue with your configuration.  </p></li>
<li><p><code>create_and_deploy_browser_test</code>
Will automatically create Playwright tests if given the test name, the URL you want to test, and a schedule. This approach uses a template-based method, rather than a machine learning-based method, and all the tests it outputs will appear similar.   </p></li>
<li><p><code>llm_create_and_deploy_test_from_prompt</code>
Similar to <code>create_and_deploy_browser_test</code>, but the main difference is that it uses an LLM to create tests based on a prompt you give it. The tests should reflect the prompt you provided. To run this function you'll provide a test name, URL, prompt, and schedule.</p></li>
</ul>
<h2 id="whycreatethissolutionasanmcpserver">Why create this solution as an MCP server?</h2>
<p>The reason this was developed as an MCP server, as opposed to just a standalone script or a standard CLI, is that it can be structured and interacted with in a more conversational manner. It enables an LLM to generate dynamic Playwright testing while maintaining consistent arguments, environment variables, and responses to ensure accuracy and reliability. Thus, it becomes a reliable workflow that other agents or developers can compose with additional tools. In other words, the MCP layer turns your LLM-based test authoring into a standardized, reusable capability instead of a one-off script. To learn more about the direction of MCP, be sure to check out our article on the <a href="https://www.elastic.co/search-labs/blog/mcp-current-state">topic.</a></p>
<h2 id="implementationconsiderations">Implementation considerations</h2>
<p>When creating a solution like this one, one thing to be mindful of is your use of tokens. An early version of this solution took approximately twenty minutes to create synthetic tests and ultimately led to severe rate-limiting. </p>
<p>Another issue faced during the building process was striking a balance between creating a template that facilitates the creation of a Playwright script and having an LLM create Playwright scripts based on prompts that didn't feel cookie-cutter. While using a more LLM approach an issue faced was that the scripts often didn't work or were based on parameters that didn't exist and a more templated approach was more reliable but felt repetitive. The final version of this solution attempted to balance this by using elements of the template while adjusting the LLM parameter of temperature, which controls the randomness or creativity of a large language model's output. </p>
<p>While testing this solution, a failing test also emerged that required navigating past a pop-up. In more complex cases, this may serve as a building block that requires additional domain knowledge to create a complete passing Playwright test.</p>
<h2 id="howtogetstarted">How to get started</h2>
<h3 id="prerequisites">Prerequisites</h3>
<ul>
<li>The version of Python that is used is Python 3.12.1 but you can use any version of Python higher than 3.10.   </li>
<li>This application uses Elastic Observability version 9.1.2, but you can use any version of Elastics Observability that is higher than 8.10. You can also use <a href="https://www.elastic.co/cloud/serverless">Elastic Cloud Serverless</a> as well.  </li>
<li>You will also need an OpenAI API key to use the LLM capabilities of this application. You will want to configure an environment variable for your OpenAI API Key, which you can find on the API keys page in <a href="https://platform.openai.com/api-keys">OpenAI's developer portal</a>.</li>
</ul>
<h3 id="step1installthepackagesandclonetherepository">Step 1: Install the packages and clone the repository</h3>
<p>In order for this MCP server to run locally you will need to install the the following packages: </p>
<pre><code>pip install fastmcp openai
npm install -g playwright @elastic/synthetics
</code></pre>
<p>You will use <a href="https://gofastmcp.com/getting-started/welcome">FastMCP 2.0</a> to create the MCP server, and <a href="https://github.com/openai/openai-python">OpenAI</a> to generate tests based on prompts that you provide. Additionally, you will want to clone the repository to obtain a local copy of the server.</p>
<h3 id="step2setupaconfigurationfileinwarp">Step 2: Set up a configuration file in Warp</h3>
<p>Inside of Warp, you will want to go to the side panel, where it says MCP servers and where it says “add”. </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc67bb0095d239f6f/6a7f0d359090b0390b84ea15/02-add-mcp.jpg" alt="Add MCP Server" /></p>
<p>After that, you will be prompted to add a JSON configuration file that should resemble the following. Be sure to add your own Kibana URL, update the correct path, and include your own keys and tokens.</p>
<pre><code>{
 "elastic-synthetics": {
   "command": "python",
   "args": ["elastic_synthetics_server.py"],
   "env": {
     "PYTHONPATH": ".",
     "ELASTIC_KIBANA_URL": "https://your-kibana-url.elastic-cloud.com",
     "ELASTIC_API_KEY": "your-api-key-here",
     "ELASTIC_PROJECT_ID": "mcp-synthetics-demo",
     "ELASTIC_SPACE": "default",
     "ELASTIC_AUTO_PUSH": "true",
     "ELASTIC_USE_JAVASCRIPT": "false",
     "ELASTIC_INSTALL_DEPENDENCIES": "true",
     "OPENAI_API_KEY": "sk-your-openai-key",
     "LLM_MODEL": "gpt-4o"
   },
   "working_directory": "/path/to/your/file",
   "start_on_launch": true 
   }
}
</code></pre>
<h3 id="step3askaquestionorcallthetoolsdirectly">Step 3: Ask a question or call the tools directly</h3>
<p>Now that you've set up locally, you will want to toggle agent mode and select the LLM you wish to use. The reason why Gemini-Pro-2.5 was chosen for this blog post is that it provides a straightforward answer, while other LLMs selected returned a very lengthy response. </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt645bcec459cec042/6a7f0d3833fa8a3c4220272a/03-agent-mode.jpg" alt="Agent mode" /></p>
<p>To start using the MCP tools, from your MCP server, you can ask a question that contains the test name, URL, prompt, and schedule. </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt69de803d89d49ee9/6a7f0d3b1967ea6ee8330787/04-full-question-answer.jpg" alt="Full question and answer" /></p>
<p>You can also call the directly by typing <code>llm_create_and_deploy_test_from_prompt()</code> and the program will prompt you for the relevant details:<br />
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt400063dcb9e2ed05/6a7f0d3d33fa8a2ba1202730/05-call-mcp-tool.jpg" alt="Call MCP Tool" /></p>
<p>Inside Kibana, you should see your monitor listed if you click under Applications and select Monitors listed under Synthetics. You can also find a link to your monitor in the response of your MCP tool. </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0c8e1e9e79a7ca38/6a7f0d4073d9bd5cca29db4d/06-kibana-monitors.jpg" alt="Monitors in Kibana" /></p>
<h2 id="whatsgoingoninside">What's Going On Inside</h2>
<p>This code sample consists of three primary functions, which are MCP tools that you can call from your MCP client, including <code>diagnose_warp_mcp_config</code>, <code>create_and_deploy_browser_test</code> and <code>llm_create_and_deploy_test_from_prompt</code>.</p>
<h3 id="debuggingenvironmentissues">Debugging environment issues</h3>
<p>There were various issues that came up while creating this application around environment variable loading, so there was a need to create an MCP that could be called depending on errors that may be present. </p>
<p>The tool <code>diagnose_warp_mcp_config</code> kicks off with a decorator <code>@mcp.tool()</code> which allows it to be called and listed in the list of available tools. This tool is designed to help debug issues with Elastic-specific environment variables for troubleshooting purposes. First, it loads in the environment variables and looks for the Elastic specific variables, after it does some security masking so it doesn't show any variables and hides sensitive information like API keys in the output, showing only the first eight characters followed by "…". This tool determines if the minimum required credentials (Kibana URL and API Key) are present to proceed with deployment and provides a report letting you know to address any issues that may exist. </p>
<pre><code>@mcp.tool()
def diagnose_warp_mcp_config() -&gt; Dict[str, Any]:
   """Diagnose Warp MCP environment configuration for Elastic Synthetics"""
   try:
       env_vars = load_env_from_warp_mcp()

       # Check for required variables
       kibana_url = env_vars.get('ELASTIC_KIBANA_URL') or env_vars.get('KIBANA_URL')
       api_key = env_vars.get('ELASTIC_API_KEY') or env_vars.get('API_KEY')
       project_id = env_vars.get('ELASTIC_PROJECT_ID') or env_vars.get('PROJECT_ID')
       space = env_vars.get('ELASTIC_SPACE') or env_vars.get('SPACE', 'default')

       # Mask sensitive values for display
       masked_vars = {}
       for key, value in env_vars.items():
           if 'API_KEY' in key or 'TOKEN' in key:
               masked_vars[key] = f"{value[:8]}..." if value and len(value) &gt; 8 else "***"
           else:
               masked_vars[key] = value

       deployment_ready = bool(kibana_url and api_key)

       return safe_json_response({
           "status": "success",
           "environment_variables": masked_vars,
           "required_check": {
               "kibana_url": bool(kibana_url),
               "api_key": bool(api_key),
               "project_id": bool(project_id),
               "space": bool(space)
           },
           "deployment_ready": deployment_ready,
           "recommendations": [
               "Environment variables detected" if env_vars else "No environment variables found",
               "Kibana URL configured" if kibana_url else "Missing ELASTIC_KIBANA_URL or KIBANA_URL",
               "API Key configured" if api_key else "Missing ELASTIC_API_KEY or API_KEY",
               "Ready for deployment" if deployment_ready else "Missing required credentials"
           ]
       })

   except Exception as e:
       return safe_json_response({
           "status": "error",
           "error": str(e),
           "error_type": type(e).__name__
       })
</code></pre>
<h3 id="creatingsynthetictestsbasedonatemplate">Creating synthetic tests based on a template</h3>
<p>While developing this solution to generate tests based on a prompt, the process wasn't always smooth. Early versions encountered issues with accuracy, hallucinations, and the creation of loops. To make progress, a version that relied on creating a test template to verify the mechanics of the solution, such as whether the test could pass and be deployed to Elastic correctly, was a logical next step. </p>
<p>This solution automates the entire process of creating a synthetic browser test that will regularly check if a website is working correctly, then deploys it to Elastic Observability Synthetics. Similar to <code>diagnose_warp_mcp_config</code>, the MCP tool <code>create_and_deploy_browser_test</code> starts with the decorator <code>@mcp.tool()</code> and checks to make sure that the proper environment variables are loaded. </p>
<p>From there, it creates a TypeScript test file that is based on templates and generates dynamic test steps based on the target website's characteristics, including navigating to the website, verifying the page title exists, checking page load performance, taking a screenshot, verifying page content is visible, and finally saves the test file in a <code>synthetic_tests</code> directory.</p>
<p>Finally, it wraps Elastic's CLI tool <code>@elastic/synthetics</code> to push the test to Kibana, allowing you to set which geographic locations to run tests from, how often to run the test, and the project and workspace settings.</p>
<p>You check out the full code for this MCP tool <a href="https://github.com/JessicaGarson/MCP-Elastic-Synthetics/blob/main/elastic_synthetics_server.py#L943">here.</a></p>
<h3 id="creatingsynthetictestsbasedonaprompt">Creating synthetic tests based on a prompt</h3>
<p>While creating browser tests based on a templated approach is a good starting point, it felt generic and cookie-cutter. But it made a helpful structure to build an LLM-based function on top of.</p>
<p>The MCP tool <code>llm_create_and_deploy_test_from_prompt</code> begins by ensuring that basic parameters, including locations, schedule, and directories, are listed. Additionally, it aims to learn more about the target website to inform the AI and initialize the OpenAI client and model, which is GPT-4o. </p>
<p>After setting up the LLM, it converts natural language requests into actual Playwright test code, then cleans and validates the AI-generated code to prevent issues like injection attacks or malformed syntax. It draws inspiration from the templated approach, wrapping AI-generated steps within a proven, reliable test framework template. Finally, it deploys the test to Elastic in a similar manner to the previous tool. </p>
<p>You can find the code for this tool <a href="https://github.com/JessicaGarson/MCP-Elastic-Synthetics/blob/main/elastic_synthetics_server.py#L1559">here</a>.</p>
<h2 id="conclusionandnextsteps">Conclusion and next steps</h2>
<p>Synthetic monitoring in Elastic Observability makes it easy to test complete user journeys and keep your site reliable, with simple setup and a Playwright integration. A tool like this can provide a starting point for tests that you can iterate on after.</p>
<p>A solution like this is just the start of an MCP implementation that automatically generates Playwright tests for you and can be expanded in the future to include heartbeat monitors, utilize the <a href="https://github.com/microsoft/playwright-mcp">Playwright MCP server</a>, or consider experimenting with <a href="https://www.anthropic.com/news/claude-for-chrome">Claude for Chrome</a> to create synthetic testing.  </p>
<p>Check out more articles on <a href="https://www.elastic.co/observability-labs/blog/category/infrastructure-monitoring">Observability Labs on Infrastructure Monitoring</a></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/mcp-elastic-synthetics</link>
    <guid isPermaLink="false">mcp-elastic-synthetics</guid>
    <category><![CDATA[Incident Management]]></category>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[LLM Observability]]></category>
    <dc:creator><![CDATA[Jessica Garson]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7f7fbdc61045d078/6a7f0d43fc63ab4f3364cc71/retro.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 17 Sep 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elastic's RAG-based AI Assistant: Analyze application issues with LLMs and private GitHub issues]]></title>
    <description><![CDATA[In this blog, we review how GitHub issues and other GitHub documents from internal and external GitHub repositories can be used in root cause analysis with Elastic’s RAG-based AI Assistant.]]></description>
    <content:encoded><![CDATA[<p>As an SRE, analyzing applications is more complex than ever. Not only do you have to ensure the application is running optimally to ensure great customer experiences, but you must also understand the inner workings in some cases to help troubleshoot. Analyzing issues in a production-based service is a team sport. It takes the SRE, DevOps, development, and support to get to the root cause and potentially remediate. If it's impacting, then it's even worse because there is a race against time. Regardless of the situation, there is a ton of information that needs to be consumed and processed. This includes not only what the customer is experiencing, but also internal data to help provide the most appropriate resolution.</p>
<p>Elastic’s AI Assistant helps improve analysis for SREs, DevOps, Devs, and others. In a single window using natural language questions, you can analyze using not only general information but combine it with things like:</p>
<ul>
<li><p>Issues from internal GitHub repos, Jira, etc.</p></li>
<li><p>Documents from internal wiki sites from Confluence, etc.</p></li>
<li><p>Customer issues from your support service</p></li>
<li><p>And more</p></li>
</ul>
<p>In this blog, we will walk you through how to:</p>
<ol>
<li><p>Ingest an external GitHub repository (<a href="https://github.com/open-telemetry/opentelemetry-demo">OpenTelemetry demo repo</a>) with code and issues into Elastic. Apply Elastic Learned Sparse EncodeR (ELSER) and store it in a specific index for the AI Assistant.</p></li>
<li><p>Ingest internal GitHub repository with runbook information into Elastic. Apply ELSER and store the processed data in a specific index for the AI Assistant.</p></li>
<li><p>Use these two indices when analyzing issues for the OpenTelemetry demo in Elastic using the AI Assistant.</p></li>
</ol>
<h2 id="3simplequestionsusinggithubdatawithaiassistant">3 simple questions using GitHub data with AI Assistant</h2>
<p>Before we walk through the steps for setting up data from GitHub, let’s review what an SRE can do with the AI Assistant and GitHub repos.</p>
<p>We initially connect to GitHub using an Elastic GitHub connector and ingest and process two repos: the OpenTelemetry demo repo (public) and an internal runbook repo (Elastic internal).</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf92f1c70c95f58fd/6a7f09153ce8e211efcf520b/1.png" alt="1 - elasticsearch connectors" /></p>
<p>With these two loaded and parsed by ELSER, we ask the AI Assistant some simple questions generally asked during analysis.</p>
<h3 id="howmanyissuesareopenfortheopentelemetrydemo">How many issues are open for the OpenTelemetry demo?</h3>
<p>Since we ingested the entire repo (as of April 26, 2024) with a doc count of 1,529, we ask it a simple question regarding the total number of issues that are open. We specifically tell the AI Assistant to search our internal index to ensure the LLM knows to ask Elastic to search its internal index for the total number of issues.</p>
<div>
    
</div>
<h3 id="arethereanyissuesfortherustbasedshippingservice">Are there any issues for the Rust based shippingservice?</h3>
<p>Elastic’s AI Assistant uses ELSER to traverse the loaded GitHub repo and finds the open issue against the shippingservice (which is the following <a href="https://github.com/open-telemetry/opentelemetry-demo/issues/346">issue</a> at the time of writing this post).</p>
<div>
    
</div>
<h3 id="istherearunbookforthecartservice">Is there a runbook for the Cartservice?</h3>
<p>Since we loaded an internal GitHub repo with a few sample runbooks, the Elastic AI Assistant properly finds the runbook.</p>
<div>
    
</div>
<p>As we go through this blog, we will talk about how the AI Assistant finds these issues using ELSER and how you can configure it to use your own GitHub repos.</p>
<h2 id="retrievalaugmentedgenerationragwithelasticaiassistant">Retrieval augmented generation (RAG) with Elastic AI Assistant</h2>
<p>Elastic has the most advanced RAG-based AI Assistant for both Observability and Security. It can help you analyze your data using:</p>
<ul>
<li><p>Your favorite LLM (OpenAI, Azure OpenAI, AWS Bedrock, etc.)</p></li>
<li><p>Any internal information (GitHub, Confluence, customer issues, etc.) you can either connect to or bring into Elastic’s indices</p></li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt406757fa6ae9b581/6a7f0918fc63ab6eb264cac5/2.png" alt="Elastic AI Assistant — connecting internal and external information" /></p>
<p>The reason Elastic’s AI Assistant can do this is because it supports RAG, which helps retrieve internal information along with LLM-based knowledge.</p>
<p>Adding relevant internal information for an SRE into Elastic:</p>
<ul>
<li><p>As data comes in, such as in your GitHub repository, ELSER is applied to the data, and embeddings (weights and tokens into a sparse vector field) are added to capture semantic meaning and context of the data.</p></li>
<li><p>This data (GitHub, Confluence, etc.) is processed with embeddings and is stored in an index that can be searched by the AI Assistant.</p></li>
</ul>
<p>When you query the AI Assistant for information:</p>
<ul>
<li><p>The query goes through the same inference process as the ingested data using ELSER. The input query generates a “sparse vector,” which is used to find the most relevant highly ranked information in the ingested data (GitHub, Confluence, etc.).</p></li>
<li><p>The retrieved data is then combined with the query and also sent over to the LLM, which will then add its own knowledge base information (if there is anything to add), or it might ask Elastic (via function calls) to analyze, chart, or even search further. If a function call is made to Elastic and a response is provided, it will be added by the LLM to its response.</p></li>
<li><p>The results will be the most contextual based answer combining both LLM and anything relevant from your internal data.</p></li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3d3c6b7cdcc080d5/6a7f091bc2cc090b712493d2/3.png" alt="3 - elastic's RAG flowchart" /></p>
<h2 id="applicationprerequisitesandconfig">Application, prerequisites, and config</h2>
<p>If you want to try the steps in this blog, here are some prerequisites:</p>
<ul>
<li><p>An Elastic Cloud account — <a href="https://cloud.elastic.co/">sign up now</a></p></li>
<li><p><a href="https://github.com/open-telemetry/opentelemetry-demo">OpenTelemetry demo</a> running and connected to Elastic (<a href="https://www.elastic.co/guide/en/observability/current/apm-open-telemetry-direct.html#apm-instrument-apps-otel">APM documentation</a>)</p></li>
<li><p>Whatever internal GitHub repo you want to use with some information that is useful for analysis (In our walk through, we will be using a GitHub repo that houses runbooks for different scenarios when Elastic does demos).</p></li>
<li><p>Account with your favorite or approved LLM (OpenAI, Azure OpenAI, AWS Bedrock)</p></li>
</ul>
<h2 id="addingthegithubrepostoelastic">Adding the GitHub repos to Elastic</h2>
<p>The first step is to set up the GitHub connector and connect to your GitHub repo. Elastic has several connectors from GitHub, Confluence, Google Drive, Jira, AWS S3, Microsoft Teams, Slack, and more. So while we will go over the GitHub connector in this blog, don’t forget about other connectors.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt60fcb46741cdd76a/6a7f091e227b1c216e598468/4.png" alt="4 - select a connector" /></p>
<p>Once you select the GitHub connector and give it a name, you need to add two items:</p>
<ul>
<li><p>GitHub token</p></li>
<li><p>The URL open-telemetry/opentelemetry-demo</p></li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt080e701b21bc65d7/6a7f0921c2cc09859b2493da/5.png" alt="5 - configuration" /></p>
<p>Next, add it to an index in the wizard.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt927ade33c31fe513/6a7f0924e88c6526e500b51e/6.png" alt="6 - attach an index" /></p>
<h2 id="createapipelineandprocessthedatawithelser">Create a pipeline and process the data with ELSER</h2>
<p>In order to add the embeddings we discussed in the section above, we need to add the following to the connector:</p>
<ul>
<li><p>Create a pipeline in the configuration wizard.</p></li>
<li><p>Create a custom pipeline.</p></li>
<li><p>Add the ML inference pipeline.</p></li>
<li><p>Select ELSERv2 ML Model to add the embeddings.</p></li>
<li><p>Select the fields that need to be evaluated as part of the inference pipeline.</p></li>
<li><p>Test and save the inference pipeline and the overall pipeline.</p></li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfb9084210cb0a5ee/6a7f09271967eaeac23305c3/7.png" alt="7" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte35327bdc86f6a8a/6a7f092abdcff02bf2c42cb3/8.png" alt="8" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7491582e508268c1/6a7f092cb4377029644d6b29/9.png" alt="9" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2e9f75a9dd88cd5a/6a7f0930b43770dcdd4d6b2d/10.png" alt="10" /></p>
<h2 id="syncthedata">Sync the data</h2>
<p>Now that the pipeline is created, you need to start to sync the github repo. As the documents from the github repo come in, they will go through the pipeline and embeddings will be added.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt807d1625b5108268/6a7f0932bd2198c959757f47/11.png" alt="11" /></p>
<h2 id="embeddings">Embeddings</h2>
<p>Once the pipeline is set up, sync the data in the connector. As the GitHub repository comes in, the inference pipeline will process the data as follows:</p>
<ul>
<li><p>As data comes in from your GitHub repository, ELSER is applied to the data, and embeddings (weights and tokens into a sparse vector field) are added to capture semantic meaning and context of the data.</p></li>
<li><p>This data is processed with embeddings and is stored in an index that can be searched by the AI Assistant.</p></li>
</ul>
<p>When you look at the OpenTelemetry GitHub documents that were ingested, you will see how the weights and token are added to the predicted_value field in the index.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdef07598d4c6a40d/6a7f0935b4377067a64d6b31/12.png" alt="12" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt09e0948fdc925073/6a7f0939bd21985d30757f51/13.png" alt="13" /></p>
<p>These embeddings will now be used to find the most contextually relevant document when the user asks the AI Assistant a query, which might use this.</p>
<h2 id="checkifaiassistantcanusetheindex">Check if AI Assistant can use the index</h2>
<p>Elastic’s AI Assistant uses ELSER to traverse the loaded Github repo and finds the open issue against the shippingservice. (which is the following <a href="https://github.com/open-telemetry/opentelemetry-demo/issues/346">issue</a> at the time of writing this post).</p>
<div>
    
</div>
<p>Based on the response, we can see that the AI Assistant can now use the index to find the issue and use it for further analysis.</p>
<h2 id="conclusion">Conclusion</h2>
<p>You’ve now seen how easy Elastic’s RAG-based AI Assistant is to set up. You can bring in documents from multiple locations (GitHub, Confluent, Slack, etc.). We’ve shown the setup for GitHub and OpenTelemetry. This internal information can be useful in managing issues, accelerating resolution, and improving customer experiences. Check out our other blogs on how the AI Assistant can help SREs do better analysis, lower MTTR, and improve operations overall:</p>
<ul>
<li><p><a href="https://www.elastic.co/blog/analyzing-opentelemetry-apps-elastic-ai-assistant-apm">Analyzing OpenTelemetry apps with Elastic AI Assistant and APM</a></p></li>
<li><p><a href="https://www.elastic.co/blog/elastic-ai-assistant-observability-escapes-kibana">The Elastic AI Assistant for Observability escapes Kibana!</a></p></li>
<li><p><a href="https://www.elastic.co/blog/elastic-ai-assistant-observability-microsoft-azure-openai">Getting started with the Elastic AI Assistant for Observability and Microsoft Azure OpenAI</a></p></li>
<li><p><a href="https://www.elastic.co/blog/whats-new-elastic-8-13-0">Elastic 8.13: GA of Amazon Bedrock in the Elastic AI Assistant for Observability</a></p></li>
<li><p><a href="https://www.elastic.co/blog/sre-troubleshooting-ai-assistant-observability-runbooks">Enhancing SRE troubleshooting with the AI Assistant for Observability and your organization's runbooks</a></p></li>
<li><p><a href="https://www.elastic.co/blog/context-aware-insights-elastic-ai-assistant-observability">Context-aware insights using the Elastic AI Assistant for Observability</a></p></li>
<li><p><a href="https://www.elastic.co/blog/elastic-ai-assistant-observability-amazon-bedrock">Getting started with the Elastic AI Assistant for Observability and Amazon Bedrock</a></p></li>
</ul>
<h2 id="tryitout">Try it out</h2>
<p>Existing Elastic Cloud customers can access many of these features directly from the <a href="https://cloud.elastic.co/">Elastic Cloud console</a>. Not taking advantage of Elastic on cloud? <a href="https://www.elastic.co/cloud/cloud-trial-overview">Start a free trial</a>.</p>
<p>All of this is also possible in your environments. <a href="https://www.elastic.co/observability/universal-profiling">Learn how to get started today</a>.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>
<p><em>In this blog post, we may have used or referred to third party generative AI tools, which are owned and operated by their respective owners. Elastic does not have any control over the third party tools and we have no responsibility or liability for their content, operation or use, nor for any loss or damage that may arise from your use of such tools. Please exercise caution when using AI tools with personal, sensitive or confidential information. Any data you submit may be used for AI training or other purposes. There is no guarantee that information you provide will be kept secure or confidential. You should familiarize yourself with the privacy practices and terms of use of any generative AI tools prior to use.</em></p>
<p><em>Elastic, Elasticsearch, ESRE, Elasticsearch Relevance Engine and associated marks are trademarks, logos or registered trademarks of Elasticsearch N.V. in the United States and other countries. All other company and product names are trademarks, logos or registered trademarks of their respective owners.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/elastic-rag-ai-assistant-application-issues-llm-github</link>
    <guid isPermaLink="false">elastic-rag-ai-assistant-application-issues-llm-github</guid>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfdbcb18819ebcd43/6a7f093b5967e55e8c5dd152/AI_fingertip_touching_human_fingertip.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 08 May 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Analyzing OpenTelemetry apps with Elastic AI Assistant and APM]]></title>
    <description><![CDATA[Elastic Observability provides native OpenTelemetry support, but analyzing applications logs, metrics, and traces can be daunting. Elastic Observability not only provides AIOps features but also an AI Assistant (co-pilot) to help get to MTTR faster.]]></description>
    <content:encoded><![CDATA[<p>OpenTelemetry is rapidly becoming the most expansive project within the Cloud Native Computing Foundation (CNCF), boasting as many commits as Kubernetes and garnering widespread support from customers. Numerous companies are adopting OpenTelemetry and integrating it into their applications. Elastic® offers detailed <a href="https://www.elastic.co/blog/getting-started-opentelemetry-instrumentation-sample-app">guides</a> on implementing OpenTelemetry for applications. However, like many applications, pinpointing and resolving issues can be time-consuming.</p>
<p>The <a href="https://www.elastic.co/blog/context-aware-insights-elastic-ai-assistant-observability">Elastic AI Assistant</a> significantly enhances the process, not only in identifying but also in resolving issues. This is further enhanced by Elastic’s new Service Level Objective (SLO) capability, allowing you to streamline your entire site reliability engineering (SRE) process from detecting potential issues to enhancing the overall customer experience.</p>
<p>In this blog, we will demonstrate how you, as an SRE, can detect issues in a service equipped with OpenTelemetry. We will explore problem identification using Elastic APM, Elastic’s AIOps capabilities, and the Elastic AI Assistant.</p>
<p>We will illustrate this using the <a href="https://github.com/elastic/opentelemetry-demo">OpenTelemetry demo</a>, with a <a href="https://opentelemetry.io/docs/demo/feature-flags/">feature flag (cartService)</a> that is activated.</p>
<p>Our walkthrough will encompass two scenarios:</p>
<ol>
<li><p>When the SLO for cart service becomes noncompliant, we will analyze the error through Elastic APM. The Elastic AI Assistant will assist by providing a runbook and a GitHub issue to facilitate issue analysis.</p></li>
<li><p>Should the SLO for the cart service be noncompliant, we will examine the trace that indicates a high failure rate. We will employ AIOps for failure correlation and the AI Assistant to analyze logs and Kubernetes metrics directly from the Assistant.</p></li>
</ol>
<h2 id="prerequisitesandconfig">Prerequisites and config</h2>
<p>If you plan on following this blog, here are some of the components and details we used to set up the configuration:</p>
<ul>
<li><p>Ensure you have an account on <a href="http://cloud.elastic.co/">Elastic Cloud</a> and a deployed stack (<a href="https://www.elastic.co/guide/en/elastic-stack/current/installing-elastic-stack.html">see instructions here</a>).</p></li>
<li><p>We used the OpenTelemetry Demo. Directions for using Elastic with OpenTelemetry Demo are <a href="https://github.com/elastic/opentelemetry-demo">here</a>.</p></li>
<li><p>Additionally you will need to connect your AI Assistant to your favorite LLM. We used Azure OpenAI GPT-4.</p></li>
<li><p>We also ran the OpenTelemetry Demo on Kubernetes, specifically on GKE.</p></li>
</ul>
<h2 id="slononcompliance">SLO noncompliance</h2>
<p>Elastic APM recently released the SLO (Service Level Objectives) feature in <a href="https://www.elastic.co/guide/en/observability/8.12/slo.html">8.12</a>. This feature enables setting measurable performance targets for services, such as <a href="https://sre.google/sre-book/monitoring-distributed-systems/">availability, latency, traffic, errors, and saturation or define your own</a>. Key components include:</p>
<ul>
<li><p>Defining and monitoring SLIs (Service Level Indicators)</p></li>
<li><p>Monitoring error budgets indicating permissible performance shortfalls</p></li>
<li><p>Alerting on burn rates showing error budget consumption</p></li>
</ul>
<p>We set up two SLOs for cart service:</p>
<ul>
<li><p><strong>Availability SLO</strong> , which monitors its availability by ensuring that transactions succeed. We set up the feature flag in the OpenTelemetry application, which generates an error for EmptyCart transactions 10% of the time.</p></li>
<li><p><strong>Latency SLO</strong> to ensure transactions are not going below a specific latency, which will reduce customer experiences.</p></li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf3eaf5cb49ab688a/6a7f0223e02fac902d5d60ff/image1.png" alt="1 - SLOs" /></p>
<p>Because of the OTel cartservice feature flag, the availability SLO is triggered, and within the SLO details, we see that over a seven-day period the availability is well below our target of 99.9, at 95.5. Additionally all the error budget that was available is also exhausted.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt26f1432b0e332202/6a7f0226b43770cf024d6825/image2.png" alt="2 - cart service otel" /></p>
<p>With SLO, you can easily identify when issues with customer experience occur, or when potential issues with services arise before they become potentially worse.</p>
<h2 id="scenario1analyzingapmtraceandlogswithaiassistant">Scenario 1: Analyzing APM trace and logs with AI Assistant</h2>
<p>Once the SLO is found as non-compliant, we can dive into cart service to investigate in Elastic APM. The following walks through the set of steps you can take in Elastic APM and how to use the AI Assistant to analyze the issue:</p>
<div>
    
</div>
<p>From the video, we can see that once in APM, we took the following steps.</p>
<ol>
<li><p>Investigated the trace EmptyCart, which was experiencing larger than normal failure rates.</p></li>
<li><p>The trace showed a significant number of failures, which also resulted in slightly larger latency.</p></li>
<li><p>We used AIOps failure correlation to identify the potential component causing the failure, which correlated to a field value of FailedPrecondition.</p></li>
<li><p>While filtering on that value and reviewing the logs, we still couldn’t understand what this meant.</p></li>
<li><p>This is where you can use Elastic’s AI Assistant to further your understanding of the issue.</p></li>
</ol>
<p>AI Assistant helped us analyze the following:</p>
<ol>
<li><p>It helped us understand what the log message meant and that it was related to the Redis connection failure issue.</p></li>
<li><p>Because we couldn’t connect to Redis, we asked the AI Assistant to give us the metrics for the Redis Kubernetes pods.</p></li>
<li><p>We learned there were two pods for Redis from the logs over the last two hours.</p></li>
<li><p>However, we also learned that the memory of one seems to be increasing.</p></li>
<li><p>It seems that Redis restarted (hence the second pod), and with this information we could dive deeper into what happened to Redis.</p></li>
</ol>
<p>You can see how quickly we could correlate a significant amount of information, logs, metrics, and traces through the AI Assistant and Elastic’s APM capabilities. We didn’t have to go through multiple screens to hunt down information.</p>
<h2 id="scenario2analyzingapmerrorwithaiassistant">Scenario 2: Analyzing APM error with AI Assistant</h2>
<p>Once the SLO is found as noncompliant, we can dive into cart service to investigate in Elastic APM. The following walks through the set of steps you can take in Elastic APM and use the AI Assistant to analyze the issue:</p>
<div>
    
</div>
<p>From the video, we can see that once in APM, we took the following steps:</p>
<ol>
<li><p>We noticed a specific error for the APM service.</p></li>
<li><p>We investigated this in the error tab, and while we see it’s an issue with connection to Redis, we still need more information.</p></li>
<li><p>The AI Assistant helps us understand the stacktrace and provides some potential causes for the error and ways to diagnose and resolve it.</p></li>
<li><p>We also asked it for a runbook, created by our SRE team, which gives us steps to work through this particular issue.</p></li>
</ol>
<p>But as you can see, AI Assistant provides us not only with information about the error message but also how to diagnose it and potentially resolve it with an internal runbook.</p>
<h2 id="achievingoperationalexcellenceoptimalperformanceandreliability">Achieving operational excellence, optimal performance, and reliability</h2>
<p>We’ve shown how an OpenTelemetry instrumented application (OTel demo) can be analyzed using Elastic’s features, especially the AI Assistant coupled with Elastic APM, AIOps, and the latest SLO features. Elastic significantly streamlines the process of identifying and resolving issues within your applications.</p>
<p>Through our detailed walkthrough of two distinct scenarios, we have seen how Elastic APM and the AI Assistant can efficiently analyze and address noncompliance with SLOs in a cart service. The ability to quickly correlate information, logs, metrics, and traces through these tools not only saves time but also enhances the overall effectiveness of the troubleshooting process.</p>
<p>The use of Elastic's AI Assistant in these scenarios underscores the value of integrating advanced AI capabilities into operational workflows. It goes beyond simple error analysis, offering insights into potential causes and providing actionable solutions, sometimes even with customized runbooks. This integration of technology fundamentally changes how SREs approach problem-solving, making the process more efficient and less reliant on manual investigation.</p>
<p>Overall, the advancements in Elastic’s APM, AIOps capabilities, and the AI Assistant, particularly in handling OpenTelemetry data, represent a significant step forward in operational excellence. These tools enable SREs to not only react swiftly to emerging issues but also proactively manage and optimize the performance and reliability of their services, thereby ensuring an enhanced customer experience.</p>
<h2 id="tryitout">Try it out</h2>
<p>Existing Elastic Cloud customers can access many of these features directly from the <a href="https://cloud.elastic.co/">Elastic Cloud console</a>. Not taking advantage of Elastic on cloud? <a href="https://www.elastic.co/cloud/cloud-trial-overview">Start a free trial</a>.</p>
<blockquote>
  <ul>
  <li><a href="https://www.elastic.co/blog/service-level-objectives-slos-logs-metrics">Build better Service Level Objectives (SLOs) from logs and metrics</a></li>
  <li><a href="https://www.elastic.co/blog/whats-new-elastic-observability-8-12-0">Elastic Observability 8.12: GA for AI Assistant, SLO, and Mobile APM support</a></li>
  <li><a href="https://www.elastic.co/blog/native-opentelemetry-support-in-elastic-observability">Native Observability support in Elastic Observability</a></li>
  <li><a href="https://www.elastic.co/blog/context-aware-insights-elastic-ai-assistant-observability">Context-aware insights using the Elastic AI Assistant for Observability</a></li>
  </ul>
</blockquote>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>
<p><em>In this blog post, we may have used or referred to third party generative AI tools, which are owned and operated by their respective owners. Elastic does not have any control over the third party tools and we have no responsibility or liability for their content, operation or use, nor for any loss or damage that may arise from your use of such tools. Please exercise caution when using AI tools with personal, sensitive or confidential information. Any data you submit may be used for AI training or other purposes. There is no guarantee that information you provide will be kept secure or confidential. You should familiarize yourself with the privacy practices and terms of use of any generative AI tools prior to use.</em></p>
<p><em>Elastic, Elasticsearch, ESRE, Elasticsearch Relevance Engine and associated marks are trademarks, logos or registered trademarks of Elasticsearch N.V. in the United States and other countries. All other company and product names are trademarks, logos or registered trademarks of their respective owners.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/analyzing-opentelemetry-apps-elastic-ai-assistant-apm</link>
    <guid isPermaLink="false">analyzing-opentelemetry-apps-elastic-ai-assistant-apm</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb9decfe96627ccd0/6a7f022977b034eedd3ff0a1/ecs-otel-announcement-3.jpeg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 12 Mar 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Enhancing SRE troubleshooting with the AI Assistant for Observability and your organization's runbooks]]></title>
    <description><![CDATA[Empower your SRE team with this guide to enriching Elastic's AI Assistant Knowledge Base with your organization's internal observability information for enhanced alert remediation and incident management.]]></description>
    <content:encoded><![CDATA[<p>The <a href="https://www.elastic.co/blog/context-aware-insights-elastic-ai-assistant-observability">Observability AI Assistant</a> helps users explore and analyze observability data using a natural language interface, by leveraging automatic function calling to request, analyze, and visualize your data to transform it into actionable observability. The Assistant can also set up a Knowledge Base, powered by <a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-elser.html">Elastic Learned Sparse EncodeR</a> (ELSER) to provide additional context and recommendations from private data, alongside the large language models (LLMs) using RAG (Retrieval Augmented Generation). Elastic’s Stack — as a vector database with out-of-the-box semantic search and connectors to LLM integrations and the Observability solution — is the perfect toolkit to extract the maximum value of combining your company's unique observability knowledge with generative AI.</p>
<h2 id="enhancedtroubleshootingforsres">Enhanced troubleshooting for SREs</h2>
<p>Site reliability engineers (SRE) in large organizations often face challenges in locating necessary information for troubleshooting alerts, monitoring systems, or deriving insights due to scattered and potentially outdated resources. This issue is particularly significant for less experienced SREs who may require assistance even with the presence of a runbook. Recurring incidents pose another problem, as the on-call individual may lack knowledge about previous resolutions and subsequent steps. Mature SRE teams often invest considerable time in system improvements to minimize "fire-fighting," utilizing extensive automation and documentation to support on-call personnel.</p>
<p>Elastic® addresses these challenges by combining generative AI models with relevant search results from your internal data using RAG. The <a href="https://www.elastic.co/guide/en/observability/current/obs-ai-assistant.html">Observability AI Assistant's internal Knowledge Base</a>, powered by our semantic search retrieval model <a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-elser.html">ELSER</a>, can recall information at any point during a conversation, providing RAG responses based on internal knowledge.</p>
<p>This Knowledge Base can be enriched with your organization's information, such as runbooks, GitHub issues, internal documentation, and Slack messages, allowing the AI Assistant to provide specific assistance. The Assistant can also document and store specific information from an ongoing conversation with an SRE while troubleshooting issues, effectively creating runbooks for future reference. Furthermore, the Assistant can generate summaries of incidents, system status, runbooks, post-mortems, or public announcements.</p>
<p>This ability to retrieve, summarize, and present contextually relevant information is a game-changer for SRE teams, transforming the work from chasing documents and data to an intuitive, contextually sensitive user experience.The Knowledge Base (see <a href="https://www.elastic.co/guide/en/observability/current/obs-ai-assistant.html#obs-ai-requirements">requirements</a>) serves as a central repository of Observability knowledge, breaking documentation silos and integrating tribal knowledge, making this information accessible to SREs enhanced with the power of LLMs.</p>
<p>Your LLM provider may collect query telemetry when using the AI Assistant. If your data is confidential or has sensitive details, we recommend you verify the data treatment policy of the LLM connector you provided to the AI Assistant.</p>
<p>In this blog post, we will cover different ways to enrich your Knowledge Base (KB) with internal information. We will focus on a specific alert, indicating that there was an increase in logs with “502 Bad Gateway” errors that has surpassed the alert’s threshold.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt432418d2871ff281/6a7f1b0873d9bd18b729df69/elastic-blog-1.png" alt="1 - threshold breached" /></p>
<h2 id="howtotroubleshootanalertwiththeknowledgebase">How to troubleshoot an alert with the Knowledge Base</h2>
<p>Before the KB has been enriched with internal information, when the SRE asks the AI Assistant about how to troubleshoot an alert, the response from the LLM will be based on the data it learned during training; however, the LLM is not able to answer questions related to private, recent, or emerging knowledge. In this case, when asking for the steps to troubleshoot the alert, the response will be based on generic information.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf2577ea1ce6b71b1/6a7f1b0b05b7b519c318bd51/elastic-blog-2.png" alt="2 - troubleshooting steps" /></p>
<p>However, once the KB has been enriched with your runbooks, when your team receives a new alert on “502 Bad Gateway” Errors, they can use AI Assistant to access the internal knowledge to troubleshoot it, using semantic search to find the appropriate runbook in the Knowledge Base.</p>
<p>In this blog, we will cover different ways to add internal information on how to troubleshoot an alert to the Knowledge Base:</p>
<ol>
<li><p>Ask the assistant to remember the content of an existing runbook.</p></li>
<li><p>Ask the Assistant to summarize and store in the Knowledge Base the steps taken during a conversation and store it as a runbook.</p></li>
<li><p>Import your runbooks from GitHub or another external source to the Knowledge Base using our Connector and APIs.</p></li>
</ol>
<p>After the runbooks have been added to the KB, the AI Assistant is now able to recall the internal and specific information in the runbooks. By leveraging the retrieved information, the LLM could provide more accurate and relevant recommendations for troubleshooting the alert. This could include suggesting potential causes for the alert, steps to resolve the issue, preventative measures for future incidents, or asking the assistant to help execute the steps mentioned in the runbook using functions. With more accurate and relevant information at hand, the SRE could potentially resolve the alert more quickly, reducing downtime and improving service reliability.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte9f381e6f96debcb/6a7f1b0e73d9bd5ba529df6d/Screenshot_2023-11-10_at_9.52.38_AM.png" alt="3 - troubleshooting 502 Bad gateway" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb7975cbd842b6cd8/6a7f1b11c2e91480da016ffa/elastic-blog-4.png" alt="4 - (5) test the backend directly" /></p>
<p>Your Knowledge Base documents will be stored in the indices <em>.kibana-observability-ai-assistant-kb-</em>*. Have in mind that LLMs have restrictions on the amount of information the model can read and write at once, called token limit. Imagine you're reading a book, but you can only remember a certain number of words at a time. Once you've reached that limit, you start to forget the earlier words you've read. That's similar to how a token limit works in an LLM.</p>
<p>To keep runbooks within the token limit for Retrieval Augmented Generation (RAG) models, ensure the information is concise and relevant. Use bullet points for clarity, avoid repetition, and use links for additional information. Regularly review and update the runbooks to remove outdated or irrelevant information. The goal is to provide clear, concise, and effective troubleshooting information without compromising the quality due to token limit constraints. LLMs are great for summarization, so you could ask the AI Assistant to help you make the runbooks more concise.</p>
<h2 id="asktheassistanttorememberthecontentofanexistingrunbook">Ask the assistant to remember the content of an existing runbook</h2>
<p>The easiest way to store a runbook into the Knowledge Base is to just ask the AI Assistant to do it! Open a new conversation and ask “Can you store this runbook in the KB for future reference?” followed by pasting the content of the runbook in plain text.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt88e751fe6aabf649/6a7f1b146c6eac1f20f145b5/elastic-blog-5.png" alt="5 - new conversation - let's work on this together" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcbdbc2440f80ddda/6a7f1b1696b5a6f0e687b89f/elastic-blog-6.png" alt="6 - new converastion" /></p>
<p>The AI Assistant will then store it in the Knowledge Base for you automatically, as simple as that.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt678daca55cfe0ff7/6a7f1b19fc63ab131a64d08e/elastic-blog-7.png" alt="7 - storing a runbook" /></p>
<h2 id="asktheassistanttosummarizeandstorethestepstakenduringaconversationintheknowledgebase">Ask the Assistant to summarize and store the steps taken during a conversation in the Knowledge Base</h2>
<p>You can also ask the AI Assistant to remember something while having a conversation — for example, after you have troubleshooted an alert using the AI Assistant, you could ask to "remember how to troubleshoot this alert for next time." The AI Assistant will create a summary of the steps taken to troubleshoot the alert and add it to the Knowledge Base, effectively creating runbooks for future reference. Next time you are faced with a similar situation, the AI Assistant will recall this information and use it to assist you.</p>
<p>In the following demo, the user asks the Assistant to remember the steps that have been followed to troubleshoot the root cause of an alert, and also to ping the Slack channel when this happens again. In a later conversation with the Assistant, the user asks what can be done about a similar problem, and the AI Assistant is able to remember the steps and also reminds the user to ping the Slack channel.</p>
<p>After receiving the alert, you can open the AI Assistant chat and test troubleshooting the alert. After investigating an alert, ask the AI Assistant to summarize the analysis and the steps taken to root cause. To remember them for the next time, we have a similar alert and add extra instruction like to warn the Slack channel.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt59ebedea4c01cbd8/6a7f1b1dbd21980b3c7584bf/elastic-blog-8.png" alt="8. -teal box" /></p>
<p>The Assistant will use the built-in functions to summarize the steps and store them into your Knowledge Base, so they can be recalled in future conversations.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3ab3c30c33361206/6a7f1b20c2cc0977992499ca/Screenshot_2023-11-08_at_11.34.08_AM.png" alt="9 - Elastic assistant chat (CROP)" /></p>
<p>Open a new conversation, and ask what are the steps to take when troubleshooting a similar alert to the one we just investigated. The Assistant will be able to recall the information stored in the KB that is related to the specific alert, using semantic search based on <a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-elser.html">ELSER</a>, and provide a summary of the steps taken to troubleshoot it, including the last indication of informing the Slack channel.</p>
<div>
    
</div>
<h2 id="importyourrunbooksstoredingithubtotheknowledgebaseusingapisorourgithubconnector">Import your runbooks stored in GitHub to the Knowledge Base using APIs or our GitHub Connector</h2>
<p>You can also add proprietary data into the Knowledge Base programmatically by ingesting it (e.g., GitHub Issues, Markdown files, Jira tickets, text files) into Elastic.</p>
<p>If your organization has created runbooks that are stored in Markdown documents in GitHub, follow the steps in the next section of this blog post to index the runbook documents into your Knowledge Base.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt55e94e2bbca1d4c5/6a7f1b23ead8ec8c11baac5e/elastic-blog-10.png" alt="10 - github handling 502" /></p>
<p>The steps to ingest documents into the Knowledge Base are the following:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte50ccc306ed43ff6/6a7f1b26227b1cac36598a09/elastic-blog-11.png" alt="11 - using internal knowledge" /></p>
<h3 id="ingestyourorganizationsknowledgeintoelasticsearch">Ingest your organization’s knowledge into Elasticsearch</h3>
<p><strong>Option 1:</strong> <strong>Use the</strong> <a href="https://www.elastic.co/guide/en/enterprise-search/current/crawler.html"><strong>Elastic web crawler</strong></a> <strong>.</strong> Use the web crawler to programmatically discover, extract, and index searchable content from websites and knowledge bases. When you ingest data with the web crawler, a search-optimized <a href="https://www.elastic.co/blog/what-is-an-elasticsearch-index">Elasticsearch® index</a> is created to hold and sync webpage content.</p>
<p><strong>Option 2: Use Elasticsearch's</strong> <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html"><strong>Index API</strong></a> <strong>.</strong> <a href="https://www.elastic.co/guide/en/cloud/current/ec-ingest-guides.html">Watch tutorials</a> that demonstrate how you can use the Elasticsearch language clients to ingest data from an application.</p>
<p><strong>Option 3: Build your own connector.</strong> Follow the steps described in this blog: <a href="https://www.elastic.co/search-labs/how-to-create-customized-connectors-for-elasticsearch">How to create customized connectors for Elasticsearch</a>.</p>
<p><strong>Option 4: Use Elasticsearch</strong> <a href="https://www.elastic.co/guide/en/workplace-search/current/workplace-search-content-sources.html"><strong>Workplace Search connectors</strong></a> <strong>.</strong> For example, the <a href="https://www.elastic.co/guide/en/workplace-search/current/workplace-search-github-connector.html">GitHub connector</a> can automatically capture, sync, and index issues, Markdown files, pull requests, and repos.</p>
<ul>
<li>Follow the steps to <a href="https://www.elastic.co/guide/en/workplace-search/current/workplace-search-github-connector.html#github-configuration">configure the GitHub Connector in GitHub</a> to create an OAuth App from the GitHub platform.</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt934623424f99c138/6a7f1b29bd21985ea47584c3/elastic-blog-12.png" alt="12 - elastic workplace search" /></p>
<ul>
<li>Now you can connect a GitHub instance to your organization. Head to your organization’s <strong>Search &gt; Workplace Search</strong> administrative dashboard, and locate the Sources tab.</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt562317ca772f6b3d/6a7f1b2ceab5be27ce20ab08/Screenshot_2023-11-08_at_10.19.19_AM.png" alt="13 - screenshot" /></p>
<ul>
<li>Select <strong>GitHub</strong> (or GitHub Enterprise) in the Configured Sources list, and follow the GitHub authentication flow as presented. Upon the successful authentication flow, you will be redirected to Workplace Search and will be prompted to select the Organization you would like to synchronize.</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6c465cdefe93ce4d/6a7f1b2fde231504f2fd80af/elastic-blog-14.png" alt="14 - configure and connect" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt54d1b7727cb79ab9/6a7f1b32eab5be3b6220ab0c/elastic-blog-15.png" alt="15 - how to add github" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf86ab1779e93317c/6a7f1b35bdcff09009c432af/elastic-blog-16.png" alt="16 - github" /></p>
<ul>
<li>After configuring the connector and selecting the organization, the content should be synchronized and you will be able to see it in Sources. If you don’t need to index all the available content, you can specify the indexing rules via the API. This will help shorten indexing times and limit the size of the index. See <a href="https://www.elastic.co/guide/en/workplace-search/current/workplace-search-customizing-indexing-rules.html">Customizing indexing</a>.</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltded840b7b410baf3/6a7f1b37eab5be779e20ab10/elastic-blog-17.png" alt="17 - source overview" /></p>
<ul>
<li>The source has created an index in Elastic with the content (Issues, Markdown Files…) from your organization. You can find the index name by navigating to <strong>Stack Management &gt; Index Management</strong> , activating the <strong>Include hidden Indices</strong> button on the right, and searching for “GitHub.”</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt22ee12a22538f12f/6a7f1b3b05b7b517f518bd55/elastic-blog-18.png" alt="18 - index mgmt" /></p>
<ul>
<li>You can explore the documents you have indexed by creating a Data View and exploring it in Discover. Go to <strong>Stack Management &gt; Kibana &gt; Data Views &gt; Create data view</strong> and introduce the data view Name, Index pattern (make sure you activate “Allow hidden and system indices” in advanced options), and Timestamp field:</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt10bb270db2987d53/6a7f1b3eb437702e514d711e/elastic-blog-19.png" alt="19 - create data view" /></p>
<ul>
<li>You can now explore the documents in Discover using the data view:</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0626e95bf462405f/6a7f1b4142a11770e795c32b/elastic-blog-20.png" alt="20 - data view" /></p>
<h3 id="reindexyourinternalrunbooksintotheaiassistantsknowledgebaseindexusingitssemanticsearchpipeline">Reindex your internal runbooks into the AI Assistant’s Knowledge Base Index, using it's semantic search pipeline</h3>
<p>Your Knowledge Base documents are stored in the indices <em>.kibana-observability-ai-assistant-kb-*</em>. To add your internal runbooks imported from GitHub to the KB, you just need to reindex the documents from the index you created in the previous step to the KB’s index. To add the semantic search capabilities to the documents in the KB, the reindex should also use the ELSER pipeline preconfigured for the KB, <em>.kibana-observability-ai-assistant-kb-ingest-pipeline</em>.</p>
<p>By creating a Data View with the KB index, you can explore the content in Discover.</p>
<p>You execute the query below in <strong>Management &gt; Dev Tools</strong> , making sure to replace the following, both on “_source” and “inline”:</p>
<ul>
<li>InternalDocsIndex : name of the index where your internal docs are stored</li>
<li>text_field : name of the field with the text of your internal docs</li>
<li>timestamp : name of the field of the timestamp in your internal docs</li>
<li>public : (true or false) if true, makes a document available to all users in the defined <a href="https://www.elastic.co/guide/en/kibana/current/xpack-spaces.html">Kibana Space</a> (if is defined) or in all spaces (if is not defined); if false, document will be restricted to the user indicated in</li>
<li>(optional) space : if defined, restricts the internal document to be available in a specific <a href="https://www.elastic.co/guide/en/kibana/current/xpack-spaces.html">Kibana Space</a></li>
<li>(optional) user.name : if defined, restricts the internal document to be available for a specific user</li>
<li>(optional) "query" filter to index only certain docs (see below)</li>
</ul>
<pre><code>POST _reindex
{
    "source": {
        "index": "&lt;InternalDocsIndex&gt;",
        "_source": [
            "&lt;text_field&gt;",
            "&lt;timestamp&gt;",
            "namespace",
            "is_correction",
            "public",
            "confidence"
        ]
    },
    "dest": {
        "index": ".kibana-observability-ai-assistant-kb-000001",
        "pipeline": ".kibana-observability-ai-assistant-kb-ingest-pipeline"
    },
    "script": {
        "inline": "ctx._source.text=ctx._source.remove(\"&lt;text_field&gt;\");ctx._source.namespace=\"&lt;space&gt;\";ctx._source.is_correction=false;ctx._source.public=&lt;public&gt;;ctx._source.confidence=\"high\";ctx._source['@timestamp']=ctx._source.remove(\"&lt;timestamp&gt;\");ctx._source['user.name'] = \"&lt;user.name&gt;\""
    }
}
</code></pre>
<p>You may want to specify the type of documents that you reindex in the KB — for example, you may only want to reindex Markdown documents (like Runbooks). You can add a “query” filter to the documents in the source. In the case of GitHub, runbooks are identified with the “type” field containing the string “file,” and you could add that to the reindex query like indicated below. To add also GitHub Issues, you can also include in the query “type” field containing the string “issues”:</p>
<pre><code>"source": {
        "index": "&lt;InternalDocsIndex&gt;",
        "_source": [
            "&lt;text_field&gt;",
            "&lt;timestamp&gt;",
            "namespace",
            "is_correction",
            "public",
            "confidence"
        ],
    "query": {
      "terms": {
        "type": ["file"]
      }
    }
</code></pre>
<p>Great! Now that the data is stored in your Knowledge Base, you can ask the Observability AI Assistant any questions about it:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta473e0043f5dbf04/6a7f1b442f00b25aabefef31/elastic-blog-21.png" alt="21 - new conversation" /></p>
<div>
    
</div>
<div>
    
</div>
<h2 id="conclusion">Conclusion</h2>
<p>In conclusion, leveraging internal Observability knowledge and adding it to the Elastic Knowledge Base can greatly enhance the capabilities of the AI Assistant. By manually inputting information or programmatically ingesting documents, SREs can create a central repository of knowledge accessible through the power of Elastic and LLMs. The AI Assistant can recall this information, assist with incidents, and provide tailored observability to specific contexts using Retrieval Augmented Generation. By following the steps outlined in this article, organizations can unlock the full potential of their Elastic AI Assistant.</p>
<p><a href="https://www.elastic.co/generative-ai/ai-assistant">Start enriching your Knowledge Base with the Elastic AI Assistant today</a> and empower your SRE team with the tools they need to excel. Follow the steps outlined in this article and take your incident management and alert remediation processes to the next level. Your journey toward a more efficient and effective SRE operation begins now.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>
<p><em>In this blog post, we may have used or referred to third party generative AI tools, which are owned and operated by their respective owners. Elastic does not have any control over the third party tools and we have no responsibility or liability for their content, operation or use, nor for any loss or damage that may arise from your use of such tools. Please exercise caution when using AI tools with personal, sensitive or confidential information. Any data you submit may be used for AI training or other purposes. There is no guarantee that information you provide will be kept secure or confidential. You should familiarize yourself with the privacy practices and terms of use of any generative AI tools prior to use.</em></p>
<p><em>Elastic, Elasticsearch, ESRE, Elasticsearch Relevance Engine and associated marks are trademarks, logos or registered trademarks of Elasticsearch N.V. in the United States and other countries. All other company and product names are trademarks, logos or registered trademarks of their respective owners.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/sre-troubleshooting-ai-assistant-observability-runbooks</link>
    <guid isPermaLink="false">sre-troubleshooting-ai-assistant-observability-runbooks</guid>
    <category><![CDATA[Agentic Observability]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Kubernetes]]></category>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[LLM Observability]]></category>
    <dc:creator><![CDATA[Almudena Sanz Olivé,Katrin Freihofner,Tom Grabowski]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2d0f6fc2d38fa05b/6a7f1b47bd21987d717584c9/11-hand.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 08 Nov 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Managing your applications on Amazon ECS EC2-based clusters with Elastic Observability]]></title>
    <description><![CDATA[Learn how to manage applications on Amazon ECS clusters based on EC2 instances and how simple it is to use Elastic agents with the AWS and docker integrations to provide a complete picture of your apps, ECS service, and corresponding EC2 instances.]]></description>
    <content:encoded><![CDATA[<p>In previous blogs, we explored how Elastic Observability can help you monitor various AWS services and analyze them effectively:</p>
<ul>
<li><a href="https://www.elastic.co/blog/aws-service-metrics-monitor-observability-easy">Managing fundamental AWS services such as Amazon EC2, Amazon RDS, Amazon VPC, and NAT gateway</a></li>
<li><a href="https://www.elastic.co/blog/aws-kinesis-data-firehose-elastic-observability-analytics">Data can be ingested into Elastic observability using a serverless forwarder or Amazon Kinesis Data Firehose</a></li>
<li><a href="https://www.elastic.co/blog/vpc-flow-logs-monitoring-analytics-observability">Ingesting and analyzing AWS VPC Flow logs</a></li>
</ul>
<p>One of the more heavily used AWS container services is Amazon ECS (Elastic Container Service). While there is a trend toward using Fargate to simplify the setup and management of ECS clusters, many users still prefer using Amazon ECS with EC2 instances. It may not be as straightforward or efficient as AWS Fargate, but it offers more control over the underlying infrastructure.</p>
<p>In the most recent blog, we explored how <a href="https://www.elastic.co/blog/elastic-agent-monitor-ecs-aws-fargate-elastic-observability">Elastic Observability helps manage Amazon ECS with Fargate</a>. However, this blog will review how to manage an Amazon ECS cluster with EC2 instances using Elastic Observability instead.</p>
<p>In general, when setting up Amazon ECS-based clusters with EC2, you may or may not have access to the EC2 instances. This determines what you can use with Elastic Observability in monitoring your EC2-based ECS cluster. Hence, there are two components you can use in monitoring the EC2-based ECS cluster with Elastic Observability:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt147b9faa326e450c/6a85cc71bc5bb384fcf81b2b/elastic-blog-1-amazon-ecs.png" alt="amazon ecs" /></p>
<p>As you can see in the diagram above, the two components are:</p>
<ol>
<li><p> <strong>Baseline setup</strong> __ <strong>:</strong>  The elastic agent running the AWS integration is configured to obtain ECS metrics and logs from cloud watch. This agent runs on an instance that is not part of the ECS cluster because it allows you to see ALL ECS clusters and other AWS Services, such as EKS, RDS, and EC2.</p></li>
<li><p> <strong>Additional setup:</strong>  If you have access to the EC2 instances in the ECS cluster, then you can run Elastic’s docker integration in each EC2 instance. This gives you significantly more details on the containers than AWS container insights. And it does not require AWS Cloudwatch, which can be fairly costly.</p></li>
</ol>
<p>Using either just the baseline or the additional setup, you will have to set up AWS CloudWatch Container Insights for the ECS cluster. However, the docker integration with the additional setup can provide additional information to the AWS CloudWatch Container Insights.</p>
<p>Hence, we will review how you can monitor the various components of an EC2-based ECS cluster:</p>
<ul>
<li>EC2 instances in the ASG group</li>
<li>ECS services running in the ECS cluster</li>
<li>ECS tasks (containers)</li>
</ul>
<p>Also, we will review how you can obtain metrics and logs from the ECS cluster with and without AWS Cloudwatch. We’ll show you how to use:</p>
<ul>
<li>AWS CloudWatch Container Insights (from Cloudwatch)</li>
<li>Docker metrics (non-Cloudwatch)</li>
<li>Amazon ECS logs via Cloudwatch</li>
</ul>
<h2 id="prerequisitesandconfiguration">Prerequisites and configuration</h2>
<p>If you plan on following this blog, here are some of the components and details we used to set up the configuration:</p>
<ul>
<li>An account on <a href="http://cloud.elastic.co">Elastic Cloud</a> and a deployed stack (<a href="https://www.elastic.co/guide/en/elastic-stack/current/installing-elastic-stack.html">see instructions here</a>) — ensure that you have both.</li>
<li>A <a href="https://hub.docker.com/_/nginx">nginx</a> container and a <a href="https://github.com/containerstack/alpine-stress">stress container</a> — we will use these two basic containers to help highlight the load on the Elastic ECS Cluster.</li>
<li>An ECS EC2 Cluster in an Auto Scaling Group — ensure you have access in order to load up the Elastic agent on the EC2 instances, or you can create an AMI and use that as the baseline image for your ECS cluster.</li>
<li>An EC2 instance anywhere in your account that is not part of the ECS cluster and has public access (to send metrics and logs)</li>
</ul>
<h2 id="whatwillyouseeinelasticobservabilityonceitsallsetup">What will you see in Elastic Observability once it's all set up?</h2>
<p>If you utilize the baseline configuration with ECS EC2 cluster configured with AWS CloudWatch Container Insights configured, the Elastic Agent configured with the following Elastic agent integrations:</p>
<ul>
<li>ECS integration</li>
<li>EC2 integration</li>
<li>AWS Cloudwatch Integration with metrics and logging</li>
</ul>
<p>Then you will be able to get the following information in Elastic dashboards:</p>
<ul>
<li>Containers in the cluster (AWS CloudWatch Container Insights via Elastic Agent and AWS Cloudwatch integration)</li>
<li>Services in the cluster (AWS CloudWatch Container Insights via Elastic Agent and AWS Cloudwatch integration)</li>
<li>CPU and memory utilization of the ECS Cluster (Elastic Agent with ECS integration)</li>
<li>EC2 CPU and memory utilization of the instance in the cluster (Elastic Agent with EC2 integration)</li>
<li>CPU and memory utilization per container (via AWS CloudWatch Container Insights via Elastic Agent and AWS Cloudwatch integration)</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6f1993af959a0a6e/6a85cc741aa1e1343eff8d83/elastic-blog-2-containers-in-cluster.png" alt="containers in cluster" /></p>
<p>If the additional configuration using Elastic agents with docker integration per ECS EC2 instance is used, you will be able to get a direct feed of metrics via docker. The following metrics can be viewed:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbe62d55a71be2bf2/6a85cc77f9373d3a8c96f5b8/elastic-blog-3-metrics-graphs.png" alt="metrics graphs" /></p>
<p>Let’s see how to set this all up.</p>
<h2 id="settingitallup">Setting it all up</h2>
<p>Over the next few steps, I’ll walk through:</p>
<ul>
<li>Getting an account on Elastic Cloud</li>
<li>Bringing up an ECS EC2 cluster and potentially setting up your own AMI</li>
<li>Setting up the containers <a href="https://hub.docker.com/_/nginx">nginx</a> and a <a href="https://github.com/containerstack/alpine-stress">stress container</a></li>
<li>Setting up the Elastic agent with docker container integration on the ECS EC2 instances</li>
<li>Setting up the Elastic agent with AWS, Cloudwatch, and ECS integrations on an independent EC2 instance</li>
</ul>
<h3 id="step1createanaccountonelasticcloud">Step 1: Create an account on Elastic Cloud</h3>
<p>Follow the instructions to <a href="https://cloud.elastic.co/registration?fromURI=/home">get started on Elastic Cloud</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt729e8dd0d43f9e98/6a85cc7a99083f38e540fa01/elastic-blog-4-free-trial.png" alt="free trial" /></p>
<h3 id="step2setupanecsclusterwithec2instances">Step 2: Set up an ECS Cluster with EC2 instances</h3>
<p>When creating a cluster, you have two options when setting it up using the console:</p>
<ul>
<li>Create a new ASG group where you will only be allowed to use the preloaded set of Amazon Linux (2 or 2023) based AMIs</li>
<li>Set up your own ASG Cluster prior to setting up the ECS Cluster and select this from the options. This option will give you more control over what Linux version and the ability to add things like Elastic agents in the AMI used for the instances in the ASG.</li>
</ul>
<p>Regardless of either option, you will need to turn on <strong>Container Insights</strong> (see the bottom part of the image below).</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt23b7fd2c3de3b660/6a85cc7df5f1a031362ec933/elastic-blog-5-infrastructure.png" alt="infrastructure" /></p>
<p>Once the cluster is setup, you can go to AWS Cloudwatch where you should see Container Insights for your cluster:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5b91cb55114bb5e7/6a85cc809a32f17b2ba7e010/elastic-blog-6-container-insights.png" alt="container insights" /></p>
<h3 id="step3setupelasticagentwithdockerintegration">Step 3: Set up Elastic agent with docker integration</h3>
<p>Next, you will need to add an Elastic agent to each one of the instances. In the Elastic cloud, set up an Elastic policy with the docker and system integrations as such:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3ae989d6345a02e9/6a85cc824710c68330d3cb8f/elastic-blog-7-ecs-ec2-cluster-policy.png" alt="cluster policy" /></p>
<p>Next, add an agent for the policy, then copy the appropriate install script (in our case it was Linux since we were running Amazon Linux 2), and run it on every EC2 instance in the cluster:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt16988c0d57d08572/6a85cc866826662b0e1eac3b/elastic-blog-8-add-agent.png" alt="add agent" /></p>
<p>Once this is added you should see agents in the fleet. Each agent will be on each EC2 instance:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf6a1f58551ef7adc/6a85cc89abdc29807812252c/elastic-blog-9-fleet.png" alt="fleet" /></p>
<p>If you decide to set up an ECS EC2 cluster with your own ASG and don’t use Amazon Lunix AMIs (2 or 2023 version), you will have to:</p>
<ul>
<li>Pick your base image to base an AMI on</li>
<li><a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-agent-install.html">Add an ECS agent and register each instance to the AMI base image manually</a></li>
<li><a href="https://www.elastic.co/guide/en/fleet/current/install-standalone-elastic-agent.html">Add the Elastic agent — standalone version</a> — this step will require you to configure your Elastic endpoint and API key (or simply add the script in the “add agent” part of the configuration above when using the UI)</li>
<li>Create the AMI once all the above components are added</li>
<li>Use the newly created AMI in creating the ASG for ECS cluster</li>
</ul>
<h3 id="step4setupanelasticagentwiththeawsintegration">Step 4: Set up an Elastic agent with the AWS integration</h3>
<p>From the integrations tab in Elastic Cloud, select AWS integration and select add agent. You will then have to walk through the configuration of the AWS integration.</p>
<p>At a minimum, ensure that you have the following configuration options turned on:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8a64f60f0c19f3e2/6a85cc8b342d6983cc21b113/elastic-blog-10-toggles.png" alt="toggles" /></p>
<p>This will ensure that not only EC2 metrics and logs are ingested but that all CloudWatch metrics and logs are also ingested. ECS metrics and logs are stored in CloudWatch.</p>
<p>If you want to ensure only logs from the specific ECS cluster are ingested, you can also restrict what to ingest by several parameters. In our setup, we are collecting only logs from Log Group with a prefix of /aws/ecs/containerinsights/EC2BasedCluster/.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd5a5c6cf8e1ad163/6a85cc8e1aa1e126dfff8d8d/elastic-blog-11-cloudwatch.png" alt="cloudwatch" /></p>
<p>Once this policy is set up, add an agent like in Step 1.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt85dd93aa8f483a84/6a85cc919bf994835f0a059b/elastic-blog-12-add-agent-testing-aws.png" alt="add agent testing aws" /></p>
<p>However, this agent needs to be added to an EC2 instance which is independent of the ECS cluster.</p>
<p>Once installed, this agent will help pull in:</p>
<ul>
<li>All EC2 instance metrics across your account (which can be adjusted in the integration policy)</li>
<li>Ingest AWS CloudWatch Container Insights from ECS</li>
<li>ECS metrics such as:</li>
<li>aws.ecs.metrics.CPUReservation.avg</li>
<li>aws.ecs.metrics.CPUUtilization.avg</li>
<li>aws.ecs.metrics.GPUReservation.avg</li>
<li>aws.ecs.metrics.MemoryReservation.avg</li>
<li>aws.ecs.metrics.MemoryUtilization.avg</li>
<li><a href="https://docs.elastic.co/integrations/aws/ecs">More - see the full list here</a></li>
</ul>
<h3 id="step5settingupservicesandcontainers">Step 5: Setting up services and containers</h3>
<p>In running this configuration, we used <a href="https://hub.docker.com/_/nginx">nginx</a> and a <a href="https://github.com/containerstack/alpine-stress">stress container</a> before we go into the task.</p>
<p>In order to initiate service and containers on ECS, you will need to set up a task for each of these containers. But more importantly, you will need to ensure that the roles for both of the following:</p>
<p>"taskRoleArn": "arn:aws:iam::xxxxx:role/ecsTaskExecutionRol"executionRoleArn":,</p>
<p>"arn:aws:iam::xxxxx:role/ecsTaskExecutionRole",</p>
<p>have the following permissions:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt83a1840913b40744/6a85cc94bc5bb39d39f81b31/elastic-blog-13-permissions.png" alt="permissions" /></p>
<p>Most importantly, you should ensure that this permission is added:</p>
<p>AmazonEC2ContainerServiceforEC2Role</p>
<p>It will ensure containers can be brought up on the EC2 instances in the cluster.</p>
<p>Once you have the right permissions, then set up the following tasks.</p>
<p>Here is the task JSON for NGINX:</p>
<pre><code>{
  "family": "NGINX",
  "containerDefinitions": [
    {
      "name": "nginx",
      "image": "nginx: latest",
      "cpu": 0,
      "portMappings": [
        {
          "name": "nginx-80-tcp",
          "containerPort": 80,
          "hostPort": 80,
          "protocol": "tcp",
          "appProtocol": "http"
        }
      ],
      "essential": true,
      "environment": [],
      "environmentFiles": [],
      "mountPoints": [],
      "volumesFrom": [],
      "ulimits": [],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-create-group": "true",
          "awslogs-group": "/ecs/",
          "awslogs-region": "us-west-2",
          "awslogs-stream-prefix": "ecs"
        },
        "secretOptions": []
      }
    }
  ],
  "taskRoleArn": "arn:aws:iam::xxxxxx:role/ecsTaskExecutionRole",
  "executionRoleArn": "arn:aws:iam::xxxxx:role/ecsTaskExecutionRole",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["EC2"],
  "cpu": "256",
  "memory": "512",
  "runtimePlatform": {
    "cpuArchitecture": "X86_64",
    "operatingSystemFamily": "LINUX"
  }
}
</code></pre>
<p>Here is the task JSON for stress container:</p>
<pre><code>{
  "family": "stressLoad",
  "containerDefinitions": [
    {
      "name": "stressLoad",
      "image": "containerstack/alpine-stress",
      "cpu": 0,
      "memory": 512,
      "memoryReservation": 512,
      "portMappings": [],
      "essential": true,
      "entryPoint": ["sh", "-c"],
      "command": [
        "/usr/local/bin/stress --cpu 2 --io 2 --vm 1 --vm-bytes 128M --timeout 6000s"
      ],
      "environment": [],
      "mountPoints": [],
      "volumesFrom": [],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-create-group": "true",
          "awslogs-group": "/ecs/",
          "awslogs-region": "us-west-2",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ],
  "taskRoleArn": "arn:aws:iam::xxxxx:role/ecsTaskExecutionRole",
  "executionRoleArn": "arn:aws:iam::xxxxx:role/ecsTaskExecutionRole",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["EC2"],
  "cpu": "256",
  "memory": "512",
  "runtimePlatform": {
    "cpuArchitecture": "X86_64",
    "operatingSystemFamily": "LINUX"
  }
}
</code></pre>
<p>Once you have defined the tasks, ensure you bring up each service (one for each task) with the launch type of EC2:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd5e714ecefbb58cf/6a85cc979829261768583922/elastic-blog-14-environment.png" alt="environment" /></p>
<p>You should have two services running now.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6e6c3364b0c587f3/6a85cc9b501a85c7a6fbb363/elastic-blog-15-ec2basedcluster.png" alt="ec2basedcluster" /></p>
<h3 id="step6checkonmetricsandlogsinelasticcloud">Step 6: Check on metrics and logs in Elastic Cloud</h3>
<p>Go to Elastic Cloud and ensure that you are getting metrics and logs from the ECS Cluster. First, check to see if you are receiving metrics by viewing the built-in dashboard called [Metrics Docker] Overview.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt19fc8feb232fe845/6a85cc9ef9373d4bfb96f5c0/elastic-blog-16-docker.png" alt="Docker image" /></p>
<p> <strong>With some work on this dashboard by adding in container insight metrics and docker metrics, you should be able to see:</strong> </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbe62d55a71be2bf2/6a85cc77f9373d3a8c96f5b8/elastic-blog-3-metrics-graphs.png" alt="graphs" /></p>
<p>If you only have the ECS integration and the Elastic agent in Step 2, then you will need to create a new dashboard:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6f1993af959a0a6e/6a85cc741aa1e1343eff8d83/elastic-blog-2-containers-in-cluster.png" alt="cluster" /></p>
<p>This dashboard can be set up with the following metrics:</p>
<ul>
<li>Containers in the cluster (containerInsights via Elastic Agent and AWS Cloudwatch integration). Set up a TSVB panel using the following metric: aws.dimensions.ClusterName : "EC2BasedCluster" with aws.containerinsights.metrics.TaskCount.max</li>
<li>Services in the cluster (containerInsights via Elastic Agent and AWS Cloudwatch integration). Use the following configuration to setup the chart:</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt068290e85c9a03c6/6a85cca280984c6fef66900a/elastic-blog-17-table.png" alt="table" /></p>
<ul>
<li>CPU and memory utilization of the ECS Cluster (Elastic Agent with ECS integration). Use the following configuration to set up both CPU and memory utilization charts:</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd33eca51e079f3e0/6a85cca4342d690caf21b117/elastic-blog-18-line.png" alt="line" /></p>
<ul>
<li>EC2 CPU and storage utilization of the instance in the cluster (Elastic Agent with EC2 integration). Use the following configuration to set up both CPU and memory utilization charts:</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blteb520047e596d3b5/6a85cca75c2790ca4af59b43/elastic-blog-19-bar-vertical-stacked.png" alt="bar vertical stacked" /></p>
<ul>
<li>(Not shown): CPU and memory utilization per container (via containerInsights via Elastic Agent and AWS Cloudwatch integration)</li>
</ul>
<h3 id="step7lookatlogsfromyourecscluster">Step 7: Look at logs from your ECS cluster</h3>
<p>Since we set up AWS CloudWatch logs collection in Step 2, we can view these logs in Discover by filtering on the logs group arn /aws/ecs/containerinsights/EC2BasedCluster/.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0a81cad78b73a182/6a85ccab078290ccad321794/elastic-blog-20-logs.png" alt="logs" /></p>
<h2 id="summary">Summary</h2>
<p>I hope you’ve gotten an appreciation for how Elastic Observability can help your <a href="https://www.elastic.co/observability/aws-monitoring">AWS monitoring</a> ECS service metrics. Here’s a quick recap of lessons and what you learned:</p>
<ul>
<li>Elastic Observability supports ingesting and analysis of AWS ECS service metrics and the corresponding EC2 metrics through the AWS integration on the Elastic Agent. It’s easy to set up ingest from AWS Services via the Elastic Agent.</li>
<li>Elastic Observability can also get container metrics via the Docker integration running on Elastic agents on each of the EC2 instances in the ECS EC2 auto scaling group.</li>
<li>Elastic has multiple out-of-the-box (OOTB) AWS service dashboards that can be used as baselines to get your own customized view.</li>
</ul>
<p>Ready to get started? Start your own <a href="https://aws.amazon.com/marketplace/pp/prodview-voru33wi6xs7k?trk=5fbc596b-6d2a-433a-8333-0bd1f28e84da%E2%89%BBchannel=el">7-day free trial</a> by signing up via <a href="https://aws.amazon.com/marketplace/pp/prodview-voru33wi6xs7k?trk=d54b31eb-671c-49ba-88bb-7a1106421dfa%E2%89%BBchannel=el">AWS Marketplace</a> and quickly spin up a deployment in minutes on any of the <a href="https://www.elastic.co/guide/en/cloud/current/ec-reference-regions.html#ec_amazon_web_services_aws_regions">Elastic Cloud regions on AWS</a> around the world. Your AWS Marketplace purchase of Elastic will be included in your monthly consolidated billing statement and will draw against your committed spend with AWS.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/manage-applications-amazon-ecs-ec2-clusters-observability</link>
    <guid isPermaLink="false">manage-applications-amazon-ecs-ec2-clusters-observability</guid>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2a5f17f66772a6d4/6a85ccad18249c460318f7f9/library-branding-elastic-observability-midnight-1680x980.png" length="0" type="image/png"/>
    <pubDate>Tue, 15 Aug 2023 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>