rpi: бэкап OneDrive на HDD + SMART/бэкап-метрики через pushgateway на fr1
- servers/rpi.md: новая домашняя Raspberry Pi 4 (Trixie, cloud-init), HDD 4 ТБ, грабли с питанием двух HDD от USB - stacks/rpi-backup: rclone sync с вечным архивом версий, еженедельная сверка хэшей, smartd, пуш SMART и статуса бэкапов - fr1: pushgateway (127.0.0.1:9091), приём по SSH через forced command ~/bin/pushgw-ingest, алерты группы rpi Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
3604e843c8
commit
d1269162e7
@@ -33,6 +33,22 @@ services:
|
||||
- '127.0.0.1:9093:9093'
|
||||
networks: [monitoring]
|
||||
|
||||
# pushgateway: метрики от хостов за NAT (rpi), которые не скрейпнуть.
|
||||
# Порт только на 127.0.0.1 — пуш идёт по SSH через forced command ~/bin/pushgw-ingest
|
||||
# (см. servers/fr1.md), наружу не торчит. Персистентность — чтобы рестарт не обнулял алерты.
|
||||
pushgateway:
|
||||
image: prom/pushgateway:latest
|
||||
container_name: pushgateway
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- '--persistence.file=/data/metrics'
|
||||
- '--persistence.interval=1m'
|
||||
volumes:
|
||||
- pushgateway_data:/data
|
||||
ports:
|
||||
- '127.0.0.1:9091:9091'
|
||||
networks: [monitoring]
|
||||
|
||||
grafana:
|
||||
image: grafana/grafana:latest
|
||||
container_name: grafana
|
||||
@@ -93,6 +109,7 @@ services:
|
||||
networks: [monitoring]
|
||||
|
||||
volumes:
|
||||
pushgateway_data:
|
||||
prometheus_data:
|
||||
grafana_data:
|
||||
alertmanager_data:
|
||||
|
||||
@@ -35,3 +35,9 @@ scrape_configs:
|
||||
- job_name: openvpn
|
||||
file_sd_configs:
|
||||
- files: ["/etc/prometheus/targets/openvpn/*.yml"]
|
||||
|
||||
# honor_labels: сохраняем job/instance, с которыми хост запушил, а не pushgateway:9091
|
||||
- job_name: pushgateway
|
||||
honor_labels: true
|
||||
static_configs:
|
||||
- targets: ["pushgateway:9091"]
|
||||
|
||||
@@ -35,3 +35,67 @@ groups:
|
||||
labels: {severity: critical}
|
||||
annotations:
|
||||
summary: "КРИТИЧНО: диск почти полон на {{ $labels.instance }} ({{ $labels.mountpoint }}): свободно {{ printf \"%.0f\" $value }}%"
|
||||
|
||||
# Метрики от rpi приходят через pushgateway (SSH-пуш, см. servers/rpi.md).
|
||||
# push_time_seconds — когда группа последний раз обновлялась.
|
||||
- name: rpi
|
||||
rules:
|
||||
- alert: PushMetricsStale
|
||||
expr: time() - push_time_seconds{job=~"smart|backup_.*"} > 3 * 3600
|
||||
for: 10m
|
||||
labels: {severity: warning}
|
||||
annotations:
|
||||
summary: "{{ $labels.instance }}: метрики {{ $labels.job }} не приходят {{ $value | humanizeDuration }} — хост/пуш лежит"
|
||||
|
||||
- alert: SmartHealthFailed
|
||||
expr: smart_health_passed == 0
|
||||
labels: {severity: critical}
|
||||
annotations:
|
||||
summary: "{{ $labels.instance }}: SMART FAILED на диске {{ $labels.model }} ({{ $labels.serial }}) — менять диск"
|
||||
|
||||
- alert: SmartBadSectors
|
||||
expr: smart_attribute_raw{id=~"5|197|198"} > 0
|
||||
labels: {severity: critical}
|
||||
annotations:
|
||||
summary: "{{ $labels.instance }}: {{ $labels.name }} = {{ $value }} на {{ $labels.model }} ({{ $labels.serial }}) — диск начал сыпаться"
|
||||
|
||||
- alert: SmartBadSectorsGrowing
|
||||
expr: increase(smart_attribute_raw{id=~"5|197|198"}[1d]) > 0
|
||||
labels: {severity: critical}
|
||||
annotations:
|
||||
summary: "{{ $labels.instance }}: {{ $labels.name }} растёт на {{ $labels.model }} — срочно вторую копию"
|
||||
|
||||
- alert: SmartUdmaCrcErrors
|
||||
expr: increase(smart_attribute_raw{id="199"}[1d]) > 0
|
||||
labels: {severity: warning}
|
||||
annotations:
|
||||
summary: "{{ $labels.instance }}: CRC-ошибки на {{ $labels.model }} — кабель/USB/питание"
|
||||
|
||||
- alert: SmartSelftestFailed
|
||||
expr: smart_selftest_last_passed == 0
|
||||
labels: {severity: critical}
|
||||
annotations:
|
||||
summary: "{{ $labels.instance }}: самотест SMART провален на {{ $labels.model }} ({{ $labels.serial }})"
|
||||
|
||||
- alert: SmartTemperatureHigh
|
||||
expr: smart_temperature_celsius > 55
|
||||
for: 15m
|
||||
labels: {severity: warning}
|
||||
annotations:
|
||||
summary: "{{ $labels.instance }}: диск {{ $labels.model }} греется: {{ $value }}°C"
|
||||
|
||||
- alert: BackupFailed
|
||||
expr: backup_last_exit_code != 0
|
||||
labels: {severity: warning}
|
||||
annotations:
|
||||
summary: "{{ $labels.instance }}: {{ $labels.job }} завершился с кодом {{ $value }} — см. journalctl на хосте"
|
||||
|
||||
# sync каждые 4ч → 12ч = три пропуска подряд; verify раз в неделю → 8 дней
|
||||
- alert: BackupStale
|
||||
expr: |
|
||||
(time() - backup_last_success_timestamp{job="backup_onedrive"} > 12 * 3600)
|
||||
or (time() - backup_last_success_timestamp{job="backup_onedrive_verify"} > 8 * 86400)
|
||||
for: 10m
|
||||
labels: {severity: critical}
|
||||
annotations:
|
||||
summary: "{{ $labels.instance }}: {{ $labels.job }} не было успешного прогона {{ $value | humanizeDuration }}"
|
||||
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
# Forced command для пуш-ключей хостов за NAT (authorized_keys: command="~/bin/pushgw-ingest <instance>").
|
||||
# Клиент передаёт только имя job (ssh ... <job>), метрики — в stdin в text-формате Prometheus.
|
||||
# instance задаётся ключом, а не клиентом: хост не может писать метрики от чужого имени.
|
||||
instance="$1"
|
||||
job="$SSH_ORIGINAL_COMMAND"
|
||||
case "$instance" in ""|*[!a-z0-9_-]*) echo "bad instance" >&2; exit 2;; esac
|
||||
case "$job" in ""|*[!a-z0-9_]*) echo "bad job" >&2; exit 2;; esac
|
||||
exec curl -sS --fail-with-body --max-time 30 -X PUT --data-binary @- \
|
||||
"http://127.0.0.1:9091/metrics/job/$job/instance/$instance"
|
||||
Reference in New Issue
Block a user