Raspberry Pi sistem istatistiklerini MQTT mesajları olarak yayınlamak, çalıştırdığınız her Pi'nin sağlıklı olup olmadığını tek bakışta görmenin en basit yoludur. Bu rehberde CPU sıcaklığını, yükü, belleği, disk kullanımını, çalışma süresini ve güç kısıtlama (throttling) bayraklarını sabit aralıklarla raporlayan, retained bir durum mesajıyla kendini çevrim içi ilan eden ve last will sayesinde broker'ın onu çevrim dışı olarak işaretlemesine izin veren bir Python servisi yazacaksınız. Aynı betik tek bir Pi'de de elli Pi'de de değişmeden çalışır.
Raspberry Pi'ler Neden MQTT ile İzlenmeli
Sahadaki Pi'ler öngörülebilir şekillerde arızalanır: SD kartlar loglarla dolar, kasalar ısıyı hapseder ve CPU kendini kısıtlar, ucuz güç adaptörleri düşük voltaja yol açar, Wi-Fi kopmaları da cihazı sessizce erişilemez hale getirir. Geleneksel izleme yığınları her makineyi tek tek sorgular; bu da port açmak ya da gelen erişim gerektiren bir ajan çalıştırmak demektir. MQTT bu düzeni tersine çevirir. Her Pi broker'a tek bir giden bağlantı kurar ve kendi verilerini gönderir; böylece NAT arkasında, mobil router'larda ve kontrolünüzde olmayan ağlarda da çalışır.
Broker ayrıca varlık bilgisini (presence) bedavaya verir. Retained bir online durumu ve offline değerinde bir last will ile abone olan her dashboard, her Pi'nin güncel durumunu anında görür ve bir cihaz kaybolduğunda broker bunu günceller. Mekanizma size yabancıysa önce bir MQTT broker'ın ne yaptığını okuyun.
Metrikler ve Kaynakları
| Metrik | Kaynak | Neden önemli |
|---|---|---|
cpu_temp |
/sys/class/thermal/thermal_zone0/temp |
Sıcaklık yükseldikçe kısıtlama başlar |
load1, load5 |
os.getloadavg() |
Çekirdek sayısının üzerinde süren yük birikmiş iş demektir |
cpu_pct |
psutil.cpu_percent() |
Kısa vadeli CPU kullanımı |
mem_pct |
psutil.virtual_memory() |
Uzun süre çalışan servislerdeki bellek sızıntıları |
disk_pct |
psutil.disk_usage("/") |
Dolu bir kök dosya sistemi her şeyi bozar |
uptime_s |
psutil.boot_time() |
Beklenmedik yeniden başlatmalar sıfırlanma olarak görünür |
throttled |
vcgencmd get_throttled |
Düşük voltaj ve termal kısıtlama bayrakları |
wifi_rssi |
/proc/net/wireless |
Zayıf sinyal kopmaları açıklar |
get_throttled değeri bir bit alanıdır. Bit 0 şu anda düşük voltaj olduğunu, bit 2 şu anda kısıtlama yapıldığını, bit 16 ve 18 ise bu durumların açılıştan beri yaşandığını gösterir. Sıfırdan farklı bir değer tek başına alarm sebebidir; çünkü neredeyse her zaman eninde sonunda SD kartı bozacak bir güç kaynağı sorununa işaret eder. Raspberry Pi dokümantasyonu tüm bitleri listeler.
Bir Filo için Topic Tasarımı
Her cihaz için bir alt ağaç kullanın ve cihaz kimliği olarak hostname'i seçin. Ücretsiz genel MQTT broker'ında test ederken, başkalarının verileriyle çakışmaması için rastgele bir segment ekleyin:
connectmq/demo/a9e217/fleet/<hostname>/stats JSON, every 30 s, not retained
connectmq/demo/a9e217/fleet/<hostname>/status "online" / "offline", retained, last will
İzleme istemcisi ardından connectmq/demo/a9e217/fleet/+/status ve connectmq/demo/a9e217/fleet/+/stats topic'lerine abone olur ve yeni Pi'leri ilk yayınlarında otomatik olarak keşfeder.
Bağımlılıkları Kurun
Servisin sistem Python paketlerine bağımlı olmaması için her Pi'de bir sanal ortam oluşturun:
sudo apt update && sudo apt install -y python3-venv
python3 -m venv ~/pistats-env
~/pistats-env/bin/pip install "paho-mqtt>=2.0" psutil
Raspberry Pi Sistem İstatistikleri MQTT Yayıncısı
Bunu ~/pistats.py olarak kaydedin:
#!/usr/bin/env python3
"""Publish Raspberry Pi health metrics over MQTT."""
import json
import os
import socket
import ssl
import subprocess
import time
import paho.mqtt.client as mqtt
import psutil
HOST = os.getenv("MQTT_HOST", "broker.connectmq.com")
PORT = int(os.getenv("MQTT_PORT", "1883"))
USERNAME = os.getenv("MQTT_USERNAME") # üretimde YOUR_USERNAME
PASSWORD = os.getenv("MQTT_PASSWORD") # üretimde YOUR_PASSWORD
HOSTNAME = socket.gethostname()
CLIENT_ID = os.getenv("MQTT_CLIENT_ID", f"pistats-{HOSTNAME}-a9e217")
PREFIX = os.getenv("MQTT_PREFIX", "connectmq/demo/a9e217/fleet")
INTERVAL = int(os.getenv("INTERVAL", "30"))
STATS_TOPIC = f"{PREFIX}/{HOSTNAME}/stats"
STATUS_TOPIC = f"{PREFIX}/{HOSTNAME}/status"
def cpu_temp():
try:
with open("/sys/class/thermal/thermal_zone0/temp") as f:
return round(int(f.read().strip()) / 1000, 1)
except (OSError, ValueError):
return None
def throttled():
try:
out = subprocess.run(["vcgencmd", "get_throttled"],
capture_output=True, text=True, timeout=2).stdout
return int(out.strip().split("=")[1], 16)
except (OSError, IndexError, ValueError, subprocess.TimeoutExpired):
return None
def wifi_rssi():
try:
with open("/proc/net/wireless") as f:
lines = f.readlines()[2:]
for line in lines:
parts = line.split()
if parts and parts[0].startswith("wlan"):
return int(float(parts[3]))
except (OSError, IndexError, ValueError):
pass
return None
def collect():
load1, load5, _ = os.getloadavg()
t = throttled()
return {
"ts": int(time.time()),
"cpu_temp": cpu_temp(),
"cpu_pct": psutil.cpu_percent(interval=None),
"load1": round(load1, 2),
"load5": round(load5, 2),
"mem_pct": psutil.virtual_memory().percent,
"disk_pct": psutil.disk_usage("/").percent,
"uptime_s": int(time.time() - psutil.boot_time()),
"throttled": t,
"undervoltage_now": bool(t & 0x1) if t is not None else None,
"undervoltage_seen": bool(t & 0x10000) if t is not None else None,
"wifi_rssi": wifi_rssi(),
}
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print(f"Connect failed: {reason_code}")
return
print(f"Connected to {HOST}:{PORT} as {CLIENT_ID}")
client.publish(STATUS_TOPIC, "online", qos=1, retain=True)
def on_disconnect(client, userdata, flags, reason_code, properties):
print(f"Disconnected: {reason_code}, paho will reconnect")
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id=CLIENT_ID, protocol=mqtt.MQTTv5)
if USERNAME:
client.username_pw_set(USERNAME, PASSWORD)
if PORT == 8883:
client.tls_set(cert_reqs=ssl.CERT_REQUIRED)
client.will_set(STATUS_TOPIC, "offline", qos=1, retain=True)
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.reconnect_delay_set(min_delay=2, max_delay=60)
client.connect_async(HOST, PORT, keepalive=60)
client.loop_start()
psutil.cpu_percent(interval=None) # sayacı hazırla
try:
while True:
time.sleep(INTERVAL)
if client.is_connected():
client.publish(STATS_TOPIC, json.dumps(collect()), qos=0)
finally:
client.publish(STATUS_TOPIC, "offline", qos=1, retain=True).wait_for_publish(3)
client.disconnect()
client.loop_stop()
Bazı bilinçli tercihler: istatistikler QoS 0 kullanır, çünkü arada bir örneğin kaybolması önemli değildir ve bir sonraki kısa süre içinde gelir. Durum mesajı QoS 1 ve retain kullanır, çünkü varlık bilgisi doğru olmalıdır. connect_async ile loop_start, açılışta ağ henüz hazır olmasa bile betiğin başlamasını sağlar ve paho arka planda yeniden denemeye devam eder. Düzgün bir kapanışta finally bloğu offline mesajını açıkça yayınlar, çünkü last will yalnızca beklenmedik bir bağlantı kopmasında tetiklenir.
Betiği bir kez elle çalıştırın ve çıktıyı başka bir makineden izleyin:
~/pistats-env/bin/python ~/pistats.py
mosquitto_sub -h broker.connectmq.com -p 1883 -i watcher-a9e217 \
-t 'connectmq/demo/a9e217/fleet/#' -v
Pi'nin ağ kablosunu çekin; yaklaşık bir buçuk keepalive aralığı sonra broker onun adına offline yayınlar. İşini yapan şey MQTT last will özelliğidir.
systemd Servisi Olarak Çalıştırın
Kimlik bilgilerini betiğin dışında tutmak için bir ortam dosyası oluşturun:
sudo tee /etc/pistats.env >/dev/null <<'EOF'
MQTT_HOST=broker.connectmq.com
MQTT_PORT=1883
INTERVAL=30
EOF
sudo chmod 600 /etc/pistats.env
sudo tee /etc/systemd/system/pistats.service >/dev/null <<'EOF'
[Unit]
Description=Publish Raspberry Pi stats over MQTT
After=network-online.target
Wants=network-online.target
[Service]
User=pi
EnvironmentFile=/etc/pistats.env
ExecStart=/home/pi/pistats-env/bin/python /home/pi/pistats.py
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now pistats
journalctl -u pistats -f
Kullanıcıyı ve yolları kendi hesabınıza göre ayarlayın. Açılışta çalışan betiklere dair daha fazla kalıp için Python MQTT betiğini açılışta çalıştırma yazısına bakın.
Filoyu Özel Bir Broker'a Taşımak
Sistem istatistikleri hostname, çalışma süresi ve ağ ayrıntılarını açığa çıkarır; bu yüzden gerçek bir filo genel bir broker'a raporlamamalıdır. Bir bulut MQTT broker'ı olan ConnectMQ ile portalda bir broker oluşturur ve kimlik bilgilerinizi alırsınız. Yalnızca ortam dosyasını değiştirin:
MQTT_HOST=broker.connectmq.com
MQTT_PORT=8883
MQTT_USERNAME=YOUR_USERNAME
MQTT_PASSWORD=YOUR_PASSWORD
MQTT_CLIENT_ID=YOUR_CLIENT_ID
MQTT_PREFIX=site1/fleet
ConnectMQ client ID'leri verilen kimlik bilgilerine bağlar; bu nedenle her Pi'nin portaldan alınmış kendi kimlik bilgisi seti olmalıdır. 8883 portu, Pi'nin zaten güvendiği bir Let's Encrypt sertifikasıyla TLS kullanır. Ücretsiz Serverless planı 25 bağlantıya kadar izin verir; bu, oldukça büyük bir ev veya laboratuvar filosunu karşılar. Ücretli Serverless planı ise çok daha ileri gider.
ConnectMQ portalı ek yazılım gerektirmeden izleme tarafında da yardımcı olur. Canlı topic gezgini her Pi'nin son istatistiklerini gösterir; eşik alarm kuralları da cpu_temp gibi bir değerin bir sınırı aşmasını işaretleyebilir ve sonradan incelemek için bir alarm kaydı tutulur.
Anlamlı Alarm Eşikleri Seçmek
Sayı toplamak kolaydır; hangilerinin bildirimi hak ettiğine karar vermek ise filo izlemenin asıl değer kattığı yerdir. Sürekli çalan alarmlar görmezden gelinir; bu yüzden neredeyse her zaman gerçek bir soruna işaret eden kısa bir koşul listesiyle başlayın:
- Açılıştan beri düşük voltaj görülmesi. Sıfırdan farklı her
undervoltage_seendeğeri zayıf bir güç kaynağına veya ince bir USB kablosuna işaret eder. SD kartı bozmadan önce düzeltin. - CPU sıcaklığının yüksek kalması. Güncellemeler sırasındaki kısa sıçramalar normaldir. Dakikalarca kısıtlama noktasına yakın kalan bir sıcaklık; tıkalı bir havalandırma, eksik bir soğutucu veya doğrudan güneş alan bir kasa demektir.
- Yaklaşık yüzde 90'ın üzerinde disk kullanımı. Loglar ve container imajları sessizce büyür. Dolu bir kök dosya sistemi paket güncellemelerini, veritabanlarını ve bazen açılışın kendisini bozar.
- Çalışma süresinin beklenmedik şekilde sıfırlanması.
uptime_splanlı bir yeniden başlatma olmadan düşerse Pi çökmüş ya da gücü kesilmiştir; sebep genellikle güç veya ısıdır. - Birkaç aralıktan uzun süre çevrim dışı kalmak. Kısa bir Wi-Fi kesintisi kimseyi alarma geçirmesin diye retained durumu son istatistik mesajının yaşıyla birleştirin.
Yük ortalaması ve bellek, alarm yerine trend olarak izlendiğinde daha faydalıdır. Günler içinde yavaş bir artış, genellikle sızıntı yapan bir servisi sorun çıkarmadan çok önce ortaya çıkarır.
Verileri Kullanmak
Sayılar akmaya başladığında ne kadar araç istediğinize karar verin:
- Hızlı bakış: ConnectMQ'nun topic gezgini veya herhangi bir masaüstü MQTT istemcisi.
- Duvar ekranı: Raspberry Pi kiosk dashboard'u her Pi için bir kutucuk gösterebilir.
- Geçmiş ve grafikler: Raspberry Pi Grafana ve InfluxDB rehberindeki gibi Telegraf'tan InfluxDB'ye, üzerinde Grafana ile.
Sorun Giderme
| Belirti | Sebep | Çözüm |
|---|---|---|
throttled değeri null |
vcgencmd eksik veya izin yok |
libraspberrypi-bin kurun, kullanıcıyı video grubuna ekleyin |
İlk örnekte cpu_pct hep 0.0 |
Sayaç hazırlanmamış | Betik döngüden önce sayacı hazırlar |
| Durum sürekli online ve offline arasında gidip geliyor | Aynı client ID'ye sahip iki Pi | Her Pi'ye benzersiz bir client ID verin |
Kablo çekildikten sonra offline hiç gelmiyor |
Keepalive zaman aşımı bekleniyor | Keepalive'ın yaklaşık 1,5 katı kadar bekleyin |
Ethernet'te wifi_rssi değeri null |
Kablosuz arayüz yok | Beklenen durum; görmezden gelin veya alanı kaldırın |
ConnectMQ ile Başlayın
Her Pi'nin sağlık durumunu tek bir yerde toplayın: app.connectmq.com adresinden kaydolun, kredi kartı olmadan ücretsiz bir Serverless broker oluşturun ve kimlik bilgilerini /etc/pistats.env dosyasına ekleyin. Pi projelerinin tüm serisi için Raspberry Pi MQTT rehberiyle başlayın.
Sık Sorulan Sorular
Bir Raspberry Pi sistem istatistiklerini ne sıklıkla yayınlamalı?
Sağlık izleme için 30 ila 60 saniyede bir yeterlidir. Daha kısa aralıklar fazla bir şey göstermeden trafiği artırır; çünkü sıcaklık ve disk kullanımı yavaş değişir.
İstatistik yayınlamak Raspberry Pi'yi yavaşlatır mı?
Kayda değer ölçüde hayır. Birkaç dosyayı okuyup 30 saniyede bir küçük bir JSON mesajı göndermek, bir Pi Zero 2 W'de bile ihmal edilebilir bir yüktür.
Bir Pi'nin çevrim dışı olduğunu nasıl anlarım?
Retained durum topic'ine abone olun. Betik offline değerinde bir last will ayarlar; böylece Pi beklenmedik şekilde bağlantıyı kopardığında broker bunu otomatik olarak yayınlar.
GPU sıcaklığı veya özel metrikler ekleyebilir miyim?
Evet, collect() sözlüğüne alanlar ekleyin. Güncel Pi modellerinde CPU ve GPU aynı yonga üzerindedir; dolayısıyla termal bölge değeri zaten ikisini birden yansıtır.



