Dashboards met AI: Hoe je een on-prem LLM integreert met Grafana Mimir
geschreven door Vincent de Vries op 11 maart 2025 14:47:19 CET
Ooit jezelf betrapt op schreeuwen tegen je monitor?
Als systeembeheerder heb je vast weleens gefrustreerd op je toetsenbord geslagen en gedacht: "Wat is jouw probleem?!" Maar wat als je die vraag rechtstreeks aan Grafana kon stellen—en ook nog eens nuttig antwoord kreeg?
Hoe kan je dit mogelijk maken?
Stel je voor dat je ChatGPT koppelt aan je monitoringomgeving en gewoon kunt vragen wat er aan de hand is. Dat is het idee—maar in plaats van ChatGPT gebruiken we Ollama, een krachtige Large Language Model (LLM) gebaseerd op Meta’s LLama. In tegenstelling tot ChatGPT draait Ollama lokaal en is volledig gratis te gebruiken.
Intelligentie toevoegen aan je monitoring
Een LLM weet natuurlijk niet uit zichzelf iets over jouw IT-omgeving. Daar komt RAG (Retrieval-Augmented Generation) om de hoek kijken. Door een LLM te combineren met realtime data uit Grafana (Grafana Mimir) of Prometheus, slaan we de brug tussen ruwe metrics en bruikbare inzichten.
In dit artikel laten we zien hoe je Ollama kunt integreren met RAG en Grafana Mimir om je monitoring naar een hoger niveau te tillen en direct bruikbare antwoorden over je data te krijgen.
Let op: De hardwareconfiguratie in dit artikel is voldoende om alles draaiende te krijgen, maar verwacht geen topprestaties. De grootste bottleneck is het draaien van de LLM op een CPU in plaats van een GPU, wat de verwerking aanzienlijk vertraagt. De kans dat je zomaar een high-end GPU hebt liggen is natuurlijk klein. Toch is het enorm bevredigend om waardevolle inzichten uit je omgeving te krijgen—zelfs als je even moet wachten op een antwoord.
Prerequisites
De volgende hardware wordt in dit artikel gebruikt:
1 x Ubuntu 22.04 met 4GB RAM en 2 vCPU's (Server01)
1 x Ubuntu 22.04 met 16GB RAM en 4 (v)CPU Cores (Server02)
Overview
Het bovenstaande diagram illustreert hoe de componenten met elkaar samenwerken:
- Indexeren van gegevens: Eerst gebruiken we LlamaIndex om een index op te bouwen van de gegevens die worden opgehaald via de Grafana Mimir API. Je moet de index opnieuw opbouwen telkens wanneer je nieuwe gegevens wilt toevoegen.
- Gebruikersinteractie: De gebruiker draait een query in LlamaIndex via een Python script.
- Ophalen en genereren van een antwoord: LlamaIndex haalt relevante gegevens op uit de index en communiceert met Ollama, die de query verwerkt en een antwoord genereert.
Grafana installeren
Eerst installeren we een Grafana LGTM-stack met testgegevens via Docker Compose. Als je al een eigen Grafana omgeving hebt met daarin Mimir geïnstalleerd, kun je die in plaats van de LGTM-stack gebruiken.
Open een terminal op Server01 en installeer Docker Compose. Je kunt hiervoor een online handleiding volgen voor de installatie-instructies.
Zodra Docker Compose is geïnstalleerd, kloon je de Intro to MLTP-repository naar Server01 en navigeer je naar de map van de repository.
Voer ten slotte het benodigde Docker Compose commando uit om de Grafana LGTM-stack te starten. Dit zal Grafana, Grafana Loki, Grafana Tempo en Grafana Mimir opzetten, zodat je een complete testomgeving tot je beschikking krijgt.
git clone https://github.com/grafana/intro-to-mltp
cd intro-to-mltp
docker-compose up
Ollama en LlamaIndex installeren
Het installeren van Ollama is eenvoudig. Open op Server02 een terminal en voer het installatiecommando uit zoals in de onderstaande snippet. Zorg ervoor dat je over de juiste rechten beschikt of gebruik indien nodig sudo. Voor meer details kun je de officiële Ollama-pagina bezoeken.
curl -fsSL https://ollama.com/install.sh | sh
Na de installatie van Ollama is de volgende stap het downloaden van een geschikt model voor het verwerken van prompts. In dit artikel gebruiken we het Mistral model, dat we in Ollama importeren met het commando ollama pull.
ollama pull mistral
Vervolgens moeten we LlamaIndex instellen. In tegenstelling tot normale software is LlamaIndex een verzameling van Python libraries. Omdat LlamaIndex veel dependencies heeft, wordt sterk aanbevolen om eerst een Python virtual environment (venv) aan te maken. Dit helpt om dependency conflicten te voorkomen.
Om een virtual environment op te zetten, voer je de onderstaande commando’s uit voordat je LlamaIndex installeert. In het onderstaande voorbeeld maken we een virtual environment aan met de naam venv01, maar je kunt een andere naam gebruiken als je dat wilt.
sudo apt-get install python3-pip
sudo pip3 install virtualenv
virtualenv venv01
source venv01/bin/activate
Na het activeren van onze virtuele omgeving kunnen we de LlamaIndex packages installeren met pip.
pip install llama-index llama-index-llms-ollama llama-index-embeddings-huggingface sentence-transformerspip
Het testen van de API's
Laten we eerst controleren of we Ollama kunnen gebruiken met behulp van de Python libraries. Plaats de onderstaande Python code in een bestand genaamd test_llama.py. Dit script doet het volgende:
- Importeert de benodigde modules uit llama_index om met Ollama als LLM te werken.
- Initialiseert Ollama met het mistral model.
- Configureert LlamaIndex om de Ollama instantie globaal te gebruiken.
- Test de setup door het model de vraag te stellen: "What is the capital of Sweden? en de respons af te drukken.
NB: Je kan de prompts ook in Nederlands invoeren, maar je zal een ENgels antwoord krijgen.
from llama_index.llms.ollama import Ollama
from llama_index.core import Settings
# Initialize Ollama as the LLM
llm = Ollama(model="mistral")
# Set up LlamaIndex to use Ollama globally
Settings.llm = llm
# Test Ollama completion
print(llm.complete("What is the capital of Sweden?"))
De ouput van het antwoord zou er ongeveer zo uit moeten zien:
Vervolgens gaan we controleren of we gegevens kunnen ophalen uit Grafana Mimir. Plaats de onderstaande Python-code in een bestand genaamd test_mimir.py en vervang server01 door de juiste servernaam voor jouw Mimir-endpoint. Het script voert de volgende taken uit:
Ophalen van alle beschikbare metrics
- Stuurt een request naar de Mimir API om een lijst op te halen van alle beschikbare metrics.
- Indien succesvol, wordt het totale aantal beschikbare metrics op het scherm weergegeven en worden de eerste 10 als voorbeeld weergegeven.
Tijdreeksgegevens opvragen voor elke metriek
- Definieert een tijdsbereik (laatste 1 uur) met datapunten op intervallen van 1 minuut.
- Loopt door de eerste 50 metrics (om overbelasting van het systeem te voorkomen).
- Stuurt API request om tijdreeks data op te halen voor elke metric.
- Extract de timestamp, metric en bijbehorende labels.
Gegevens opslaan en verwerken
- Slaat de verzamelde gegevens op in een Pandas DataFrame voor verdere verwerking.
- Drukt een voorbeeld af van de eerste 5 rijen als het ophalen van data succesvol was.
import requests
import json
import time
import pandas as pd
# Mimir API Endpoints
MIMIR_LABELS_URL = "http://server01:9009/prometheus/api/v1/label/__name__/values"
MIMIR_QUERY_URL = "http://server01:9009/prometheus/api/v1/query_range"
### 1Fetch All Available Metric Names ###
response = requests.get(MIMIR_LABELS_URL)
# Check if request was successful
if response.status_code != 200:
print(f"Error fetching metrics: {response.status_code} - {response.text}")
exit(1)
data = response.json()
# Validate response structure
if "data" in data and isinstance(data["data"], list):
metric_names = data["data"]
print(f"Found {len(metric_names)} metrics.")
print("Sample metrics:", metric_names[:10]) # Show first 10 metrics
else:
print("No metrics found! Exiting.")
exit(1)
### Query Data for Each Metric ###
# Define the time range (last 1 hour)
end = int(time.time()) # Current timestamp
start = end - 3600 # 1 hour ago
step = "60s" # 1-minute interval
all_metric_data = []
# Loop through first 50 metrics to avoid overload
for metric in metric_names[:50]:
print(f"Fetching data for: {metric}")
params = {
"query": metric,
"start": start,
"end": end,
"step": step,
}
response = requests.get(MIMIR_QUERY_URL, params=params)
# Check if request was successful
if response.status_code != 200:
print(f"Error fetching {metric}: {response.status_code} - {response.text}")
continue # Skip this metric and move to the next one
metric_data = response.json()
# Check if data exists
if "data" in metric_data and "result" in metric_data["data"]:
for series in metric_data["data"]["result"]:
for value in series["values"]:
timestamp, metric_value = value
all_metric_data.append({
"timestamp": timestamp,
"metric_name": metric,
"value": float(metric_value),
"labels": json.dumps(series["metric"]),
})
### Convert Data to Pandas DataFrame ###
df = pd.DataFrame(all_metric_data)
if not df.empty:
print("Data successfully retrieved! Preview:")
print(df.head()) # Show first 5 rows
else:
print("No time-series data fetched from Mimir!")
Als alles goed is gegaan zou de je onderstaande output moeten krijgen:
Als we er zeker van zijn dat de test scripts geen fouten geven, kunnen we de volgende stap nemen en Grafana Mimir data gaan indexeren met LlamaIndex.
Indexeren van Grafana Mimir-data
Plaats de onderstaande Python code in een bestand genaamd index_mimir.py en vervang server01 door de juiste servernaam voor je Mimir endpoint. Hieronder wordt een beknopte uitleg gegeven van de stappen in het script.
Configureren van LlamaIndex met Ollama en HuggingFace Embeddings:
- Stel Ollama in als het taalmodel, waarbij mistral wordt gebruikt.
- Configureert HuggingFace's sentence-transformer model voor tekstembedding.
Ophalen van beschikbare Prometheus-metric namen uit Mimir:
- Verstuurd een HTTP GET request naar de Mimir API om alle beschikbare metric namen op te halen.
- Als de request succesvol is, worden de eerste 10 metric namen weergegeven.
- Als er geen metrics worden gevonden, stopt het script.
Ophalen van tijdreeksgegevens voor metrics:
- Definieert een tijdsbereik van 1 uur met een interval van 1 minuut.
- Itereert over de eerste 50 metrics om overbelasting van het systeem te voorkomen.
- Query de Mimir API voor tijdreeks data van elke metric.
- Extraheert de timestamp, metric, waarde en labels en slaat deze op in een lijst.
Omzetten van opgehaalde data naar een Pandas DataFrame:
- Als er geen data beschikbaar is, stopt de uitvoering.
- Anders wordt een voorbeeldweergave van de eerste paar rijen afgedrukt.
Voorbereiden van data voor LlamaIndex-vectorzoekopdrachten:
- Zet elke rij in het DataFrame om naar een LlamaIndex-document.
- Elk document bevat een tekstuele representatie van de metric gegevens.
Indexeren van de data met LlamaIndex:
- Creëert een vectorgebaseerde index met het lokale embedding-model.
- Slaat de index op schijf op (./mimir_index), zodat deze later opnieuw kan worden gebruikt.
Eindresultaat:
Bevestigt dat de Mimir-data succesvol is geïndexeerd en klaar is voor gebruik met Ollama.
import requests
import json
import time
import pandas as pd
from llama_index.core import Document, VectorStoreIndex, Settings
from llama_index.llms.ollama import Ollama
# Try importing the HuggingFace embedding model
try:
from llama_index.embeddings.huggingface import HuggingFaceEmbedding #LlamaIndex v0.9+
except ImportError:
from llama_index.embeddings import HuggingFaceEmbedding # LlamaIndex v1.0+
# Set LlamaIndex to use Ollama and a local embedding model
Settings.llm = Ollama(model="mistral") # Use Ollama for language processing
Settings.embed_model = HuggingFaceEmbedding(model_name="sentence-transformers/all-MiniLM-L6-v2") # Use local embeddings
# Mimir API Endpoints
MIMIR_LABELS_URL = "http://server01:9009/prometheus/api/v1/label/__name__/values"
MIMIR_QUERY_URL = "http://server01:9009/prometheus/api/v1/query_range"
### Fetch All Available Metric Names ###
response = requests.get(MIMIR_LABELS_URL)
# Check if request was successful
if response.status_code != 200:
print(f"Error fetching metrics: {response.status_code} - {response.text}")
exit(1)
data = response.json()
# Validate response structure
if "data" in data and isinstance(data["data"], list):
metric_names = data["data"]
print(f"Found {len(metric_names)} metrics.")
print("🔹 Sample metrics:", metric_names[:10]) # Show first 10 metrics
else:
print("No metrics found! Exiting.")
exit(1)
### Query Data for Each Metric ###
# Define the time range (last 1 hour)
end = int(time.time()) # Current timestamp
start = end - 3600 # 1 hour ago
step = "60s" # 1-minute interval
all_metric_data = []
# Loop through first 50 metrics to avoid overload
for metric in metric_names[:50]:
print(f" Fetching data for: {metric}")
params = {
"query": metric,
"start": start,
"end": end,
"step": step,
}
response = requests.get(MIMIR_QUERY_URL, params=params)
# Check if request was successful
if response.status_code != 200:
print(f"Error fetching {metric}: {response.status_code} - {response.text}")
continue # Skip this metric and move to the next one
metric_data = response.json()
# Check if data exists
if "data" in metric_data and "result" in metric_data["data"]:
for series in metric_data["data"]["result"]:
for value in series["values"]:
timestamp, metric_value = value
all_metric_data.append({
"timestamp": timestamp,
"metric_name": metric,
"value": float(metric_value),
"labels": json.dumps(series["metric"]),
})
### Convert Data to Pandas DataFrame ###
df = pd.DataFrame(all_metric_data)
if df.empty:
print("No time-series data fetched from Mimir! Exiting.")
exit(1) # Stop execution if no data is retrieved
print("Data successfully retrieved! Preview:")
print(df.head()) # Show first 5 rows
### Convert Data for LlamaIndex ###
documents = []
for _, row in df.iterrows():
doc_text = f"Timestamp: {row['timestamp']}, Metric: {row['metric_name']}, Value: {row['value']}, Labels: {row['labels']}"
documents.append(Document(text=doc_text))
# Create LlamaIndex Vector Store using local embeddings
index = VectorStoreIndex.from_documents(documents)
# Save Index
index.storage_context.persist(persist_dir="./mimir_index")
print("Mimir data successfully indexed for Ollama!")
Onze data prompten
Met onze index kunnen we nu verder gaan met het prompten van ons model. Dit moeten we echter wel met een Python script doen. Plaats de Python code in een bestand genaamd prompt_mimir.py.
import os
import argparse
from llama_index.core import load_index_from_storage, StorageContext, Settings
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.llms.ollama import Ollama
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
# Force multi-threading
os.environ["OMP_NUM_THREADS"] = str(os.cpu_count())
os.environ["MKL_NUM_THREADS"] = str(os.cpu_count())
os.environ["OPENBLAS_NUM_THREADS"] = str(os.cpu_count())
os.environ["NUMEXPR_NUM_THREADS"] = str(os.cpu_count())
# Parse command-line arguments
parser = argparse.ArgumentParser(description="Query LlamaIndex using Ollama.")
parser.add_argument("query", type=str, help="The query to ask the index.")
args = parser.parse_args()
# Set LlamaIndex to use Ollama as LLM with higher timeout and max threads
Settings.llm = Ollama(model="mistral", request_timeout=120, num_threads=0) # Use all CPU cores
Settings.embed_model = HuggingFaceEmbedding(model_name="sentence-transformers/all-MiniLM-L6-v2")
# Load storage context before loading the index
storage_context = StorageContext.from_defaults(persist_dir="./mimir_index")
# Load existing index with storage context
index = load_index_from_storage(storage_context=storage_context)
# Create a retriever from the index
retriever = index.as_retriever()
# Create query engine using the retriever
query_engine = RetrieverQueryEngine(retriever=retriever)
# Use the query from the command-line argument
response = query_engine.query(args.query)
# Print the response
print("\n ^=^t^m Query:", args.query)
print(" ^=^r Response:", response)
Met het script kan je de Ollama via de index nu prompten als volgt:
python3 prompt_mimir.py "What is taking most resources? Give the timestamps in human readable dates and give me the name of the metrics"
Als alles goed is gegaan, zou de uitvoer er ongeveer uit moeten zien als de onderstaande screenshot.
Conclusie
Door Ollama te koppelen aan Grafana Mimir door middel van een RAG model, hebben we ruwe metrics omgezet in iets dat je daadwerkelijk kunt bevragen met een LLM zoals Ollama. Natuurlijk draait een LLM niet snel op CPU’s, maar zelfs al is het langzaam, dan is het zeker leuk om te zien dat je waardevolle antwoorden kan krijgen door gewoon een vraag te stellen aan de LLM.
Met wat finetuning—zoals een GPU toevoegen—kun je deze setup een stuk sneller maken.
Wil je met ons over Grafana praten?
Je bent misschien ook geïnteresseerd in
Gerelateerde blogs

Dashboards met AI: Maak een eenvoudige prompt-UI voor LlamaIndex

Nog geen reacties
Vertel ons wat je denkt