mirror of
https://github.com/vhaudiquet/homeprod.git
synced 2026-07-28 16:40:49 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3eb7065f3a |
@@ -1,53 +1,43 @@
|
||||
// Grafana Alloy config for the docker host (replaces Promtail, EOL since
|
||||
// 2026-03-02). Tails Docker container logs via docker_sd discovery, attaches
|
||||
// container_name/compose_service/stream labels, and pushes them to the
|
||||
// in-cluster Loki at loki-internal.lan (LAN-only Traefik Ingress on port 80).
|
||||
// River config produced by `alloy convert -f promtail` from an equivalent
|
||||
// Promtail YAML (so component names and argument shapes are guaranteed
|
||||
// correct for Alloy v1.18).
|
||||
// Grafana Alloy config for the docker host
|
||||
// Tails Docker container logs via the docker_sd discovery +
|
||||
// file streaming, applies container_name/image labels, and pushes them to
|
||||
// the in-cluster Loki at loki-internal.lan (LAN-only Traefik Ingress).
|
||||
|
||||
discovery.docker "docker" {
|
||||
host = "unix:///var/run/docker.sock"
|
||||
refresh_interval = "5s"
|
||||
logging {
|
||||
level = "info"
|
||||
format = "logfmt"
|
||||
}
|
||||
|
||||
loki.process "docker" {
|
||||
forward_to = [loki.write.default.receiver]
|
||||
|
||||
// Parse docker json-file log lines
|
||||
// ({"log":"...","stream":"stdout","time":"..."}).
|
||||
stage.json {
|
||||
expressions = {
|
||||
log = "log",
|
||||
stream = "stream",
|
||||
}
|
||||
}
|
||||
stage.labels {
|
||||
values = {
|
||||
stream = null,
|
||||
}
|
||||
}
|
||||
// Discover running Docker containers via the host socket.
|
||||
discovery.docker "containers" {
|
||||
host = "unix:///var/run/docker.sock"
|
||||
}
|
||||
|
||||
discovery.relabel "docker" {
|
||||
targets = []
|
||||
// Relabel rules: strip the leading slash from __meta_docker_container_name and
|
||||
// expose useful labels (container name, compose service, image).
|
||||
discovery.relabel "containers" {
|
||||
targets = discovery.docker.containers.targets
|
||||
|
||||
// Strip leading / from container name.
|
||||
rule {
|
||||
source_labels = ["__meta_docker_container_name"]
|
||||
regex = "/(.*)"
|
||||
target_label = "container_name"
|
||||
replacement = "$1"
|
||||
}
|
||||
|
||||
// Compose service name (if label is set).
|
||||
rule {
|
||||
source_labels = ["__meta_docker_container_label_com_docker_compose_service"]
|
||||
target_label = "compose_service"
|
||||
}
|
||||
|
||||
// Log stream (stdout/stderr).
|
||||
rule {
|
||||
source_labels = ["__meta_docker_container_log_stream"]
|
||||
target_label = "stream"
|
||||
}
|
||||
|
||||
// Mark the source so dashboards/alerts can distinguish the docker host.
|
||||
rule {
|
||||
target_label = "source"
|
||||
@@ -55,17 +45,39 @@ discovery.relabel "docker" {
|
||||
}
|
||||
}
|
||||
|
||||
loki.source.docker "docker" {
|
||||
host = "unix:///var/run/docker.sock"
|
||||
targets = discovery.docker.docker.targets
|
||||
forward_to = [loki.process.docker.receiver]
|
||||
relabel_rules = discovery.relabel.docker.rules
|
||||
refresh_interval = "5s"
|
||||
// Tail the container log files based on the docker_sd discovery. Each target
|
||||
// exposes a `__path__`idders pointing at /var/lib/docker/containers/<id>/<id>-json.log
|
||||
// (the default docker json-file driver path; we mount that host path read-only).
|
||||
local.file "container_logs" {
|
||||
targets = discovery.relabel.containers.output
|
||||
tail_from_end = true
|
||||
}
|
||||
|
||||
// Parse docker json-file logs ({"log":"...","stream":"stdout","time":"..."}).
|
||||
loki.process "containers" {
|
||||
forward_to = [loki.write.default.receiver]
|
||||
|
||||
stage.json {
|
||||
expressions = {
|
||||
log = "log",
|
||||
stream = "stream",
|
||||
time = "time",
|
||||
}
|
||||
}
|
||||
stage.labels {
|
||||
values = {
|
||||
stream = "stream",
|
||||
}
|
||||
}
|
||||
stage.timestamp {
|
||||
source = "time"
|
||||
format = "RFC3339Nano"
|
||||
}
|
||||
}
|
||||
|
||||
// Push to Loki in-cluster via the LAN-only Traefik Ingress (port 80).
|
||||
loki.write "default" {
|
||||
endpoint {
|
||||
url = "http://loki-internal.lan/loki/api/v1/push"
|
||||
}
|
||||
external_labels = {}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
# Container image
|
||||
image:
|
||||
repository: stalwartlabs/stalwart
|
||||
tag: v0.16.11
|
||||
tag: 0.16.15
|
||||
pullPolicy: IfNotPresent
|
||||
# Single-node RocksDB deployment.
|
||||
replicaCount: 1
|
||||
@@ -144,4 +144,4 @@ sops:
|
||||
-----END PGP MESSAGE-----
|
||||
fp: DC6910268E657FF70BA7EC289974494E76938DDC
|
||||
encrypted_regex: ^(password|value|ssh-key|api-key|user|username|privateKey|clientSecret|clientId|apiKey|extraArgs.*|.*Secret.*|extraEnvVars|.*SECRET.*|.*secret.*|key|.*Password|.*\.ya?ml)$
|
||||
version: 3.10.2
|
||||
version: 3.10.2
|
||||
@@ -0,0 +1,27 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: alertmanager-config
|
||||
namespace: observability
|
||||
labels:
|
||||
app.kubernetes.io/name: alertmanager
|
||||
app.kubernetes.io/component: notifiers
|
||||
type: Opaque
|
||||
stringData:
|
||||
alertmanager.yaml: |
|
||||
global:
|
||||
resolve_timeout: 5m
|
||||
|
||||
route:
|
||||
receiver: default
|
||||
group_by: ["alertname", "severity", "instance"]
|
||||
group_wait: 30s
|
||||
group_interval: 5m
|
||||
repeat_interval: 4h
|
||||
|
||||
receivers:
|
||||
- name: default
|
||||
webhook_configs:
|
||||
# n8n workflow webhook
|
||||
- url: "http://n8n.lan/webhook/observability-alert"
|
||||
send_resolved: true
|
||||
@@ -12,57 +12,82 @@ data:
|
||||
// Grafana Alloy — log collection only (metrics cluster+host collection is
|
||||
// handled by vmagent/node-exporter/cAdvisor elsewhere).
|
||||
// Forwards pod logs to the in-cluster Loki single-binary.
|
||||
// River config produced by `alloy convert -f promtail` from an equivalent
|
||||
// Promtail YAML (so component names and argument shapes are guaranteed
|
||||
// correct for Alloy v1.18).
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
discovery.kubernetes "kubernetes_pods" {
|
||||
role = "pod"
|
||||
logging {
|
||||
level = "info"
|
||||
format = "logfmt"
|
||||
}
|
||||
|
||||
selectors {
|
||||
role = "pod"
|
||||
field = "spec.nodeName=" + coalesce(sys.env("HOSTNAME"), constants.hostname)
|
||||
// Tail every pod's log files written under /var/log/pods/* on the host.
|
||||
local.file_match "pods" {
|
||||
path_targets = [{
|
||||
__path__ = "/var/log/pods/*/*/*.log",
|
||||
}]
|
||||
}
|
||||
|
||||
// Discover Kubernetes metadata for the tailed files (pod_name, namespace,
|
||||
// container_name, uid).
|
||||
discovery.kubernetes "pods" {
|
||||
role = "pod"
|
||||
}
|
||||
|
||||
// Stream-stage relabel: read the filename to extract namespace/pod/container
|
||||
// metadata in line with Loki/Promtail's expectations.
|
||||
lrelabel "pods" {
|
||||
targets = local.file_match.pods.targets
|
||||
rule {
|
||||
source_labels = ["__path__"]
|
||||
regex = "/var/log/pods/(?P<namespace>[^/]+)/(?P<pod>[^/]+)/(?P<container>[^/]+)/(?P<uid>[^/]+)/.*"
|
||||
target_label = "namespace"
|
||||
replacement = "$namespace"
|
||||
}
|
||||
rule {
|
||||
source_labels = ["__path__"]
|
||||
regex = "/var/log/pods/([^/]+)/([^/]+)/([^/]+)/[^/]+/.*"
|
||||
target_label = "pod"
|
||||
replacement = "$pod"
|
||||
}
|
||||
rule {
|
||||
source_labels = ["__path__"]
|
||||
regex = "/var/log/pods/([^/]+)/([^/]+)/([^/]+)/[^/]+/.*"
|
||||
target_label = "container"
|
||||
replacement = "$container"
|
||||
}
|
||||
}
|
||||
|
||||
loki.process "kubernetes_pods" {
|
||||
// Tail the actual log files.
|
||||
local.file "pods_logs" {
|
||||
targets = lrelabel.pods.output
|
||||
read_from = "beginning"
|
||||
tail_from_end = false
|
||||
}
|
||||
|
||||
// Parse CRI-style lines ({"log":"...","stream":"stdout","time":"..."}).
|
||||
loki.process "pods" {
|
||||
forward_to = [loki.write.default.receiver]
|
||||
|
||||
// CRI-style log lines on the host: {"log":"...","stream":"stdout","time":"..."}
|
||||
stage.cri { }
|
||||
}
|
||||
|
||||
discovery.relabel "kubernetes_pods" {
|
||||
targets = discovery.kubernetes.kubernetes_pods.targets
|
||||
|
||||
rule {
|
||||
source_labels = ["__meta_kubernetes_namespace"]
|
||||
target_label = "namespace"
|
||||
stage.json {
|
||||
expressions = {
|
||||
log = "log",
|
||||
stream = "stream",
|
||||
time = "time",
|
||||
}
|
||||
}
|
||||
rule {
|
||||
source_labels = ["__meta_kubernetes_pod_name"]
|
||||
target_label = "pod"
|
||||
stage.labels {
|
||||
values = {
|
||||
stream = "stream",
|
||||
}
|
||||
}
|
||||
rule {
|
||||
source_labels = ["__meta_kubernetes_pod_container_name"]
|
||||
target_label = "container"
|
||||
stage.timestamp {
|
||||
source = "time"
|
||||
format = "RFC3339Nano"
|
||||
}
|
||||
}
|
||||
|
||||
loki.source.file "kubernetes_pods" {
|
||||
targets = discovery.relabel.kubernetes_pods.output
|
||||
forward_to = [loki.process.kubernetes_pods.receiver]
|
||||
|
||||
file_match {
|
||||
enabled = true
|
||||
}
|
||||
legacy_positions_file = "/tmp/positions.yaml"
|
||||
}
|
||||
|
||||
// Push to Loki in-cluster (no auth on .lan).
|
||||
loki.write "default" {
|
||||
endpoint {
|
||||
url = "http://loki.observability.svc.cluster.local:3100/loki/api/v1/push"
|
||||
}
|
||||
external_labels = {}
|
||||
}
|
||||
@@ -9,7 +9,9 @@ resources:
|
||||
- vmstack-release.yaml
|
||||
- loki-release.yaml
|
||||
- alloy-release.yaml
|
||||
- vmalert-rules.yaml
|
||||
- alloy-config.yaml
|
||||
- alertmanager-config.yaml
|
||||
- vm-internal-ingress.yaml
|
||||
- loki-internal-ingress.yaml
|
||||
secretGenerator:
|
||||
|
||||
@@ -1,34 +1,10 @@
|
||||
# Loki Helm values
|
||||
# Uses Monolithic deployment mode with filesystem storage on a Longhorn PVC.
|
||||
# Loki single-binary Helm values
|
||||
# SOPS-encrypted before commit (per .sops.yaml rules).
|
||||
# Uses the new chart-v18 "Monolithic" deployment mode (SingleBinary mode is
|
||||
# deprecated in Loki 4 / chart >=18.x). Bundles storage on a Longhorn PVC —
|
||||
# no minio, no gateway. Resource-light; tuned for a 2-node home cluster.
|
||||
loki:
|
||||
deploymentMode: Monolithic
|
||||
# Filesystem storage — chunks + rules on the PVC. The chart v18 requires
|
||||
# an explicit loki.storage block; without it the workload template errors
|
||||
# with "Please define loki.storage.bucketNames.chunks".
|
||||
storage:
|
||||
type: filesystem
|
||||
filesystem:
|
||||
chunks_directory: /var/loki/chunks
|
||||
rules_directory: /var/loki/rules
|
||||
# Required schema config. tsdb store + filesystem object store, schema v13.
|
||||
# See https://grafana.com/docs/loki/latest/operations/storage/schema/.
|
||||
schemaConfig:
|
||||
configs:
|
||||
- from: 2024-04-01T00:00:00Z
|
||||
store: tsdb
|
||||
object_store: filesystem
|
||||
schema: v13
|
||||
index:
|
||||
prefix: index_
|
||||
period: 24h
|
||||
# For monolithic mode, point tsdb_shipper at the local store (no index gateway).
|
||||
storage_config:
|
||||
tsdb_shipper:
|
||||
active_index_directory: /var/loki/index
|
||||
cache_location: /var/loki/index-cache
|
||||
filesystem:
|
||||
chunks_directory: /var/loki/chunks
|
||||
rules_directory: /var/loki/rules
|
||||
monolithic:
|
||||
persistence:
|
||||
storageClassName: longhorn
|
||||
@@ -45,9 +21,10 @@ loki:
|
||||
# 7d log retention (user can tune later).
|
||||
limitsConfig:
|
||||
retention_period: 7d
|
||||
# Disable distributed components + gateway + minio (monolithic only).
|
||||
# Disable subcharts we don't need in monolithic mode.
|
||||
test:
|
||||
enabled: false
|
||||
# Single binary only — disable distributed components.
|
||||
backend:
|
||||
replicas: 0
|
||||
read:
|
||||
@@ -61,27 +38,27 @@ gateway:
|
||||
minio:
|
||||
enabled: false
|
||||
sops:
|
||||
lastmodified: "2026-07-28T16:09:30Z"
|
||||
mac: ENC[AES256_GCM,data:phQ6t8rBVG5rdOR3H4GEWJHiCU1rj0MOzjp2jn2nneOUbbclyn4L4ga7YHOuoce0HEEMWniWxchT2eB0BZM3gtOd3yyBq3z3vTQKI12TsTzQSXxm+AgM4DL855BhPrFJCNZYeXb6IAGVkcRK/N7ihuGWiZORBsPaIcYseW9kjf4=,iv:wNIkcPRr1d79odGsT+yJJ+zqlUVTtqj8c/YkbhqOQjA=,tag:LSXyM6QiCM9qoit++nUkBw==,type:str]
|
||||
lastmodified: "2026-07-28T13:46:53Z"
|
||||
mac: ENC[AES256_GCM,data:bk1sxO2qIY/FT3+9dTCfpxKBbb9NlyEfTFuUihURUA/AmMJHV4XOx4EaBK6YFbSOVCLny5/aDbUDfF5DMudZTvKMNyU5sWq4X3+6BBZaL3vHSw2+UTA8pdpkNYpLl4uDq5aDFvAN2v/f/XwOpMIEte1L+yi7Rl96hq3Is9TYxjg=,iv:0Ayu7eiEVRAml+SycHWgmmP+rf4fZxhXAALor6d01rI=,tag:+aVf2mehHqak3K5xOHSJCA==,type:str]
|
||||
pgp:
|
||||
- created_at: "2026-07-28T16:09:30Z"
|
||||
- created_at: "2026-07-28T13:46:53Z"
|
||||
enc: |-
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
hQILA7uy4qQr71wiAQ/3c4Q36xa3tNPub4HCEUmJwK7Nmhdrg+sihZYR/TOHuDuV
|
||||
sY3GX+HwaRaMh4rOiJpCDa9E3qJY+qh9Wf+rgq+vaL9hmfNBlC+2q2xGv2u6FdiN
|
||||
xXE501qBbZjyNm/9fVAnCxbTBZP2mkRgdzMHfK2dnhIcL0/5LK7NAu+PsMQ0J8uq
|
||||
J5JYCqQTp/bfoRi8O4YXJD38u5d0UyEIy7HImKSn21za6CUErHEgdOTGUzw+UTH9
|
||||
kYHQykiBVML6bpp3YkqIquBm1tDzNuPvKSynnswJS5DQnGwsWVmESA3Rgfes2doe
|
||||
4d2f4Bso8+1+BRgbPkmp2c3/GdNFeUjjj6ViRar1bX2Bmt/ODLjoxAriRzS5ChRe
|
||||
MBJkKcW1hWotQE1IZgYC8A2I4dvDAbjeTwWmyJW7T1NV0er2rDd8TC9aQfhOPSeh
|
||||
mXdF18PXSmonmAdCj8Fu6Zfd6kcMQUQdI5L3BLRAQNs6+0r/tNjPH8vgYQtRWtxO
|
||||
acguClDwTrMwGM33HiBAYpoZ4hpCk43PRDNf0o2JkDy8tr3uTeBkmHPIA80UsIi8
|
||||
/YmrLzzaF0TaN7OvDLbw/5HUR1R2Wt2Mc3GnTdJIxMogqcw+jO86cNm2v7FJX5v1
|
||||
wG4ocHbrbfH1lcte3ZW8E5z3evuUeXVgUTL7dRHdNWmfL3HUnBlX+H+z7qWoNdJc
|
||||
AUhXpEvLTZ0LkA0ZMi749sUcL9cLsXkKJhAPbiF/3ytc7tLc40QsTY53uuqGPU/8
|
||||
LVVX7jUjqBrmeUENYj3zSN3kxcI1OX/qzHdTQ0LFSPPUrCEPtE8hWJrLaUE=
|
||||
=Nr2M
|
||||
hQIMA7uy4qQr71wiAQ//cYdDJn/PZWg3nx7eNeHjByT8kpYiTgi28Hbx0MmaR5Dd
|
||||
koqKVn47tSO8UkoIYkJ2uoImBJMmIqSLg5XjIhleUmSrZnrCLm0zhdgljMnkphME
|
||||
N9NYHc2gd65inztRDoZi12zxeDdPLcSo1tKPBvIQi33tWODbT6E8TJ4YGKBEv1+X
|
||||
NggSqgADxmYevrVYrzJtHCf7o369CUjHUedSkGPDEk7ZNKimb2kEdi4qKV+QFiX7
|
||||
y4iyqgeEepTjmloXSF6diC1F4cak96MqsNeIpiJLU5L1vWGGvb5J9znijpgqL6eX
|
||||
aompNyrpUrwrF2dbsfb32o/AW4FjdJsC+7RfLPWs9qBneoUtU7LPisZpN+xQ9kgt
|
||||
cx0CVY966y/0WTPm3coIhzO90fzL1kAn7+Bl5W5WhMBFRGDq7K0oqYcgdQmcr45o
|
||||
yegnYxV4uO6Vgf9NV9zEqCvq+UqRZUwJhQSdmaPkvWQDhx91y7lca0WP90xwYMhN
|
||||
jUvhHVcpZ7b++sbxNnvbhmnBBkAlwesQHVc85WSvok4kb3KpM1wju/j0KmOP2/lb
|
||||
YazrzeXu47diSttopRU+JphtUQyuOdTqQ+k5x/1gt7qd8R4IbfKAFMKWPC/KLBnH
|
||||
d4UIwa+ln/lWhhuR+/PxaPZrXGlm5SidM3r2SlH+1iGJfRzq1USaeEn9Vfp6OVvS
|
||||
XgFZngqHN4Wv9zdT71EUGeixMz142maTlcPBM1OnHgiIkf3gLoInizU1VrZz/f8s
|
||||
OOB4rHLwFjqj//Lze896q0kaWtnvZOuKdvTRsE7ep8VqAG2pVKwzcpINqnyTZss=
|
||||
=ww9K
|
||||
-----END PGP MESSAGE-----
|
||||
fp: DC6910268E657FF70BA7EC289974494E76938DDC
|
||||
encrypted_regex: ^(password|value|ssh-key|api-key|user|username|privateKey|clientSecret|clientId|apiKey|extraArgs.*|.*Secret.*|extraEnvVars|.*SECRET.*|.*secret.*|key|.*Password|.*\.ya?ml)$
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: vmalert-rules
|
||||
namespace: observability
|
||||
labels:
|
||||
app.kubernetes.io/name: vmalert
|
||||
app.kubernetes.io/component: alerting-rules
|
||||
data:
|
||||
node.rules: |
|
||||
groups:
|
||||
- name: node
|
||||
rules:
|
||||
- alert: HighNodeCPU
|
||||
expr: 100 - (avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 90
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "High CPU on {{ $labels.instance }}"
|
||||
description: "CPU usage above 90% for 5m on {{ $labels.instance }}."
|
||||
- alert: HighNodeRAM
|
||||
expr: 1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) > 0.9
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "RAM >90% on {{ $labels.instance }}"
|
||||
description: "Available RAM below 10% for 10m on {{ $labels.instance }}."
|
||||
- alert: LowDiskSpace
|
||||
expr: 1 - (node_filesystem_avail_bytes / node_filesystem_size_bytes{fstype!~"tmpfs|overlay"}) > 0.85
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Disk >85% full on {{ $labels.instance }} {{ $labels.mountpoint }}"
|
||||
description: "Filesystem {{ $labels.mountpoint }} on {{ $labels.instance }} is more than 85% full for 10m."
|
||||
@@ -12,7 +12,7 @@ spec:
|
||||
name: vm
|
||||
namespace: observability
|
||||
chart: victoria-metrics-k8s-stack
|
||||
version: "0.87.0"
|
||||
version: "0.43.2"
|
||||
interval: 1m
|
||||
valuesFrom:
|
||||
- kind: Secret
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
# victoria-metrics-k8s-stack Helm values
|
||||
# The chart installs the VictoriaMetrics operator + CRDs and creates VMSingle,
|
||||
# VMAgent, VMAlert, VMAlertmanager CRs.
|
||||
# victoria-metrics-k8s-stack Helm values
|
||||
# SOPS-encrypted before commit (per .sops.yaml rules).
|
||||
# All values for this chart live here; vmagent, grafana, vmalert, alertmanager,
|
||||
# kube-state-metrics and node-exporter are sub-charts toggled on/off below.
|
||||
# ---------------------------------------------------------------------------
|
||||
# VictoriaMetrics single-binary (the metrics database)
|
||||
# ---------------------------------------------------------------------------
|
||||
vmsingle:
|
||||
enabled: true
|
||||
spec:
|
||||
# Keep 7d of metrics; user can tune later.
|
||||
retentionPeriod: 7d
|
||||
storage:
|
||||
storageClassName: longhorn
|
||||
@@ -22,30 +24,26 @@ vmsingle:
|
||||
limits:
|
||||
cpu: "1"
|
||||
memory: 1Gi
|
||||
# ---------------------------------------------------------------------------
|
||||
# vmagent — scrapes node-exporter, kube-state-metrics, kubelet, etc.
|
||||
# The docker host runs its own vmagent that remote_writes through
|
||||
# vm-internal.lan (the Traefik Ingress → this vmsingle).
|
||||
# ---------------------------------------------------------------------------
|
||||
# vmagent scrapes the cluster (node-exporter, kube-state-metrics, cAdvisor).
|
||||
# That same vmagent is reused for the inner cluster; the docker host runs its
|
||||
# own vmagent that remote_writes through vm-internal.lan.
|
||||
vmagent:
|
||||
enabled: true
|
||||
spec:
|
||||
selectAllByDefault: true
|
||||
scrapeInterval: 20s
|
||||
tolerations:
|
||||
- key: ENC[AES256_GCM,data:SyutB0y9QtqQ,iv:EKRXNGtUovQYsESHFQTGygP09O2UWtLuBEQQcYZm3Oc=,tag:zh7CYOZleSgbbQhYZvcT+w==,type:str]
|
||||
value: ENC[AES256_GCM,data:So+8xmHYSxs=,iv:7sfUBAaMZd7imjVx14lusVn58cJQ4HpTkaPy5Eal2dY=,tag:hRBjT6wuEfG3Z+ZiPJhwQQ==,type:str]
|
||||
operator: Equal
|
||||
effect: NoSchedule
|
||||
- key: ENC[AES256_GCM,data:lj9OurynU8pRt4/MhgyevjmV0b7Zq+WG/9+zRRvPeA+RYZsV+A==,iv:LjuhErDT0bDlLcpo/AlNykz8Xu89B+nx1ja+HkPNXwo=,tag:5xM6XHrCdEejE7ituQ8W4Q==,type:str]
|
||||
operator: Exists
|
||||
effect: NoSchedule
|
||||
# Tolerate the failover taint so the scrape DaemonSet covers all nodes.
|
||||
tolerations:
|
||||
- key: ENC[AES256_GCM,data:pERZJq2cU/V7,iv:eMeR91aKxAJmKpKLEL6pA9rcIHOrd+Mg+RmA3gt1xGw=,tag:l2igrTTHLclHPEO3bCq/dg==,type:str]
|
||||
value: ENC[AES256_GCM,data:m0pJQf4RLt4=,iv:Au3k95QkD2wv3OP/yDyNQKDN5ZrDIuNAwhXaMCEEn+g=,tag:mEcqTG4e0aYwoHr4TtmgTg==,type:str]
|
||||
operator: Equal
|
||||
effect: NoSchedule
|
||||
- key: ENC[AES256_GCM,data:lDW9tKXBee7p5HjH/FWmzpwtnrazCuohAQm47B5htn++6QUYIA==,iv:yJJdF/XaFXghwBOwC2lByMRM+M5MNP51ho3qTfAKqq0=,tag:WFNE1PMRFss+M/I6j/8Nbg==,type:str]
|
||||
operator: Exists
|
||||
effect: NoSchedule
|
||||
# ---------------------------------------------------------------------------
|
||||
# kube-state-metrics + node-exporter (metrics sources)
|
||||
# ---------------------------------------------------------------------------
|
||||
kube-state-metrics:
|
||||
kubeStateMetrics:
|
||||
enabled: true
|
||||
prometheus-node-exporter:
|
||||
nodeExporter:
|
||||
enabled: true
|
||||
# ---------------------------------------------------------------------------
|
||||
# Grafana (UI) — served behind ingress at grafana.lan.
|
||||
@@ -60,20 +58,15 @@ grafana:
|
||||
size: 5Gi
|
||||
adminUser: admin
|
||||
# SOPS encrypts this when the file is processed.
|
||||
adminPassword: ENC[AES256_GCM,data:vpPvJA/PktMnsg/IovYfsD4lDBndUw==,iv:b4eLMEcUMJj4BwyeYw1kw2sZuieyr2xL9et+R7ss0BU=,tag:u0RcTp6mkQNAGkblG4/xww==,type:str]
|
||||
# Provision a Loki datasource alongside the chart's default VictoriaMetrics
|
||||
adminPassword: ENC[AES256_GCM,data:VXtozCJ6XZ7JqWuIrDTmZkfWDeglVXl9JZ9T0sY2OUDY9MM=,iv:PO4leV1zpTFZTB2/hWipitGec4zrHfW/gOkJBrGk24k=,tag:9GinyiqoA4qrbyeKJ33Iuw==,type:str]
|
||||
# Provision both the chart's default VictoriaMetrics datasource AND a Loki
|
||||
# datasource so metric/log correlation works in one UI.
|
||||
datasources:
|
||||
datasources.yaml:
|
||||
apiVersion: ENC[AES256_GCM,data:Uw==,iv:KYTMBV53yMbojMaqNinYpm/Uj5ylV86jcdJTdxuy+G0=,tag:IbIpiSBQFw7YSTQg0WRGqg==,type:int]
|
||||
datasources:
|
||||
- name: ENC[AES256_GCM,data:BPfR8Q==,iv:MkMhH3IJfBZzbIcqQw7y6bfJB/4Ew4feZNugjovk5O0=,tag:jaa43tzIXkE8kWZp2R/kcg==,type:str]
|
||||
type: ENC[AES256_GCM,data:WWncww==,iv:hPqJE0KCidSBnf11wxFH+Je+YKztWmeToBqi39Qf4ZQ=,tag:c9enJ2VgQqDJVbasvgG+Dw==,type:str]
|
||||
url: ENC[AES256_GCM,data:YkS66tpWXpW62Dtful0KNCwoZ3rG3nQtDBUsNQUwdQ+eqtG38JTrAudQqnsRD51q,iv:Mu3Sc6T2xypdot9dA1DHc8nD7UI8372nqlveLITR82E=,tag:1VmmIg+xR9qjWcvPKVYsJQ==,type:str]
|
||||
access: ENC[AES256_GCM,data:af3GpjE=,iv:MAYKfklF+wCC/XN+4snebjPAnW4KpYzaMMGYds7rG18=,tag:X+oFiDAiCb1f7cfREqFT7A==,type:str]
|
||||
isDefault: ENC[AES256_GCM,data:Zgu4Hn8=,iv:nnVB/in75nPAQos0Dfl6abBrHwT8tCt5X7lcCYBZbLc=,tag:u1UvkSj6SjcMm06NzKMGDA==,type:bool]
|
||||
jsonData:
|
||||
maxLines: ENC[AES256_GCM,data:88JJqA==,iv:lKsZa024I6223UEu0SHuZ3N9LZrzw0WBTfC+L2/3ljI=,tag:YvqieXK36xMmC/sSJ11NIg==,type:int]
|
||||
additionalDataSources:
|
||||
- name: Loki
|
||||
type: loki
|
||||
url: http://loki.observability.svc.cluster.local:3100
|
||||
access: proxy
|
||||
isDefault: false
|
||||
ingress:
|
||||
enabled: true
|
||||
ingressClassName: traefik
|
||||
@@ -85,17 +78,19 @@ grafana:
|
||||
pathType: Prefix
|
||||
tls: []
|
||||
# ---------------------------------------------------------------------------
|
||||
# vmalert — evaluates VMRule CRs against VictoriaMetrics, forwards firing
|
||||
# alerts to Alertmanager. selectAllByDefault picks up all VMRules in the
|
||||
# namespace (including our vmalert-rules.yaml VMRule CR).
|
||||
# vmalert — evaluates the node.rules ConfigMap (see vmalert-rules.yaml) against
|
||||
# VictoriaMetrics, forwards firing alerts to Alertmanager below.
|
||||
# ---------------------------------------------------------------------------
|
||||
vmalert:
|
||||
enabled: true
|
||||
spec:
|
||||
selectAllByDefault: true
|
||||
evaluationInterval: 20s
|
||||
notifiers:
|
||||
- url: http://vm-victoria-metrics-k8s-stack-alertmanager.observability.svc.cluster.local:9093
|
||||
rule:
|
||||
configMap: vmalert-rules
|
||||
configMapKeyRef:
|
||||
key: ENC[AES256_GCM,data:7obbOvuMe8Cpdg==,iv:oIzOCzFFA0cptWQMPiIOi5b76dR3zp96Y8JrdKOkE8A=,tag:ArteBgOhdz/7N4wNpWE83g==,type:str]
|
||||
name: vmalert-rules
|
||||
notifier:
|
||||
alertmanagerUrl: http://vm-victoria-metrics-k8s-stack-alertmanager.observability.svc.cluster.local:9093
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
@@ -104,95 +99,52 @@ vmalert:
|
||||
cpu: 200m
|
||||
memory: 128Mi
|
||||
# ---------------------------------------------------------------------------
|
||||
# Alertmanager — 1 replica; inline config with a single n8n webhook receiver.
|
||||
# n8n runs on the docker host and fans out to email/Telegram/whatever.
|
||||
# Alertmanager — 1 replica; reads its config from the alertmanager-config
|
||||
# Secret (see alertmanager-config.yaml), which has a single n8n webhook receiver.
|
||||
# ---------------------------------------------------------------------------
|
||||
alertmanager:
|
||||
enabled: true
|
||||
spec:
|
||||
replicaCount: 1
|
||||
port: "9093"
|
||||
selectAllByDefault: true
|
||||
storage:
|
||||
volumeClaimTemplate:
|
||||
spec:
|
||||
storageClassName: longhorn
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 2Gi
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 64Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 128Mi
|
||||
config:
|
||||
route:
|
||||
receiver: n8n-webhook
|
||||
group_wait: 30s
|
||||
group_interval: 5m
|
||||
repeat_interval: 4h
|
||||
receivers:
|
||||
- name: n8n-webhook
|
||||
webhook_configs:
|
||||
- url: http://n8n.lan/webhook/observability-alert
|
||||
send_resolved: true
|
||||
extraRules:
|
||||
node-alerts:
|
||||
groups:
|
||||
- name: node
|
||||
rules:
|
||||
- alert: HighNodeCPU
|
||||
expr: 100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: High CPU on {{ $labels.instance }}
|
||||
description: CPU usage above 80% for 10 minutes.
|
||||
- alert: HighNodeRAM
|
||||
expr: 100 - ((node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100) > 85
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: High RAM on {{ $labels.instance }}
|
||||
description: RAM usage above 85% for 10 minutes.
|
||||
- alert: LowDiskSpace
|
||||
expr: |
|
||||
100 - ((node_filesystem_avail_bytes{mountpoint!~"/run.*|/var/lib/docker.*"} /
|
||||
node_filesystem_size_bytes{mountpoint!~"/run.*|/var/lib/docker.*"}) * 100) > 85
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: Low disk space on {{ $labels.instance }} {{ $labels.mountpoint }}
|
||||
description: Disk usage above 85% for 10 minutes.
|
||||
replicaCount: 1
|
||||
storage:
|
||||
storageClassName: longhorn
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
size: 2Gi
|
||||
configFromSecretRef: ENC[AES256_GCM,data:C6NDZCkX76QLaZLw/vY3B01+AA==,iv:RSpe3Zv1mPfo0nj73tWrf5IBd+Sfqo1NUrERvmVPBgs=,tag:4ZGHA2Kyj9zpGn6eylNbXw==,type:str]
|
||||
configKey: alertmanager.yaml
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 64Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 128Mi
|
||||
# ---------------------------------------------------------------------------
|
||||
# Storage class default
|
||||
# ---------------------------------------------------------------------------
|
||||
defaultStorageClass: longhorn
|
||||
sops:
|
||||
lastmodified: "2026-07-28T16:39:09Z"
|
||||
mac: ENC[AES256_GCM,data:Ymxmmkui0Nuv35Yq6v4BXM4xGhvjAufexW7fIm9fUIdTTXZ3e10PitNCZXKiaDwwCJfdEkNZri7jPAYzhTzKMIdcTKnOMImsMjdiJZwRwwbKcYVay0A6KSLJ8eFjDRRYhlCXbsAIRlQHpFh4UkssKdu40mIiudU0qIVm04ppbno=,iv:R8kpITPYSbQ1yZw5IK0fArssxUtIjNmvzknOrLJQcuk=,tag:/CRcr1VZAIKIPbcag05uTw==,type:str]
|
||||
lastmodified: "2026-07-28T13:46:53Z"
|
||||
mac: ENC[AES256_GCM,data:ZyCYO9b3OMJo8Br9tzbK+bN3rZvlkk/scOEUSHwkAABeQxL7JDbcpjHL8WauSY57wLnl3sK9JQnUHS+/ghOiZu/QDuodbhpoRrE8jpdjo1+qZaqJBqjbG1NIYU/aLQnHr4bnIzh8oX0PYnsu2uSqkg1c0c1KCJmrvHtXlFwtWOs=,iv:zh8yVzExwT29JF+4jLcZgmuSPM6R/d/2IWTcYfK3Qn4=,tag:LYTYY6rJVEKJRsOp6G/T0g==,type:str]
|
||||
pgp:
|
||||
- created_at: "2026-07-28T16:39:08Z"
|
||||
- created_at: "2026-07-28T13:46:53Z"
|
||||
enc: |-
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
hQIMA7uy4qQr71wiARAAmOxyx5eH5Jykp/GDL79REgf61iGujtPiUVy8CT3o7O5k
|
||||
tTGma6rRXK5wZV/iGN3wOQehF19Oy2iYwMfxilRwpB69RFwyKPKdHDDWMWNXv5Th
|
||||
wJnNjVjWZ0xiRautWPn/TjDOJZ6j6p7lQ3IcQ3vzuap1RVRIXv081DxJGR9JbeYl
|
||||
khmrl1+zecwxS87JK+fLko9NIBb5JgFEtEf1RvLyFak8nZu5cNCu+PCykALvUaO7
|
||||
V5NAaF1aV9cdz8G9TZWmHZsIlDDE9/oro2aLR7cr8uxnKQhWerttqvyDYNwYzNKW
|
||||
qgsUplTxTmyuc9DdnLDfJ55Ncuv9qS5/4/p5GpsHrteD6oiPNjXaxR8cBVFLvGwp
|
||||
kQb/VZgOGAnkYlMCUG8APiwRhbpAlyy51hKBqVh+a9iRlzZEWHKlxcvIrcFAQaHt
|
||||
sDXyg36F0GCoHiMAqHKG0kRcbTV7Sq0JZVoLU/qxvaDfqvyCZO7X/U/YsSWNbWL0
|
||||
emyBfeSvBX9h62NJx2aw8xKyiL+aeVfzSKAaXHMd/r4gKxBw/kVHAljKwRzbL85j
|
||||
UHeLUvqVkXs1mtIqGgUnpCcAVuV83omgcOwF44FitG/f9GCx+UWTvScwaw5ywuG3
|
||||
J/4fOaA3K0Z4e/mOejARd4jLSPktSRpA01k9kjYY8UBn7TzQ0LrMLj5dxh8BpWrS
|
||||
XgECZapi+q6IjIses7+JqkWiP25hcglhqK3LAiUapA0c5UmAa5Vf7jfnkFm8eUdZ
|
||||
bA7ZnhX8nxK9n7JdSxrZhrbPWMO8hF/XKi7KTZMvgXFlb4/qP96y7oeqzKYcnvE=
|
||||
=pmSq
|
||||
hQIMA7uy4qQr71wiAQ/+KP62YdvAUJjxvFoWH1skMELYx9Lj/5TRXnrHOtqQrpXC
|
||||
UzKNgS3XFmgeUcpA3wXvhTEPhICtdC9KDvxkF0jPTATqM2WFPcF7V2vjhTjuAMtU
|
||||
/g4RR44j6U2WRZJFjs0tEpVGVrw+HXiaACdgqy1P6OmnmgvTSBvZvj3AsBpHOHeM
|
||||
teaE11couXeYnrZ9NJeCvhbcvJfYAkdpqyCLJ9UWEro3QFm4z6tvs6D4rq+nAhBb
|
||||
MntlvArijXunkwbHLZVPh8SyfxgGooy9PBSEX+ymyuxPYmjTlarYgO7oBQJUWjRk
|
||||
BqkKns9KKNPyaUP/37tXJA8Gvgog/Z9JKDT28lq5noe6CAeHbBbUUo4hZZA14zcD
|
||||
/Lmkc03KdOKf/eLnEFGtoWjQJ38FD5+uAh5Zkixm1WW0wV/PdqevJrG8mRjn8FuP
|
||||
6GU+nWo+77BxDyKvA1O13iOtTnGZtg/Z+lXVUbpcM8F7QCGrabhLiWVe51iWjYfU
|
||||
/Ub7HZHRDXmiz7Q6Kem5Wleoy3ME/lxFblx9v0LbO3Dc0lj/85EVNm9os8tt5PD0
|
||||
cvGE/cGsZfUfgwbjEVpPnRN0SzlQKfEr9KVQP4DGNAqDOzEl2AD4Dg3l29oAgSda
|
||||
ZRXdYbEN/ZIAGUg1wNVfZJf2KoJA+wm8c4uk7KAAI0+bIeaongovU4vnylVCHIbS
|
||||
XgGDxEIDZGOueHms+xEMIW6QmdazFdkaLumY0XrEInHMwqhKM0NOyZL2zd/liZtE
|
||||
wb8xm/V+eOWhjnE2RynNBCwMI6VeVYUPkin2MKQcq+nr7SVpWQsm2+Jbj5YtWCk=
|
||||
=yKiR
|
||||
-----END PGP MESSAGE-----
|
||||
fp: DC6910268E657FF70BA7EC289974494E76938DDC
|
||||
encrypted_regex: ^(password|value|ssh-key|api-key|user|username|privateKey|clientSecret|clientId|apiKey|extraArgs.*|.*Secret.*|extraEnvVars|.*SECRET.*|.*secret.*|key|.*Password|.*\.ya?ml)$
|
||||
|
||||
Reference in New Issue
Block a user