This repository has no description
4.9 kB
110 lines
1# vmalert rules. metrics runs against VictoriaMetrics, logs (LogsQL) against
2# VictoriaLogs; vmalert needs one instance per datasource type.
3{ lib }:
4let
5 inherit (import ./default.nix) alwaysOn;
6
7 alert = name: expr: for: summary: {
8 alert = name;
9 inherit expr for;
10 labels.severity = "warning";
11 annotations.summary = summary;
12 };
13in
14{
15 metrics.groups = [
16 {
17 name = "hosts";
18 rules = [
19 # absent_over_time keeps the host label of its selector, so one term
20 # per machine yields one alert per missing machine.
21 (alert "HostDown" (lib.concatMapStringsSep " or " (
22 host: ''absent_over_time(host_uptime{host="${host}"}[10m])''
23 ) alwaysOn) "0m" "{{ $labels.host }} sent no metrics for 10 minutes")
24 # Network mounts (and their autofs placeholders) are reported by
25 # every machine that mounts them; the agent drops tmpfs & co.
26 # neoprism's /var/download is always above 90%, see DownloadSpace*;
27 # skipped on every host that mounts the same dataset.
28 (alert "DiskAlmostFull"
29 ''host_filesystem_used_ratio{filesystem!~"cifs|smb3|nfs|nfs4|autofs|fuse.sshfs"} > 0.9 unless on (device) host_filesystem_used_ratio{host="neoprism",mountpoint="/var/download"}''
30 "15m"
31 "{{ $labels.host }}: {{ $labels.mountpoint }} is {{ $value | humanizePercentage }} full"
32 )
33 (alert "DownloadSpaceLow"
34 ''host_filesystem_free_bytes{host="neoprism",mountpoint="/var/download"} < 200e9''
35 "15m"
36 "{{ $labels.host }}: {{ $labels.mountpoint }} has only {{ $value | humanize }}B free"
37 )
38 (
39 alert "DownloadSpaceCritical"
40 ''host_filesystem_free_bytes{host="neoprism",mountpoint="/var/download"} < 100e9''
41 "15m"
42 "{{ $labels.host }}: {{ $labels.mountpoint }} has only {{ $value | humanize }}B free"
43 // {
44 labels.severity = "critical";
45 }
46 )
47 (alert "HighMemory" "1 - host_memory_available_bytes / host_memory_total_bytes > 0.95" "15m"
48 "{{ $labels.host }} uses {{ $value | humanizePercentage }} of its memory"
49 )
50 (alert "MemoryPressure" "rate(host_pressure_memory_some_seconds_total[10m]) > 0.1" "5m"
51 "{{ $labels.host }}: tasks stalled on memory {{ $value | humanizePercentage }} of the time"
52 )
53 # Without a sample just before its window (a gap in VictoriaMetrics,
54 # a new series) increase() returns the whole counter, so OOMKill and
55 # NvmeMediaErrors require one via present_over_time.
56 (alert "OOMKill"
57 "increase(host_memory_oom_kill_events_total[10m]) > 0 and present_over_time(host_memory_oom_kill_events_total[1m] offset 10m)"
58 "0m"
59 "{{ $labels.host }}: {{ $value }} processes OOM-killed in the last 10 minutes"
60 )
61 # NVMe drives set critical_warning bit 0x04 (and so fail smart_status)
62 # once they exceed their rated endurance; their wear is covered by
63 # NvmeSpareLow instead. 251 masks out bit 0x04.
64 (alert "SmartFailed"
65 ''smartctl_device_smart_status == 0 unless on (host, device) smartctl_device{protocol="NVMe"}''
66 "5m"
67 "{{ $labels.host }}: {{ $labels.device }} fails its SMART health check"
68 )
69 (alert "NvmeCriticalWarning" "bitmap_and(smartctl_device_critical_warning, 251) != 0" "5m"
70 "{{ $labels.host }}: {{ $labels.device }} reports critical warning {{ $value }}"
71 )
72 (alert "NvmeMediaErrors"
73 "increase(smartctl_device_media_errors[1h]) > 0 and present_over_time(smartctl_device_media_errors[2m] offset 1h)"
74 "0m"
75 "{{ $labels.host }}: {{ $labels.device }} had {{ $value }} new media errors in the last hour"
76 )
77 (alert "NvmeSpareLow" "smartctl_device_available_spare < smartctl_device_available_spare_threshold"
78 "5m"
79 "{{ $labels.host }}: {{ $labels.device }} has only {{ $value }}% spare left"
80 )
81 ];
82 }
83 {
84 name = "gatus";
85 rules = [
86 (alert "GatusEndpointDown" "gatus_results_endpoint_success == 0" "5m"
87 "{{ $labels.name }} fails as seen from {{ $labels.vantage }}"
88 )
89 ];
90 }
91 ];
92
93 logs.groups = [
94 {
95 name = "journal";
96 type = "vlogs";
97 interval = "1m";
98 rules = [
99 # systemd logs "<unit>: Failed with result '<result>'."; the anchored
100 # regexp skips lines merely quoting the phrase, such as VictoriaLogs
101 # warnings that echo a rejected query.
102 (alert "SystemdUnitFailed"
103 ''_time:15m "Failed with result" _msg:~"^[^ ]+: Failed with result '" | stats by (host, unit) count() as failures''
104 "0m"
105 "{{ $labels.unit }} failed on {{ $labels.host }}"
106 )
107 ];
108 }
109 ];
110}