embedded-deck
TEKNİK REHBER LİNUX ARAÇLAR PROMETHEUS 2026

Prometheus & Grafana
Gömülü sistemleri gözlemleyin.

CPU frekansından sıcaklığa, bellek baskısından özel donanım metriklerine — Prometheus pull modeli ve Grafana görselleştirme ile gömülü cihaz filonuzu tek yerden izleyin.

00 Neden metrik izleme?

Gömülü sistemler genellikle uzak konumlarda, müdahale imkânı kısıtlı ortamlarda çalışır. Sorun yaşandıktan sonra log incelemek yerine, sorun oluşmadan önce eşik aşımlarını tespit etmek — gözlemlenebilirlik (observability) — prodüksiyon kalitesinin temel göstergesidir.

Gömülü sistemde monitoring gereksinimleri

Thermal yönetimiSoC sıcaklığı eşiği aşmadan önce uyarı — kritik uygulamalarda thermal throttling öncesi aksiyon
Bellek baskısıOOM killer devreye girmeden önce bellek tükenmesini tespit et — mmzone, slab, fragmentation
Depolama sağlığıeMMC/flash yıpranma, I/O gecikme artışı, filesystem doluluk — veri kaybı önleme
Ağ kalitesiPaket kayıpları, yüksek gecikme, bant genişliği doyması — bağlantı sorunlarının erken tespiti
Uygulama sağlığıProcess çökmesi, restart sayısı, yanıt süresi — servis kalitesi izleme

Gözlemlenebilirlik üçgeni

              Metrics (Prometheus)
                 / sayısal veriler \
                /                   \
               /                     \
       Logs --+---- Observability ----+-- Traces
        (loki)    üçgeni tamamlar    (jaeger/tempo)
    

Bu rehberde odak noktamız metrics — sayısal zaman serisi verisi. Logs ve traces ayrı konulardır; Loki ve Tempo Grafana ekosisteminde bu eksikleri kapatır ancak gömülü sistemlerde genellikle metrics tek başına yeterlidir.

01 Prometheus mimarisi

Prometheus, pull (çekme) tabanlı bir izleme sistemidir. Hedef sistemler HTTP /metrics endpoint'i sunar; Prometheus periyodik olarak bu endpoint'i scrape eder ve TSDB'ye (Time Series Database) yazar.

  ┌──────────────┐     scrape     ┌──────────────────────────┐
  │  node_exporter│◄──────────────│                          │
  │  :9100/metrics│               │   Prometheus Server      │
  └──────────────┘               │                          │
                                  │   ┌──────────────────┐   │
  ┌──────────────┐               │   │   TSDB (chunks)   │   │
  │custom_exporter◄──────────────│   │   /data/chunks_  │   │
  │  :8080/metrics│               │   └──────────────────┘   │
  └──────────────┘               │           │               │
                                  │     PromQL engine         │
  ┌──────────────┐    push        │           │               │
  │  Pushgateway │───────────────►│           │               │
  └──────────────┘               └─────────┬─┘               │
                                            │                 │
                              ┌─────────────▼───────────────┐│
                              │     Alertmanager            ││
                              │  (email/slack/pagerduty)    ││
                              └─────────────────────────────┘│
                                            │
                              ┌─────────────▼───────────────┐
                              │       Grafana               │
                              │  Datasource: Prometheus      │
                              └─────────────────────────────┘
    

prometheus.yml temel yapılandırma

global:
  scrape_interval:     15s   # Her 15 saniyede bir scrape
  evaluation_interval: 15s   # Alert kurallarını değerlendir

alerting:
  alertmanagers:
    - static_configs:
        - targets: ['alertmanager:9093']

rule_files:
  - "alerts/*.yml"

scrape_configs:
  # Prometheus'un kendisini izlemesi
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  # Statik hedef listesi
  - job_name: 'embedded_devices'
    scrape_interval: 30s
    scrape_timeout: 10s
    static_configs:
      - targets:
          - '192.168.1.10:9100'   # cihaz-1
          - '192.168.1.11:9100'   # cihaz-2
          - '192.168.1.12:9100'   # cihaz-3
    relabel_configs:
      - source_labels: [__address__]
        regex: '(.*):.*'
        target_label: instance

  # Dinamik keşif (file-based SD)
  - job_name: 'dynamic_devices'
    file_sd_configs:
      - files: ['/etc/prometheus/targets/*.json']
        refresh_interval: 1m

TSDB saklama ayarları

# Prometheus başlatma parametreleri
/usr/local/bin/prometheus \
  --config.file=/etc/prometheus/prometheus.yml \
  --storage.tsdb.path=/var/lib/prometheus \
  --storage.tsdb.retention.time=30d \     # 30 gün saklama
  --storage.tsdb.retention.size=10GB \    # veya 10GB ile sınırla
  --web.listen-address=0.0.0.0:9090 \
  --web.enable-lifecycle                  # /-/reload endpoint'i

02 node_exporter

node_exporter, Linux sistem metriklerini Prometheus formatında sunan resmi exporter'dır. /proc ve /sys dosya sistemlerinden okuyarak CPU, bellek, disk, ağ ve sıcaklık metriklerini yayınlar.

ARM binary kurulumu (gömülü cihaz)

# ARMv7 (Raspberry Pi 2/3 32-bit)
wget https://github.com/prometheus/node_exporter/releases/download/v1.8.2/\
node_exporter-1.8.2.linux-armv7.tar.gz
tar xf node_exporter-1.8.2.linux-armv7.tar.gz
sudo cp node_exporter-1.8.2.linux-armv7/node_exporter /usr/local/bin/

# ARM64 (Raspberry Pi 4/5, 64-bit OS)
wget https://github.com/prometheus/node_exporter/releases/download/v1.8.2/\
node_exporter-1.8.2.linux-arm64.tar.gz

# systemd servisi
sudo tee /etc/systemd/system/node_exporter.service << 'EOF'
[Unit]
Description=Prometheus Node Exporter
After=network.target

[Service]
Type=simple
User=node_exporter
ExecStart=/usr/local/bin/node_exporter \
    --collector.systemd \
    --collector.processes \
    --collector.hwmon \
    --collector.thermal_zone \
    --collector.cpu.info \
    --web.listen-address=:9100
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

sudo useradd -rs /bin/false node_exporter
sudo systemctl daemon-reload
sudo systemctl enable --now node_exporter

Önemli metrikler

MetrikTipAçıklama
node_cpu_seconds_totalCounterMod bazında CPU zamanı (user/system/idle/iowait)
node_memory_MemAvailable_bytesGaugeKullanılabilir bellek — MemFree + cache/buffer
node_disk_io_time_seconds_totalCounterDisk I/O'da geçen toplam süre — utilization için kullanılır
node_network_receive_bytes_totalCounterArayüz başına alınan byte sayısı
node_thermal_zone_tempGaugeTermal zon sıcaklığı (Celsius × 1000)
node_hwmon_temp_celsiusGaugehwmon sensör sıcaklıkları (CPU, GPU, board)
node_filesystem_avail_bytesGaugeDosya sistemi kullanılabilir alan
node_load1Gauge1 dakikalık yük ortalaması
node_vmstat_oom_killCounterOOM killer tetiklenme sayısı
NOT

Sıcaklık metrikleri için --collector.hwmon ve --collector.thermal_zone collector'larının açık olması gerekir. node_thermal_zone_temp değeri Kelvin × 1000 cinsinden gelir — Celsius'a çevirmek için / 1000 yapmanız gerekir. node_hwmon_temp_celsius ise doğrudan Celsius cinsinden gelir.

03 Custom exporter yazma

node_exporter'ın sunmadığı uygulama düzeyi veya donanıma özgü metrikleri yayınlamak için custom exporter yazılır. Python veya Go tercih edilir; Go binary boyutu nedeniyle gömülü hedefler için Python daha pratiktir.

Python ile custom exporter

pip install prometheus_client
#!/usr/bin/env python3
"""
Özel gömülü sistem exporter'ı
Raspberry Pi ADC sensör değerlerini ve GPIO durumunu izler
"""
import time
import os
from prometheus_client import start_http_server, Gauge, Counter, Histogram

# Metrik tanımlamaları
adc_voltage = Gauge(
    'embedded_adc_voltage_volts',
    'ADC kanalı gerilim değeri',
    ['channel']          # label
)

gpio_state = Gauge(
    'embedded_gpio_state',
    'GPIO pin durumu (0=low, 1=high)',
    ['pin', 'direction']
)

i2c_errors = Counter(
    'embedded_i2c_errors_total',
    'I2C okuma hataları',
    ['bus', 'device']
)

sensor_read_duration = Histogram(
    'embedded_sensor_read_seconds',
    'Sensör okuma süresi',
    ['sensor_type'],
    buckets=[.001, .005, .01, .025, .05, .1, .25, .5, 1.0]
)

def read_adc_channel(channel: int) -> float:
    """
    /sys/bus/iio/devices/iio:device0/in_voltageN_raw oku
    ve voltaja çevir (12-bit ADC, 3.3V ref)
    """
    path = f"/sys/bus/iio/devices/iio:device0/in_voltage{channel}_raw"
    try:
        with open(path) as f:
            raw = int(f.read().strip())
        return raw * 3.3 / 4095.0
    except (FileNotFoundError, ValueError):
        return -1.0

def read_gpio_sysfs(pin: int) -> int:
    """GPIO değerini sysfs'ten oku"""
    try:
        with open(f"/sys/class/gpio/gpio{pin}/value") as f:
            return int(f.read().strip())
    except FileNotFoundError:
        return -1

def collect_metrics():
    """Periyodik metrik toplama"""
    for ch in range(4):
        with sensor_read_duration.labels('adc').time():
            v = read_adc_channel(ch)
        if v >= 0:
            adc_voltage.labels(channel=str(ch)).set(v)

    for pin in [17, 27, 22, 23]:
        state = read_gpio_sysfs(pin)
        if state >= 0:
            gpio_state.labels(pin=str(pin), direction='input').set(state)

if __name__ == '__main__':
    start_http_server(8080)
    print("Exporter başladı: http://0.0.0.0:8080/metrics")
    while True:
        collect_metrics()
        time.sleep(10)  # 10 saniyede bir güncelle

Go ile minimal exporter

package main

import (
    "net/http"
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

var cpuFreqMHz = prometheus.NewGaugeVec(
    prometheus.GaugeOpts{
        Name: "embedded_cpu_freq_mhz",
        Help: "CPU çekirdek frekansı MHz cinsinden",
    },
    []string{"cpu"},
)

func init() {
    prometheus.MustRegister(cpuFreqMHz)
}

func main() {
    http.Handle("/metrics", promhttp.Handler())
    http.ListenAndServe(":9200", nil)
}

04 PromQL sorgu dili

PromQL (Prometheus Query Language), zaman serisi veri üzerinde anlık sorgular ve range sorgular yapmanızı sağlar. Grafana panelleri, alerting kuralları ve API çağrıları PromQL kullanır.

Temel fonksiyonlar

# CPU kullanımı (son 5 dakikanın ortalaması, %)
100 - (avg by (instance) (
    rate(node_cpu_seconds_total{mode="idle"}[5m])
) * 100)

# Belirli instance için:
100 - (avg by (instance) (
    rate(node_cpu_seconds_total{mode="idle", instance="192.168.1.10:9100"}[5m])
) * 100)

# Disk I/O utilization (%):
rate(node_disk_io_time_seconds_total{device="mmcblk0"}[5m]) * 100

# Ağ bant genişliği (Mbps):
rate(node_network_receive_bytes_total{device="eth0"}[1m]) * 8 / 1e6

# Kullanılabilir bellek yüzdesi:
node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100

# Sıcaklık (thermal_zone — Celsius):
node_thermal_zone_temp / 1000

# Dosya sistemi doluluk %:
100 - (node_filesystem_avail_bytes{mountpoint="/"} /
       node_filesystem_size_bytes{mountpoint="/"} * 100)

Aggregation operatörleri

# Tüm cihazların ortalama CPU kullanımı:
avg(100 - rate(node_cpu_seconds_total{mode="idle"}[5m]) * 100)

# En yüksek CPU kullanan 5 cihaz:
topk(5, 100 - avg by (instance) (
    rate(node_cpu_seconds_total{mode="idle"}[5m])
) * 100)

# Bellek ortalaması by job:
avg by (job) (node_memory_MemAvailable_bytes)

# Her instance için maksimum sıcaklık:
max by (instance) (node_hwmon_temp_celsius)

# Toplam ağ trafiği (tüm cihazlar):
sum(rate(node_network_receive_bytes_total[5m]))

# irate: anlık rate (spike tespiti için):
irate(node_cpu_seconds_total{mode="user"}[1m])

# histogram_quantile: P95 yanıt süresi:
histogram_quantile(0.95,
    rate(embedded_sensor_read_seconds_bucket[5m])
)

Alert kuralı örneği

# /etc/prometheus/alerts/embedded.yml
groups:
  - name: embedded_alerts
    rules:
      - alert: HighCPUTemperature
        expr: node_hwmon_temp_celsius > 80
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "Yüksek CPU sıcaklığı {{ $labels.instance }}"
          description: "Sıcaklık {{ $value }}°C — kritik eşik: 85°C"

      - alert: LowDiskSpace
        expr: (node_filesystem_avail_bytes{mountpoint="/"} /
               node_filesystem_size_bytes{mountpoint="/"}) * 100 < 10
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Disk dolmak üzere {{ $labels.instance }}"

      - alert: OOMKillerTriggered
        expr: increase(node_vmstat_oom_kill[1h]) > 0
        labels:
          severity: critical

05 Grafana kurulumu ve dashboard

Grafana, Prometheus TSDB verilerini görselleştirmek için en yaygın kullanılan açık kaynak araçtır. Panel, dashboard, variable ve annotation özellikleri ile esnek izleme arayüzleri oluşturulur.

Docker Compose ile hızlı kurulum

# docker-compose.yml
version: '3.8'
services:
  prometheus:
    image: prom/prometheus:v2.51.0
    ports: ['9090:9090']
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - ./alerts:/etc/prometheus/alerts
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.retention.time=30d'
      - '--web.enable-lifecycle'

  grafana:
    image: grafana/grafana:10.4.0
    ports: ['3000:3000']
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=embedded123
      - GF_USERS_ALLOW_SIGN_UP=false
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning
    depends_on: [prometheus]

  alertmanager:
    image: prom/alertmanager:v0.27.0
    ports: ['9093:9093']
    volumes:
      - ./alertmanager.yml:/etc/alertmanager/alertmanager.yml

volumes:
  prometheus_data:
  grafana_data:

Grafana provisioning ile otomatik datasource

# grafana/provisioning/datasources/prometheus.yml
apiVersion: 1
datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true
    editable: true
    jsonData:
      timeInterval: "15s"
      queryTimeout: "60s"

Dashboard özellikleri

Variable$instance değişkeni ile cihaz seçimi — tek dashboard, çok cihaz
AnnotationDeploy zamanları, alarm olayları dashboard'a işaret olarak eklenir
AlertPanel bazında Grafana alerting — Grafana 8+ ile Unified Alerting
RepeatVariable ile panel çoğaltma — her cihaz için ayrı satır

06 Embedded device dashboard

Gömülü sistem dashboard'u, genel sunucu dashboard'undan farklı metriklere odaklanır: CPU frekans ölçekleme, termal yönetim, bellek baskısı ve özel donanım metrikleri.

Dashboard JSON panel örnekleri

# CPU Frekans Skalama (ölçekleme governors ile)
# Metrik: node_cpu_frequency_hertz (cpufreq collector)
# Panel tipi: Time series
# PromQL:
node_cpu_frequency_hertz{instance="$instance"} / 1e9
# Legend: {{cpu}} GHz

# Thermal Zone Sıcaklığı
# PromQL:
node_thermal_zone_temp{instance="$instance"} / 1000
# Threshold: Yellow=70, Red=80, Unit: Celsius

# Bellek Baskısı (memory pressure)
# PromQL - kswapd aktivitesi:
rate(node_vmstat_pgpgout[5m]) + rate(node_vmstat_pgpgin[5m])

# OOM Kill sayacı (stat panel)
# PromQL:
increase(node_vmstat_oom_kill{instance="$instance"}[24h])

# eMMC I/O gecikme dağılımı
# PromQL:
rate(node_disk_read_time_seconds_total{instance="$instance",device="mmcblk0"}[5m]) /
rate(node_disk_reads_completed_total{instance="$instance",device="mmcblk0"}[5m]) * 1000
# Unit: Milliseconds

Dashboard JSON template (kısaltılmış)

{
  "title": "Embedded Device Overview",
  "uid": "embedded-overview",
  "templating": {
    "list": [{
      "name": "instance",
      "type": "query",
      "datasource": "Prometheus",
      "query": "label_values(node_uname_info, instance)",
      "refresh": 2
    }]
  },
  "panels": [
    {
      "title": "CPU Kullanımı",
      "type": "timeseries",
      "targets": [{
        "expr": "100 - avg by(cpu)(rate(node_cpu_seconds_total{mode='idle',instance='$instance'}[5m]))*100",
        "legendFormat": "{{cpu}}"
      }]
    }
  ]
}

07 collectd entegrasyonu

Bazı gömülü sistemlerde Prometheus exporter çalıştırmak uygun olmayabilir — collectd, onlarca yıllık olgunluğa sahip hafif bir metrik toplama daemonu'dur ve Prometheus ile köprülenir.

write_prometheus plugin

# /etc/collectd/collectd.conf

Hostname "gömülü-cihaz-01"
Interval 30

LoadPlugin cpu
LoadPlugin memory
LoadPlugin interface
LoadPlugin disk
LoadPlugin sensors     # lm-sensors
LoadPlugin thermal
LoadPlugin processes

# Prometheus bridge — /metrics endpoint açar
LoadPlugin write_prometheus
<Plugin write_prometheus>
    Port "9103"
</Plugin>

# CPU detayı
<Plugin cpu>
    ReportByCpu true
    ReportByState true
    ValuesPercentage true
</Plugin>

# Network interface
<Plugin interface>
    Interface "eth0"
    Interface "wlan0"
    IgnoreSelected false
</Plugin>

collectd → Prometheus scrape config

# prometheus.yml
scrape_configs:
  - job_name: 'collectd_bridge'
    static_configs:
      - targets:
          - '192.168.1.10:9103'   # collectd write_prometheus
    metric_relabel_configs:
      # collectd metrik adlarını normalize et
      - source_labels: [__name__]
        regex: 'collectd_(.*)'
        target_label: __name__
        replacement: 'node_${1}'
NOT

collectd ile Prometheus arasında bir diğer yol collectd-exporter köprüsüdür. collectd binary protokolüyle collectd-exporter'a veri gönderir, exporter bunu Prometheus formatına çevirir. Bu yaklaşım write_prometheus plugin'ine göre daha esnek metrik dönüşümü imkânı sunar.

08 Pratik: Raspberry Pi fleet izleme

Birden fazla Raspberry Pi'yi merkezi Prometheus + Grafana ile izleyen eksiksiz bir kurulum.

Hedef mimari

  Raspberry Pi 1 (192.168.1.10) → node_exporter :9100
  Raspberry Pi 2 (192.168.1.11) → node_exporter :9100
  Raspberry Pi 3 (192.168.1.12) → node_exporter :9100 + custom_exporter :8080
        │
        │  HTTP scrape (pull model)
        ▼
  Linux Sunucu (192.168.1.1)
  ├── Prometheus :9090
  ├── Grafana    :3000
  └── Alertmanager :9093 → Email/Slack
    

Raspberry Pi'ye node_exporter kurulumu (Ansible)

# hosts.yml
all:
  hosts:
    pi1: {ansible_host: 192.168.1.10}
    pi2: {ansible_host: 192.168.1.11}
    pi3: {ansible_host: 192.168.1.12}
  vars:
    ansible_user: pi
    ansible_become: yes
# playbook.yml
- hosts: all
  tasks:
    - name: node_exporter ARM64 indir
      get_url:
        url: "https://github.com/prometheus/node_exporter/releases/\
download/v1.8.2/node_exporter-1.8.2.linux-arm64.tar.gz"
        dest: /tmp/node_exporter.tar.gz

    - name: Arşivden çıkar
      unarchive:
        src: /tmp/node_exporter.tar.gz
        dest: /tmp/
        remote_src: yes

    - name: Binary kopyala
      copy:
        src: /tmp/node_exporter-1.8.2.linux-arm64/node_exporter
        dest: /usr/local/bin/node_exporter
        mode: '0755'
        remote_src: yes

    - name: Systemd servisi kur
      copy:
        content: |
          [Unit]
          Description=Node Exporter
          [Service]
          ExecStart=/usr/local/bin/node_exporter \
            --collector.hwmon \
            --collector.thermal_zone \
            --collector.systemd
          Restart=always
          [Install]
          WantedBy=multi-user.target
        dest: /etc/systemd/system/node_exporter.service

    - name: Servisi başlat
      systemd:
        name: node_exporter
        enabled: yes
        state: started
        daemon_reload: yes

Raspberry Pi özel metrikleri

# vcgencmd ile GPU sıcaklığı ve throttle durumu
# /usr/local/bin/rpi_exporter.sh

#!/bin/bash
echo "# HELP rpi_temp_celsius Raspberry Pi SoC sıcaklığı"
echo "# TYPE rpi_temp_celsius gauge"
TEMP=$(vcgencmd measure_temp | grep -oP '\d+\.\d+')
echo "rpi_temp_celsius $TEMP"

echo "# HELP rpi_throttled Raspberry Pi throttling durumu"
echo "# TYPE rpi_throttled gauge"
THROTTLE=$(vcgencmd get_throttled | grep -oP '0x\w+')
echo "rpi_throttled $((THROTTLE))"

echo "# HELP rpi_cpu_freq_hz CPU frekansı"
echo "# TYPE rpi_cpu_freq_hz gauge"
FREQ=$(vcgencmd measure_clock arm | grep -oP '\d+$')
echo "rpi_cpu_freq_hz $FREQ"
# textfile collector ile Prometheus'a sun:
# node_exporter başlatma:
/usr/local/bin/node_exporter \
  --collector.textfile.directory=/var/lib/node_exporter/textfile_collector

# Cron ile her dakika güncelle:
* * * * * root /usr/local/bin/rpi_exporter.sh > \
  /var/lib/node_exporter/textfile_collector/rpi.prom
NOT

Raspberry Pi'nin vcgencmd get_throttled çıktısı hex bit maskesidir. Bit 0: under-voltage, Bit 1: arm frequency capped, Bit 2: currently throttled, Bit 3: soft temperature limit. Grafana'da bu bitleri ayrıştırmak için PromQL'de % 2 ve bitwise operatörler kullanılabilir.