Skip to content

Detection ​

The detector watches cluster state and external observability sources, deduplicates signals, and writes Incident CRDs. It never calls an LLM. Detection is deterministic, bounded, and cheap — LLM work happens only after an Incident exists.

Signal sources ​

Six sources feed the same Incident pipeline:

SourceMechanismRequiresTypical latency
Kubernetes API eventscontroller-runtime informersKubernetes onlysub-second
PromQL rulesperiodic eval against Prometheus HTTP APIPrometheus configuredscrape interval
metrics-serverpolling the Metrics APImetrics-server available~15s
Classical anomalyrolling window + MAD/EWMAany metric sourcescrape interval
Log pattern driftDrain3 via a Python sidecarLoki or Elasticsearchstreaming
AlertManager webhookHTTP receiverAlertManager pushing to the detectorpush

All six feed a single fingerprint-based dedup pipeline, so one root cause produces one Incident even when it surfaces across multiple sources.

Kubernetes events ​

Primary mechanism. Informers filtered at cache setup time by the label selectors on active SREWatch resources.

SignalTrigger
CrashLoopBackOffPod containerStatus.waiting.reason == "CrashLoopBackOff"
OOMKilledPod lastState.terminated.reason == "OOMKilled"
EvictedPod phase=Failed AND status.reason == "Evicted"
FailedSchedulingEvent.reason == "FailedScheduling" on a Pod
ImagePullBackOffPod waiting reason ImagePullBackOff or ErrImagePull
UnhealthyProbeEvent.reason == "Unhealthy"
BackoffLimitExceededJob condition BackoffLimitExceeded
ReplicaSetNotProgressingDeployment Progressing condition false for > 10m

Debouncing. The same signal on the same workload within 30s aggregates into an existing signal — count goes up, last-seen moves forward. Different workloads or different signal types get separate evaluation.

Pod-to-workload resolution. Incidents are workload-scoped, not pod-scoped. The detector resolves pod → ReplicaSet → Deployment or StatefulSet via owner references and caches the result.

PromQL rules ​

When Prometheus is configured, each DetectionPolicy with PromQL rules runs a goroutine evaluating expressions on the configured evaluation interval (default 30s).

Rule expressions are validated against the Prometheus parser at admission time — malformed expressions fail the CRD apply. Hold-for logic ensures a rule has to satisfy its threshold for the for: duration before firing.

Results are post-filtered to watched workloads: the rule must produce a series with a recognizable workload label (pod, deployment, etc.), and the detector drops anything not matching an active SREWatch.

metrics-server ​

metrics-server ships on every major managed Kubernetes. The detector auto-discovers metrics.k8s.io/v1beta1 at startup.

When available, two features unlock:

Evidence enrichment (always on). On Incident creation, the detector captures a snapshot of CPU/memory usage plus requests and limits for every pod in the watched workload. The snapshot lives on Incident.status.resourceSnapshot and gives the investigator an instant "was the pod at 98% of its memory limit?" answer without waiting on a Prometheus range query.

metrics-server-backed anomaly (opt-in). The same MAD/EWMA logic used against Prometheus, sourced from PodMetrics and NodeMetrics at 15–30s resolution. Four supported targets: PodCPU, PodMemory, NodeCPU, NodeMemory. Useful when Prometheus isn't available or you want a lightweight tier.

Classical anomaly detection ​

Source-agnostic. Runs against whatever metric source is configured — Prometheus or metrics-server — using the same two algorithms:

MAD (Median Absolute Deviation). Robust to outliers. Good default for most metrics.

EWMA (Exponentially Weighted Moving Average). Lightweight, good for slow drift.

STL decomposition is a v2 feature — there's no mature Go implementation and the operator won't block v1 on porting one.

State management. Each detector holds a rolling window per (workload, metric) in memory. Windows checkpoint to ConfigMaps every 5 minutes so a detector restart loses at most five minutes of baseline.

Log pattern drift ​

Drain3 groups log lines into templates. When a template emerges that wasn't seen before, or a rare template spikes, the detector fires a signal.

Drain3 runs as a Python sidecar inside the detector Pod, talking to the Go detector over gRPC.

Input. Sampled log lines tailed from Loki or Elasticsearch. Sampling is adaptive: 5% random by default, 100% while an Incident on the workload is open, and 0% while a high-severity SensitiveDataFinding is active on the workload.

Signals.

SignalMeaning
NewTemplateTemplate not seen in windowSize
TemplateSpikeExisting template's rate exceeds threshold × rollingAverage
KnownErrorTemplateTemplate matches a curated always-error list

AlertManager webhook ​

Every detector exposes /alertmanager. Point AlertManager at it and alerts become palena-sre signals.

Each alert's workload is derived from its labels — the label keys are configurable (pod and namespace by default). Alerts without a matching SREWatch are dropped silently. The same fingerprint dedup collapses AlertManager alerts with native Kubernetes event signals or PromQL rule signals for the same root cause.

Fingerprint and dedup ​

The fingerprint is the deduplication primitive:

fingerprint = sha256(workload.uid + ":" + signalClass + ":" + 5-minute-bucket)

Same workload UID + same signal class in the same bucket → same fingerprint → same Incident.

Signal emitted
  │
  ▼
Compute fingerprint
  │
  ▼
Look up existing Incident by label palena.ai/fingerprint
(filter: phase NOT IN {Resolved, PostMortemed})
  │
  ├─── Found? ──► Append signal, update lastSeen
  │
  ▼
Create a new Incident; capture resource snapshot if metrics-server available

Signal classes. What counts as "the same class" for dedup.

ClassMembers
CrashLoopCrashLoopBackOff, BackoffLimitExceeded, KubePodCrashLooping
MemoryOOMKilled, MemoryAnomaly, KubeMemoryOvercommit
SchedulingFailedScheduling, UnschedulablePod
ImagePullImagePullBackOff, ErrImagePull
ProbeUnhealthyProbe, PodNotReady
ErrorRateHighErrorRate, LogPatternDrift (error-class templates)
LatencyLatencyAnomaly, SLOLatencyBudgetBurn

What the detector doesn't do ​

  • Call LLMs
  • Send notifications
  • Execute recommendations
  • Write anything outside of Incident CRDs and its own metrics

It is deliberately small: read cluster state and external sources, write Incident CRDs.

Released under the Apache 2.0 License.