Monitorowanie Raspberry Pi: Prometheus i Grafana
Część 7 z 7 serii Homelab na Raspberry Pi

Management Summary
Monitoring pokazuje stan Raspberry Pi w czasie, zamiast dopiero po awarii usługi. Prometheus zbiera pomiary, na przykład temperatury i obciążenia, a Grafana przedstawia je na czytelnych wykresach. W małej instalacji serwerowej pomaga to zauważyć przegrzewanie, problemy z zasilaniem lub rosnące wykorzystanie zasobów. Ostrzeżenia zwracają uwagę na nietypowe sytuacje, ale sam wykres nie wyjaśnia przyczyny. Brakujące i stare dane także mają znaczenie, ponieważ cisza nie dowodzi prawidłowego działania.
Spis treści
Obserwacja obciążenia bez obietnic przewidywania awarii
Prometheus zapisuje pomiary w czasie, a Grafana przedstawia wyniki. Na Raspberry Pi node_exporter udostępnia pomiary Linuksa do pobrania. Niewielki skrypt zbierający dane dodaje temperaturę i ostrzeżenia firmware, czyli wbudowanego oprogramowania sterującego urządzeniem. Monitoring pomaga badać wąskie gardła. Panel nie zapobiega przegrzaniu, nie naprawia zasilacza i nie przewiduje daty awarii nośnika.
Przykład dotyczy oddzielnej, nowej instalacji Raspberry Pi OS z Pythonem 3, Docker Engine i Compose. Istniejące konta, katalogi kolektora i usługi wymagają sprawdzenia przed instalacją. Konfiguracja hosta wymaga uprawnień administracyjnych. Wszystkie poniższe usługi sieciowe nasłuchują na lokalnym adresie 127.0.0.1, nazywanym loopback. Inne urządzenia w sieci nie mogą łączyć się bezpośrednio; dostęp z przeglądarki odbywa się przez szyfrowany tunel SSH do Pi.

1. Odczyt firmware z obsługą błędów
Plik rpi-hardware-metrics.py odczytuje wyłącznie temperaturę i stan ograniczania wydajności. Każde polecenie ma limit czasu, a nieoczekiwana odpowiedź przerywa przebieg. Nieudane wykonanie pozostawia poprzedni plik bez zmian. Metryka ostatniego sukcesu odróżnia stare pomiary od nowych; sam poprawny odczyt HTTP nie wykrywa zatrzymanego kolektora.
#!/usr/bin/env python3
import os
import re
import subprocess
import sys
import tempfile
import time
from pathlib import Path
FLAGS = ("undervoltage", "frequency_capped", "throttled", "soft_temperature_limit")
def read_value(argument):
return subprocess.run(
["/usr/bin/vcgencmd", argument], check=True,
capture_output=True, text=True, timeout=5
).stdout.strip()
def collect(reader=read_value):
temperature = re.fullmatch(r"temp=(-?[0-9]+(?:[.][0-9]+)?)'C", reader("measure_temp"))
throttle = re.fullmatch(r"throttled=0x([0-9a-fA-F]{1,8})", reader("get_throttled"))
if temperature is None or throttle is None:
raise ValueError("Unexpected vcgencmd output")
mask = int(throttle.group(1), 16)
lines = [
"# HELP rpi_soc_temperature_celsius SoC temperature",
"# TYPE rpi_soc_temperature_celsius gauge",
"rpi_soc_temperature_celsius " + temperature.group(1),
"# HELP rpi_soc_throttled_bitmask Complete firmware bitmask",
"# TYPE rpi_soc_throttled_bitmask gauge",
f"rpi_soc_throttled_bitmask {mask}",
"# HELP rpi_hardware_flag Firmware flag; current or since boot",
"# TYPE rpi_hardware_flag gauge",
]
for state, offset in (("current", 0), ("since_boot", 16)):
for bit, name in enumerate(FLAGS):
value = (mask >> (offset + bit)) & 1
lines.append(f'rpi_hardware_flag{{state="{state}",flag="{name}"}} {value}')
lines += [
"# HELP rpi_collector_last_success_timestamp_seconds Last successful collection",
"# TYPE rpi_collector_last_success_timestamp_seconds gauge",
f"rpi_collector_last_success_timestamp_seconds {time.time():.3f}",
]
return "\n".join(lines) + "\n"
def publish(directory, content):
directory = Path(directory)
fd, temporary = tempfile.mkstemp(prefix=".rpi-", suffix=".tmp", dir=directory)
try:
with os.fdopen(fd, "w", encoding="utf-8") as output:
output.write(content)
os.fchmod(output.fileno(), 0o644)
os.replace(temporary, directory / "rpi_metrics.prom")
finally:
if os.path.exists(temporary):
os.unlink(temporary)
if __name__ == "__main__":
if len(sys.argv) != 2:
raise SystemExit("Usage: rpi-hardware-metrics.py TEXTFILE_DIRECTORY")
publish(sys.argv[1], collect())Flagi bieżące i odnotowane od rozruchu otrzymują oddzielne etykiety. Przykładowo 0x50000 oznacza wcześniejsze zbyt niskie napięcie i dławienie, bez stwierdzenia, że którykolwiek stan trwa obecnie. Próbkowanie co minutę może pominąć krótkie stany bieżące. Flagi historyczne zachowują część informacji, ale nie podają czasu zdarzenia. Nieznane przyszłe bity pozostają widoczne w pełnej masce.
Napięcie rdzenia zostało świadomie pominięte. Wewnętrzna szyna zasilania nie jest napięciem wejściowym USB ani diagnozą uszkodzonego zasilacza. Pi 5 udostępnia oddzielne odczyty układu zarządzania zasilaniem (PMIC), na przykład vcgencmd pmic_read_adc EXT5V_V; dodanie takich danych wymaga parsera właściwego dla modelu i testów uprawnień.
2. Instalacja kolektora hosta i timera
Polecenia instalacji zakładają obecność trzech pokazanych plików w katalogu roboczym oraz grupy video. rpi-metrics to nowe konto usługi bez logowania; istniejące konto wymaga kontroli zamiast ponownego tworzenia. Raspberry Pi OS musi udostępniać /usr/bin/vcgencmd. Dostęp do urządzenia zależy od systemu i wymaga sprawdzenia na koncie usługi.
sudo apt update
sudo apt install prometheus-node-exporter python3
sudo useradd --system --user-group --groups video --no-create-home --shell /usr/sbin/nologin rpi-metrics
sudo install -d -o rpi-metrics -g rpi-metrics -m 755 /var/lib/node_exporter/textfile_collector
sudo install -d -m 755 /usr/local/libexec
sudo install -m 755 rpi-hardware-metrics.py /usr/local/libexec/rpi-hardware-metrics.py
sudo install -m 644 rpi-collector.service rpi-collector.timer /etc/systemd/system/Plik rpi-collector.service zawiera:
[Unit]
Description=Collect Raspberry Pi firmware metrics
[Service]
Type=oneshot
User=rpi-metrics
SupplementaryGroups=video
ExecStart=/usr/bin/python3 /usr/local/libexec/rpi-hardware-metrics.py /var/lib/node_exporter/textfile_collector
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/node_exporter/textfile_collectorPlik rpi-collector.timer zawiera:
[Unit]
Description=Collect Raspberry Pi firmware metrics periodically
[Timer]
OnBootSec=30s
OnUnitActiveSec=60s
AccuracySec=1s
[Install]
WantedBy=timers.targetsudo -u rpi-metrics /usr/bin/vcgencmd measure_temp oraz odpowiednie wywołanie get_throttled sprawdzają rzeczywisty dostęp. Eksporter potrzebuje odczytu katalogu i wygenerowanego pliku. Po instalacji właścicielem skryptu jest root; konto usługi zapisuje tylko w katalogu wynikowym kolektora.
W przypadku pakietu Debiana polecenie systemctl cat prometheus-node-exporter pokazuje sposób ładowania argumentów startowych. Przy użyciu /etc/default/prometheus-node-exporter istniejące ARGS wymaga opcji --collector.textfile.directory=/var/lib/node_exporter/textfile_collector i --web.listen-address=127.0.0.1:9100. Pozostałe potrzebne opcje pozostają zachowane; powielone ustawienia katalogu lub adresu nasłuchu trzeba uzgodnić. Decyduje konfiguracja rzeczywiście zainstalowanego pakietu.
Po konfiguracji polecenia sudo systemctl restart prometheus-node-exporter, sudo systemctl daemon-reload i sudo systemctl enable --now rpi-collector.timer aktywują usługi. sudo systemctl start rpi-collector.service wykonuje pierwszy przebieg. Błędy pozostają widoczne przez journalctl -u rpi-collector.service; ponowienie instalacji nie dopisuje zduplikowanych zadań cron.
3. Przechowywanie pomiarów w Prometheusie
Poniższy compose.yaml używa sieci hosta w Linuksie, dzięki czemu loopback kontenera dociera do eksportera hosta. Jawne adresy nasłuchu ograniczają Prometheusa i Grafanę do lokalnych połączeń. Chroniony plik .env definiuje przetestowane obrazy zgodne z ARM w PROMETHEUS_IMAGE i GRAFANA_IMAGE oraz unikatowe GRAFANA_ADMIN_PASSWORD dla pierwszej inicjalizacji. Istniejąca baza Grafany zachowuje dotychczasowe hasło administratora.
services:
prometheus:
image: ${PROMETHEUS_IMAGE:?set tested prom/prometheus release}
network_mode: host
restart: unless-stopped
command:
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.path=/prometheus
- --storage.tsdb.retention.time=7d
- --web.listen-address=127.0.0.1:9090
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- ./alerts.yml:/etc/prometheus/alerts.yml:ro
- prometheus-data:/prometheus
grafana:
image: ${GRAFANA_IMAGE:?set tested grafana/grafana release}
network_mode: host
restart: unless-stopped
environment:
GF_SERVER_HTTP_ADDR: 127.0.0.1
GF_SERVER_HTTP_PORT: "3000"
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:?set initial admin password}
GF_AUTH_ANONYMOUS_ENABLED: "false"
volumes:
- grafana-data:/var/lib/grafana
volumes:
prometheus-data:
grafana-data:Plik prometheus.yml w tym samym katalogu zawiera:
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- /etc/prometheus/alerts.yml
scrape_configs:
- job_name: raspberry_pi
static_configs:
- targets: ["127.0.0.1:9100"]Siedmiodniowa retencja jest przykładem, a nie limitem zajętości dysku. Nazwane wolumeny zachowują dane podczas odtwarzania kontenerów. Wolne miejsce, obciążenie zapisem i kopie zapasowe wymagają osobnego monitorowania. Odczyt co 15 sekund nie zwiększa częstotliwości pomiarów kolektora wykonywanych mniej więcej co minutę.
4. Oddzielenie reguł alertów od powiadomień
Poniższy alerts.yml sprawdza pomiary oraz poprawność zbierania danych. Próg 75 °C jest przykładową wartością operacyjną, a nie uniwersalną granicą sprzętu. Reguła brakujących serii zakłada dokładnie jeden cel w tym zadaniu; wiele hostów wymaga wykrywania braków osobno dla każdego celu.
groups:
- name: raspberry_pi
rules:
- alert: PiExporterDown
expr: up{job="raspberry_pi"} == 0
for: 2m
- alert: PiCollectorStale
expr: time() - rpi_collector_last_success_timestamp_seconds{job="raspberry_pi"} > 180
for: 1m
- alert: PiCollectorMissing
expr: absent(rpi_collector_last_success_timestamp_seconds{job="raspberry_pi"})
for: 2m
- alert: PiTextfileError
expr: node_textfile_scrape_error{job="raspberry_pi"} == 1
for: 1m
- alert: PiTemperatureHigh
expr: rpi_soc_temperature_celsius{job="raspberry_pi"} > 75
for: 5m
- alert: PiCurrentFirmwareWarning
expr: rpi_hardware_flag{job="raspberry_pi",state="current"} == 1docker compose config --quiet sprawdza konfigurację Compose. Narzędzia promtool check config i promtool check rules z wybranego obrazu walidują pliki Prometheusa przed uruchomieniem. docker compose up -d uruchamia zestaw. Reguły pokazują stany oczekujących i aktywnych alertów w Prometheusie; e-mail lub push wymaga osobno skonfigurowanej i przetestowanej integracji z Alertmanagerem. Sam próg panelu Grafany również nie jest regułą powiadomień.
Na komputerze roboczym polecenie ssh -N -L 3000:127.0.0.1:3000 -L 9090:127.0.0.1:9090 ACCOUNT@PI_ADDRESS przekierowuje lokalne porty przeglądarki. Symbole zastępcze oznaczają rzeczywiste konto SSH i adres Pi. Grafana otwiera się pod http://127.0.0.1:3000; źródło Prometheus używa http://127.0.0.1:9090, ponieważ oba kontenery współdzielą sieć hosta.
Przydatne panele i ograniczenia pomiarów
rpi_soc_temperature_celsius: próbkowana temperatura SoC.rpi_hardware_flag{state="current"}: bieżące flagi firmware;state="since_boot"pokazuje odnotowane zdarzenia.100 * rate(node_disk_io_time_seconds_total[5m]): procent czasu zajętości urządzenia z ograniczeniami rozliczania czasu przez jądro. To nie jest zużycie flash ani opóźnienie żądania.100 * node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes: procent dostępnej pamięci RAM, a nie tempo zmian na sekundę.
Właściwy wybór urządzeń zapobiega mylącym porównaniom dysków fizycznych i urządzeń wirtualnych. Ocena zużycia nośnika wymaga obsługiwanych danych SMART/NVMe lub wskaźników producenta; wiele kart SD nie udostępnia użytecznych danych o zużyciu. Sam wysoki czas zajętości nie identyfikuje zużytego kontrolera.
Pytania i odpowiedzi
Jak sprawdzić przepływ danych bez obciążania nośnika?
Pierwszą kontrolę stanowią dostępność eksportera, up, node_textfile_scrape_error, wiek ostatniego sukcesu i logi timera. W oddzielnym środowisku testowym zatrzymany timer powinien wywołać alert starych danych; błędne syntetyczne odpowiedzi poleceń powinny pozostawić ostatni poprawny plik bez zmian. Intensywny test losowego zapisu nie jest do tego potrzebny.
Czy flaga zbyt niskiego napięcia identyfikuje przyczynę?
Nie. Flaga informuje o wykrytym stanie. Zasilacz, przewód, złącza i obciążenie peryferiami wymagają osobnej analizy. Temperatura i dławienie opisują objawy; ani wykres, ani flaga nie gwarantują zapobiegania awarii.
Czy 127.0.0.1 zawsze oznacza hosta?
Nie. Zwykłe kontenery bridge mają oddzielne interfejsy loopback. Przykład jawnie używa sieci hosta. Inna topologia wymaga osiągalnego adresu eksportera i zgodnego adresu źródła danych Grafany.
Źródła
- Dokumentacja Prometheusa
- Dokumentacja Grafany
- vcgencmd: temperatura, bity stanu, napięcie rdzenia i zasilanie Pi 5
- Node Exporter: pomiary hosta i atomowa wymiana pliku
- Prometheus: aktywacja alertów i czas oczekiwania
- Linux: zajętość dysku i pozostałe statystyki wejścia/wyjścia
- Grafana: lokalny adres nasłuchu i zmienne środowiskowe
- PromQL: absent i time
- Prometheus: adres nasłuchu i okres przechowywania
Homelab na Raspberry Pi
- Raspberry Pi 4 i 5: uruchamianie systemu z dysku SSD przez zmianę bootloadera
- UPS dla Raspberry Pi: pięć rozwiązań i kryteria wyboru
- Lokalny CI/CD dla Raspberry Pi: Automatyzacja wdrożeń Docker Compose
- Serwer domowy Docker Compose: Traefik, TLS i monitorowanie aktualizacji
- Jellyfin na Raspberry Pi 5: Direct Play i ograniczenia transkodowania
- Raspberry Pi jako utwardzona brama WireGuard VPN ze Split-Tunnelingiem
- Monitorowanie Raspberry Pi: Prometheus i Grafana