Compare commits

...
Author SHA1 Message Date
claude-botandClaude Opus 5 2d263113c4 feat(monitoring): alert on CrowdSec, starting with the failure that hides
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
Three rules, and the first one is firing right now for a real reason.

CrowdsecNotReadingLogs. Both agents are frozen at their line counts —
29,917 and 35,449 — with zero increase over an hour, while Traefik keeps
logging and Loki keeps ingesting. cs_bucket_poured_total has not moved in two
hours either: nothing is flowing into any scenario. CrowdSec has not detected
anything since 2026-08-27.

Nothing else shows it. Pods are Running, the LAPI answers, the bouncers pull
and enforce. The line counters are cumulative, so `cscli metrics` reports a
large number and reads as healthy; only the rate distinguishes "has read" from
"is reading". I made exactly that mistake earlier in the day: I checked the
cumulative figure, concluded the pipeline was working, and dismissed a wiki
note about the tailer not reattaching after a log rotation as stale. It was
not stale. And the absence of alerts since 08-27, which I read as a quiet
period, was the symptom.

CrowdsecEdgeBouncerNotPulling counts rather than names. The LAPI registers a
new bouncer per observed client address, and what it observes is the
netbird-ingress pod IP, so `edge@10.244.0.113` and its successors come and go
as those pods roll — a name-based rule would decay. `or vector(0)` is there
because without it the total-failure case is the one the alert misses: all
bouncers silent means the inner filter is empty, count() returns no series,
and nothing fires.

CrowdsecTargetDown is the ordinary one.

Both expressions were run against live data before being written down: the
bouncer count returns 2, and the acquisition rate returns 0 — which is how the
outage was found in the first place.

This does not fix the stalled tailer. Restarting the agents picks up the
current file, but the reattach behaviour is the actual bug and belongs in its
own change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 06:45:36 +02:00
2 changed files with 82 additions and 0 deletions
@@ -24,6 +24,7 @@ resources:
- alloy-servicemonitor.yaml
- cnpg-podmonitors.yaml
- crowdsec-servicemonitor.yaml
- prometheusrule-crowdsec.yaml
- home-assistant-servicemonitor.yaml
- immich-servicemonitor.yaml
- loki-servicemonitor.yaml
@@ -0,0 +1,81 @@
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: crowdsec
namespace: monitoring
labels:
# kube-prometheus-stack's Prometheus selects on this label.
release: kube-prometheus-stack
spec:
groups:
- name: crowdsec.rules
rules:
- alert: CrowdsecNotReadingLogs
# The failure that hides. Everything stays green — pods Running,
# LAPI answering, bouncers pulling, decisions being enforced — while
# the agents read nothing and therefore detect nothing. The counters
# are cumulative, so `cscli metrics` keeps showing a large number and
# looks healthy; only the rate tells the truth.
#
# This alert was written after finding exactly that state: both
# agents frozen at their line counts for hours while Traefik kept
# logging, and zero events poured into any scenario. It had been that
# way since 2026-08-27, and the only outward sign was an absence of
# alerts, which reads like quiet rather than blindness.
#
# Known cause per the wiki: the tailer does not reattach after the
# container log rotates.
expr: |
sum(rate(cs_filesource_hits_total[30m])) == 0
for: 30m
labels:
severity: critical
annotations:
summary: "CrowdSec liest seit 30min keine Logzeilen"
description: >-
Die Agents lesen nichts mehr, obwohl Traefik weiter loggt — es
wird also nichts erkannt. Prüfen: `cscli metrics` im Agent-Pod,
und ob die getailte Datei unter /var/log/containers noch dem
laufenden Traefik-Pod entspricht. Ein Neustart der Agents nimmt
die aktuelle Datei wieder auf.
- alert: CrowdsecEdgeBouncerNotPulling
# Both proxies must be fetching decisions. A bouncer that stops
# pulling keeps enforcing whatever it last held, and a bouncer whose
# process died enforces nothing at all — a clean shutdown deletes its
# nftables tables. Neither shows up anywhere else.
#
# Counted rather than named on purpose. The LAPI registers a new
# bouncer entry per observed client address, and what it observes is
# the netbird-ingress pod IP, so names like `edge@10.244.0.113` come
# and go as those pods roll. The count is stable; the names are not.
#
# `or vector(0)` matters: without it, all bouncers going silent makes
# the inner filter empty, count() returns no series, and the alert
# never fires — the total-failure case would be the one it misses.
expr: |
(count(sum by (bouncer) (rate(cs_lapi_bouncer_requests_total[15m])) > 0) or vector(0)) < 2
for: 15m
labels:
severity: critical
annotations:
summary: "Weniger als zwei CrowdSec-Bouncer holen Decisions ab"
description: >-
Erwartet werden proxy-1 und proxy-2. Holt einer nichts mehr ab,
setzt er einen veralteten Stand durch; ist sein Dienst beendet,
setzt er gar nichts mehr durch, weil ein sauberer Stop die
nftables-Tabellen löscht. Prüfen: `systemctl status
crowdsec-firewall-bouncer` auf beiden Hosts.
- alert: CrowdsecTargetDown
expr: |
up{namespace="crowdsec"} == 0
for: 10m
labels:
severity: warning
annotations:
summary: "CrowdSec-Scrape-Target {{ $labels.pod }} antwortet nicht"
description: >-
Ein LAPI- oder Agent-Pod ist nicht erreichbar. Für sich noch kein
Ausfall der Durchsetzung — die Bouncer behalten ihren letzten
Stand —, aber solange das anhält, kommen keine neuen Decisions an.