Je hebt Prometheus voor metrics. Je kunt zien wat er gebeurt in je clusters. Maar als iets kapotgaat, vertellen metrics je dat er iets mis is — logs vertellen je waarom.
Het traditionele antwoord is Elasticsearch. Het is krachtig, flexibel, en… duur. Het indexeert alles, wat betekent dat je betaalt voor elke byte aan logdata in CPU, geheugen en opslag.
Loki kiest een andere aanpak: indexeer labels, niet content. Het is dezelfde filosofie die Prometheus efficiënt maakt voor metrics, toegepast op logs.
Waarom Loki?
Loki is ontworpen door Grafana Labs met specifieke doelen:
- Kostenefficiënt — Indexeer alleen metadata (labels), sla logregels gecomprimeerd op
- Kubernetes native — Labels van Kubernetes metadata automatisch
- Grafana integratie — Dezelfde dashboards, dezelfde alerting, dezelfde workflow
- Operationeel simpel — Geen JVM tuning, geen cluster management complexiteit
De trade-off: je kunt niet efficiënt full-text zoeken over alle logs. Je moet eerst weten op welke labels je wilt filteren. Voor Kubernetes workloads waar je meestal naar specifieke pods, namespaces of services kijkt, is dit prima.
Architectuur
flowchart TD
subgraph cluster["Kubernetes Cluster"]
subgraph nodes["Nodes"]
P1["Promtail<br/>(DaemonSet)"]
P2["Promtail"]
P3["Promtail"]
end
PODS["Pod Logs<br/>(/var/log/pods)"]
end
P1 --> PODS
P2 --> PODS
P3 --> PODS
P1 --> L["Loki"]
P2 --> L
P3 --> L
L --> OS["Object Storage<br/>(chunks)"]
L --> G["Grafana"]
Promtail draait op elke node als DaemonSet, tailt logbestanden, voegt labels toe en stuurt naar Loki.
Loki ontvangt logs, indexeert op labels, comprimeert chunks, slaat op in object storage.
Grafana bevraagt Loki met LogQL, toont in Explore of dashboards.
Loki Installeren
Met de Grafana Helm charts:
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
# Installeer Loki met simple scalable deployment
helm install loki grafana/loki \
--namespace monitoring \
--create-namespace \
--values loki-values.yaml
Basis values voor een single-binary deployment (goed voor kleine clusters):
# loki-values.yaml
loki:
auth_enabled: false
commonConfig:
replication_factor: 1
storage:
type: filesystem
schemaConfig:
configs:
- from: 2024-01-01
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
singleBinary:
replicas: 1
persistence:
size: 50Gi
# Disable componenten niet nodig voor single binary
backend:
replicas: 0
read:
replicas: 0
write:
replicas: 0
Voor productie met object storage:
# loki-values.yaml
loki:
auth_enabled: false
commonConfig:
replication_factor: 3
storage:
type: s3
s3:
endpoint: minio.storage:9000
bucketnames: loki-chunks
access_key_id: ${MINIO_ACCESS_KEY}
secret_access_key: ${MINIO_SECRET_KEY}
insecure: true
schemaConfig:
configs:
- from: 2024-01-01
store: tsdb
object_store: s3
schema: v13
index:
prefix: index_
period: 24h
# Schaalbare deployment
backend:
replicas: 3
read:
replicas: 3
write:
replicas: 3
Promtail Installeren
Promtail verzamelt logs van je nodes:
helm install promtail grafana/promtail \
--namespace monitoring \
--set config.clients[0].url=http://loki:3100/loki/api/v1/push
Promtail configuratie voor Kubernetes:
# promtail-values.yaml
config:
clients:
- url: http://loki:3100/loki/api/v1/push
snippets:
# Voeg Kubernetes metadata toe als labels
pipelineStages:
- cri: {}
- labeldrop:
- filename
- match:
selector: '{app="nginx"}'
stages:
- regex:
expression: '^(?P<remote_addr>[\d\.]+) - (?P<remote_user>\S+) \[(?P<time_local>[^\]]+)\] "(?P<request>[^"]+)" (?P<status>\d+)'
- labels:
status:
# DaemonSet tolerations voor alle nodes
tolerations:
- operator: Exists
Labels Begrijpen
Labels zijn alles in Loki. Ze bepalen hoe logs worden geïndexeerd en bevraagd.
Default Kubernetes labels van Promtail:
| Label | Bron | Voorbeeld |
|---|---|---|
namespace | Pod namespace | default, monitoring |
pod | Pod naam | nginx-abc123 |
container | Container naam | nginx, sidecar |
node_name | Node | worker-1 |
app | Pod label | nginx |
job | Scrape config | kubernetes-pods |
High cardinality waarschuwing: Voeg geen labels toe die veel unieke waarden hebben (zoals request IDs, user IDs of timestamps). Dit vernietigt Loki’s performance. Houd labels beperkt tot dimensies waarop je filtert.
Goede labels:
namespace,app,environment,team
Slechte labels:
request_id,user_id,trace_id,timestamp
LogQL: Logs Bevragen
LogQL is Loki’s query taal. Het lijkt op PromQL maar voor logs.
Basis Queries
# Alle logs van een namespace
{namespace="production"}
# Specifieke app
{app="frontend", namespace="production"}
# Meerdere containers
{container=~"nginx|envoy"}
# Exclude een namespace
{namespace!="kube-system"}
Content Filteren
# Regels met "error"
{app="frontend"} |= "error"
# Regels ZONDER "health"
{app="frontend"} != "health"
# Regex match
{app="frontend"} |~ "status=(4|5)[0-9]{2}"
# Case insensitive
{app="frontend"} |~ "(?i)error"
Parsen en Extracten
# Parse JSON logs
{app="api"} | json
# Extract specifiek veld
{app="api"} | json | status_code >= 500
# Parse met pattern
{app="nginx"} | pattern `<ip> - - [<_>] "<method> <path> <_>" <status>`
# Gebruik geëxtraheerde velden
{app="nginx"} | pattern `<_> - - [<_>] "<method> <path> <_>" <status>` | status >= 400
Aggregaties (Log Metrics)
# Tel errors per app
sum by (app) (count_over_time({namespace="production"} |= "error" [5m]))
# Rate van requests
sum(rate({app="nginx"} | pattern `<_> "<method> <path> <_>" <status>` [1m])) by (status)
# Bytes per namespace
sum by (namespace) (bytes_over_time({job="kubernetes-pods"}[1h]))
Grafana Integratie
Voeg Loki toe als data source in Grafana:
apiVersion: v1
kind: ConfigMap
metadata:
name: grafana-datasources
namespace: monitoring
data:
loki.yaml: |
apiVersion: 1
datasources:
- name: Loki
type: loki
url: http://loki:3100
access: proxy
jsonData:
maxLines: 1000
Explore View
Grafana Explore is perfect voor log onderzoek:
- Selecteer Loki data source
- Bouw query met label browser
- Filter met content matches
- Klik op logregels voor context
Dashboard Panels
Voeg logs toe aan je dashboards:
{
"type": "logs",
"datasource": "Loki",
"targets": [
{
"expr": "{namespace=\"production\", app=\"frontend\"} |= \"error\"",
"refId": "A"
}
],
"options": {
"showTime": true,
"showLabels": false,
"wrapLogMessage": true
}
}
Metrics en Logs Correleren
De kracht van Grafana: hetzelfde dashboard toont metrics en logs.
# Prometheus panel die error rate toont
sum(rate(http_requests_total{status=~"5.."}[5m])) by (app)
# Loki panel die error logs toont
{app="$app"} |= "error"
Variabele $app linkt beide panels. Klik op een spike in de metrics, zie de errors in de logs.
Alerting op Logs
Loki ondersteunt alerting via Grafana of zijn eigen ruler:
# Grafana alert rule
apiVersion: 1
groups:
- name: LogAlerts
rules:
- alert: HighErrorRate
expr: |
sum(count_over_time({namespace="production"} |= "error" [5m])) > 100
for: 5m
labels:
severity: warning
annotations:
summary: "Hoge error rate in productie logs"
Met Loki’s ruler component:
# loki-rules.yaml
groups:
- name: errors
rules:
- alert: CriticalError
expr: |
count_over_time({app="payment-service"} |= "CRITICAL" [1m]) > 0
for: 0m
labels:
severity: critical
annotations:
summary: "Kritieke error in payment service"
Retentie en Opslag
Configureer retentie in Loki:
loki:
limits_config:
retention_period: 30d
compactor:
working_directory: /var/loki/compactor
retention_enabled: true
retention_delete_delay: 2h
retention_delete_worker_count: 150
Per-tenant retentie (bij multi-tenancy):
loki:
limits_config:
retention_period: 30d # Default
overrides:
production:
retention_period: 90d # Houd productie logs langer
development:
retention_period: 7d # Dev logs vervallen sneller
Performance Tuning
Chunk Size
Grotere chunks = minder index entries, betere compressie, hogere latency voor kleine queries:
loki:
ingester:
chunk_target_size: 1572864 # 1.5MB
chunk_idle_period: 30m
max_chunk_age: 2h
Query Limieten
Voorkom runaway queries:
loki:
limits_config:
max_query_length: 721h # Max tijdsbereik
max_query_parallelism: 32 # Concurrent sub-queries
max_entries_limit_per_query: 5000
Caching
Voeg caching toe voor betere query performance:
loki:
memcached:
chunk_cache:
enabled: true
host: memcached.monitoring
results_cache:
enabled: true
host: memcached.monitoring
Structured Logging Best Practices
Om het meeste uit Loki te halen, log in gestructureerd formaat:
{
"level": "error",
"message": "Failed to process order",
"order_id": "12345",
"error": "payment declined",
"duration_ms": 234
}
Bevraag gestructureerde logs eenvoudig:
{app="order-service"} | json | level="error" | duration_ms > 1000
Configureer je apps om JSON te outputten:
# Spring Boot
logging.pattern.console: '{"timestamp":"%d","level":"%p","logger":"%c","message":"%m"}%n'
# Node.js met winston
const logger = winston.createLogger({
format: winston.format.json(),
});
# Go met zap
logger, _ := zap.NewProduction()
Mijn Productie Setup
# Loki met object storage
loki:
auth_enabled: false
commonConfig:
replication_factor: 3
storage:
type: s3
s3:
endpoint: minio.storage:9000
bucketnames: loki-data
limits_config:
retention_period: 30d
ingestion_rate_mb: 10
ingestion_burst_size_mb: 20
# Drie-replica deployment
backend:
replicas: 3
persistence:
size: 50Gi
read:
replicas: 3
write:
replicas: 3
persistence:
size: 50Gi
# Promtail op alle nodes
promtail:
tolerations:
- operator: Exists
config:
clients:
- url: http://loki:3100/loki/api/v1/push
Belangrijke keuzes:
- Object storage: MinIO voor sovereignty, geen cloud afhankelijkheid
- 30 dagen retentie: Genoeg voor debugging, niet oneindig
- Drie replica’s: Overleeft node failures
- Alle nodes: Promtail draait overal, inclusief control plane
Loki vs Elasticsearch
| Aspect | Loki | Elasticsearch |
|---|---|---|
| Indexering | Alleen labels | Full-text |
| Opslag kosten | Lager | Hoger |
| Query flexibiliteit | Label-first | Full-text search |
| Operations | Simpeler | Complex |
| Geheugengebruik | Lager | Hoger (JVM) |
| Grafana integratie | Native | Goed |
Kies Loki wanneer:
- Je bevraagt op bekende dimensies (namespace, app, pod)
- Kosten belangrijk zijn
- Je operationele simpelheid wilt
- Je al in het Grafana ecosysteem zit
Kies Elasticsearch wanneer:
- Je full-text search nodig hebt over alle logs
- Je niet weet waar je naar zoekt
- Log analytics een primaire use case is
Waarom Dit Ertoe Doet
Logs zijn het verhaal van je systeem. Metrics vertellen je de gezondheidsscore, logs vertellen je het verhaal.
Met Loki krijg je:
- Betaalbare log retentie — Bewaar logs zonder het budget te breken
- Kubernetes-native labels — Bevraag op wat ertoe doet (namespace, app, pod)
- Unified observability — Dezelfde Grafana, dezelfde workflow als metrics
Gecombineerd met Prometheus/Thanos voor metrics en traces (apart behandeld), heb je complete observability zonder de operationele complexiteit van de ELK stack.
Logs zijn het verhaal dat je systeem over zichzelf vertelt. Loki zorgt dat je het je kunt veroorloven om te luisteren.
